Coverage Report

Created: 2025-11-16 06:40

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