From fbbe2612a3385d1ded02a52d20ad7fd2da4501f4 Mon Sep 17 00:00:00 2001 From: Dmitriy Musatkin <63878209+DmitriyMusatkin@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:46:20 -0800 Subject: [PATCH] RSA PKCS1.5 SHA1 signing (#201) --- include/aws/cal/rsa.h | 1 + source/darwin/securityframework_rsa.c | 3 + source/rsa.c | 3 +- source/unix/openssl_rsa.c | 9 ++ source/windows/bcrypt_rsa.c | 11 ++- tests/CMakeLists.txt | 3 + tests/rsa_test.c | 120 +++++++++++++++++++++++++- 7 files changed, 146 insertions(+), 4 deletions(-) diff --git a/include/aws/cal/rsa.h b/include/aws/cal/rsa.h index 1df34f11..1e1829fe 100644 --- a/include/aws/cal/rsa.h +++ b/include/aws/cal/rsa.h @@ -19,6 +19,7 @@ enum aws_rsa_encryption_algorithm { enum aws_rsa_signature_algorithm { AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256, + AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, AWS_CAL_RSA_SIGNATURE_PSS_SHA256, }; diff --git a/source/darwin/securityframework_rsa.c b/source/darwin/securityframework_rsa.c index c9c02ec9..8135f879 100644 --- a/source/darwin/securityframework_rsa.c +++ b/source/darwin/securityframework_rsa.c @@ -132,6 +132,9 @@ static int s_map_rsa_signing_algo_to_sec(enum aws_rsa_signature_algorithm algori case AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256: *out = kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256; return AWS_OP_SUCCESS; + case AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1: + *out = kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1; + return AWS_OP_SUCCESS; case AWS_CAL_RSA_SIGNATURE_PSS_SHA256: #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 /* macOS 10.13 */)) || \ (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* iOS v11 */)) || \ diff --git a/source/rsa.c b/source/rsa.c index 483d08f2..c0d798bd 100644 --- a/source/rsa.c +++ b/source/rsa.c @@ -145,7 +145,8 @@ int aws_rsa_key_pair_sign_message( AWS_PRECONDITION(aws_byte_cursor_is_valid(&digest)); AWS_FATAL_ASSERT( - algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256 || algorithm == AWS_CAL_RSA_SIGNATURE_PSS_SHA256); + algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256 || algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1 || + algorithm == AWS_CAL_RSA_SIGNATURE_PSS_SHA256); if (digest.len > AWS_SHA256_LEN) { AWS_LOGF_ERROR( diff --git a/source/unix/openssl_rsa.c b/source/unix/openssl_rsa.c index fb70cd51..b4906b7e 100644 --- a/source/unix/openssl_rsa.c +++ b/source/unix/openssl_rsa.c @@ -241,6 +241,15 @@ static int s_set_signature_ctx_from_algo(EVP_PKEY_CTX *ctx, enum aws_rsa_signatu EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()), "EVP_PKEY_CTX_set_signature_md")) { return AWS_OP_ERR; } + } else if (algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1) { + if (s_reinterpret_evp_error_as_crt( + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING), "EVP_PKEY_CTX_set_rsa_padding")) { + return AWS_OP_ERR; + } + if (s_reinterpret_evp_error_as_crt( + EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()), "EVP_PKEY_CTX_set_signature_md")) { + return AWS_OP_ERR; + } } else if (algorithm == AWS_CAL_RSA_SIGNATURE_PSS_SHA256) { if (s_reinterpret_evp_error_as_crt( EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING), "EVP_PKEY_CTX_set_rsa_padding")) { diff --git a/source/windows/bcrypt_rsa.c b/source/windows/bcrypt_rsa.c index d9e7c8d2..2a3fe885 100644 --- a/source/windows/bcrypt_rsa.c +++ b/source/windows/bcrypt_rsa.c @@ -181,6 +181,9 @@ static int s_sign_padding_info_init(union sign_padding_info *info, enum aws_rsa_ if (algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256) { info->pkcs1.pszAlgId = BCRYPT_SHA256_ALGORITHM; return AWS_OP_SUCCESS; + } else if (algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1) { + info->pkcs1.pszAlgId = BCRYPT_SHA1_ALGORITHM; + return AWS_OP_SUCCESS; } else if (algorithm == AWS_CAL_RSA_SIGNATURE_PSS_SHA256) { info->pss.pszAlgId = BCRYPT_SHA256_ALGORITHM; info->pss.cbSalt = 32; @@ -203,6 +206,8 @@ static int s_rsa_sign( } ULONG length_written = 0; + bool is_pkcs1_padding = + (algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256 || algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1); NTSTATUS status = BCryptSignHash( key_pair_impl->key_handle, &padding_info, @@ -211,7 +216,7 @@ static int s_rsa_sign( out->buffer + out->len, (ULONG)(out->capacity - out->len), (ULONG *)&length_written, - algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256 ? BCRYPT_PAD_PKCS1 : BCRYPT_PAD_PSS); + is_pkcs1_padding ? BCRYPT_PAD_PKCS1 : BCRYPT_PAD_PSS); if (s_reinterpret_bc_error_as_crt(status, "BCryptSignHash")) { goto on_error; @@ -244,6 +249,8 @@ static int s_rsa_verify( return aws_raise_error(AWS_ERROR_CAL_UNSUPPORTED_ALGORITHM); } /* okay, now we've got a windows compatible signature, let's verify it. */ + bool is_pkcs1_padding = + (algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256 || algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1); NTSTATUS status = BCryptVerifySignature( key_pair_impl->key_handle, &padding_info, @@ -251,7 +258,7 @@ static int s_rsa_verify( (ULONG)digest.len, signature.ptr, (ULONG)signature.len, - algorithm == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA256 ? BCRYPT_PAD_PKCS1 : BCRYPT_PAD_PSS); + is_pkcs1_padding ? BCRYPT_PAD_PKCS1 : BCRYPT_PAD_PSS); if (status == STATUS_INVALID_SIGNATURE) { return aws_raise_error(AWS_ERROR_CAL_SIGNATURE_VALIDATION_FAILED); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e3966cbc..346e38a6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -77,15 +77,18 @@ add_test_case(rsa_encryption_roundtrip_oaep_sha256_from_user) add_test_case(rsa_encryption_roundtrip_oaep_sha512_from_user) add_test_case(rsa_signing_roundtrip_pkcs1_sha256_from_user) add_test_case(rsa_signing_roundtrip_pss_sha256_from_user) +add_test_case(rsa_signing_roundtrip_pkcs1_sha1_from_user) add_test_case(rsa_getters) add_test_case(rsa_private_pkcs1_der_parsing) add_test_case(rsa_public_pkcs1_der_parsing) add_test_case(rsa_verify_signing_pkcs1_sha256) +add_test_case(rsa_verify_signing_pkcs1_sha1) add_test_case(rsa_verify_signing_pss_sha256) add_test_case(rsa_decrypt_pkcs1) add_test_case(rsa_decrypt_oaep256) add_test_case(rsa_decrypt_oaep512) add_test_case(rsa_signing_mismatch_pkcs1_sha256) +add_test_case(rsa_signing_mismatch_pkcs1_sha1) add_test_case(aes_cbc_NIST_CBCGFSbox256_case_1) add_test_case(aes_cbc_NIST_CBCVarKey256_case_254) diff --git a/tests/rsa_test.c b/tests/rsa_test.c index af645531..7dc9e7f3 100644 --- a/tests/rsa_test.c +++ b/tests/rsa_test.c @@ -186,6 +186,11 @@ static const char *TEST_RSA_SIGNATURE_PKCS1 = "Gqu9pLlPvSFIW+5ZFo9ZCxMmPR8LnAeiu "TYJ45P3c94lQIQD3SVJ3XMSAyAEWTE2pcj0F/oPzzxLcXK9cyv2Iphe4XuBjWCOVdHgFg" "rD/yAA8b+B94AqE9U/B2+k9/C3Bz2YApo="; +static const char *TEST_RSA_SIGNATURE_PKCS1_SHA1 = + "QJxUx+4OM+v1wh0z0PJWMeGBdvjhQHxjFCdzDLeNH6zoJpgPtFXNk6i83ff75MgPpW0m33" + "zrrzLWfzLojZFi2KFXYu1Y39We3AfREEOyG+porTmxNQ4dJH29joXS0XNf52dJpN04Lw3WN" + "XUHDP6eG2K71uXlsus2Tm0uBe4TF0g="; + static const char *TEST_RSA_SIGNATURE_PSS = "j//04sVoqQVSmUgH+Id0oad7OgW+hGnIqx6hjr28VnVk75Obig+n3tJGWd0r+3S4ARxf2fK" "7taVvJXISQ5aWJAYx6QRgR+25rcE96eOfi6L7ShIZIUYFzGxhc9wpUMGbqHEIhm+8QP7uNo4D" "FmaPzJMgGDKL2qhedxnjtg3p8E4="; @@ -247,6 +252,44 @@ static int s_rsa_verify_signing_pkcs1_sha256(struct aws_allocator *allocator, vo } AWS_TEST_CASE(rsa_verify_signing_pkcs1_sha256, s_rsa_verify_signing_pkcs1_sha256); +static int s_rsa_verify_signing_pkcs1_sha1(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + struct aws_byte_cursor message = aws_byte_cursor_from_c_str(TEST_ENCRYPTION_STRING); + + aws_cal_library_init(allocator); + + struct aws_byte_buf public_key_buf; + ASSERT_SUCCESS(s_byte_buf_decoded_from_base64_cur( + allocator, aws_byte_cursor_from_c_str(TEST_PKCS1_RSA_PUBLIC_KEY_1024), &public_key_buf)); + struct aws_rsa_key_pair *key_pair_public = + aws_rsa_key_pair_new_from_public_key_pkcs1(allocator, aws_byte_cursor_from_buf(&public_key_buf)); + ASSERT_NOT_NULL(key_pair_public); + + uint8_t hash[AWS_SHA1_LEN]; + AWS_ZERO_ARRAY(hash); + struct aws_byte_buf hash_value = aws_byte_buf_from_empty_array(hash, sizeof(hash)); + aws_sha1_compute(allocator, &message, &hash_value, 0); + struct aws_byte_cursor hash_cur = aws_byte_cursor_from_buf(&hash_value); + + struct aws_byte_buf signature_buf; + ASSERT_SUCCESS(s_byte_buf_decoded_from_base64_cur( + allocator, aws_byte_cursor_from_c_str(TEST_RSA_SIGNATURE_PKCS1_SHA1), &signature_buf)); + struct aws_byte_cursor signature_cur = aws_byte_cursor_from_buf(&signature_buf); + + ASSERT_SUCCESS(aws_rsa_key_pair_verify_signature( + key_pair_public, AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, hash_cur, signature_cur)); + + aws_byte_buf_clean_up(&hash_value); + aws_byte_buf_clean_up(&signature_buf); + aws_byte_buf_clean_up(&public_key_buf); + aws_rsa_key_pair_release(key_pair_public); + + aws_cal_library_clean_up(); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(rsa_verify_signing_pkcs1_sha1, s_rsa_verify_signing_pkcs1_sha1); + static int s_rsa_verify_signing_pss_sha256(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct aws_byte_cursor message = aws_byte_cursor_from_c_str(TEST_ENCRYPTION_STRING); @@ -410,7 +453,12 @@ static int s_rsa_signing_roundtrip_helper( uint8_t hash[AWS_SHA256_LEN]; AWS_ZERO_ARRAY(hash); struct aws_byte_buf hash_value = aws_byte_buf_from_empty_array(hash, sizeof(hash)); - aws_sha256_compute(allocator, &message, &hash_value, 0); + if (algo == AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1) { + aws_sha1_compute(allocator, &message, &hash_value, 0); + } else { + aws_sha256_compute(allocator, &message, &hash_value, 0); + } + struct aws_byte_cursor hash_cur = aws_byte_cursor_from_buf(&hash_value); /*since our apis work by appending to buffer, lets make sure they dont @@ -494,6 +542,20 @@ static int s_rsa_signing_roundtrip_pkcs1_sha256_from_user(struct aws_allocator * } AWS_TEST_CASE(rsa_signing_roundtrip_pkcs1_sha256_from_user, s_rsa_signing_roundtrip_pkcs1_sha256_from_user); +static int s_rsa_signing_roundtrip_pkcs1_sha1_from_user(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_cal_library_init(allocator); + + ASSERT_SUCCESS(s_rsa_signing_roundtrip_from_user( + allocator, AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, TEST_RSA_SIGNATURE_PKCS1_SHA1)); + + aws_cal_library_clean_up(); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(rsa_signing_roundtrip_pkcs1_sha1_from_user, s_rsa_signing_roundtrip_pkcs1_sha1_from_user); + static int s_rsa_signing_roundtrip_pss_sha256_from_user(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -747,3 +809,59 @@ static int s_rsa_signing_mismatch_pkcs1_sha256(struct aws_allocator *allocator, return AWS_OP_SUCCESS; } AWS_TEST_CASE(rsa_signing_mismatch_pkcs1_sha256, s_rsa_signing_mismatch_pkcs1_sha256); + +static int s_rsa_signing_mismatch_pkcs1_sha1(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + struct aws_byte_cursor message = aws_byte_cursor_from_c_str(TEST_ENCRYPTION_STRING); + + aws_cal_library_init(allocator); + + struct aws_byte_buf public_key_buf; + ASSERT_SUCCESS(s_byte_buf_decoded_from_base64_cur( + allocator, aws_byte_cursor_from_c_str(TEST_PKCS1_RSA_PRIVATE_KEY_1024), &public_key_buf)); + struct aws_rsa_key_pair *key_pair_private = + aws_rsa_key_pair_new_from_private_key_pkcs1(allocator, aws_byte_cursor_from_buf(&public_key_buf)); + ASSERT_NOT_NULL(key_pair_private); + + uint8_t hash[AWS_SHA1_LEN]; + AWS_ZERO_ARRAY(hash); + struct aws_byte_buf hash_value = aws_byte_buf_from_empty_array(hash, sizeof(hash)); + aws_sha1_compute(allocator, &message, &hash_value, 0); + struct aws_byte_cursor hash_cur = aws_byte_cursor_from_buf(&hash_value); + + struct aws_byte_buf signature_buf; + ASSERT_SUCCESS(aws_byte_buf_init(&signature_buf, allocator, aws_rsa_key_pair_signature_length(key_pair_private))); + ASSERT_SUCCESS( + aws_rsa_key_pair_sign_message(key_pair_private, AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, hash_cur, &signature_buf)); + struct aws_byte_cursor signature_cur = aws_byte_cursor_from_buf(&signature_buf); + + hash[5] += 59; /* modify digest to force signature mismatch */ + + ASSERT_ERROR( + AWS_ERROR_CAL_SIGNATURE_VALIDATION_FAILED, + aws_rsa_key_pair_verify_signature( + key_pair_private, AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, hash_cur, signature_cur)); + + hash[5] -= 59; /* undo digest modification and corrupt signature */ + signature_buf.buffer[5] += 59; + ASSERT_ERROR( + AWS_ERROR_CAL_SIGNATURE_VALIDATION_FAILED, + aws_rsa_key_pair_verify_signature( + key_pair_private, AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, hash_cur, signature_cur)); + + struct aws_byte_cursor short_signature_cur = aws_byte_cursor_from_c_str("bad signature"); + ASSERT_ERROR( + AWS_ERROR_CAL_SIGNATURE_VALIDATION_FAILED, + aws_rsa_key_pair_verify_signature( + key_pair_private, AWS_CAL_RSA_SIGNATURE_PKCS1_5_SHA1, hash_cur, short_signature_cur)); + + aws_byte_buf_clean_up(&hash_value); + aws_byte_buf_clean_up(&signature_buf); + aws_byte_buf_clean_up(&public_key_buf); + aws_rsa_key_pair_release(key_pair_private); + + aws_cal_library_clean_up(); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(rsa_signing_mismatch_pkcs1_sha1, s_rsa_signing_mismatch_pkcs1_sha1);