Coverage Report

Created: 2025-12-31 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl36/crypto/ec/curve448/eddsa.c
Line
Count
Source
1
/*
2
 * Copyright 2017-2024 The OpenSSL Project Authors. All Rights Reserved.
3
 * Copyright 2015-2016 Cryptography Research, Inc.
4
 *
5
 * Licensed under the Apache License 2.0 (the "License").  You may not use
6
 * this file except in compliance with the License.  You can obtain a copy
7
 * in the file LICENSE in the source distribution or at
8
 * https://www.openssl.org/source/license.html
9
 *
10
 * Originally written by Mike Hamburg
11
 */
12
#include <string.h>
13
#include <openssl/crypto.h>
14
#include <openssl/evp.h>
15
#include "crypto/ecx.h"
16
#include "curve448_local.h"
17
#include "word.h"
18
#include "ed448.h"
19
#include "internal/numbers.h"
20
21
24
#define COFACTOR 4
22
23
static c448_error_t oneshot_hash(OSSL_LIB_CTX *ctx, uint8_t *out, size_t outlen,
24
    const uint8_t *in, size_t inlen,
25
    const char *propq)
26
24
{
27
24
    EVP_MD_CTX *hashctx = EVP_MD_CTX_new();
28
24
    EVP_MD *shake256 = NULL;
29
24
    c448_error_t ret = C448_FAILURE;
30
31
24
    if (hashctx == NULL)
32
0
        return C448_FAILURE;
33
34
24
    shake256 = EVP_MD_fetch(ctx, "SHAKE256", propq);
35
24
    if (shake256 == NULL)
36
0
        goto err;
37
38
24
    if (!EVP_DigestInit_ex(hashctx, shake256, NULL)
39
24
        || !EVP_DigestUpdate(hashctx, in, inlen)
40
24
        || !EVP_DigestFinalXOF(hashctx, out, outlen))
41
0
        goto err;
42
43
24
    ret = C448_SUCCESS;
44
24
err:
45
24
    EVP_MD_CTX_free(hashctx);
46
24
    EVP_MD_free(shake256);
47
24
    return ret;
48
24
}
49
50
static void clamp(uint8_t secret_scalar_ser[EDDSA_448_PRIVATE_BYTES])
51
24
{
52
24
    secret_scalar_ser[0] &= -COFACTOR;
53
24
    secret_scalar_ser[EDDSA_448_PRIVATE_BYTES - 1] = 0;
54
24
    secret_scalar_ser[EDDSA_448_PRIVATE_BYTES - 2] |= 0x80;
55
24
}
56
57
static c448_error_t hash_init_with_dom(OSSL_LIB_CTX *ctx, EVP_MD_CTX *hashctx,
58
    uint8_t prehashed,
59
    uint8_t for_prehash,
60
    const uint8_t *context,
61
    size_t context_len,
62
    const char *propq)
63
36
{
64
    /* ASCII: "SigEd448", in hex for EBCDIC compatibility */
65
36
    const char dom_s[] = "\x53\x69\x67\x45\x64\x34\x34\x38";
66
36
    uint8_t dom[2];
67
36
    EVP_MD *shake256 = NULL;
68
69
36
    if (context_len > UINT8_MAX)
70
0
        return C448_FAILURE;
71
72
36
    dom[0] = (uint8_t)(2 - (prehashed == 0 ? 1 : 0)
73
36
        - (for_prehash == 0 ? 1 : 0));
74
36
    dom[1] = (uint8_t)context_len;
75
76
36
    shake256 = EVP_MD_fetch(ctx, "SHAKE256", propq);
77
36
    if (shake256 == NULL)
78
0
        return C448_FAILURE;
79
80
36
    if (!EVP_DigestInit_ex(hashctx, shake256, NULL)
81
36
        || !EVP_DigestUpdate(hashctx, dom_s, sizeof(dom_s) - 1)
82
36
        || !EVP_DigestUpdate(hashctx, dom, sizeof(dom))
83
36
        || !EVP_DigestUpdate(hashctx, context, context_len)) {
84
0
        EVP_MD_free(shake256);
85
0
        return C448_FAILURE;
86
0
    }
87
88
36
    EVP_MD_free(shake256);
89
36
    return C448_SUCCESS;
90
36
}
91
92
/* In this file because it uses the hash */
93
c448_error_t
94
ossl_c448_ed448_convert_private_key_to_x448(
95
    OSSL_LIB_CTX *ctx,
96
    uint8_t x[X448_PRIVATE_BYTES],
97
    const uint8_t ed[EDDSA_448_PRIVATE_BYTES],
98
    const char *propq)
99
0
{
100
    /* pass the private key through oneshot_hash function */
101
    /* and keep the first X448_PRIVATE_BYTES bytes */
102
0
    return oneshot_hash(ctx, x, X448_PRIVATE_BYTES, ed,
103
0
        EDDSA_448_PRIVATE_BYTES, propq);
104
0
}
105
106
c448_error_t
107
ossl_c448_ed448_derive_public_key(
108
    OSSL_LIB_CTX *ctx,
109
    uint8_t pubkey[EDDSA_448_PUBLIC_BYTES],
110
    const uint8_t privkey[EDDSA_448_PRIVATE_BYTES],
111
    const char *propq)
112
24
{
113
    /* only this much used for keygen */
114
24
    uint8_t secret_scalar_ser[EDDSA_448_PRIVATE_BYTES];
115
24
    curve448_scalar_t secret_scalar;
116
24
    unsigned int c;
117
24
    curve448_point_t p;
118
119
24
    if (!oneshot_hash(ctx, secret_scalar_ser, sizeof(secret_scalar_ser),
120
24
            privkey,
121
24
            EDDSA_448_PRIVATE_BYTES,
122
24
            propq))
123
0
        return C448_FAILURE;
124
125
24
    clamp(secret_scalar_ser);
126
127
24
    ossl_curve448_scalar_decode_long(secret_scalar, secret_scalar_ser,
128
24
        sizeof(secret_scalar_ser));
129
130
    /*
131
     * Since we are going to mul_by_cofactor during encoding, divide by it
132
     * here. However, the EdDSA base point is not the same as the decaf base
133
     * point if the sigma isogeny is in use: the EdDSA base point is on
134
     * Etwist_d/(1-d) and the decaf base point is on Etwist_d, and when
135
     * converted it effectively picks up a factor of 2 from the isogenies.  So
136
     * we might start at 2 instead of 1.
137
     */
138
72
    for (c = 1; c < C448_EDDSA_ENCODE_RATIO; c <<= 1)
139
48
        ossl_curve448_scalar_halve(secret_scalar, secret_scalar);
140
141
24
    ossl_curve448_precomputed_scalarmul(p, ossl_curve448_precomputed_base,
142
24
        secret_scalar);
143
144
24
    ossl_curve448_point_mul_by_ratio_and_encode_like_eddsa(pubkey, p);
145
146
    /* Cleanup */
147
24
    ossl_curve448_scalar_destroy(secret_scalar);
148
24
    ossl_curve448_point_destroy(p);
149
24
    OPENSSL_cleanse(secret_scalar_ser, sizeof(secret_scalar_ser));
150
151
24
    return C448_SUCCESS;
152
24
}
153
154
c448_error_t
155
ossl_c448_ed448_sign(OSSL_LIB_CTX *ctx,
156
    uint8_t signature[EDDSA_448_SIGNATURE_BYTES],
157
    const uint8_t privkey[EDDSA_448_PRIVATE_BYTES],
158
    const uint8_t pubkey[EDDSA_448_PUBLIC_BYTES],
159
    const uint8_t *message, size_t message_len,
160
    uint8_t prehashed, const uint8_t *context,
161
    size_t context_len, const char *propq)
162
0
{
163
0
    curve448_scalar_t secret_scalar;
164
0
    EVP_MD_CTX *hashctx = EVP_MD_CTX_new();
165
0
    c448_error_t ret = C448_FAILURE;
166
0
    curve448_scalar_t nonce_scalar;
167
0
    uint8_t nonce_point[EDDSA_448_PUBLIC_BYTES] = { 0 };
168
0
    unsigned int c;
169
0
    curve448_scalar_t challenge_scalar;
170
171
0
    if (hashctx == NULL)
172
0
        return C448_FAILURE;
173
174
0
    {
175
        /*
176
         * Schedule the secret key, First EDDSA_448_PRIVATE_BYTES is serialized
177
         * secret scalar,next EDDSA_448_PRIVATE_BYTES bytes is the seed.
178
         */
179
0
        uint8_t expanded[EDDSA_448_PRIVATE_BYTES * 2];
180
181
0
        if (!oneshot_hash(ctx, expanded, sizeof(expanded), privkey,
182
0
                EDDSA_448_PRIVATE_BYTES, propq))
183
0
            goto err;
184
0
        clamp(expanded);
185
0
        ossl_curve448_scalar_decode_long(secret_scalar, expanded,
186
0
            EDDSA_448_PRIVATE_BYTES);
187
188
        /* Hash to create the nonce */
189
0
        if (!hash_init_with_dom(ctx, hashctx, prehashed, 0, context,
190
0
                context_len, propq)
191
0
            || !EVP_DigestUpdate(hashctx,
192
0
                expanded + EDDSA_448_PRIVATE_BYTES,
193
0
                EDDSA_448_PRIVATE_BYTES)
194
0
            || !EVP_DigestUpdate(hashctx, message, message_len)) {
195
0
            OPENSSL_cleanse(expanded, sizeof(expanded));
196
0
            goto err;
197
0
        }
198
0
        OPENSSL_cleanse(expanded, sizeof(expanded));
199
0
    }
200
201
    /* Decode the nonce */
202
0
    {
203
0
        uint8_t nonce[2 * EDDSA_448_PRIVATE_BYTES];
204
205
0
        if (!EVP_DigestFinalXOF(hashctx, nonce, sizeof(nonce)))
206
0
            goto err;
207
0
        ossl_curve448_scalar_decode_long(nonce_scalar, nonce, sizeof(nonce));
208
0
        OPENSSL_cleanse(nonce, sizeof(nonce));
209
0
    }
210
211
0
    {
212
        /* Scalarmul to create the nonce-point */
213
0
        curve448_scalar_t nonce_scalar_2;
214
0
        curve448_point_t p;
215
216
0
        ossl_curve448_scalar_halve(nonce_scalar_2, nonce_scalar);
217
0
        for (c = 2; c < C448_EDDSA_ENCODE_RATIO; c <<= 1)
218
0
            ossl_curve448_scalar_halve(nonce_scalar_2, nonce_scalar_2);
219
220
0
        ossl_curve448_precomputed_scalarmul(p, ossl_curve448_precomputed_base,
221
0
            nonce_scalar_2);
222
0
        ossl_curve448_point_mul_by_ratio_and_encode_like_eddsa(nonce_point, p);
223
0
        ossl_curve448_point_destroy(p);
224
0
        ossl_curve448_scalar_destroy(nonce_scalar_2);
225
0
    }
226
227
0
    {
228
0
        uint8_t challenge[2 * EDDSA_448_PRIVATE_BYTES];
229
230
        /* Compute the challenge */
231
0
        if (!hash_init_with_dom(ctx, hashctx, prehashed, 0, context, context_len,
232
0
                propq)
233
0
            || !EVP_DigestUpdate(hashctx, nonce_point, sizeof(nonce_point))
234
0
            || !EVP_DigestUpdate(hashctx, pubkey, EDDSA_448_PUBLIC_BYTES)
235
0
            || !EVP_DigestUpdate(hashctx, message, message_len)
236
0
            || !EVP_DigestFinalXOF(hashctx, challenge, sizeof(challenge)))
237
0
            goto err;
238
239
0
        ossl_curve448_scalar_decode_long(challenge_scalar, challenge,
240
0
            sizeof(challenge));
241
0
        OPENSSL_cleanse(challenge, sizeof(challenge));
242
0
    }
243
244
0
    ossl_curve448_scalar_mul(challenge_scalar, challenge_scalar, secret_scalar);
245
0
    ossl_curve448_scalar_add(challenge_scalar, challenge_scalar, nonce_scalar);
246
247
0
    OPENSSL_cleanse(signature, EDDSA_448_SIGNATURE_BYTES);
248
0
    memcpy(signature, nonce_point, sizeof(nonce_point));
249
0
    ossl_curve448_scalar_encode(&signature[EDDSA_448_PUBLIC_BYTES],
250
0
        challenge_scalar);
251
252
0
    ossl_curve448_scalar_destroy(secret_scalar);
253
0
    ossl_curve448_scalar_destroy(nonce_scalar);
254
0
    ossl_curve448_scalar_destroy(challenge_scalar);
255
256
0
    ret = C448_SUCCESS;
257
0
err:
258
0
    EVP_MD_CTX_free(hashctx);
259
0
    return ret;
260
0
}
261
262
c448_error_t
263
ossl_c448_ed448_sign_prehash(
264
    OSSL_LIB_CTX *ctx,
265
    uint8_t signature[EDDSA_448_SIGNATURE_BYTES],
266
    const uint8_t privkey[EDDSA_448_PRIVATE_BYTES],
267
    const uint8_t pubkey[EDDSA_448_PUBLIC_BYTES],
268
    const uint8_t hash[64], const uint8_t *context,
269
    size_t context_len, const char *propq)
270
0
{
271
0
    return ossl_c448_ed448_sign(ctx, signature, privkey, pubkey, hash, 64, 1,
272
0
        context, context_len, propq);
273
0
}
274
275
static c448_error_t
276
c448_ed448_pubkey_verify(const uint8_t *pub, size_t pub_len)
277
0
{
278
0
    curve448_point_t pk_point;
279
280
0
    if (pub_len != EDDSA_448_PUBLIC_BYTES)
281
0
        return C448_FAILURE;
282
283
0
    return ossl_curve448_point_decode_like_eddsa_and_mul_by_ratio(pk_point, pub);
284
0
}
285
286
c448_error_t
287
ossl_c448_ed448_verify(
288
    OSSL_LIB_CTX *ctx,
289
    const uint8_t signature[EDDSA_448_SIGNATURE_BYTES],
290
    const uint8_t pubkey[EDDSA_448_PUBLIC_BYTES],
291
    const uint8_t *message, size_t message_len,
292
    uint8_t prehashed, const uint8_t *context,
293
    uint8_t context_len, const char *propq)
294
45
{
295
45
    curve448_point_t pk_point, r_point;
296
45
    c448_error_t error;
297
45
    curve448_scalar_t challenge_scalar;
298
45
    curve448_scalar_t response_scalar;
299
    /* Order in little endian format */
300
45
    static const uint8_t order[] = {
301
45
        0xF3, 0x44, 0x58, 0xAB, 0x92, 0xC2, 0x78, 0x23, 0x55, 0x8F, 0xC5, 0x8D,
302
45
        0x72, 0xC2, 0x6C, 0x21, 0x90, 0x36, 0xD6, 0xAE, 0x49, 0xDB, 0x4E, 0xC4,
303
45
        0xE9, 0x23, 0xCA, 0x7C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
304
45
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
305
45
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00
306
45
    };
307
45
    int i;
308
309
    /*
310
     * Check that s (second 57 bytes of the sig) is less than the order. Both
311
     * s and the order are in little-endian format. This can be done in
312
     * variable time, since if this is not the case the signature if publicly
313
     * invalid.
314
     */
315
126
    for (i = EDDSA_448_PUBLIC_BYTES - 1; i >= 0; i--) {
316
126
        if (signature[i + EDDSA_448_PUBLIC_BYTES] > order[i])
317
4
            return C448_FAILURE;
318
122
        if (signature[i + EDDSA_448_PUBLIC_BYTES] < order[i])
319
41
            break;
320
122
    }
321
41
    if (i < 0)
322
0
        return C448_FAILURE;
323
324
41
    error = ossl_curve448_point_decode_like_eddsa_and_mul_by_ratio(pk_point, pubkey);
325
326
41
    if (C448_SUCCESS != error)
327
4
        return error;
328
329
37
    error = ossl_curve448_point_decode_like_eddsa_and_mul_by_ratio(r_point, signature);
330
37
    if (C448_SUCCESS != error)
331
1
        return error;
332
333
36
    {
334
        /* Compute the challenge */
335
36
        EVP_MD_CTX *hashctx = EVP_MD_CTX_new();
336
36
        uint8_t challenge[2 * EDDSA_448_PRIVATE_BYTES];
337
338
36
        if (hashctx == NULL
339
36
            || !hash_init_with_dom(ctx, hashctx, prehashed, 0, context,
340
36
                context_len, propq)
341
36
            || !EVP_DigestUpdate(hashctx, signature, EDDSA_448_PUBLIC_BYTES)
342
36
            || !EVP_DigestUpdate(hashctx, pubkey, EDDSA_448_PUBLIC_BYTES)
343
36
            || !EVP_DigestUpdate(hashctx, message, message_len)
344
36
            || !EVP_DigestFinalXOF(hashctx, challenge, sizeof(challenge))) {
345
0
            EVP_MD_CTX_free(hashctx);
346
0
            return C448_FAILURE;
347
0
        }
348
349
36
        EVP_MD_CTX_free(hashctx);
350
36
        ossl_curve448_scalar_decode_long(challenge_scalar, challenge,
351
36
            sizeof(challenge));
352
36
        OPENSSL_cleanse(challenge, sizeof(challenge));
353
36
    }
354
0
    ossl_curve448_scalar_sub(challenge_scalar, ossl_curve448_scalar_zero,
355
36
        challenge_scalar);
356
357
36
    ossl_curve448_scalar_decode_long(response_scalar,
358
36
        &signature[EDDSA_448_PUBLIC_BYTES],
359
36
        EDDSA_448_PRIVATE_BYTES);
360
361
    /* pk_point = -c(x(P)) + (cx + k)G = kG */
362
36
    ossl_curve448_base_double_scalarmul_non_secret(pk_point,
363
36
        response_scalar,
364
36
        pk_point, challenge_scalar);
365
36
    return c448_succeed_if(ossl_curve448_point_eq(pk_point, r_point));
366
36
}
367
368
c448_error_t
369
ossl_c448_ed448_verify_prehash(
370
    OSSL_LIB_CTX *ctx,
371
    const uint8_t signature[EDDSA_448_SIGNATURE_BYTES],
372
    const uint8_t pubkey[EDDSA_448_PUBLIC_BYTES],
373
    const uint8_t hash[64], const uint8_t *context,
374
    uint8_t context_len, const char *propq)
375
0
{
376
0
    return ossl_c448_ed448_verify(ctx, signature, pubkey, hash, 64, 1, context,
377
0
        context_len, propq);
378
0
}
379
380
int ossl_ed448_sign(OSSL_LIB_CTX *ctx, uint8_t *out_sig,
381
    const uint8_t *message, size_t message_len,
382
    const uint8_t public_key[57], const uint8_t private_key[57],
383
    const uint8_t *context, size_t context_len,
384
    const uint8_t phflag, const char *propq)
385
0
{
386
0
    return ossl_c448_ed448_sign(ctx, out_sig, private_key, public_key, message,
387
0
               message_len, phflag, context, context_len,
388
0
               propq)
389
0
        == C448_SUCCESS;
390
0
}
391
392
/*
393
 * This function should not be necessary since ossl_ed448_verify() already
394
 * does this check internally.
395
 * For some reason the FIPS ACVP requires a EDDSA KeyVer test.
396
 */
397
int ossl_ed448_pubkey_verify(const uint8_t *pub, size_t pub_len)
398
0
{
399
0
    return c448_ed448_pubkey_verify(pub, pub_len);
400
0
}
401
402
int ossl_ed448_verify(OSSL_LIB_CTX *ctx,
403
    const uint8_t *message, size_t message_len,
404
    const uint8_t signature[114], const uint8_t public_key[57],
405
    const uint8_t *context, size_t context_len,
406
    const uint8_t phflag, const char *propq)
407
45
{
408
45
    return ossl_c448_ed448_verify(ctx, signature, public_key, message,
409
45
               message_len, phflag, context, (uint8_t)context_len,
410
45
               propq)
411
45
        == C448_SUCCESS;
412
45
}
413
414
int ossl_ed448_public_from_private(OSSL_LIB_CTX *ctx, uint8_t out_public_key[57],
415
    const uint8_t private_key[57], const char *propq)
416
24
{
417
24
    return ossl_c448_ed448_derive_public_key(ctx, out_public_key, private_key,
418
24
               propq)
419
24
        == C448_SUCCESS;
420
24
}