Newer
Older
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/*
* bsspeke.c - BS-SPEKE over Curve25519
*
* Author: Charles V. Wright <cvwright@futo.org>
*
* Copyright (c) 2022 FUTO Holdings, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "monocypher.h"
#include "bsspeke.h"
void
bsspeke_client_init(bsspeke_client_ctx *ctx,
const char* client_id, const size_t client_id_len,
const char* server_id, const size_t server_id_len,
const char* password, const size_t password_len
) {
ctx->client_id = (uint8_t *)client_id;
ctx->client_id_len = client_id_len;
ctx->server_id = (uint8_t *)server_id;
ctx->server_id_len = server_id_len;
ctx->password = (uint8_t *)password;
ctx->password_len = password_len;
}
void
bsspeke_client_generate_message1(bsspeke_msg1_t *msg1,
bsspeke_client_ctx *client
) {
// 1. Hash the client's password, client_id, server_id to a point on the curve
uint8_t scalar_hash[32];
uint8_t curve_point[32];
{
crypto_blake2b_ctx bctx;
// Give us a 256 bit (32 byte) hash; Don't use a key
crypto_blake2b_general_init(&bctx, 32, NULL, 0);
// Add the client id, server id, and the password to the hash
crypto_blake2b_update(&bctx,
(const uint8_t *)(client->password),
client->password_len);
crypto_blake2b_update(&bctx,
(const uint8_t *)(client->client_id),
client->client_id_len);
crypto_blake2b_update(&bctx,
(const uint8_t *)(client->server_id),
client->server_id_len);
// Write the digest value into `scalar_hash`
crypto_blake2b_final(&bctx, scalar_hash);
}
// Now use Elligator to map our scalar hash to a point on the curve
crypto_hidden_to_curve(curve_point, scalar_hash);
// 2. Generate random r
// * Actually generate 1/r first, and clamp() it
// That way we know it will always lead us back to a point on the curve
// * Then use the inverse of 1/r as `r`
// FIXME: On second thought, monocypher seems to handle all of this complexity for us. Let's see what happens if we just do things the straightforward way for now...
arc4random_buf(client->r, 32);
crypto_x25519_clamp(client->r);
// 3. Multiply our curve point by r
crypto_x25519_scalarmult(msg1->blind, client->r, curve_point, 256);
return;
}
void
bsspeke_server_init(bsspeke_server_ctx *ctx,
const char* server_id
) {
ctx->server_id = server_id;
}
void
bsspeke_server_generate_message2(bsspeke_msg2_t *msg2,
const bsspeke_msg1_t *msg1,
const uint8_t *salt, const size_t salt_len,
uint8_t P[32], uint8_t V[32],
uint32_t phf_blocks,
uint32_t phf_iterations,
bsspeke_server_ctx *server_ctx
) {
// Hash the salt
uint8_t H_salt[32];
crypto_blake2b_general(H_salt, 32, NULL, 0, salt, salt_len);
// Use clamp() to ensure we stay on the curve in the multiply below
crypto_x25519_clamp(H_salt);
// Multiply H(salt) by msg1->blind, save into msg2->blind_salt
crypto_x25519_scalarmult(msg2->blind_salt, H_salt, msg1->blind, 256);
// Generate random ephemeral private key b, save it in ctx->b
arc4random_buf(server_ctx->b, 32);
crypto_x25519_clamp(server_ctx->b);
// Compute public key B = b * P, save it in msg2->B
crypto_x25519_scalarmult(server_ctx->B, server_ctx->b, P, 256);
// Copy the public key into the outgoing message as well
memcpy(msg2->B, server_ctx->B, 32);
// Copy the PHF params too
msg2->phf_blocks = phf_blocks;
msg2->phf_iterations = phf_iterations;
return;
}
int
bsspeke_client_generate_message3(bsspeke_msg3_t *msg3,
const bsspeke_msg2_t *msg2,
bsspeke_client_ctx *client_ctx
) {
// Sanity checks first, before we do any work
if( msg2->phf_blocks < BSSPEKE_MIN_PHF_BLOCKS ) {
return -1;
}
if( msg2->phf_iterations < BSSPEKE_MIN_PHF_ITERATIONS ) {
return -1;
}
uint8_t oblivious_salt[32];
// Multiply the blinded salt by 1/r to get the oblivious salt
// Here we rely on Monocypher to do the heavy lifting for us
crypto_x25519_inverse(oblivious_salt, client_ctx->r, msg2->blind_salt);
// Hash the oblivious salt together with the id's to create the salt for the PHF
uint8_t password_hash[64];
uint8_t phf_salt[32];
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, oblivious_salt, 32);
crypto_blake2b_update(&hash_ctx,
client_ctx->client_id,
client_ctx->client_id_len);
crypto_blake2b_update(&hash_ctx,
client_ctx->server_id,
client_ctx->server_id_len);
crypto_blake2b_final(&hash_ctx, phf_salt);
}
void *work_area;
if ((work_area = malloc(msg2->phf_blocks * 1024)) == NULL) {
return -1;
}
crypto_argon2i(password_hash, 64, work_area,
msg2->phf_blocks, msg2->phf_iterations,
client_ctx->password, client_ctx->password_len,
phf_salt, 32);
free(work_area);
// p || v = pwKdf(password, BlindSalt, idC, idS, settings)
uint8_t *p = &(password_hash[0]);
uint8_t *v = &(password_hash[32]);
// Hash p onto the curve to get this user's base point P
uint8_t P[32];
crypto_hidden_to_curve(P, p);
// Generate a random ephemeral private key a, store it in ctx->a
arc4random_buf(client_ctx->a, 32);
crypto_x25519_clamp(client_ctx->a);
// Generate the ephemeral public key A = a * P, store it in msg3->A
crypto_x25519_scalarmult(msg3->A, client_ctx->a, P, 256);
// Compute the two Diffie-Hellman shared secrets
// DH shared secret from a * B
uint8_t a_B[32];
crypto_x25519(a_B, client_ctx->a, msg2->B);
// DH shared secret from v * B
uint8_t v_B[32];
crypto_x25519(v_B, v, msg2->B);
// Hash everything we know so far to generate our key, save it in ctx->K_c
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx,
client_ctx->client_id,
client_ctx->client_id_len);
crypto_blake2b_update(&hash_ctx,
client_ctx->server_id,
client_ctx->server_id_len);
crypto_blake2b_update(&hash_ctx, msg3->A, 32);
crypto_blake2b_update(&hash_ctx, msg2->B, 32);
crypto_blake2b_update(&hash_ctx, a_B, 32);
crypto_blake2b_update(&hash_ctx, v_B, 32);
crypto_blake2b_final(&hash_ctx, client_ctx->K_c);
}
// Hash k and the client modifier to get our verifier, save it in msg3->client_verifier
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, client_ctx->K_c, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_CLIENT_MODIFIER,
BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, msg3->client_verifier);
}
return 0;
}
int
bsspeke_server_generate_message4(bsspeke_msg4_t *msg4,
const bsspeke_msg3_t *msg3,
bsspeke_server_ctx *server_ctx
) {
// Compute the two Diffie-Hellman shared secrets
// DH shared secret from b * A
uint8_t b_A[32];
crypto_x25519(b_A, server_ctx->b, msg3->A);
// DH shared secret from b * V
uint8_t b_V[32];
crypto_x25519(b_V, server_ctx->b, server_ctx->V);
// Hash everything we've learned so far to generate k, save it in ctx->k
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx,
server_ctx->client_id,
server_ctx->client_id_len);
crypto_blake2b_update(&hash_ctx,
server_ctx->server_id,
server_ctx->server_id_len);
crypto_blake2b_update(&hash_ctx, msg3->A, 32);
crypto_blake2b_update(&hash_ctx, server_ctx->B, 32);
crypto_blake2b_update(&hash_ctx, b_A, 32);
crypto_blake2b_update(&hash_ctx, b_V, 32);
crypto_blake2b_final(&hash_ctx, server_ctx->K_s);
}
// Check that the client's hash is correct
// Compute H( k || VERIFY_CLIENT_MODIFIER )
uint8_t my_client_verifier[32];
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, server_ctx->K_s, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_CLIENT_MODIFIER,
BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, my_client_verifier);
}
// Compare vs msg3->client_verifier
if( crypto_verify32(msg3->client_verifier, my_client_verifier) != 0 ) {
return -1;
}
// Compute our own verifier H( k || VERIFY_SERVER_MODIFIER ), save it in msg4->server_verifier
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, server_ctx->K_s, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_SERVER_MODIFIER,
BSSPEKE_VERIFY_SERVER_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, msg4->server_verifier);
}
// If we made it this far, return success
return 0;
}
int
bsspeke_client_verify_message4(const bsspeke_msg4_t *msg4,
const bsspeke_client_ctx *client_ctx
) {
// Compute our own version of the server's verifier hash
uint8_t my_server_verifier[32];
{
crypto_blake2b_ctx hash_ctx;
crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
crypto_blake2b_update(&hash_ctx, client_ctx->K_c, 32);
crypto_blake2b_update(&hash_ctx,
BSSPEKE_VERIFY_SERVER_MODIFIER,
BSSPEKE_VERIFY_SERVER_MODIFIER_LEN);
crypto_blake2b_final(&hash_ctx, my_server_verifier);
}
// If the hashes don't match, return failure
if( crypto_verify32(msg4->server_verifier, my_server_verifier) != 0 ) {
return -1;
}
// Otherwise, return success
return 0;
}
int main(int argc, char *argv[])
{
// Before execution of the protocol
// Both have:
// idS = server identity
char *server_id;
// Client has:
// idC = client identity
char *client_id;
char *password;
// Server has these for "idC":
// salt
// settings
// P = hashToPoint(p)
// V = v * P
uint8_t salt[32];
uint8_t P[32]; // curve point
uint8_t V[32]; // curve point
// Step 1: Client hashes password, maps to a point on the curve, blinds with a random value
// C: r = random()
// C: R = r * hashToPoint(H(password, idC, idS))
// C->S: idC, R
uint8_t r[32]; // scalar
uint8_t R[32]; // curve point
// Step 2: Server generates response with blind salt
// S: b = random()
// S: B = b * P
// S: R' = H(salt) * R
// C<-S: B, R', settings
uint8_t b[32]; // scalar
uint8_t B[32]; // curve point
uint8_t R_prime[32]; // curve point
// Step 3:
// C: BlindSalt = (1/r) * R'
// C: p || v = pwKdf(password, BlindSalt, idC, idS, settings)
// C: P = hashToPoint(p)
// C: a = random()
// C: A = a * P
// C: K_c = H(idC, idS, A, B, a * B, v * B)
// C: verifierC = H(K_c, verifyCModifier)
// C->S: A, verifierC[, encryptedDataC]
uint8_t blind_salt[32]; // curve point
uint8_t password_hash[64]; // hash
uint8_t client_P[32]; // curve point
uint8_t a[32]; // scalar
uint8_t A[32]; // curve point
uint8_t K_c[32]; // hash
uint8_t verifierC[32]; // hash
// Step 4:
// S: K_s = H(idC, idS, A, B, b * A, b * V)
// S: Checks verifierC == H(K_s, verifyCModifier)
// S: verifierS = H(K_s, verifySModifier)
// C<-S: verifierS[, encryptedDataS]
uint8_t K_s[32]; // hash
uint8_t server_hash[32]; // hash -- should match verifierC
// Step 5:
// C: Checks verifierS == H(K_c, verifySModifier)
uint8_t client_hash[32]; // hash -- should match verifierS
return 0;
}