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/ml_kem/ml_kem_ref/symmetric-shake.c b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c index c9f9d9f2a3..72bf98c2f2 100644 --- a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c +++ b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c @@ -48,7 +48,8 @@ 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_Squeeze always returns 1 when |ctx->padded| flag is cleared + // SHAKE_Squeeze always returns 1 when |ctx->state| flag is different + // from |KECCAK1600_STATE_FINAL| SHAKE_Squeeze(out, ctx, nblocks * SHAKE128_BLOCKSIZE); } @@ -100,6 +101,7 @@ void kyber_shake256_rkprf(ml_kem_params *params, uint8_t out[KYBER_SSBYTES], con // SHAKE_Absorb always returns 1 processing all data blocks that don't need pad SHAKE_Absorb(&ctx, input, params->ciphertext_bytes); - // SHAKE_Squeeze always returns 1 when |ctx->padded| flag is cleared (no previous calls to SHAKE_Squeeze) + // 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 a7431e5498..e0a4ba38a2 100644 --- a/crypto/fipsmodule/sha/internal.h +++ b/crypto/fipsmodule/sha/internal.h @@ -73,10 +73,12 @@ extern "C" { // 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 restricts the incremental calls to SHAKE_Final . -// KECCAK1600_STATE_FINAL can be called once. SHAKE_Squeeze cannot be called after SHAKE_Final. -// SHAKE_Squeeze should be called for streaming XOF output. +// 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; @@ -412,32 +414,34 @@ OPENSSL_EXPORT int SHA3_Init(KECCAK1600_CTX *ctx, size_t bitlen); // 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 absorbs it through |FIPS202_Finalize|. -// 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); // SHAKE_Init initialises |ctx| fields through |FIPS202_Init| and // returns 1 on success and 0 on failure. -OPENSSL_EXPORT int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size); +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|. -OPENSSL_EXPORT int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, +int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, size_t len); -// SHAKE_Squeeze pads the last data block absorbs it through |FIPS202_Finalize| on first call. -// It writes |len| bytes of incremental XOF output to |md|, returns 1 on -// success and 0 on failure. It can be called multiple times. -OPENSSL_EXPORT int SHAKE_Squeeze(uint8_t *md, KECCAK1600_CTX *ctx, 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. -OPENSSL_EXPORT int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len); +// 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. -OPENSSL_EXPORT size_t Keccak1600_Absorb(uint64_t A[KECCAK1600_ROWS][KECCAK1600_ROWS], +// 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); // Keccak1600_Squeeze generates |out| value of |len| bytes (per call). It can be called diff --git a/crypto/fipsmodule/sha/sha3.c b/crypto/fipsmodule/sha/sha3.c index 4761b83ac5..77002a03e4 100644 --- a/crypto/fipsmodule/sha/sha3.c +++ b/crypto/fipsmodule/sha/sha3.c @@ -111,7 +111,7 @@ 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->state = KECCAK1600_STATE_ABSORB; } @@ -148,7 +148,7 @@ static int FIPS202_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { 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; } @@ -156,7 +156,7 @@ static int FIPS202_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { // 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); + 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; @@ -173,7 +173,7 @@ static int FIPS202_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; @@ -194,13 +194,15 @@ static int FIPS202_Finalize(uint8_t *md, KECCAK1600_CTX *ctx) { // 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); + 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; @@ -208,14 +210,18 @@ static int FIPS202_Finalize(uint8_t *md, KECCAK1600_CTX *ctx) { // SHA3 APIs implement SHA3 functionalities on top of FIPS202 API layer 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; + 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) { @@ -223,6 +229,10 @@ int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { return 0; } + if (data == NULL && len != 0) { + return 0; + } + if (len == 0) { return 1; } @@ -231,8 +241,11 @@ int SHA3_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) { } // SHA3_Final should be called once to process final digest value -// |ctx->state| flag does not need to be updated int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) { + if (md == NULL || ctx == NULL) { + return 0; + } + if (ctx->md_size == 0) { return 1; } @@ -249,13 +262,17 @@ int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) { } 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; } - 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) { @@ -263,6 +280,10 @@ int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, size_t len) { return 0; } + if (data == NULL && len != 0) { + return 0; + } + if (len == 0) { return 1; } @@ -270,10 +291,14 @@ int SHAKE_Absorb(KECCAK1600_CTX *ctx, const void *data, size_t len) { return FIPS202_Update(ctx, data, len); } -// SHAKE_Final is a single-shot API and can be called once to finalize absorb and squeeze phases +// 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; if (ctx->md_size == 0) { return 1; @@ -287,7 +312,6 @@ int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) { ctx->state = KECCAK1600_STATE_FINAL; FIPS_service_indicator_update_state(); - return 1; } @@ -305,8 +329,9 @@ int SHAKE_Squeeze(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) { return 0; } + // Skip FIPS202_Finalize if the input has been padded and + // the last block has been processed if (ctx->state == KECCAK1600_STATE_ABSORB) { - // Skip FIPS202_Finalize if the input has been padded and the last block has been processed if (FIPS202_Finalize(md, ctx) == 0) { return 0; } 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