diff --git a/crypto/evp_extra/p_pqdsa.c b/crypto/evp_extra/p_pqdsa.c index 57584765fe..612ea0e69d 100644 --- a/crypto/evp_extra/p_pqdsa.c +++ b/crypto/evp_extra/p_pqdsa.c @@ -302,21 +302,31 @@ EVP_PKEY *EVP_PKEY_pqdsa_new_raw_private_key(int nid, const uint8_t *in, size_t EVP_PKEY *ret = EVP_PKEY_pqdsa_new(nid); if (ret == NULL || ret->pkey.pqdsa_key == NULL) { - // EVP_PKEY_kem_new sets the appropriate error. + // EVP_PKEY_pqdsa_new sets the appropriate error. goto err; } - const PQDSA *pqdsa = PQDSA_KEY_get0_dsa(ret->pkey.pqdsa_key); - if (pqdsa->private_key_len != len) { + // Get PQDSA instance and validate lengths + const PQDSA *pqdsa = PQDSA_KEY_get0_dsa(ret->pkey.pqdsa_key); + if (len != pqdsa->private_key_len && len != pqdsa->keygen_seed_len) { OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); goto err; } CBS cbs; CBS_init(&cbs, in, len); - if (!PQDSA_KEY_set_raw_private_key(ret->pkey.pqdsa_key, &cbs)) { - // PQDSA_KEY_set_raw_private_key sets the appropriate error. - goto err; + + // Set key based on input length + if (len == pqdsa->private_key_len) { + if (!PQDSA_KEY_set_raw_private_key(ret->pkey.pqdsa_key, &cbs)) { + // PQDSA_KEY_set_raw_private_key sets the appropriate error. + goto err; + } + } else if (len == pqdsa->keygen_seed_len) { + if (!PQDSA_KEY_set_raw_keypair_from_seed(ret->pkey.pqdsa_key, &cbs)) { + // PQDSA_KEY_set_raw_keypair_from_seed sets the appropriate error. + goto err; + } } return ret; diff --git a/crypto/evp_extra/p_pqdsa_asn1.c b/crypto/evp_extra/p_pqdsa_asn1.c index aa47734593..29a6ba9e7a 100644 --- a/crypto/evp_extra/p_pqdsa_asn1.c +++ b/crypto/evp_extra/p_pqdsa_asn1.c @@ -153,31 +153,30 @@ static int pqdsa_priv_decode(EVP_PKEY *out, CBS *params, CBS *key, CBS *pubkey) return 0; } - // Set the private key - if (!PQDSA_KEY_set_raw_private_key(out->pkey.pqdsa_key, key)) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); - return 0; - } - - // Create buffers to store public key based on size - size_t pk_len = out->pkey.pqdsa_key->pqdsa->public_key_len; - uint8_t *public_key = OPENSSL_malloc(pk_len); - - if (public_key == NULL) { - OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE); + // check the size of the provided input against the private key and seed len + if (CBS_len(key) != out->pkey.pqdsa_key->pqdsa->private_key_len && + CBS_len(key) != out->pkey.pqdsa_key->pqdsa->keygen_seed_len) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); return 0; } - // Construct the public key from the private key - if (!out->pkey.pqdsa_key->pqdsa->method->pqdsa_pack_pk_from_sk( - public_key, CBS_data(key))) { - OPENSSL_free(public_key); - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); - return 0; + // See https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/ + // The caller can either provide the full key of size |private_key_len| or + // |keygen_seed_len|. + if (CBS_len(key) == out->pkey.pqdsa_key->pqdsa->private_key_len) { + + // Set the private key + if (!PQDSA_KEY_set_raw_private_key(out->pkey.pqdsa_key, key)) { + // PQDSA_KEY_set_raw_private_key sets the appropriate error. + return 0; + } + + } else if (CBS_len(key) == out->pkey.pqdsa_key->pqdsa->keygen_seed_len) { + if (!PQDSA_KEY_set_raw_keypair_from_seed(out->pkey.pqdsa_key, key)) { + // PQDSA_KEY_set_raw_keypair_from_seed sets the appropriate error. + return 0; + } } - - out->pkey.pqdsa_key->public_key = public_key; - return 1; } diff --git a/crypto/evp_extra/p_pqdsa_test.cc b/crypto/evp_extra/p_pqdsa_test.cc index a01e27bb0d..bc1833c9eb 100644 --- a/crypto/evp_extra/p_pqdsa_test.cc +++ b/crypto/evp_extra/p_pqdsa_test.cc @@ -3,10 +3,12 @@ #include #include +#include #include #include #include #include +#include #include "../test/test_util.h" #include @@ -944,6 +946,165 @@ static const uint8_t mldsa87kPublicKeySPKI[] = { 0x67, 0x46, 0xC8, 0x68, 0xB7, 0x65, 0x05, 0x20, 0x02, 0x70, 0xDA, 0x6B, 0xC7, 0x34}; +// https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/06/ +// C.2. Example Public Key +const char *mldsa_44_pub_pem_str = +"-----BEGIN PUBLIC KEY-----\n" +"MIIFMjALBglghkgBZQMEAxEDggUhANeytHJUquDbReeTDUqY0sl9jxOX0Xidr6Fw\n" +"JLMW6b7JT8mUbULxm3mnQTu6oz5xSctC7VEVaTrAQfrLmIretf4OHYYxGEmVtZLD\n" +"l9IpTi4U+QqkFLo4JomaxD9MzKy8JumoMrlRGNXLQzy++WYLABOOCBf2HnYsonTD\n" +"atVU6yKqwRYuSrAay6HjjE79j4C2WzM9D3LlXf5xzpweu5iJ58VhBsD9c4A6Kuz+\n" +"r97XqjyyztpU0SvYzTanjPl1lDtHq9JeiArEUuV0LtHo0agq+oblkMdYwVrk0oQN\n" +"kryhpQkPQElll/yn2LlRPxob2m6VCqqY3kZ1B9Sk9aTwWZIWWCw1cvYu2okFqzWB\n" +"ZwxKAnd6M+DKcpX9j0/20aCjp2g9ZfX19/xg2gI+gmxfkhRMAvfRuhB1mHVT6pNn\n" +"/NdtmQt/qZzUWv24g21D5Fn1GH3wWEeXCaAepoNZNfpwRgmQzT3BukAbqUurHd5B\n" +"rGerMxncrKBgSNTE7vJ+4TqcF9BTj0MPLWQtwkFWYN54h32NirxyUjl4wELkKF9D\n" +"GYRsRBJiQpdoRMEOVWuiFbWnGeWdDGsqltOYWQcf3MLN51JKe+2uVOhbMY6FTo/i\n" +"svPt+slxkSgnCq/R5QRMOk/a/Z/zH5B4S46ORZYUSg2vWGUR09mWK56pWvGXtOX8\n" +"YPKx7RXeOlvvX4m9x52RBR2bKBbnT6VFMe/cHL501EiFf0drzVjyHAtlOzt2pOB2\n" +"plWaMCcYVVzGP3SFmqurkl8COGHKjND3utsocfZ9VTJtdFETWtRfShumkRj7ssij\n" +"DuyTku8/l3Bmya3VxxDMZHsVFNIX2VjHAXw+kP0gwE5nS5BIbpNwoxoAHTL0c5ee\n" +"SQZ0nn5Hf6C3RQj4pfI3gxK4PCW9OIygsP/3R4uvQrcWZ+2qyXxGsSlkPlhuWwVa\n" +"DCEZRtTzbmdb7Vhg+gQqMV2YJhZNapI3w1pfv0lUkKW9TfJIuVxKrneEtgVnMWas\n" +"QkW1tLCCoJ6TI+YvIHjFt2eDRG3v1zatOjcC1JsImESQCmGDM5e8RBmzDXqXoLOH\n" +"wZEUdMTUG1PjKpd6y28Op122W7OeWecB52lX3vby1EVZwxp3EitSBOO1whnxaIsU\n" +"7QvAuAGz5ugtzUPpwOn0F0TNmBW9G8iCDYuxI/BPrNGxtoXdWisbjbvz7ZM2cPCV\n" +"oYC08ZLQixC4+rvfzCskUY4y7qCl4MkEyoRHgAg/OwzS0Li2r2e8NVuUlAJdx7Cn\n" +"j6gOOi2/61EyiFHWB4GY6Uk2Ua54fsAlH5Irow6fUd9iptcnhM890gU5MXbfoySl\n" +"Er2Ulwo23TSlFKhnkfDrNvAUWwmrZGUbSgMTsplhGiocSIkWJ1mHaKMRQGC6RENI\n" +"bfUVIqHOiLMJhcIW+ObtF43VZ7MEoNTK+6iCooNC8XqaomrljbYwCD0sNY/fVmw/\n" +"XWKkKFZ7yeqM6VyqDzVHSwv6jzOaJQq0388gg76O77wQVeGP4VNw7ssmBWbYP/Br\n" +"IRquxDyim1TM0A+IFaJGXvC0ZRXMfkHzEk8J7/9zkwmrWLKaFFmgC85QOOk4yWeP\n" +"cusOTuX9quZtn4Vz/Jf8QrSVn0v4th14Qz6GsDNdbpGRxNi/SHs5BcEIz9asJLDO\n" +"t9y3z1H4TQ7Wh7lerrHFM8BvDZcCPZKnCCWDe1m6bLfU5WsKh8IDhiro8xW6WSXo\n" +"7e+meTaaIgJ2YVHxapZfn4Hs52zAcLVYaeTbl4TPBcgwsyQsgxI=\n" +"-----END PUBLIC KEY-----\n"; + +const char *mldsa_65_pub_pem_str = +"-----BEGIN PUBLIC KEY-----\n" +"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\n" +"HgLy6ERP\n" +"-----END PUBLIC KEY-----\n"; + +const char *mldsa_87_pub_pem_str = +"-----BEGIN PUBLIC KEY-----\n" +"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\n" +"5VvgYiIvi6kKl0BzMmsxISrs4KNKYA==\n" +"-----END PUBLIC KEY-----\n"; + +// https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/06/ +// C.1. Example Private Key +const char *mldsa_44_priv_pem_str = +"-----BEGIN PRIVATE KEY-----\n" +"MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob\n" +"HB0eHw==\n" +"-----END PRIVATE KEY-----\n"; + +const char *mldsa_65_priv_pem_str = +"-----BEGIN PRIVATE KEY-----\n" +"MDICAQAwCwYJYIZIAWUDBAMSBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob\n" +"HB0eHw==\n" +"-----END PRIVATE KEY-----\n"; + +const char *mldsa_87_priv_pem_str = +"-----BEGIN PRIVATE KEY-----\n" +"MDICAQAwCwYJYIZIAWUDBAMTBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob\n" +"HB0eHw==\n" +"-----END PRIVATE KEY-----\n"; + struct PQDSATestVector { const char name[20]; const int nid; @@ -954,6 +1115,8 @@ struct PQDSATestVector { const uint8_t *kPublicKey; const uint8_t *kPublicKeySPKI; const size_t kPublicKeySPKI_len; + const char *public_pem_str; + const char *private_pem_str; int (*keygen)(uint8_t *public_key, uint8_t *private_key, const uint8_t *seed); @@ -1004,6 +1167,8 @@ static const struct PQDSATestVector parameterSet[] = { mldsa44kPublicKey, mldsa44kPublicKeySPKI, 1334, + mldsa_44_pub_pem_str, + mldsa_44_priv_pem_str, ml_dsa_44_keypair_internal, ml_dsa_44_sign_internal, ml_dsa_44_verify_internal, @@ -1019,6 +1184,8 @@ static const struct PQDSATestVector parameterSet[] = { mldsa65kPublicKey, mldsa65kPublicKeySPKI, 1974, + mldsa_65_pub_pem_str, + mldsa_65_priv_pem_str, ml_dsa_65_keypair_internal, ml_dsa_65_sign_internal, ml_dsa_65_verify_internal, @@ -1034,6 +1201,8 @@ static const struct PQDSATestVector parameterSet[] = { mldsa87kPublicKey, mldsa87kPublicKeySPKI, 2614, + mldsa_87_pub_pem_str, + mldsa_87_priv_pem_str, ml_dsa_87_keypair_internal, ml_dsa_87_sign_internal, ml_dsa_87_verify_internal, @@ -1287,22 +1456,20 @@ TEST_P(PQDSAParameterTest, RawFunctions) { EXPECT_NE(public_pkey->pkey.pqdsa_key->public_key, nullptr); EXPECT_EQ(public_pkey->pkey.pqdsa_key->private_key, nullptr); - // check that private key is present and public key is not present in private_key + // check that calling EVP_PKEY_pqdsa_new_raw_private_key populates both the + // public and private key ASSERT_NE(private_pkey, nullptr); - EXPECT_EQ(private_pkey->pkey.pqdsa_key->public_key, nullptr); + EXPECT_NE(private_pkey->pkey.pqdsa_key->public_key, nullptr); EXPECT_NE(private_pkey->pkey.pqdsa_key->private_key, nullptr); // ---- 5. Test get_raw public/private failure modes ---- uint8_t *buf = nullptr; size_t buf_size; - // Attempting to get a public/private key that is not present must fail correctly + // Attempting to get a private key that is not present must fail correctly EXPECT_FALSE(EVP_PKEY_get_raw_private_key(public_pkey.get(), buf, &buf_size)); GET_ERR_AND_CHECK_REASON(EVP_R_NOT_A_PRIVATE_KEY); - EXPECT_FALSE(EVP_PKEY_get_raw_public_key(private_pkey.get(), buf, &buf_size)); - GET_ERR_AND_CHECK_REASON(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); - // Null PKEY must fail correctly. ASSERT_FALSE(EVP_PKEY_get_raw_public_key(nullptr, pk.data(), &pk_len)); GET_ERR_AND_CHECK_REASON(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); @@ -1532,6 +1699,66 @@ TEST_P(PQDSAParameterTest, ParsePublicKey) { ASSERT_TRUE(pkey_from_der); } +// Helper function that: +// 1. Creates a BIO +// 2. Reads the provided |pem_string| into bio +// 3. Reads the PEM into DER encoding +// 4. Returns the DER data and length +static bool PEM_to_DER(const char* pem_str, uint8_t** out_der, long* out_der_len) { + char *name = nullptr; + char *header = nullptr; + + // Create BIO from memory + bssl::UniquePtr bio(BIO_new_mem_buf(pem_str, strlen(pem_str))); + if (!bio) { + return false; + } + + // Read PEM into DER + if (PEM_read_bio(bio.get(), &name, &header, out_der, out_der_len) <= 0) { + OPENSSL_free(name); + OPENSSL_free(header); + OPENSSL_free(*out_der); + *out_der = nullptr; + return false; + } + + OPENSSL_free(name); + OPENSSL_free(header); + return true; +} + +TEST_P(PQDSAParameterTest, ParsePrivateKey) { + // ---- 1. Setup phase: parse provided public/private from PEM strings ---- + CBS cbs_pub, cbs_priv; + uint8_t *der_pub = nullptr, *der_priv = nullptr; + long der_pub_len = 0, der_priv_len = 0; + + ASSERT_TRUE(PEM_to_DER(GetParam().public_pem_str, &der_pub, &der_pub_len)); + ASSERT_TRUE(PEM_to_DER(GetParam().private_pem_str, &der_priv, &der_priv_len)); + + CBS_init(&cbs_pub, der_pub, der_pub_len); + CBS_init(&cbs_priv, der_priv, der_priv_len); + + // ---- 2. Attempt to parse private key ---- + bssl::UniquePtr pkey1(EVP_parse_private_key(&cbs_priv)); + ASSERT_TRUE(pkey1); + + // ---- 3. Attempt to parse public key ---- + bssl::UniquePtr pkey2(EVP_parse_public_key(&cbs_pub)); + ASSERT_TRUE(pkey2); + + // ---- 4. Compare public keys ---- + // EVP_parse_private_key will populate both public and private key, we verify + // that the public key calculated by EVP_parse_private_key is equivalent to + // the public key that was parsed from PEM. + ASSERT_EQ(1, EVP_PKEY_cmp(pkey1.get(), pkey2.get())); + + // Clean up + OPENSSL_free(der_pub); + OPENSSL_free(der_priv); +} + TEST_P(PQDSAParameterTest, KeyConsistencyTest) { // This test: generates a random PQDSA key pair extracts the private key, and // runs the public key calculator function to populate the coresponding public key. diff --git a/crypto/fipsmodule/digest/digests.c b/crypto/fipsmodule/digest/digests.c index 88b45d2e5f..d869723685 100644 --- a/crypto/fipsmodule/digest/digests.c +++ b/crypto/fipsmodule/digest/digests.c @@ -430,7 +430,7 @@ static void shake128_init(EVP_MD_CTX *ctx) { } static void shake128_update(EVP_MD_CTX *ctx, const void *data, size_t count) { - CHECK(SHA3_Update(ctx->md_data, data, count)); + CHECK(SHAKE_Absorb(ctx->md_data, data, count)); } static void shake128_final(EVP_MD_CTX *ctx, uint8_t *md, size_t len) { @@ -455,7 +455,7 @@ static void shake256_init(EVP_MD_CTX *ctx) { } static void shake256_update(EVP_MD_CTX *ctx, const void *data, size_t count) { - CHECK(SHA3_Update(ctx->md_data, data, count)); + CHECK(SHAKE_Absorb(ctx->md_data, data, count)); } static void shake256_finalXOF(EVP_MD_CTX *ctx, uint8_t *md, size_t len) { diff --git a/crypto/fipsmodule/ec/p384.c b/crypto/fipsmodule/ec/p384.c index 707cd16c4c..ba5d780a79 100644 --- a/crypto/fipsmodule/ec/p384.c +++ b/crypto/fipsmodule/ec/p384.c @@ -83,6 +83,26 @@ static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) { #endif // EC_NISTP_USE_S2N_BIGNUM +// The wrapper functions are needed for FIPS static build. +// Otherwise, initializing ec_nistp_meth with pointers to s2n-bignum +// functions directly generates :got: references that are also thought +// to be local_target by the delocator. +static inline void p384_felem_add_wrapper(ec_nistp_felem_limb *c, + const ec_nistp_felem_limb *a, + const ec_nistp_felem_limb *b) { + p384_felem_add(c, a, b); +} + +static inline void p384_felem_sub_wrapper(ec_nistp_felem_limb *c, + const ec_nistp_felem_limb *a, + const ec_nistp_felem_limb *b) { + p384_felem_sub(c, a, b); +} + +static inline void p384_felem_neg_wrapper(ec_nistp_felem_limb *c, + const ec_nistp_felem_limb *a) { + p384_felem_opp(c, a); +} static void p384_from_generic(p384_felem out, const EC_FELEM *in) { #ifdef OPENSSL_BIG_ENDIAN @@ -273,11 +293,11 @@ static void p384_point_add(p384_felem x3, p384_felem y3, p384_felem z3, DEFINE_METHOD_FUNCTION(ec_nistp_meth, p384_methods) { out->felem_num_limbs = P384_NLIMBS; out->felem_num_bits = 384; - out->felem_add = bignum_add_p384; - out->felem_sub = bignum_sub_p384; + out->felem_add = p384_felem_add_wrapper; + out->felem_sub = p384_felem_sub_wrapper; out->felem_mul = bignum_montmul_p384_selector; out->felem_sqr = bignum_montsqr_p384_selector; - out->felem_neg = bignum_neg_p384; + out->felem_neg = p384_felem_neg_wrapper; out->felem_nz = p384_felem_nz; out->felem_one = p384_felem_one; out->point_dbl = p384_point_double; diff --git a/crypto/fipsmodule/ec/p521.c b/crypto/fipsmodule/ec/p521.c index 6364794cb5..db45e51850 100644 --- a/crypto/fipsmodule/ec/p521.c +++ b/crypto/fipsmodule/ec/p521.c @@ -126,6 +126,27 @@ static const p521_limb_t p521_felem_p[P521_NLIMBS] = { #endif // EC_NISTP_USE_S2N_BIGNUM +// The wrapper functions are needed for FIPS static build. +// Otherwise, initializing ec_nistp_meth with pointers to s2n-bignum +// functions directly generates :got: references that are also thought +// to be local_target by the delocator. +static inline void p521_felem_add_wrapper(ec_nistp_felem_limb *c, + const ec_nistp_felem_limb *a, + const ec_nistp_felem_limb *b) { + p521_felem_add(c, a, b); +} + +static inline void p521_felem_sub_wrapper(ec_nistp_felem_limb *c, + const ec_nistp_felem_limb *a, + const ec_nistp_felem_limb *b) { + p521_felem_sub(c, a, b); +} + +static inline void p521_felem_neg_wrapper(ec_nistp_felem_limb *c, + const ec_nistp_felem_limb *a) { + p521_felem_opp(c, a); +} + static p521_limb_t p521_felem_nz(const p521_limb_t in1[P521_NLIMBS]) { p521_limb_t is_not_zero = 0; for (int i = 0; i < P521_NLIMBS; i++) { @@ -289,11 +310,11 @@ static void p521_point_add(p521_felem x3, p521_felem y3, p521_felem z3, DEFINE_METHOD_FUNCTION(ec_nistp_meth, p521_methods) { out->felem_num_limbs = P521_NLIMBS; out->felem_num_bits = 521; - out->felem_add = bignum_add_p521; - out->felem_sub = bignum_sub_p521; + out->felem_add = p521_felem_add_wrapper; + out->felem_sub = p521_felem_sub_wrapper; out->felem_mul = bignum_mul_p521_selector; out->felem_sqr = bignum_sqr_p521_selector; - out->felem_neg = bignum_neg_p521; + out->felem_neg = p521_felem_neg_wrapper; out->felem_nz = p521_felem_nz; out->felem_one = p521_felem_one; out->point_dbl = p521_point_double; diff --git a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c index d31d7136fd..72bf98c2f2 100644 --- a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c +++ b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c @@ -29,8 +29,8 @@ void kyber_shake128_absorb(KECCAK1600_CTX *ctx, // SHAKE_Init always returns 1 when called with correct block size value SHAKE_Init(ctx, SHAKE128_BLOCKSIZE); - // SHA3_Update always returns 1 on first call of sizeof(extseed) (34 bytes) - SHA3_Update(ctx, extseed, sizeof(extseed)); + // SHAKE_Absorb always returns 1 on first call of sizeof(extseed) (34 bytes) + SHAKE_Absorb(ctx, extseed, sizeof(extseed)); } /************************************************* @@ -48,8 +48,9 @@ void kyber_shake128_absorb(KECCAK1600_CTX *ctx, void kyber_shake128_squeeze(KECCAK1600_CTX *ctx, uint8_t *out, int nblocks) { // Return code checks can be omitted - // SHAKE_Final always returns 1 - SHAKE_Final(out, ctx, nblocks * SHAKE128_BLOCKSIZE); + // SHAKE_Squeeze always returns 1 when |ctx->state| flag is different + // from |KECCAK1600_STATE_FINAL| + SHAKE_Squeeze(out, ctx, nblocks * SHAKE128_BLOCKSIZE); } /************************************************* @@ -94,12 +95,13 @@ void kyber_shake256_rkprf(ml_kem_params *params, uint8_t out[KYBER_SSBYTES], con // SHAKE_Init always returns 1 when called with correct block size value SHAKE_Init(&ctx, SHAKE256_BLOCKSIZE); - // SHA3_Update always returns 1 on first call of KYBER_SYMBYTES (32 bytes) - SHA3_Update(&ctx, key, KYBER_SYMBYTES); + // SHAKE_Absorb always returns 1 on first call of KYBER_SYMBYTES (32 bytes) + SHAKE_Absorb(&ctx, key, KYBER_SYMBYTES); - // SHA3_Update always returns 1 processing all data blocks that don't need pad - SHA3_Update(&ctx, input, params->ciphertext_bytes); + // SHAKE_Absorb always returns 1 processing all data blocks that don't need pad + SHAKE_Absorb(&ctx, input, params->ciphertext_bytes); - // SHAKE_Final always returns 1 + // SHAKE_Final always returns 1 when |ctx->state| flag is set to + // |KECCAK1600_STATE_ABSORB| (no previous calls to SHAKE_Final) SHAKE_Final(out, &ctx, KYBER_SSBYTES); } diff --git a/crypto/fipsmodule/sha/internal.h b/crypto/fipsmodule/sha/internal.h index 3a0a1c37ce..73d389cb61 100644 --- a/crypto/fipsmodule/sha/internal.h +++ b/crypto/fipsmodule/sha/internal.h @@ -71,6 +71,16 @@ extern "C" { // SHAKE128 has the maximum block size among the SHA3/SHAKE algorithms. #define SHA3_MAX_BLOCKSIZE SHAKE128_BLOCKSIZE +// Define state flag values for Keccak-based functions +#define KECCAK1600_STATE_ABSORB 0 +// KECCAK1600_STATE_SQUEEZE is set when |SHAKE_Squeeze| is called. +// It remains set while |SHAKE_Squeeze| is called repeatedly to output +// chunks of the XOF output. +#define KECCAK1600_STATE_SQUEEZE 1 +// KECCAK1600_STATE_FINAL is set once |SHAKE_Final| is called +// so that |SHAKE_Squeeze| cannot be called anymore. +#define KECCAK1600_STATE_FINAL 2 + typedef struct keccak_st KECCAK1600_CTX; // The data buffer should have at least the maximum number of @@ -82,7 +92,7 @@ struct keccak_st { size_t buf_load; // used bytes in below buffer uint8_t buf[SHA3_MAX_BLOCKSIZE]; // should have at least the max data block size bytes uint8_t pad; // padding character - uint8_t padded; // denotes if padding has been performed + uint8_t state; // denotes the keccak phase (absorb, squeeze, final) }; // Define SHA{n}[_{variant}]_ASM if sha{n}_block_data_order[_{variant}] is @@ -396,32 +406,43 @@ OPENSSL_EXPORT uint8_t *SHAKE128(const uint8_t *data, const size_t in_len, OPENSSL_EXPORT uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t out_len); -// SHAKE_Init initializes |ctx| with specified |block_size|, returns 1 on -// success and 0 on failure. Calls SHA3_Init under the hood. -int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size); - -// SHAKE_Final writes |len| bytes of finalized digest to |md|, returns 1 on -// success and 0 on failure. Calls SHA3_Final under the hood. -int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len); - -// SHA3_Reset zeros the bitstate and the amount of processed input. -void SHA3_Reset(KECCAK1600_CTX *ctx); - -// SHA3_Init initialises |ctx| fields and returns 1 on success and 0 on failure. +// SHA3_Init initialises |ctx| fields through |FIPS202_Init| and +// returns 1 on success and 0 on failure. OPENSSL_EXPORT int SHA3_Init(KECCAK1600_CTX *ctx, size_t bitlen); -// SHA3_Update processes all data blocks that don't need pad through -// |Keccak1600_Absorb| and returns 1 and 0 on failure. + // SHA3_Update check |ctx| pointer and |len| value, calls |FIPS202_Update| + // and returns 1 on success and 0 on failure. int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len); -// SHA3_Final pads the last data block and processes it through |Keccak1600_Absorb|. -// It processes the data through |Keccak1600_Squeeze| and returns 1 and 0 on failure. +// SHA3_Final pads the last data block and absorbs it through |FIPS202_Finalize|. +// It then calls |Keccak1600_Squeeze| and returns 1 on success +// and 0 on failure. int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx); -// Keccak1600_Absorb processes the largest multiple of |r| out of |len| bytes and -// returns the remaining number of bytes. +// SHAKE_Init initialises |ctx| fields through |FIPS202_Init| and +// returns 1 on success and 0 on failure. +int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size); + +// SHAKE_Absorb checks |ctx| pointer and |len| values. It updates and absorbs +// input blocks via |FIPS202_Update|. +int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, + size_t len); + +// SHAKE_Squeeze pads the last data block and absorbs it through +// |FIPS202_Finalize| on first call. It writes |len| bytes of incremental +// XOF output to |md| and returns 1 on success and 0 on failure. It can be +// called multiple times. +int SHAKE_Squeeze(uint8_t *md, KECCAK1600_CTX *ctx, size_t len); + +// SHAKE_Final writes |len| bytes of finalized extendible output to |md|, returns 1 on +// success and 0 on failure. It should be called once to finalize absorb and +// squeeze phases. Incremental XOF output should be generated via |SHAKE_Squeeze|. +int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len); + +// Keccak1600_Absorb processes the largest multiple of |r| (block size) out of +// |len| bytes and returns the remaining number of bytes. size_t Keccak1600_Absorb(uint64_t A[KECCAK1600_ROWS][KECCAK1600_ROWS], - const uint8_t *data, size_t len, size_t r); + const uint8_t *data, size_t len, size_t r); // Keccak1600_Squeeze generates |out| value of |len| bytes (per call). It can be called // multiple times when used as eXtendable Output Function. |padded| indicates diff --git a/crypto/fipsmodule/sha/sha3.c b/crypto/fipsmodule/sha/sha3.c index 708d554a6d..16b098023a 100644 --- a/crypto/fipsmodule/sha/sha3.c +++ b/crypto/fipsmodule/sha/sha3.c @@ -82,7 +82,7 @@ uint8_t *SHAKE128(const uint8_t *data, const size_t in_len, uint8_t *out, size_t FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; int ok = (SHAKE_Init(&ctx, SHAKE128_BLOCKSIZE) && - SHA3_Update(&ctx, data, in_len) && + SHAKE_Absorb(&ctx, data, in_len) && SHAKE_Final(out, &ctx, out_len)); OPENSSL_cleanse(&ctx, sizeof(ctx)); @@ -98,7 +98,7 @@ uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; int ok = (SHAKE_Init(&ctx, SHAKE256_BLOCKSIZE) && - SHA3_Update(&ctx, data, in_len) && + SHAKE_Absorb(&ctx, data, in_len) && SHAKE_Final(out, &ctx, out_len)); OPENSSL_cleanse(&ctx, sizeof(ctx)); FIPS_service_indicator_unlock_state(); @@ -111,9 +111,9 @@ uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t // FIPS202 APIs manage internal input/output buffer on top of Keccak1600 API layer static void FIPS202_Reset(KECCAK1600_CTX *ctx) { - memset(ctx->A, 0, sizeof(ctx->A)); + OPENSSL_memset(ctx->A, 0, sizeof(ctx->A)); ctx->buf_load = 0; - ctx->padded=0; + ctx->state = KECCAK1600_STATE_ABSORB; } static int FIPS202_Init(KECCAK1600_CTX *ctx, uint8_t pad, size_t block_size, size_t bit_len) { @@ -132,47 +132,31 @@ static int FIPS202_Init(KECCAK1600_CTX *ctx, uint8_t pad, size_t block_size, siz return 0; } -// SHA3 APIs implement SHA3 functionalities on top of FIPS202 API layer -void SHA3_Reset(KECCAK1600_CTX *ctx) { - memset(ctx->A, 0, sizeof(ctx->A)); - ctx->buf_load = 0; - ctx->padded = 0; -} - -int SHA3_Init(KECCAK1600_CTX *ctx, size_t bit_len) { - if (bit_len == SHA3_224_DIGEST_BITLENGTH || - bit_len == SHA3_256_DIGEST_BITLENGTH || - bit_len == SHA3_384_DIGEST_BITLENGTH || - bit_len == SHA3_512_DIGEST_BITLENGTH) { - // |block_size| depends on the SHA3 |bit_len| output (digest) length - return FIPS202_Init(ctx, SHA3_PAD_CHAR, SHA3_BLOCKSIZE(bit_len), bit_len); - } - return 0; -} - -int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { +static int FIPS202_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { uint8_t *data_ptr_copy = (uint8_t *) data; size_t block_size = ctx->block_size; size_t num, rem; - if (len == 0) { - return 1; + if (ctx->state == KECCAK1600_STATE_SQUEEZE || + ctx->state == KECCAK1600_STATE_FINAL ) { + return 0; } + // Case |len| equals 0 is checked in SHA3/SHAKE higher level APIs // Process intermediate buffer. num = ctx->buf_load; if (num != 0) { rem = block_size - num; if (len < rem) { - memcpy(ctx->buf + num, data_ptr_copy, len); + OPENSSL_memcpy(ctx->buf + num, data_ptr_copy, len); ctx->buf_load += len; return 1; } - // There is enough data to fill or overflow the intermediate - // buffer. So we append |rem| bytes and process the block, - // leaving the rest for later processing. - memcpy(ctx->buf + num, data_ptr_copy, rem); + // There is enough data to fill or overflow the intermediate + // buffer. So we append |rem| bytes and process the block, + // leaving the rest for later processing. + OPENSSL_memcpy(ctx->buf + num, data_ptr_copy, rem); data_ptr_copy += rem, len -= rem; if (Keccak1600_Absorb(ctx->A, ctx->buf, block_size, block_size) != 0 ) { return 0; @@ -189,54 +173,176 @@ int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { } if (rem != 0) { - memcpy(ctx->buf, data_ptr_copy + len - rem, rem); + OPENSSL_memcpy(ctx->buf, data_ptr_copy + len - rem, rem); ctx->buf_load = rem; } return 1; } -int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) { +// FIPS202_Finalize processes padding and absorb of last input block +// This function should be called once to finalize absorb and initiate squeeze phase +static int FIPS202_Finalize(uint8_t *md, KECCAK1600_CTX *ctx) { size_t block_size = ctx->block_size; size_t num = ctx->buf_load; - if (ctx->md_size == 0) { + if (ctx->state == KECCAK1600_STATE_SQUEEZE || + ctx->state == KECCAK1600_STATE_FINAL ) { + return 0; + } + + // Pad the data with 10*1. Note that |num| can be |block_size - 1| + // in which case both byte operations below are performed on + // the same byte. + OPENSSL_memset(ctx->buf + num, 0, block_size - num); + ctx->buf[num] = ctx->pad; + ctx->buf[block_size - 1] |= 0x80; + + if (Keccak1600_Absorb(ctx->A, ctx->buf, block_size, block_size) != 0) { + return 0; + } + + // ctx->buf is processed, ctx->buf_load is guaranteed to be zero + ctx->buf_load = 0; + + return 1; +} + +// SHA3 APIs implement SHA3 functionalities on top of FIPS202 API layer +int SHA3_Init(KECCAK1600_CTX *ctx, size_t bit_len) { + if (ctx == NULL) { + return 0; + } + + if (bit_len != SHA3_224_DIGEST_BITLENGTH && + bit_len != SHA3_256_DIGEST_BITLENGTH && + bit_len != SHA3_384_DIGEST_BITLENGTH && + bit_len != SHA3_512_DIGEST_BITLENGTH) { + return 0; + } + // |block_size| depends on the SHA3 |bit_len| output (digest) length + return FIPS202_Init(ctx, SHA3_PAD_CHAR, SHA3_BLOCKSIZE(bit_len), bit_len); +} + +int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { + if (ctx == NULL) { + return 0; + } + + if (data == NULL && len != 0) { + return 0; + } + + if (len == 0) { return 1; } - if (ctx->padded == 0) { - // Pad the data with 10*1. Note that |num| can be |block_size - 1| - // in which case both byte operations below are performed on - // the same byte. - memset(ctx->buf + num, 0, block_size - num); - ctx->buf[num] = ctx->pad; - ctx->buf[block_size - 1] |= 0x80; + return FIPS202_Update(ctx, data, len); +} - if (Keccak1600_Absorb(ctx->A, ctx->buf, block_size, block_size) != 0) { - return 0; - } +// SHA3_Final should be called once to process final digest value +int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) { + if (md == NULL || ctx == NULL) { + return 0; } - Keccak1600_Squeeze(ctx->A, md, ctx->md_size, block_size, ctx->padded); - ctx->padded = 1; + if (ctx->md_size == 0) { + return 1; + } - FIPS_service_indicator_update_state(); + if (FIPS202_Finalize(md, ctx) == 0) { + return 0; + } + Keccak1600_Squeeze(ctx->A, md, ctx->md_size, ctx->block_size, ctx->state); + ctx->state = KECCAK1600_STATE_FINAL; + + FIPS_service_indicator_update_state(); return 1; } -// SHAKE APIs implement SHAKE functionalities on top of FIPS202 API layer int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size) { - if (block_size == SHAKE128_BLOCKSIZE || - block_size == SHAKE256_BLOCKSIZE) { - // |block_size| depends on the SHAKE security level - // The output length |bit_len| is initialized to 0 - return FIPS202_Init(ctx, SHAKE_PAD_CHAR, block_size, 0); + if (ctx == NULL) { + return 0; + } + + if (block_size != SHAKE128_BLOCKSIZE && + block_size != SHAKE256_BLOCKSIZE) { + return 0; + } + // |block_size| depends on the SHAKE security level + // The output length |bit_len| is initialized to 0 + return FIPS202_Init(ctx, SHAKE_PAD_CHAR, block_size, 0); +} + +int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, size_t len) { + if (ctx == NULL) { + return 0; + } + + if (data == NULL && len != 0) { + return 0; + } + + if (len == 0) { + return 1; } - return 0; + + return FIPS202_Update(ctx, data, len); } +// SHAKE_Final is to be called once to finalize absorb and squeeze phases +// |ctx->state| restricts consecutive calls to FIPS202_Finalize +// Function SHAKE_Squeeze should be used for incremental XOF output int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) { + if (ctx == NULL || md == NULL) { + return 0; + } + ctx->md_size = len; - return SHA3_Final(md, ctx); + if (ctx->md_size == 0) { + return 1; + } + + if (FIPS202_Finalize(md, ctx) == 0) { + return 0; + } + + Keccak1600_Squeeze(ctx->A, md, ctx->md_size, ctx->block_size, ctx->state); + ctx->state = KECCAK1600_STATE_FINAL; + + FIPS_service_indicator_update_state(); + + return 1; +} + +// SHAKE_Squeeze can be called multiple time for incremental XOF output +int SHAKE_Squeeze(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) { + if (ctx == NULL || md == NULL) { + return 0; + } + + ctx->md_size = len; + + if (ctx->md_size == 0) { + return 1; + } + + if (ctx->state == KECCAK1600_STATE_FINAL) { + return 0; + } + + // Skip FIPS202_Finalize if the input has been padded and + // the last block has been processed + if (ctx->state == KECCAK1600_STATE_ABSORB) { + if (FIPS202_Finalize(md, ctx) == 0) { + return 0; + } + } + + Keccak1600_Squeeze(ctx->A, md, len, ctx->block_size, ctx->state); + ctx->state = KECCAK1600_STATE_SQUEEZE; + + //FIPS_service_indicator_update_state(); + return 1; } diff --git a/crypto/fipsmodule/sha/sha3_test.cc b/crypto/fipsmodule/sha/sha3_test.cc index 9e45ddf7d1..310a5af9c3 100644 --- a/crypto/fipsmodule/sha/sha3_test.cc +++ b/crypto/fipsmodule/sha/sha3_test.cc @@ -165,7 +165,6 @@ TEST(SHA3Test, NISTTestVectors) { }); } - TEST(SHA3Test, NISTTestVectors_SingleShot) { FileTestGTest("crypto/fipsmodule/sha/testvectors/SHA3_224ShortMsg.txt", [](FileTest *t) { diff --git a/crypto/ml_dsa/ml_dsa_ref/poly.c b/crypto/ml_dsa/ml_dsa_ref/poly.c index 42809f98e3..72d81d2e8d 100644 --- a/crypto/ml_dsa/ml_dsa_ref/poly.c +++ b/crypto/ml_dsa/ml_dsa_ref/poly.c @@ -316,9 +316,9 @@ void ml_dsa_poly_uniform(ml_dsa_poly *a, t[1] = nonce >> 8; SHAKE_Init(&state, SHAKE128_BLOCKSIZE); - SHA3_Update(&state, seed, ML_DSA_SEEDBYTES); - SHA3_Update(&state, t, 2); - SHAKE_Final(buf, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE); + SHAKE_Absorb(&state, seed, ML_DSA_SEEDBYTES); + SHAKE_Absorb(&state, t, 2); + SHAKE_Squeeze(buf, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE); ctr = ml_dsa_rej_uniform(a->coeffs, ML_DSA_N, buf, buflen); @@ -327,7 +327,7 @@ void ml_dsa_poly_uniform(ml_dsa_poly *a, for(i = 0; i < off; ++i) buf[i] = buf[buflen - off + i]; - SHAKE_Final(buf + off, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE); + SHAKE_Squeeze(buf + off, &state, POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCKSIZE); buflen = SHAKE128_BLOCKSIZE + off; ctr += ml_dsa_rej_uniform(a->coeffs + ctr, ML_DSA_N - ctr, buf, buflen); } @@ -418,16 +418,17 @@ void ml_dsa_poly_uniform_eta(ml_dsa_params *params, t[1] = nonce >> 8; SHAKE_Init(&state, SHAKE256_BLOCKSIZE); - SHA3_Update(&state, seed, ML_DSA_CRHBYTES); - SHA3_Update(&state, t, 2); - SHAKE_Final(buf, &state, ML_DSA_POLY_UNIFORM_ETA_NBLOCKS_MAX * SHAKE256_BLOCKSIZE); + SHAKE_Absorb(&state, seed, ML_DSA_CRHBYTES); + SHAKE_Absorb(&state, t, 2); + SHAKE_Squeeze(buf, &state, ML_DSA_POLY_UNIFORM_ETA_NBLOCKS_MAX * SHAKE256_BLOCKSIZE); ctr = rej_eta(params, a->coeffs, ML_DSA_N, buf, buflen); while(ctr < ML_DSA_N) { - SHAKE_Final(buf, &state, SHAKE256_BLOCKSIZE); + SHAKE_Squeeze(buf, &state, SHAKE256_BLOCKSIZE); ctr += rej_eta(params, a->coeffs + ctr, ML_DSA_N - ctr, buf, SHAKE256_BLOCKSIZE); } + /* FIPS 204. Section 3.6.3 Destruction of intermediate values. */ OPENSSL_cleanse(buf, sizeof(buf)); OPENSSL_cleanse(&state, sizeof(state)); @@ -459,9 +460,8 @@ void ml_dsa_poly_uniform_gamma1(ml_dsa_params *params, t[1] = nonce >> 8; SHAKE_Init(&state, SHAKE256_BLOCKSIZE); - SHA3_Update(&state, seed, ML_DSA_CRHBYTES); - SHA3_Update(&state, t, 2); - + SHAKE_Absorb(&state, seed, ML_DSA_CRHBYTES); + SHAKE_Absorb(&state, t, 2); SHAKE_Final(buf, &state, POLY_UNIFORM_GAMMA1_NBLOCKS * SHAKE256_BLOCKSIZE); ml_dsa_polyz_unpack(params, a, buf); /* FIPS 204. Section 3.6.3 Destruction of intermediate values. */ @@ -487,8 +487,8 @@ void ml_dsa_poly_challenge(ml_dsa_params *params, ml_dsa_poly *c, const uint8_t KECCAK1600_CTX state; SHAKE_Init(&state, SHAKE256_BLOCKSIZE); - SHA3_Update(&state, seed, params->c_tilde_bytes); - SHAKE_Final(buf, &state, SHAKE256_BLOCKSIZE); + SHAKE_Absorb(&state, seed, params->c_tilde_bytes); + SHAKE_Squeeze(buf, &state, SHAKE256_BLOCKSIZE); signs = 0; for(i = 0; i < 8; ++i) { @@ -502,7 +502,7 @@ void ml_dsa_poly_challenge(ml_dsa_params *params, ml_dsa_poly *c, const uint8_t for(i = ML_DSA_N-params->tau; i < ML_DSA_N; ++i) { do { if(pos >= SHAKE256_BLOCKSIZE) { - SHAKE_Final(buf, &state, SHAKE256_BLOCKSIZE); + SHAKE_Squeeze(buf, &state, SHAKE256_BLOCKSIZE); pos = 0; } diff --git a/crypto/ml_dsa/ml_dsa_ref/sign.c b/crypto/ml_dsa/ml_dsa_ref/sign.c index 7f7d1b8982..95073110e4 100644 --- a/crypto/ml_dsa/ml_dsa_ref/sign.c +++ b/crypto/ml_dsa/ml_dsa_ref/sign.c @@ -201,9 +201,9 @@ int ml_dsa_sign_internal(ml_dsa_params *params, /* FIPS 204: line 7 Compute rhoprime = CRH(key, rnd, mu) */ SHAKE_Init(&state, SHAKE256_BLOCKSIZE); - SHA3_Update(&state, key, ML_DSA_SEEDBYTES); - SHA3_Update(&state, rnd, ML_DSA_RNDBYTES); - SHA3_Update(&state, mu, ML_DSA_CRHBYTES); + SHAKE_Absorb(&state, key, ML_DSA_SEEDBYTES); + SHAKE_Absorb(&state, rnd, ML_DSA_RNDBYTES); + SHAKE_Absorb(&state, mu, ML_DSA_CRHBYTES); SHAKE_Final(rhoprime, &state, ML_DSA_CRHBYTES); /* FIPS 204: line 5 Expand matrix and transform vectors */ @@ -229,8 +229,8 @@ int ml_dsa_sign_internal(ml_dsa_params *params, ml_dsa_polyveck_pack_w1(params, sig, &w1); SHAKE_Init(&state, SHAKE256_BLOCKSIZE); - SHA3_Update(&state, mu, ML_DSA_CRHBYTES); - SHA3_Update(&state, sig, params->k * params->poly_w1_packed_bytes); + SHAKE_Absorb(&state, mu, ML_DSA_CRHBYTES); + SHAKE_Absorb(&state, sig, params->k * params->poly_w1_packed_bytes); SHAKE_Final(sig, &state, params->c_tilde_bytes); ml_dsa_poly_challenge(params, &cp, sig); ml_dsa_poly_ntt(&cp); @@ -501,9 +501,10 @@ int ml_dsa_verify_internal(ml_dsa_params *params, /* FIPS 204: line 12 Call random oracle and verify challenge */ SHAKE_Init(&state, SHAKE256_BLOCKSIZE); - SHA3_Update(&state, mu, ML_DSA_CRHBYTES); - SHA3_Update(&state, buf, params->k * params->poly_w1_packed_bytes); + SHAKE_Absorb(&state, mu, ML_DSA_CRHBYTES); + SHAKE_Absorb(&state, buf, params->k * params->poly_w1_packed_bytes); SHAKE_Final(c2, &state, params->c_tilde_bytes); + for(i = 0; i < params->c_tilde_bytes; ++i) { if(c[i] != c2[i]) { return -1; diff --git a/crypto/pkcs8/pkcs12_test.cc b/crypto/pkcs8/pkcs12_test.cc index 2a78a9794c..147567118a 100644 --- a/crypto/pkcs8/pkcs12_test.cc +++ b/crypto/pkcs8/pkcs12_test.cc @@ -395,6 +395,33 @@ static void TestRoundTrip(const char *password, const char *name, ASSERT_TRUE(certs2); ASSERT_TRUE(PKCS12_get_key_and_certs(&key2, certs2.get(), &cbs, password)); bssl::UniquePtr free_key2(key2); + + // Check that writing to a |BIO| does the same thing. + bssl::UniquePtr bio(BIO_new(BIO_s_mem())); + ASSERT_TRUE(bio); + ASSERT_TRUE(i2d_PKCS12_bio(bio.get(), pkcs12.get())); + const uint8_t *bio_data; + size_t bio_len; + ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_data, &bio_len)); + EXPECT_EQ(Bytes(bio_data, bio_len), Bytes(der, len)); + + // Check that the result round-trips with |PKCS12_set_mac| as well. The + // resulting bytes will be different due to the regenerated salt, but |pkcs12| + // should still be in a usable state with the same certs and keys encoded. + uint8_t *der2 = nullptr; + EXPECT_TRUE(PKCS12_set_mac(pkcs12.get(), password, strlen(password), nullptr, + 0, mac_iterations, nullptr)); + len = i2d_PKCS12(pkcs12.get(), &der2); + ASSERT_GT(len, 0); + bssl::UniquePtr free_der2(der2); + + CBS_init(&cbs, der2, len); + EVP_PKEY *key3 = nullptr; + certs2.reset(sk_X509_new_null()); + ASSERT_TRUE(certs2); + ASSERT_TRUE(PKCS12_get_key_and_certs(&key3, certs2.get(), &cbs, password)); + bssl::UniquePtr free_key3(key3); + // Note |EVP_PKEY_cmp| returns one for equality while |X509_cmp| returns zero. if (key) { EXPECT_EQ(1, EVP_PKEY_cmp(key2, key.get())); @@ -421,15 +448,6 @@ static void TestRoundTrip(const char *password, const char *name, static_cast(actual_name_len))); } } - - // Check that writing to a |BIO| does the same thing. - bssl::UniquePtr bio(BIO_new(BIO_s_mem())); - ASSERT_TRUE(bio); - ASSERT_TRUE(i2d_PKCS12_bio(bio.get(), pkcs12.get())); - const uint8_t *bio_data; - size_t bio_len; - ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_data, &bio_len)); - EXPECT_EQ(Bytes(bio_data, bio_len), Bytes(der, len)); } TEST(PKCS12Test, RoundTrip) { @@ -533,7 +551,7 @@ static bssl::UniquePtr MakeTestCert(EVP_PKEY *key) { return x509; } -static bool PKCS12CreateVector(std::vector *out, EVP_PKEY *pkey, +static bool PKCS12CreateVector(bssl::UniquePtr &p12, EVP_PKEY *pkey, const std::vector &certs) { bssl::UniquePtr chain(sk_X509_new_null()); if (!chain) { @@ -546,31 +564,17 @@ static bool PKCS12CreateVector(std::vector *out, EVP_PKEY *pkey, } } - bssl::UniquePtr p12(PKCS12_create(kPassword, nullptr /* name */, pkey, - nullptr /* cert */, chain.get(), 0, - 0, 0, 0, 0)); + p12.reset(PKCS12_create(kPassword, nullptr /* name */, pkey, + nullptr /* cert */, chain.get(), 0, 0, 0, 0, 0)); if (!p12) { return false; } - - int len = i2d_PKCS12(p12.get(), nullptr); - if (len < 0) { - return false; - } - out->resize(static_cast(len)); - uint8_t *ptr = out->data(); - return i2d_PKCS12(p12.get(), &ptr) == len; + return true; } -static void ExpectPKCS12Parse(bssl::Span in, +static void ExpectPKCS12Parse(bssl::UniquePtr &p12, EVP_PKEY *expect_key, X509 *expect_cert, const std::vector &expect_ca_certs) { - bssl::UniquePtr bio(BIO_new_mem_buf(in.data(), in.size())); - ASSERT_TRUE(bio); - - bssl::UniquePtr p12(d2i_PKCS12_bio(bio.get(), nullptr)); - ASSERT_TRUE(p12); - EVP_PKEY *key = nullptr; X509 *cert = nullptr; STACK_OF(X509) *ca_certs = nullptr; @@ -618,33 +622,32 @@ TEST(PKCS12Test, Order) { ASSERT_TRUE(cert3); // PKCS12_parse uses the key to select the main certificate. - std::vector p12; - ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), + bssl::UniquePtr p12; + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert1.get(), cert2.get(), cert3.get()})); ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()}); - ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert3.get(), cert1.get(), cert2.get()})); ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert3.get(), cert2.get()}); - ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert2.get(), cert3.get(), cert1.get()})); ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()}); // In case of duplicates, the last one is selected. (It is unlikely anything // depends on which is selected, but we match OpenSSL.) - ASSERT_TRUE( - PKCS12CreateVector(&p12, key1.get(), {cert1.get(), cert1b.get()})); + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert1.get(), cert1b.get()})); ExpectPKCS12Parse(p12, key1.get(), cert1b.get(), {cert1.get()}); // If there is no key, all certificates are returned as "CA" certificates. - ASSERT_TRUE(PKCS12CreateVector(&p12, nullptr, + ASSERT_TRUE(PKCS12CreateVector(p12, nullptr, {cert1.get(), cert2.get(), cert3.get()})); ExpectPKCS12Parse(p12, nullptr, nullptr, {cert1.get(), cert2.get(), cert3.get()}); // The same happens if there is a key, but it does not match any certificate. - ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), {cert2.get(), cert3.get()})); + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), {cert2.get(), cert3.get()})); ExpectPKCS12Parse(p12, key1.get(), nullptr, {cert2.get(), cert3.get()}); } @@ -663,13 +666,8 @@ TEST(PKCS12Test, CreateWithAlias) { ASSERT_EQ(res, 1); std::vector certs = {cert1.get(), cert2.get()}; - std::vector der; - ASSERT_TRUE(PKCS12CreateVector(&der, key.get(), certs)); - - bssl::UniquePtr bio(BIO_new_mem_buf(der.data(), der.size())); - ASSERT_TRUE(bio); - bssl::UniquePtr p12(d2i_PKCS12_bio(bio.get(), nullptr)); - ASSERT_TRUE(p12); + bssl::UniquePtr p12; + ASSERT_TRUE(PKCS12CreateVector(p12, key.get(), certs)); EVP_PKEY *parsed_key = nullptr; X509 *parsed_cert = nullptr; @@ -695,3 +693,48 @@ TEST(PKCS12Test, BasicAlloc) { bssl::UniquePtr p12(PKCS12_new()); ASSERT_TRUE(p12); } + +TEST(PKCS12Test, SetMac) { + bssl::UniquePtr key1 = MakeTestKey(); + ASSERT_TRUE(key1); + bssl::UniquePtr cert1 = MakeTestCert(key1.get()); + ASSERT_TRUE(cert1); + bssl::UniquePtr cert1b = MakeTestCert(key1.get()); + ASSERT_TRUE(cert1b); + bssl::UniquePtr key2 = MakeTestKey(); + ASSERT_TRUE(key2); + bssl::UniquePtr cert2 = MakeTestCert(key2.get()); + ASSERT_TRUE(cert2); + bssl::UniquePtr key3 = MakeTestKey(); + ASSERT_TRUE(key3); + bssl::UniquePtr cert3 = MakeTestCert(key3.get()); + ASSERT_TRUE(cert3); + + // PKCS12_parse uses the key to select the main certificate. + bssl::UniquePtr p12; + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), + {cert1.get(), cert2.get(), cert3.get()})); + EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr, + 0, 0, nullptr)); + ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()}); + + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), + {cert3.get(), cert1.get(), cert2.get()})); + EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr, + 0, 0, nullptr)); + ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert3.get(), cert2.get()}); + + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), + {cert2.get(), cert3.get(), cert1.get()})); + EXPECT_TRUE(PKCS12_set_mac(p12.get(), kPassword, strlen(kPassword), nullptr, + 0, 0, nullptr)); + ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()}); + + // The same password should be used with |PKCS12_create| and |PKCS12_set_mac|. + ASSERT_TRUE(PKCS12CreateVector(p12, key1.get(), + {cert1.get(), cert2.get(), cert3.get()})); + EXPECT_FALSE(PKCS12_set_mac(p12.get(), kUnicodePassword, + strlen(kUnicodePassword), nullptr, 0, 0, + nullptr)); +} + diff --git a/crypto/pkcs8/pkcs8_x509.c b/crypto/pkcs8/pkcs8_x509.c index 2e7148c45e..a0bdb4b5e9 100644 --- a/crypto/pkcs8/pkcs8_x509.c +++ b/crypto/pkcs8/pkcs8_x509.c @@ -1114,6 +1114,44 @@ static int add_encrypted_data(CBB *out, int pbe_nid, const char *password, return ret; } +static int pkcs12_gen_and_write_mac(CBB *out_pfx, const uint8_t *auth_safe_data, + size_t auth_safe_data_len, + const char *password, size_t password_len, + uint8_t *mac_salt, size_t salt_len, + int mac_iterations, const EVP_MD *md) { + int ret = 0; + uint8_t mac_key[EVP_MAX_MD_SIZE]; + uint8_t mac[EVP_MAX_MD_SIZE]; + unsigned mac_len; + if (!pkcs12_key_gen(password, password_len, mac_salt, salt_len, PKCS12_MAC_ID, + mac_iterations, EVP_MD_size(md), mac_key, md) || + !HMAC(md, mac_key, EVP_MD_size(md), auth_safe_data, auth_safe_data_len, + mac, &mac_len)) { + goto out; + } + + CBB mac_data, digest_info, mac_cbb, mac_salt_cbb; + if (!CBB_add_asn1(out_pfx, &mac_data, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1(&mac_data, &digest_info, CBS_ASN1_SEQUENCE) || + !EVP_marshal_digest_algorithm(&digest_info, md) || + !CBB_add_asn1(&digest_info, &mac_cbb, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&mac_cbb, mac, mac_len) || + !CBB_add_asn1(&mac_data, &mac_salt_cbb, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&mac_salt_cbb, mac_salt, salt_len) || + // The iteration count has a DEFAULT of 1, but RFC 7292 says "The default + // is for historical reasons and its use is deprecated." Thus we + // explicitly encode the iteration count, though it is not valid DER. + !CBB_add_asn1_uint64(&mac_data, mac_iterations) || + !CBB_flush(out_pfx)) { + goto out; + } + ret = 1; + +out: + OPENSSL_cleanse(mac_key, sizeof(mac_key)); + return ret; +} + PKCS12 *PKCS12_create(const char *password, const char *name, const EVP_PKEY *pkey, X509 *cert, const STACK_OF(X509)* chain, int key_nid, int cert_nid, @@ -1194,9 +1232,7 @@ PKCS12 *PKCS12_create(const char *password, const char *name, PKCS12 *ret = NULL; CBB cbb, pfx, auth_safe, auth_safe_oid, auth_safe_wrapper, auth_safe_data, content_infos; - uint8_t mac_key[EVP_MAX_MD_SIZE]; - if (!CBB_init(&cbb, 0) || - !CBB_add_asn1(&cbb, &pfx, CBS_ASN1_SEQUENCE) || + if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &pfx, CBS_ASN1_SEQUENCE) || !CBB_add_asn1_uint64(&pfx, 3) || // auth_safe is a data ContentInfo. !CBB_add_asn1(&pfx, &auth_safe, CBS_ASN1_SEQUENCE) || @@ -1299,32 +1335,15 @@ PKCS12 *PKCS12_create(const char *password, const char *name, // Compute the MAC. Match OpenSSL in using SHA-1 as the hash function. The MAC // covers |auth_safe_data|. + // TODO (CryptoAlg-2897): Update the default |md| to SHA-256 to align with + // OpenSSL 3.x. const EVP_MD *mac_md = EVP_sha1(); uint8_t mac_salt[PKCS5_SALT_LEN]; - uint8_t mac[EVP_MAX_MD_SIZE]; - unsigned mac_len; if (!CBB_flush(&auth_safe_data) || !RAND_bytes(mac_salt, sizeof(mac_salt)) || - !pkcs12_key_gen(password, password_len, mac_salt, sizeof(mac_salt), - PKCS12_MAC_ID, mac_iterations, EVP_MD_size(mac_md), - mac_key, mac_md) || - !HMAC(mac_md, mac_key, EVP_MD_size(mac_md), CBB_data(&auth_safe_data), - CBB_len(&auth_safe_data), mac, &mac_len)) { - goto err; - } - - CBB mac_data, digest_info, mac_cbb, mac_salt_cbb; - if (!CBB_add_asn1(&pfx, &mac_data, CBS_ASN1_SEQUENCE) || - !CBB_add_asn1(&mac_data, &digest_info, CBS_ASN1_SEQUENCE) || - !EVP_marshal_digest_algorithm(&digest_info, mac_md) || - !CBB_add_asn1(&digest_info, &mac_cbb, CBS_ASN1_OCTETSTRING) || - !CBB_add_bytes(&mac_cbb, mac, mac_len) || - !CBB_add_asn1(&mac_data, &mac_salt_cbb, CBS_ASN1_OCTETSTRING) || - !CBB_add_bytes(&mac_salt_cbb, mac_salt, sizeof(mac_salt)) || - // The iteration count has a DEFAULT of 1, but RFC 7292 says "The default - // is for historical reasons and its use is deprecated." Thus we - // explicitly encode the iteration count, though it is not valid DER. - !CBB_add_asn1_uint64(&mac_data, mac_iterations)) { + !pkcs12_gen_and_write_mac( + &pfx, CBB_data(&auth_safe_data), CBB_len(&auth_safe_data), password, + password_len, mac_salt, sizeof(mac_salt), mac_iterations, mac_md)) { goto err; } @@ -1337,7 +1356,6 @@ PKCS12 *PKCS12_create(const char *password, const char *name, } err: - OPENSSL_cleanse(mac_key, sizeof(mac_key)); CBB_cleanup(&cbb); return ret; } @@ -1353,3 +1371,102 @@ void PKCS12_free(PKCS12 *p12) { OPENSSL_free(p12->ber_bytes); OPENSSL_free(p12); } + +int PKCS12_set_mac(PKCS12 *p12, const char *password, int password_len, + unsigned char *salt, int salt_len, int mac_iterations, + const EVP_MD *md) { + GUARD_PTR(p12); + int ret = 0; + + if (mac_iterations == 0) { + mac_iterations = 1; + } + if (salt_len == 0) { + salt_len = PKCS5_SALT_LEN; + } + // Generate |mac_salt| if |salt| is NULL and copy if NULL. + uint8_t *mac_salt = OPENSSL_malloc(salt_len); + if (mac_salt == NULL) { + goto out; + } + if (salt == NULL) { + if (!RAND_bytes(mac_salt, salt_len)) { + goto out; + } + } else { + OPENSSL_memcpy(mac_salt, salt, salt_len); + } + // TODO (CryptoAlg-2897): Update the default |md| to SHA-256 to align with + // OpenSSL 3.x. + if (md == NULL) { + md = EVP_sha1(); + } + + uint8_t *storage = NULL; + CBS ber_bytes, in, pfx, authsafe, content_type, wrapped_authsafes, authsafes; + uint64_t version; + // The input may be in BER format. + CBS_init(&ber_bytes, p12->ber_bytes, p12->ber_len); + if (!CBS_asn1_ber_to_der(&ber_bytes, &in, &storage)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA); + goto out; + } + // There's no use case for |storage| anymore, so we free early. + OPENSSL_free(storage); + + if (!CBS_get_asn1(&in, &pfx, CBS_ASN1_SEQUENCE) || CBS_len(&in) != 0 || + !CBS_get_asn1_uint64(&pfx, &version)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA); + goto out; + } + if (version < 3) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_VERSION); + goto out; + } + + if (!CBS_get_asn1(&pfx, &authsafe, CBS_ASN1_SEQUENCE)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA); + goto out; + } + // Save contents of |authsafe| to write back before the CBS is advanced. + const uint8_t *orig_authsafe = CBS_data(&authsafe); + size_t orig_authsafe_len = CBS_len(&authsafe); + + // Parse for |authsafes| which is the data that we should be running HMAC on. + if (!CBS_get_asn1(&authsafe, &content_type, CBS_ASN1_OBJECT) || + !CBS_get_asn1(&authsafe, &wrapped_authsafes, + CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || + !CBS_get_asn1(&wrapped_authsafes, &authsafes, CBS_ASN1_OCTETSTRING)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA); + goto out; + } + + // Rewrite contents of |p12| with the original contents and updated MAC. + CBB cbb, out_pfx, out_auth_safe; + if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &out_pfx, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1_uint64(&out_pfx, version) || + !CBB_add_asn1(&out_pfx, &out_auth_safe, CBS_ASN1_SEQUENCE) || + !CBB_add_bytes(&out_auth_safe, orig_authsafe, orig_authsafe_len) || + !pkcs12_gen_and_write_mac(&out_pfx, CBS_data(&authsafes), + CBS_len(&authsafes), password, password_len, + mac_salt, salt_len, mac_iterations, md)) { + CBB_cleanup(&cbb); + goto out; + } + + // Verify that the new password is consistent with the original. This is + // behavior specific to AWS-LC. + OPENSSL_free(p12->ber_bytes); + if (!CBB_finish(&cbb, &p12->ber_bytes, &p12->ber_len) || + !PKCS12_verify_mac(p12, password, password_len)) { + CBB_cleanup(&cbb); + goto out; + } + + ret = 1; + +out: + OPENSSL_free(mac_salt); + return ret; +} + diff --git a/crypto/pqdsa/internal.h b/crypto/pqdsa/internal.h index 7a3aabdd66..390489a8f0 100644 --- a/crypto/pqdsa/internal.h +++ b/crypto/pqdsa/internal.h @@ -15,6 +15,10 @@ typedef struct { int (*pqdsa_keygen)(uint8_t *public_key, uint8_t *private_key); + int (*pqdsa_keygen_internal)(uint8_t *public_key, + uint8_t *private_key, + const uint8_t *seed); + int (*pqdsa_sign_message)(const uint8_t *private_key, uint8_t *sig, size_t *sig_len, @@ -76,6 +80,7 @@ PQDSA_KEY *PQDSA_KEY_new(void); void PQDSA_KEY_free(PQDSA_KEY *key); int EVP_PKEY_pqdsa_set_params(EVP_PKEY *pkey, int nid); +int PQDSA_KEY_set_raw_keypair_from_seed(PQDSA_KEY *key, CBS *in); int PQDSA_KEY_set_raw_public_key(PQDSA_KEY *key, CBS *in); int PQDSA_KEY_set_raw_private_key(PQDSA_KEY *key, CBS *in); #if defined(__cplusplus) diff --git a/crypto/pqdsa/pqdsa.c b/crypto/pqdsa/pqdsa.c index 49f5f9d58d..84e3bb09f9 100644 --- a/crypto/pqdsa/pqdsa.c +++ b/crypto/pqdsa/pqdsa.c @@ -81,6 +81,38 @@ int PQDSA_KEY_set_raw_public_key(PQDSA_KEY *key, CBS *in) { return 1; } +int PQDSA_KEY_set_raw_keypair_from_seed(PQDSA_KEY *key, CBS *in) { + // Check if the parsed length corresponds with the expected length. + if (CBS_len(in) != key->pqdsa->keygen_seed_len) { + OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); + return 0; + } + + //allocate buffers to store key pair + uint8_t *public_key = OPENSSL_malloc(key->pqdsa->public_key_len); + uint8_t *private_key = OPENSSL_malloc(key->pqdsa->private_key_len); + + // check buffers are allocated + if (public_key == NULL || private_key == NULL) { + OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE); + return 0; + } + + // attempt to generate the key from the provided seed + if (!key->pqdsa->method->pqdsa_keygen_internal(public_key, + private_key, + CBS_data(in))) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + return 0; + } + + // set the public and private key + key->public_key = public_key; + key->private_key = private_key; + + return 1; +} + int PQDSA_KEY_set_raw_private_key(PQDSA_KEY *key, CBS *in) { // Check if the parsed length corresponds with the expected length. if (CBS_len(in) != key->pqdsa->private_key_len) { @@ -93,11 +125,30 @@ int PQDSA_KEY_set_raw_private_key(PQDSA_KEY *key, CBS *in) { return 0; } + // Create buffers to store public key based on size + size_t pk_len = key->pqdsa->public_key_len; + uint8_t *public_key = OPENSSL_malloc(pk_len); + + if (public_key == NULL) { + OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE); + return 0; + } + + // Construct the public key from the private key + if (!key->pqdsa->method->pqdsa_pack_pk_from_sk(public_key, key->private_key)) { + OPENSSL_free(public_key); + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + return 0; + } + + key->public_key = public_key; + return 1; } static const PQDSA_METHOD sig_ml_dsa_44_method = { ml_dsa_44_keypair, + ml_dsa_44_keypair_internal, ml_dsa_44_sign, ml_dsa_extmu_44_sign, ml_dsa_44_verify, @@ -107,6 +158,7 @@ static const PQDSA_METHOD sig_ml_dsa_44_method = { static const PQDSA_METHOD sig_ml_dsa_65_method = { ml_dsa_65_keypair, + ml_dsa_65_keypair_internal, ml_dsa_65_sign, ml_dsa_extmu_65_sign, ml_dsa_65_verify, @@ -116,6 +168,7 @@ static const PQDSA_METHOD sig_ml_dsa_65_method = { static const PQDSA_METHOD sig_ml_dsa_87_method = { ml_dsa_87_keypair, + ml_dsa_87_keypair_internal, ml_dsa_87_sign, ml_dsa_extmu_87_sign, ml_dsa_87_verify, diff --git a/include/openssl/evp.h b/include/openssl/evp.h index d7a18271a3..306dcd7096 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -965,8 +965,10 @@ OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_pqdsa_new_raw_public_key(int nid, const uint8_ // EVP_PKEY_pqdsa_new_raw_private_key generates a new EVP_PKEY object of type // EVP_PKEY_PQDSA, initializes the PQDSA key based on |nid| and populates the -// secret key part of the PQDSA key with the contents of |in|. It returns the -// pointer to the allocated PKEY on sucess and NULL on error. +// secret key part of the PQDSA key with the contents of |in|. If the contents +// of |in| is the private key seed, then this function will generate the +// corresponding key pair and populate both public and private parts of the PKEY. +// It returns the pointer to the allocated PKEY on sucess and NULL on error. OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_pqdsa_new_raw_private_key(int nid, const uint8_t *in, size_t len); // Diffie-Hellman-specific control functions. diff --git a/include/openssl/pkcs8.h b/include/openssl/pkcs8.h index a6fd1c4cfe..027f7900df 100644 --- a/include/openssl/pkcs8.h +++ b/include/openssl/pkcs8.h @@ -125,8 +125,8 @@ OPENSSL_EXPORT EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs, // Any friendlyName attributes (RFC 2985) in the PKCS#12 structure will be // returned on the |X509| objects as aliases. See also |X509_alias_get0|. OPENSSL_EXPORT int PKCS12_get_key_and_certs(EVP_PKEY **out_key, - STACK_OF(X509) *out_certs, - CBS *in, const char *password); + STACK_OF(X509) *out_certs, CBS *in, + const char *password); // Deprecated functions. @@ -149,10 +149,10 @@ OPENSSL_EXPORT PKCS12 *d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes, size_t ber_len); // d2i_PKCS12_bio acts like |d2i_PKCS12| but reads from a |BIO|. -OPENSSL_EXPORT PKCS12* d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12); +OPENSSL_EXPORT PKCS12 *d2i_PKCS12_bio(BIO *bio, PKCS12 **out_p12); // d2i_PKCS12_fp acts like |d2i_PKCS12| but reads from a |FILE|. -OPENSSL_EXPORT PKCS12* d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12); +OPENSSL_EXPORT PKCS12 *d2i_PKCS12_fp(FILE *fp, PKCS12 **out_p12); // i2d_PKCS12 is a dummy function which copies the contents of |p12|. If |out| // is not NULL then the result is written to |*out| and |*out| is advanced just @@ -188,6 +188,23 @@ OPENSSL_EXPORT int PKCS12_parse(const PKCS12 *p12, const char *password, EVP_PKEY **out_pkey, X509 **out_cert, STACK_OF(X509) **out_ca_certs); +// PKCS12_set_mac generates the MAC for |p12| with the designated |password|, +// |salt|, |mac_iterations|, and |md| specified. |password| MUST be the same +// password originally used to encrypt |p12|. Although OpenSSL will allow an +// invalid state with a different |password|, AWS-LC will throw an error and +// return 0. +// +// If |salt| is NULL, a random salt of |salt_len| bytes is generated. If +// |salt_len| is zero, a default salt length is used instead. +// If |md| is NULL, the default is use SHA1 to align with OpenSSL. +// +// TODO (CryptoAlg-2897): Update the default |md| to SHA-256 to align with +// OpenSSL 3.x. +OPENSSL_EXPORT int PKCS12_set_mac(PKCS12 *p12, const char *password, + int password_len, unsigned char *salt, + int salt_len, int mac_iterations, + const EVP_MD *md); + // PKCS12_verify_mac returns one if |password| is a valid password for |p12| // and zero otherwise. Since |PKCS12_parse| doesn't take a length parameter, // it's not actually possible to use a non-NUL-terminated password to actually diff --git a/tests/ci/cdk/cdk/codebuild/github_ci_integration_omnibus.yaml b/tests/ci/cdk/cdk/codebuild/github_ci_integration_omnibus.yaml index ce5c38d2aa..5af3d89741 100644 --- a/tests/ci/cdk/cdk/codebuild/github_ci_integration_omnibus.yaml +++ b/tests/ci/cdk/cdk/codebuild/github_ci_integration_omnibus.yaml @@ -250,3 +250,13 @@ batch: image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-22.04_gcc-12x_latest variables: AWS_LC_CI_TARGET: "tests/ci/integration/run_ntp_integration.sh" + + - identifier: pq_tls_integration_x86_64 + buildspec: tests/ci/codebuild/common/run_simple_target.yml + env: + type: LINUX_CONTAINER + privileged-mode: false + compute-type: BUILD_GENERAL1_SMALL + image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-22.04_gcc-12x_latest + variables: + AWS_LC_CI_TARGET: "tests/ci/integration/run_pq_tls_integration.sh" diff --git a/tests/ci/cdk/cdk/ecr_stack.py b/tests/ci/cdk/cdk/ecr_stack.py index 542de0f32e..ff8a4b54ed 100644 --- a/tests/ci/cdk/cdk/ecr_stack.py +++ b/tests/ci/cdk/cdk/ecr_stack.py @@ -11,5 +11,6 @@ class EcrStack(Stack): def __init__(self, scope: Construct, id: str, repo_name: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) - ecr.Repository(scope=self, id=id, repository_name=repo_name).grant_pull_push( - iam.ServicePrincipal("codebuild.amazonaws.com")) + repo = ecr.Repository(scope=self, id=id, repository_name=repo_name) + repo.grant_pull_push(iam.ServicePrincipal("codebuild.amazonaws.com")) + repo.grant_pull(iam.ArnPrincipal("arn:aws:iam::222961743098:role/scrutini-ecr")) diff --git a/tests/ci/cdk/util/iam_policies.py b/tests/ci/cdk/util/iam_policies.py index a9202ce6bf..2558fff73a 100644 --- a/tests/ci/cdk/util/iam_policies.py +++ b/tests/ci/cdk/util/iam_policies.py @@ -207,17 +207,6 @@ def ecr_power_user_policy_in_json(ecr_repo_names): "ecr:PutImage" ], "Resource": ecr_arns - }, - { - "Sid": "scrutinice", - "Effect": "Allow", - "Principal": { - "AWS": "arn:aws:iam::222961743098:role/scrutini-ecr" - }, - "Action": [ - "ecr:BatchGetImage", - "ecr:GetDownloadUrlForLayer" - ] } ] } diff --git a/tests/ci/integration/run_pq_tls_integration.sh b/tests/ci/integration/run_pq_tls_integration.sh new file mode 100755 index 0000000000..b44425c70e --- /dev/null +++ b/tests/ci/integration/run_pq_tls_integration.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC +set -ex + +source tests/ci/common_posix_setup.sh + +SCRATCH_FOLDER=${SYS_ROOT}/"pq-tls-scratch" + +AWS_LC_BUILD_FOLDER="${SCRATCH_FOLDER}/aws-lc-build" +AWS_LC_INSTALL_FOLDER="${SCRATCH_FOLDER}/aws-lc-install" + +S2N_URL='https://github.com/aws/s2n-tls.git' +S2N_BRANCH='main' +S2N_TLS_SRC_FOLDER="${SCRATCH_FOLDER}/s2n-tls" +S2N_TLS_BUILD_FOLDER="${SCRATCH_FOLDER}/s2n-tls-build" + +rm -rf "${SCRATCH_FOLDER:?}" +mkdir -p "$SCRATCH_FOLDER" + +echo "build and install aws-lc" +aws_lc_build "$SRC_ROOT" "$AWS_LC_BUILD_FOLDER" "$AWS_LC_INSTALL_FOLDER" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=OFF + +echo "clone s2n_tls" +git clone --depth 1 --branch "$S2N_BRANCH" "$S2N_URL" "$S2N_TLS_SRC_FOLDER" + +echo "build s2n_tls with aws-lc" +cd "$S2N_TLS_SRC_FOLDER" +cmake . "-B$S2N_TLS_BUILD_FOLDER" -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$AWS_LC_INSTALL_FOLDER" +ninja -C "$S2N_TLS_BUILD_FOLDER" -j "$NUM_CPU_THREADS" + +for GROUP in X25519MLKEM768 SecP256r1MLKEM768; do + echo "TLS Handshake: aws-lc server (bssl) with s2n-tls client (s2nc) for group $GROUP" + "$AWS_LC_BUILD_FOLDER"/tool/bssl s_server -curves $GROUP -accept 45000 -debug \ + &> "$AWS_LC_BUILD_FOLDER"/s_server_out & + sleep 2 # to allow for the server to startup in the background thread + S_PID=$! + # Relying on s2nc behavior that it exits after the first handshake + "$S2N_TLS_BUILD_FOLDER"/bin/s2nc -c default_pq -i localhost 45000 &> "$S2N_TLS_BUILD_FOLDER"/s2nc_out + wait $S_PID || true + cat "$AWS_LC_BUILD_FOLDER"/s_server_out + cat "$S2N_TLS_BUILD_FOLDER"/s2nc_out + grep "libcrypto" "$S2N_TLS_BUILD_FOLDER"/s2nc_out | grep "AWS-LC" + grep "CONNECTED" "$S2N_TLS_BUILD_FOLDER"/s2nc_out + grep "KEM Group" "$S2N_TLS_BUILD_FOLDER"/s2nc_out | grep "$GROUP" + + echo "TLS Handshake: s2n-tls server (s2nd) with aws-lc client (bssl) for group $GROUP" + "$S2N_TLS_BUILD_FOLDER"/bin/s2nd -c default_pq -i localhost 45000 &> "$S2N_TLS_BUILD_FOLDER"/s2nd_out & + sleep 2 # to allow for the server to startup in the background thread + S_PID=$! + # bssl s_client normally does not exit after a handshake, but when run as a background process + # seems to exit by closing the connection after the first handshake. Relying on that behavior here. + "$AWS_LC_BUILD_FOLDER"/tool/bssl s_client -curves $GROUP -connect localhost:45000 -debug \ + &> "$AWS_LC_BUILD_FOLDER"/s_client_out & + wait $S_PID || true + cat "$S2N_TLS_BUILD_FOLDER"/s2nd_out + cat "$AWS_LC_BUILD_FOLDER"/s_client_out + grep "libcrypto" "$S2N_TLS_BUILD_FOLDER"/s2nd_out | grep "AWS-LC" + grep "CONNECTED" "$S2N_TLS_BUILD_FOLDER"/s2nd_out + grep "KEM Group" "$S2N_TLS_BUILD_FOLDER"/s2nd_out | grep "$GROUP" +done + +rm -rf "${SCRATCH_FOLDER:?}"