Coverage Report

Created: 2026-10-28 06:28

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