-
Notifications
You must be signed in to change notification settings - Fork 12
/
index.ts
172 lines (145 loc) · 7.17 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import { CryptoNoble } from '../../src/cryptoNoble.js'
// You may want to do this when 3rd party dependencies use voprf-ts
// import { Oprf as OprfCore } from '@cloudflare/voprf-ts'
// OprfCore.Crypto = CryptoNoble
import { webcrypto } from 'node:crypto'
import { Oprf, type OprfApi, type SuiteID } from '../../src/facade/index.js'
export async function facadeOprfExample(Oprf: OprfApi, suite: SuiteID = Oprf.Suite.P521_SHA512) {
// Setup: Create client and server.
const mode = Oprf.makeMode({
mode: Oprf.Mode.OPRF,
suite
})
const privateKey = await mode.keys.randomPrivate()
const server = mode.makeServer(privateKey)
const client = mode.makeClient()
// Client Server
// ====================================================
// Step 1: The client prepares arbitrary input that will be evaluated by the
// server, the blinding method produces an evaluation request, and some
// finalization data to be used later. Then, the client sends the evaluation
// request to the server.
//
// Client
// blind, blindedElement = Blind(input)
const input = 'This is the client input'
const inputBytes = new TextEncoder().encode(input)
const [finData, evalReq] = await client.blind([inputBytes])
// evalReq
// ------------------>>
// Server
// Step 2: Once the server received the evaluation request, it responds to
// the client with an evaluation.
//
// evaluation = BlindEvaluate(evalReq, info*)
const evaluation = await server.blindEvaluate(evalReq)
// evaluation.proof <- does not have member
// evaluation
// <<------------------
//
// Client
// Step 3: Finally, the client can produce the output of the OPRF protocol
// using the server's evaluation and the finalization data from the first
// step. If the mode is verifiable, this step allows the client to check the
// proof that the server used the expected private key for the evaluation.
//
// output = Finalize(finData, evaluation, info*)
const [output] = await client.finalize(finData, evaluation)
// Step 4: redemption song!
const verified = await server.verifyFinalize(inputBytes, output)
console.log(`Example OPRF - SuiteID: ${mode.suite}`)
console.log(`CryptoProvider: ${mode.crypto.id}`)
console.log(`input (${input.length} bytes): ${input}`)
console.log(`output (${output.length} bytes): ${Buffer.from(output).toString('hex')}`)
console.log(`verified: ${verified}\n`)
}
export async function facadePoprfExample(Oprf: OprfApi, suite: SuiteID = Oprf.Suite.P256_SHA256) {
// Setup: Create client and server.
const mode = Oprf.makeMode({
mode: Oprf.Mode.POPRF,
suite
})
const { privateKey, publicKey } = await mode.keys.generatePair()
const server = mode.makeServer(privateKey)
const client = mode.makeClient(publicKey)
// Client Server
// ====================================================
// Step 1: The client prepares arbitrary input that will be evaluated by the
// server, the blinding method produces an evaluation request, and some
// finalization data to be used later. Then, the client sends the evaluation
// request to the server.
const input = 'This is the client input'
const info = 'Shared info between server and client'
const inputBytes = new TextEncoder().encode(input)
const infoBytes = new TextEncoder().encode(info)
const [finData, evalReq] = await client.blind([inputBytes])
// Step 2: Once the server received the evaluation request, it responds to
// the client with an evaluation.
const evaluation = await server.blindEvaluate(evalReq, infoBytes)
// Step 3: Finally, the client can produce the output of the OPRF protocol
// using the server's evaluation and the finalization data from the first
// step. If the mode is verifiable, this step allows the client to check the
// proof that the server used the expected private key for the evaluation.
const [output] = await client.finalize(finData, evaluation, infoBytes)
// Step 4: redemption song!
const verified = await server.verifyFinalize(inputBytes, output, infoBytes)
console.log(`Example POPRF - SuiteID: ${mode.suite}`)
console.log(`CryptoProvider: ${mode.crypto.id}`)
console.log(`input (${input.length} bytes): ${input}`)
console.log(`output (${output.length} bytes): ${Buffer.from(output).toString('hex')}`)
console.log(`info (${info.length} bytes): ${info}`)
console.log(`verified: ${verified}\n`)
}
export async function facadeVoprfExample(Oprf: OprfApi, suite: SuiteID = Oprf.Suite.P384_SHA384) {
// Setup: Create client and server.
const mode = Oprf.makeMode({
mode: Oprf.Mode.VOPRF,
suite
})
const { privateKey, publicKey } = await mode.keys.generatePair()
const server = mode.makeServer(privateKey)
const client = mode.makeClient(publicKey)
// Client Server
// ====================================================
// Step 1: The client prepares arbitrary input that will be evaluated by the
// server, the blinding method produces an evaluation request, and some
// finalization data to be used later. Then, the client sends the evaluation
// request to the server.
const input = 'This is the client input'
const inputBytes = new TextEncoder().encode(input)
const [finData, evalReq] = await client.blind([inputBytes])
// Step 2: Once the server received the evaluation request, it responds to
// the client with an evaluation.
const evaluation = await server.blindEvaluate(evalReq)
// Step 3: Finally, the client can produce the output of the OPRF protocol
// using the server's evaluation and the finalization data from the first
// step. If the mode is verifiable, this step allows the client to check the
// proof that the server used the expected private key for the evaluation.
const [output] = await client.finalize(finData, evaluation)
// Step 4: redemption song!
const verified = await server.verifyFinalize(inputBytes, output)
console.log(`Example VOPRF - SuiteID: ${mode.suite}`)
console.log(`CryptoProvider: ${mode.crypto.id}`)
console.log(`input (${input.length} bytes): ${input}`)
console.log(`output (${output.length} bytes): ${Buffer.from(output).toString('hex')}`)
console.log(`verified: ${verified}\n`)
}
async function main() {
try {
if (typeof crypto === 'undefined') {
Object.assign(global, { crypto: webcrypto })
}
await facadeOprfExample(Oprf)
await facadeVoprfExample(Oprf)
// This suite requires the noble crypto provider as sjcl doesn't support
// ristretto.
const OprfNoble = Oprf.withConfig({ crypto: CryptoNoble })
await facadePoprfExample(OprfNoble, Oprf.Suite.RISTRETTO255_SHA512)
} catch (_e: unknown) {
const e = _e as Error
console.log(`Error: ${e.message}`)
console.log(`Stack: ${e.stack}`)
process.exit(1)
}
}
void main()