614 lines
22 KiB
C
614 lines
22 KiB
C
// Copyright (c) 2020, Google Inc.
|
|
// SPDX-License-Identifier: ISC
|
|
|
|
// Some of this code is taken from the ref10 version of Ed25519 in SUPERCOP
|
|
// 20141124 (http://bench.cr.yp.to/supercop.html). That code is released as
|
|
// public domain. Other parts have been replaced to call into code generated by
|
|
// Fiat (https://github.com/mit-plv/fiat-crypto) in //third_party/fiat.
|
|
//
|
|
// The field functions are shared by Ed25519 and X25519 where possible.
|
|
|
|
#include <openssl/curve25519.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <openssl/err.h>
|
|
#include <openssl/mem.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/sha.h>
|
|
|
|
#include "../../internal.h"
|
|
#include "../cpucap/internal.h"
|
|
#include "internal.h"
|
|
|
|
const uint8_t RFC8032_DOM2_PREFIX[DOM2_PREFIX_SIZE] = {
|
|
'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ',
|
|
'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ',
|
|
'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's'};
|
|
|
|
// X25519 [1] and Ed25519 [2] is an ECDHE protocol and signature scheme,
|
|
// respectively. This file contains an implementation of both using two
|
|
// different backends:
|
|
// 1) One backend is a pure C backend that should work on any platform.
|
|
// 2) The other backend is machine-optimized using s2n-bignum [3] as backend.
|
|
//
|
|
// [1]: https://datatracker.ietf.org/doc/html/rfc7748
|
|
// [2]: https://datatracker.ietf.org/doc/html/rfc8032
|
|
// [3]: https://github.com/awslabs/s2n-bignum
|
|
//
|
|
// "Clamping":
|
|
// Both X25519 and Ed25519 contain "clamping" steps; bit-twiddling, masking or
|
|
// setting specific bits. Generally, the bit-twiddling is to avoid common
|
|
// implementation errors and weak instances. Details can be found through the
|
|
// following two references:
|
|
// * https://mailarchive.ietf.org/arch/msg/cfrg/pt2bt3fGQbNF8qdEcorp-rJSJrc/
|
|
// * https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about
|
|
//
|
|
// Ed25519 domain and pre-hash functions:
|
|
// For Ed25519, dom2(F,C) is the empty string and PH the identify function,
|
|
// cf. rfc8032 5.1.
|
|
|
|
void ed25519_sha512(uint8_t out[SHA512_DIGEST_LENGTH], const void *input1,
|
|
size_t len1, const void *input2, size_t len2,
|
|
const void *input3, size_t len3, const void *input4,
|
|
size_t len4) {
|
|
SHA512_CTX hash_ctx;
|
|
SHA512_Init(&hash_ctx);
|
|
SHA512_Update(&hash_ctx, input1, len1);
|
|
SHA512_Update(&hash_ctx, input2, len2);
|
|
if (len3 != 0) {
|
|
SHA512_Update(&hash_ctx, input3, len3);
|
|
}
|
|
if (len4 != 0) {
|
|
SHA512_Update(&hash_ctx, input4, len4);
|
|
}
|
|
SHA512_Final(out, &hash_ctx);
|
|
}
|
|
|
|
// Public interface functions
|
|
|
|
void ED25519_keypair_from_seed(uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN],
|
|
uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t seed[ED25519_SEED_LEN]) {
|
|
// ED25519_keypair already ensures this with the same check, and is also the
|
|
// function that is approved for FIPS (sets the indicator). Ensuring it here
|
|
// for brevity.
|
|
boringssl_ensure_eddsa_self_test();
|
|
|
|
// Step: rfc8032 5.1.5.1
|
|
// Compute SHA512(seed).
|
|
uint8_t az[SHA512_DIGEST_LENGTH];
|
|
SHA512(seed, ED25519_SEED_LEN, az);
|
|
|
|
// Step: rfc8032 5.1.5.2
|
|
az[0] &= 248; // 11111000_2
|
|
az[31] &= 127; // 01111111_2
|
|
az[31] |= 64; // 01000000_2
|
|
|
|
// Step: rfc8032 5.1.5.[3,4]
|
|
// Compute [az]B and encode public key to a 32 byte octet.
|
|
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
|
|
ed25519_public_key_from_hashed_seed_s2n_bignum(out_public_key, az);
|
|
#else
|
|
ed25519_public_key_from_hashed_seed_nohw(out_public_key, az);
|
|
#endif
|
|
|
|
// Encoded public key is a suffix in the private key. Avoids having to
|
|
// generate the public key from the private key when signing.
|
|
OPENSSL_STATIC_ASSERT(ED25519_PRIVATE_KEY_LEN == (ED25519_SEED_LEN + ED25519_PUBLIC_KEY_LEN), ed25519_parameter_length_mismatch)
|
|
OPENSSL_memcpy(out_private_key, seed, ED25519_SEED_LEN);
|
|
OPENSSL_memcpy(out_private_key + ED25519_SEED_LEN, out_public_key,
|
|
ED25519_PUBLIC_KEY_LEN);
|
|
|
|
OPENSSL_cleanse(az, sizeof(az));
|
|
}
|
|
|
|
static int ed25519_keypair_pct(uint8_t public_key[ED25519_PUBLIC_KEY_LEN],
|
|
uint8_t private_key[ED25519_PRIVATE_KEY_LEN]) {
|
|
#if defined(AWSLC_FIPS)
|
|
uint8_t msg[16] = {16};
|
|
uint8_t out_sig[ED25519_SIGNATURE_LEN];
|
|
if (ED25519_sign_no_self_test(out_sig, msg, 16, private_key) != 1) {
|
|
// This should never happen and static analysis will say that ED25519_sign_no_self_test
|
|
// always returns 1
|
|
AWS_LC_FIPS_failure("Ed25519 keygen PCT failed");
|
|
return 0;
|
|
}
|
|
if (boringssl_fips_break_test("EDDSA_PWCT")) {
|
|
msg[0] = ~msg[0];
|
|
}
|
|
if (ED25519_verify_no_self_test(msg, 16, out_sig, public_key) != 1) {
|
|
AWS_LC_FIPS_failure("Ed25519 keygen PCT failed");
|
|
return 0;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
int ED25519_keypair_internal(uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN],
|
|
uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN]) {
|
|
// We have to avoid the self tests and digest function in ed25519_keypair_pct
|
|
// from updating the service indicator.
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_eddsa_self_test();
|
|
SET_DIT_AUTO_RESET;
|
|
|
|
// Ed25519 key generation: rfc8032 5.1.5
|
|
// Private key is 32 octets of random data.
|
|
uint8_t seed[ED25519_SEED_LEN];
|
|
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(seed, ED25519_SEED_LEN));
|
|
|
|
// Public key generation is handled in a separate function. See function
|
|
// description why this is useful.
|
|
ED25519_keypair_from_seed(out_public_key, out_private_key, seed);
|
|
OPENSSL_cleanse(seed, ED25519_SEED_LEN);
|
|
|
|
int result = ed25519_keypair_pct(out_public_key, out_private_key);
|
|
|
|
FIPS_service_indicator_unlock_state();
|
|
if (result) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ED25519_keypair(uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN],
|
|
uint8_t out_private_key[ED25519_PRIVATE_KEY_LEN]) {
|
|
// The existing public function is void, ED25519_keypair_internal can only
|
|
// fail if the PWCT fails and we're in a callback build where AWS_LC_FIPS_failure
|
|
// doesn't abort on FIPS failure.
|
|
AWSLC_ASSERT(ED25519_keypair_internal(out_public_key, out_private_key));
|
|
}
|
|
|
|
int ED25519_sign(uint8_t out_sig[ED25519_SIGNATURE_LEN],
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t private_key[ED25519_PRIVATE_KEY_LEN]) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_eddsa_self_test();
|
|
int res =
|
|
ED25519_sign_no_self_test(out_sig, message, message_len, private_key);
|
|
FIPS_service_indicator_unlock_state();
|
|
if (res) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ED25519_sign_no_self_test(
|
|
uint8_t out_sig[ED25519_SIGNATURE_LEN], const uint8_t *message,
|
|
size_t message_len, const uint8_t private_key[ED25519_PRIVATE_KEY_LEN]) {
|
|
return ed25519_sign_internal(ED25519_ALG, out_sig, message, message_len,
|
|
private_key, NULL, 0);
|
|
}
|
|
|
|
static int dom2(ed25519_algorithm_t alg, uint8_t buffer[MAX_DOM2_SIZE],
|
|
size_t *buffer_len, const uint8_t *context,
|
|
size_t context_len) {
|
|
GUARD_PTR(buffer_len);
|
|
*buffer_len = 0;
|
|
|
|
uint8_t phflag = 0;
|
|
|
|
switch (alg) {
|
|
case ED25519_ALG:
|
|
// Per rfc8032:
|
|
// For Ed25519, dom2(f,c) is the empty string. The phflag value is
|
|
// irrelevant. The context (if present at all) MUST be empty.
|
|
return context_len == 0;
|
|
case ED25519CTX_ALG:
|
|
// Per rfc8032:
|
|
// For Ed25519ctx, phflag=0. The context input SHOULD NOT be empty.
|
|
if (context_len == 0) {
|
|
return 0;
|
|
}
|
|
phflag = 0;
|
|
break;
|
|
case ED25519PH_ALG:
|
|
// For Ed25519ph, phflag=1
|
|
phflag = 1;
|
|
break;
|
|
default:
|
|
// Should never happen unless we missed a ed25519_algorithm_t enum variant
|
|
// case in this switch statement
|
|
abort();
|
|
}
|
|
|
|
OPENSSL_memcpy(buffer, RFC8032_DOM2_PREFIX, DOM2_PREFIX_SIZE);
|
|
buffer[DOM2_F_OFFSET] = phflag;
|
|
buffer[DOM2_C_OFFSET] = context_len;
|
|
if (context_len > 0) {
|
|
GUARD_PTR(context);
|
|
if (context_len > MAX_DOM2_CONTEXT_SIZE) {
|
|
return 0;
|
|
}
|
|
OPENSSL_memcpy(&buffer[DOM2_CONTEXT_OFFSET], context, context_len);
|
|
}
|
|
*buffer_len = DOM2_PREFIX_SIZE + DOM2_F_SIZE + DOM2_C_SIZE + context_len;
|
|
return 1;
|
|
}
|
|
|
|
int ed25519_sign_internal(
|
|
ed25519_algorithm_t alg,
|
|
uint8_t out_sig[ED25519_SIGNATURE_LEN],
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *ctx, size_t ctx_len) {
|
|
// NOTE: The documentation on this function says that it returns zero on
|
|
// allocation failure. While that can't happen with the current
|
|
// implementation, we want to reserve the ability to allocate in this
|
|
// implementation in the future.
|
|
|
|
if (alg == ED25519PH_ALG &&
|
|
message_len != SHA512_DIGEST_LENGTH) {
|
|
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_CRYPTO_LIB);
|
|
return 0;
|
|
}
|
|
|
|
// Ed25519 sign: rfc8032 5.1.6
|
|
//
|
|
// Step: rfc8032 5.1.6.1
|
|
// This step is a repeat of rfc8032 5.1.5.[1,2].
|
|
// seed = private_key[0:31]
|
|
// A = private_key[32:61] (per 5.1.5.4)
|
|
// Compute az = SHA512(seed).
|
|
SET_DIT_AUTO_RESET;
|
|
uint8_t az[SHA512_DIGEST_LENGTH];
|
|
SHA512(private_key, ED25519_PRIVATE_KEY_SEED_LEN, az);
|
|
// s = az[0:31]
|
|
// prefix = az[32:61]
|
|
az[0] &= 248; // 11111000_2
|
|
az[31] &= 63; // 00111111_2
|
|
az[31] |= 64; // 01000000_2
|
|
|
|
uint8_t r[SHA512_DIGEST_LENGTH];
|
|
uint8_t dom2_buffer[MAX_DOM2_SIZE] = {0};
|
|
size_t dom2_buffer_len = 0;
|
|
|
|
if (!dom2(alg, dom2_buffer, &dom2_buffer_len, ctx, ctx_len)) {
|
|
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_CRYPTO_LIB);
|
|
OPENSSL_cleanse(az, sizeof(az));
|
|
return 0;
|
|
}
|
|
|
|
// Step: rfc8032 5.1.6.2
|
|
if (dom2_buffer_len > 0) {
|
|
// Compute r = SHA512(dom2(phflag, context) || prefix || message).
|
|
ed25519_sha512(r, dom2_buffer, dom2_buffer_len,
|
|
az + ED25519_PRIVATE_KEY_SEED_LEN,
|
|
ED25519_PRIVATE_KEY_SEED_LEN, message, message_len, NULL, 0);
|
|
} else {
|
|
// Compute r = SHA512(prefix || message).
|
|
ed25519_sha512(r, az + ED25519_PRIVATE_KEY_SEED_LEN,
|
|
ED25519_PRIVATE_KEY_SEED_LEN, message, message_len, NULL, 0,
|
|
NULL, 0);
|
|
}
|
|
|
|
// Step: rfc8032 5.1.6.[3,5,6,7]
|
|
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
|
|
ed25519_sign_s2n_bignum(out_sig, r, az,
|
|
private_key + ED25519_PRIVATE_KEY_SEED_LEN, message,
|
|
message_len, dom2_buffer, dom2_buffer_len);
|
|
#else
|
|
ed25519_sign_nohw(out_sig, r, az, private_key + ED25519_PRIVATE_KEY_SEED_LEN,
|
|
message, message_len, dom2_buffer, dom2_buffer_len);
|
|
#endif
|
|
|
|
// The signature is computed from the private key, but is public.
|
|
CONSTTIME_DECLASSIFY(out_sig, 64);
|
|
|
|
OPENSSL_cleanse(az, sizeof(az));
|
|
OPENSSL_cleanse(r, sizeof(r));
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ED25519_verify(const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_eddsa_self_test();
|
|
int res =
|
|
ED25519_verify_no_self_test(message, message_len, signature, public_key);
|
|
FIPS_service_indicator_unlock_state();
|
|
if(res) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ED25519_verify_no_self_test(
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
|
|
return ed25519_verify_internal(ED25519_ALG, message, message_len, signature,
|
|
public_key, NULL, 0);
|
|
}
|
|
|
|
int ED25519ctx_sign(uint8_t out_sig[ED25519_SIGNATURE_LEN],
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_eddsa_self_test();
|
|
int res = ED25519ctx_sign_no_self_test(out_sig, message, message_len,
|
|
private_key, context, context_len);
|
|
FIPS_service_indicator_unlock_state();
|
|
return res;
|
|
}
|
|
|
|
int ED25519ctx_sign_no_self_test(
|
|
uint8_t out_sig[ED25519_SIGNATURE_LEN], const uint8_t *message,
|
|
size_t message_len, const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
return ed25519_sign_internal(ED25519CTX_ALG, out_sig, message, message_len,
|
|
private_key, context, context_len);
|
|
}
|
|
|
|
int ED25519ctx_verify(const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_eddsa_self_test();
|
|
int res = ED25519ctx_verify_no_self_test(message, message_len, signature,
|
|
public_key, context, context_len);
|
|
FIPS_service_indicator_unlock_state();
|
|
return res;
|
|
}
|
|
|
|
int ED25519ctx_verify_no_self_test(
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN], const uint8_t *context,
|
|
size_t context_len) {
|
|
return ed25519_verify_internal(ED25519CTX_ALG, message, message_len,
|
|
signature, public_key, context, context_len);
|
|
}
|
|
|
|
int ED25519ph_sign(uint8_t out_sig[ED25519_SIGNATURE_LEN],
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_hasheddsa_self_test();
|
|
int res = ED25519ph_sign_no_self_test(out_sig, message, message_len,
|
|
private_key, context, context_len);
|
|
FIPS_service_indicator_unlock_state();
|
|
if (res) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ED25519ph_sign_no_self_test(
|
|
uint8_t out_sig[ED25519_SIGNATURE_LEN], const uint8_t *message,
|
|
size_t message_len, const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
uint8_t digest[SHA512_DIGEST_LENGTH] = {0};
|
|
SHA512_CTX ctx;
|
|
SHA512_Init(&ctx);
|
|
SHA512_Update(&ctx, message, message_len);
|
|
SHA512_Final(digest, &ctx);
|
|
return ED25519ph_sign_digest_no_self_test(out_sig, digest, private_key,
|
|
context, context_len);
|
|
}
|
|
|
|
int ED25519ph_sign_digest(uint8_t out_sig[ED25519_SIGNATURE_LEN],
|
|
const uint8_t digest[SHA512_DIGEST_LENGTH],
|
|
const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_hasheddsa_self_test();
|
|
int res = ED25519ph_sign_digest_no_self_test(out_sig, digest, private_key,
|
|
context, context_len);
|
|
FIPS_service_indicator_unlock_state();
|
|
if (res) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ED25519ph_sign_digest_no_self_test(
|
|
uint8_t out_sig[ED25519_SIGNATURE_LEN],
|
|
const uint8_t digest[SHA512_DIGEST_LENGTH],
|
|
const uint8_t private_key[ED25519_PRIVATE_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
return ed25519_sign_internal(ED25519PH_ALG, out_sig, digest,
|
|
SHA512_DIGEST_LENGTH, private_key, context,
|
|
context_len);
|
|
}
|
|
|
|
int ED25519ph_verify(const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_hasheddsa_self_test();
|
|
int res = ED25519ph_verify_no_self_test(message, message_len, signature,
|
|
public_key, context, context_len);
|
|
FIPS_service_indicator_unlock_state();
|
|
if (res) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ED25519ph_verify_no_self_test(
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN], const uint8_t *context,
|
|
size_t context_len) {
|
|
uint8_t digest[SHA512_DIGEST_LENGTH] = {0};
|
|
SHA512_CTX ctx;
|
|
SHA512_Init(&ctx);
|
|
SHA512_Update(&ctx, message, message_len);
|
|
SHA512_Final(digest, &ctx);
|
|
return ED25519ph_verify_digest_no_self_test(digest, signature, public_key,
|
|
context, context_len);
|
|
}
|
|
|
|
int ED25519ph_verify_digest(const uint8_t digest[SHA512_DIGEST_LENGTH],
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN],
|
|
const uint8_t *context, size_t context_len) {
|
|
FIPS_service_indicator_lock_state();
|
|
boringssl_ensure_hasheddsa_self_test();
|
|
int res = ED25519ph_verify_digest_no_self_test(
|
|
digest, signature, public_key, context, context_len);
|
|
FIPS_service_indicator_unlock_state();
|
|
if(res) {
|
|
FIPS_service_indicator_update_state();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int ED25519ph_verify_digest_no_self_test(
|
|
const uint8_t digest[SHA512_DIGEST_LENGTH],
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN], const uint8_t *context,
|
|
size_t context_len) {
|
|
return ed25519_verify_internal(ED25519PH_ALG, digest,
|
|
SHA512_DIGEST_LENGTH, signature, public_key,
|
|
context, context_len);
|
|
}
|
|
|
|
int ed25519_verify_internal(
|
|
ed25519_algorithm_t alg,
|
|
const uint8_t *message, size_t message_len,
|
|
const uint8_t signature[ED25519_SIGNATURE_LEN],
|
|
const uint8_t public_key[ED25519_PUBLIC_KEY_LEN],
|
|
const uint8_t *ctx, size_t ctx_len) {
|
|
// Ed25519 verify: rfc8032 5.1.7
|
|
|
|
if (alg == ED25519PH_ALG &&
|
|
message_len != SHA512_DIGEST_LENGTH) {
|
|
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_CRYPTO_LIB);
|
|
return 0;
|
|
}
|
|
|
|
// Step: rfc8032 5.1.7.1 (up to decoding the public key)
|
|
// Decode signature as:
|
|
// - signature[0:31]: encoded point R, aliased to R_expected.
|
|
// - signature[32:61]: integer S.
|
|
uint8_t R_expected[32];
|
|
OPENSSL_memcpy(R_expected, signature, 32);
|
|
uint8_t S[32];
|
|
OPENSSL_memcpy(S, signature + 32, 32);
|
|
|
|
// Per rfc8032 5.1.6.6
|
|
// "the three most significant bits of the final octet are always zero"
|
|
// 224 = 11100000_2
|
|
if ((signature[63] & 224) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
// S must be in the range [0, order) in order to prevent signature
|
|
// malleability. kOrder is the order of curve25519 in little-endian form.
|
|
static const uint64_t kOrder[4] = {
|
|
UINT64_C(0x5812631a5cf5d3ed),
|
|
UINT64_C(0x14def9dea2f79cd6),
|
|
0,
|
|
UINT64_C(0x1000000000000000),
|
|
};
|
|
for (size_t i = 3;; i--) {
|
|
uint64_t word = CRYPTO_load_u64_le(S + i * 8);
|
|
if (word > kOrder[i]) {
|
|
return 0;
|
|
} else if (word < kOrder[i]) {
|
|
break;
|
|
} else if (i == 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
uint8_t dom2_buffer[MAX_DOM2_SIZE] = {0};
|
|
size_t dom2_buffer_len = 0;
|
|
if (!dom2(alg, dom2_buffer, &dom2_buffer_len, ctx, ctx_len)) {
|
|
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_CRYPTO_LIB);
|
|
return 0;
|
|
}
|
|
|
|
// Step: rfc8032 5.1.7.[1,2,3]
|
|
// Verification works by computing [S]B - [k]A' and comparing against R_expected.
|
|
int res = 0;
|
|
uint8_t R_computed_encoded[32];
|
|
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
|
|
res = ed25519_verify_s2n_bignum(R_computed_encoded, public_key, R_expected, S,
|
|
message, message_len, dom2_buffer,
|
|
dom2_buffer_len);
|
|
#else
|
|
res = ed25519_verify_nohw(R_computed_encoded, public_key, R_expected, S,
|
|
message, message_len, dom2_buffer, dom2_buffer_len);
|
|
#endif
|
|
|
|
// Comparison [S]B - [k]A' =? R_expected. Short-circuits if decoding failed.
|
|
return (res == 1) && CRYPTO_memcmp(R_computed_encoded, R_expected,
|
|
sizeof(R_computed_encoded)) == 0;
|
|
}
|
|
|
|
int ED25519_check_public_key(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
|
|
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
|
|
return ed25519_check_public_key_s2n_bignum(public_key);
|
|
#else
|
|
return ed25519_check_public_key_nohw(public_key);
|
|
#endif
|
|
}
|
|
|
|
void X25519_public_from_private(
|
|
uint8_t out_public_value[X25519_PUBLIC_VALUE_LEN],
|
|
const uint8_t private_key[X25519_PRIVATE_KEY_LEN]) {
|
|
SET_DIT_AUTO_RESET;
|
|
|
|
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
|
|
x25519_public_from_private_s2n_bignum(out_public_value, private_key);
|
|
#else
|
|
x25519_public_from_private_nohw(out_public_value, private_key);
|
|
#endif
|
|
// The public key is derived from the private key, but it is public.
|
|
CONSTTIME_DECLASSIFY(out_public_value, X25519_PUBLIC_VALUE_LEN);
|
|
}
|
|
|
|
void X25519_keypair(uint8_t out_public_value[X25519_PUBLIC_VALUE_LEN],
|
|
uint8_t out_private_key[X25519_PRIVATE_KEY_LEN]) {
|
|
SET_DIT_AUTO_RESET;
|
|
|
|
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(out_private_key, X25519_PRIVATE_KEY_LEN));
|
|
|
|
// All X25519 implementations should decode scalars correctly (see
|
|
// https://tools.ietf.org/html/rfc7748#section-5). However, if an
|
|
// implementation doesn't then it might interoperate with random keys a
|
|
// fraction of the time because they'll, randomly, happen to be correctly
|
|
// formed.
|
|
//
|
|
// Thus we do the opposite of the masking here to make sure that our private
|
|
// keys are never correctly masked and so, hopefully, any incorrect
|
|
// implementations are deterministically broken.
|
|
//
|
|
// This does not affect security because, although we're throwing away
|
|
// entropy, a valid implementation of scalarmult should throw away the exact
|
|
// same bits anyway.
|
|
out_private_key[0] |= ~248;
|
|
out_private_key[31] &= ~64;
|
|
out_private_key[31] |= ~127;
|
|
|
|
X25519_public_from_private(out_public_value, out_private_key);
|
|
}
|
|
|
|
int X25519(uint8_t out_shared_key[X25519_SHARED_KEY_LEN],
|
|
const uint8_t private_key[X25519_PRIVATE_KEY_LEN],
|
|
const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN]) {
|
|
|
|
SET_DIT_AUTO_RESET;
|
|
static const uint8_t kZeros[X25519_SHARED_KEY_LEN] = {0};
|
|
|
|
#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
|
|
x25519_scalar_mult_generic_s2n_bignum(out_shared_key, private_key, peer_public_value);
|
|
#else
|
|
x25519_scalar_mult_generic_nohw(out_shared_key, private_key, peer_public_value);
|
|
#endif
|
|
|
|
// The all-zero output results when the input is a point of small order.
|
|
return constant_time_declassify_int(
|
|
CRYPTO_memcmp(kZeros, out_shared_key, X25519_SHARED_KEY_LEN)) != 0;
|
|
}
|