Newer
Older
/*
* bsspeke.c - BS-SPEKE over Curve25519
*
* Author: Charles V. Wright <cvwright@futo.org>
*
* Copyright (c) 2022 FUTO Holdings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
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
*/
#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[])
{
return 0;
}