Skip to content

Commit ff88014

Browse files
RSA PKCS8 support (#212)
1 parent 4805a96 commit ff88014

File tree

6 files changed

+225
-6
lines changed

6 files changed

+225
-6
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ jobs:
179179
- name: Build ${{ env.PACKAGE_NAME }}
180180
run: |
181181
python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')"
182-
AWS_TEST_FIPS=1 python builder.pyz build -p ${{ env.PACKAGE_NAME }} --variant=aws-lc-fips --cmake-extra=-DFIPS=ON --cmake-extra=-DPERL_EXECUTABLE=perl --cmake-extra=-DGO_EXECUTABLE=go
182+
AWS_TEST_FIPS=1 python builder.pyz build -p ${{ env.PACKAGE_NAME }} --variant=aws-lc-fips --cmake-extra=-DFIPS=ON --cmake-extra=-DPERL_EXECUTABLE=perl --cmake-extra=-DGO_EXECUTABLE=go --cmake-extra=-DCMAKE_POLICY_VERSION_MINIMUM=3.5
183183
184184
windows:
185185
runs-on: windows-2022 # latest

include/aws/cal/exports.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
55
* SPDX-License-Identifier: Apache-2.0.
66
*/
7-
#if defined(AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined(WIN32)
7+
#if defined(AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32)
88
# ifdef AWS_CAL_USE_IMPORT_EXPORT
99
# ifdef AWS_CAL_EXPORTS
1010
# define AWS_CAL_API __declspec(dllexport)
@@ -15,14 +15,14 @@
1515
# define AWS_CAL_API
1616
# endif /* AWS_CAL_USE_IMPORT_EXPORT */
1717

18-
#else /* defined (AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined (WIN32) */
18+
#else /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
1919

20-
# if ((__GNUC__ >= 4) || defined(__clang__)) && defined(AWS_CAL_USE_IMPORT_EXPORT) && defined(AWS_CAL_EXPORTS)
20+
# if defined(AWS_CAL_USE_IMPORT_EXPORT) && defined(AWS_CAL_EXPORTS)
2121
# define AWS_CAL_API __attribute__((visibility("default")))
2222
# else
2323
# define AWS_CAL_API
24-
# endif /* __GNUC__ >= 4 || defined(__clang__) */
24+
# endif
2525

26-
#endif /* defined (AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined (WIN32) */
26+
#endif /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
2727

2828
#endif /* AWS_CAL_EXPORTS_H */

include/aws/cal/rsa.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ AWS_CAL_API struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs1
5353
struct aws_allocator *allocator,
5454
struct aws_byte_cursor key);
5555

56+
/**
57+
* Creates an RSA private key from PrivateKeyInfo as defined in rfc 5208 (aka PKCS8).
58+
* Returns a new instance of aws_rsa_key_pair if the key was successfully built.
59+
* Otherwise returns NULL.
60+
*/
61+
AWS_CAL_API struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs8(
62+
struct aws_allocator *allocator,
63+
struct aws_byte_cursor key);
64+
5665
/**
5766
* Adds one to an RSA key pair's ref count.
5867
* Returns key_pair pointer.

source/rsa.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ typedef struct aws_rsa_key_pair *(aws_rsa_key_pair_new_from_public_pkcs1_fn)(str
1414
typedef struct aws_rsa_key_pair *(aws_rsa_key_pair_new_from_private_pkcs1_fn)(struct aws_allocator *allocator,
1515
struct aws_byte_cursor private_key);
1616

17+
typedef struct aws_rsa_key_pair *(aws_rsa_key_pair_new_from_private_pkcs8_fn)(struct aws_allocator *allocator,
18+
struct aws_byte_cursor private_key);
19+
1720
#ifndef BYO_CRYPTO
1821

1922
extern struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_public_key_pkcs1_impl(
@@ -24,6 +27,10 @@ extern struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs1_impl
2427
struct aws_allocator *allocator,
2528
struct aws_byte_cursor private_key);
2629

30+
struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs8_impl(
31+
struct aws_allocator *allocator,
32+
struct aws_byte_cursor private_key);
33+
2734
#else /* BYO_CRYPTO */
2835

2936
struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_public_key_pkcs1_impl(
@@ -41,6 +48,14 @@ struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs1_impl(
4148
(void)private_key;
4249
abort();
4350
}
51+
52+
struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs8_impl(
53+
struct aws_allocator *allocator,
54+
struct aws_byte_cursor private_key) {
55+
(void)allocator;
56+
(void)private_key;
57+
abort();
58+
}
4459
#endif /* BYO_CRYPTO */
4560

4661
static aws_rsa_key_pair_new_from_public_pkcs1_fn *s_rsa_key_pair_new_from_public_key_pkcs1_fn =
@@ -49,6 +64,9 @@ static aws_rsa_key_pair_new_from_public_pkcs1_fn *s_rsa_key_pair_new_from_public
4964
static aws_rsa_key_pair_new_from_private_pkcs1_fn *s_rsa_key_pair_new_from_private_key_pkcs1_fn =
5065
aws_rsa_key_pair_new_from_private_key_pkcs1_impl;
5166

67+
static aws_rsa_key_pair_new_from_private_pkcs8_fn *s_rsa_key_pair_new_from_private_key_pkcs8_fn =
68+
aws_rsa_key_pair_new_from_private_key_pkcs8_impl;
69+
5270
struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_public_key_pkcs1(
5371
struct aws_allocator *allocator,
5472
struct aws_byte_cursor public_key) {
@@ -61,6 +79,12 @@ struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs1(
6179
return s_rsa_key_pair_new_from_private_key_pkcs1_fn(allocator, private_key);
6280
}
6381

82+
AWS_CAL_API struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs8(
83+
struct aws_allocator *allocator,
84+
struct aws_byte_cursor key) {
85+
return s_rsa_key_pair_new_from_private_key_pkcs8_fn(allocator, key);
86+
}
87+
6488
void aws_rsa_key_pair_base_clean_up(struct aws_rsa_key_pair *key_pair) {
6589
aws_byte_buf_clean_up_secure(&key_pair->priv);
6690
aws_byte_buf_clean_up_secure(&key_pair->pub);
@@ -286,3 +310,107 @@ int is_valid_rsa_key_size(size_t key_size_in_bits) {
286310

287311
return AWS_OP_SUCCESS;
288312
}
313+
314+
#ifndef BYO_CRYPTO
315+
316+
static uint8_t s_rsa_encryption_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01};
317+
318+
/**
319+
* There are likely tens of algo specifiers for rsa out there.
320+
* But in practice mostly everyone uses rsaEncryption oid for key at rest.
321+
* So for now just support that and we can add other ones later if needed.
322+
* Even though its technically encryption oid, most crypto libs are lax
323+
* and allow the key to be used for all operations.
324+
*/
325+
static struct aws_byte_cursor s_rsa_encryption_oid_cur = {
326+
.ptr = (s_rsa_encryption_oid),
327+
.len = sizeof(s_rsa_encryption_oid),
328+
};
329+
330+
/**
331+
* Win and Mac dont provide native support for loading pkcs8 keys, so
332+
* for simplicity and consistency just parse pkcs1 key from pkcs8 and
333+
* use it with existing pkcs1 loader.
334+
*/
335+
struct aws_rsa_key_pair *aws_rsa_key_pair_new_from_private_key_pkcs8_impl(
336+
struct aws_allocator *allocator,
337+
struct aws_byte_cursor private_key) {
338+
339+
struct aws_der_decoder *decoder = aws_der_decoder_new(allocator, private_key);
340+
341+
if (decoder == NULL) {
342+
return NULL;
343+
}
344+
345+
/**
346+
* Format of pkcs8 is as follows.
347+
* PrivateKeyInfo ::= SEQUENCE {
348+
* version Version, -- INTEGER (0)
349+
* algorithm AlgorithmIdentifier,
350+
* privateKey OCTET STRING, -- contains DER encoded key
351+
* attributes [0] IMPLICIT SET OF Attribute OPTIONAL
352+
* }
353+
* AlgorithmIdentifier ::= SEQUENCE {
354+
* algorithm OBJECT IDENTIFIER,
355+
* parameters ANY DEFINED BY algorithm OPTIONAL
356+
* }
357+
*/
358+
359+
struct aws_rsa_key_pair *key_pair = NULL;
360+
361+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_type(decoder) != AWS_DER_SEQUENCE) {
362+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
363+
goto on_done;
364+
}
365+
366+
/* version */
367+
struct aws_byte_cursor version_cur;
368+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_unsigned_integer(decoder, &version_cur)) {
369+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
370+
goto on_done;
371+
}
372+
373+
if (version_cur.len != 1 || version_cur.ptr[0] != 0) {
374+
aws_raise_error(AWS_ERROR_CAL_UNSUPPORTED_KEY_FORMAT);
375+
goto on_done;
376+
}
377+
378+
/* oid */
379+
struct aws_byte_cursor oid_cur;
380+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_type(decoder) != AWS_DER_SEQUENCE) {
381+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
382+
goto on_done;
383+
}
384+
385+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_type(decoder) != AWS_DER_OBJECT_IDENTIFIER ||
386+
aws_der_decoder_tlv_blob(decoder, &oid_cur)) {
387+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
388+
goto on_done;
389+
}
390+
391+
if (!aws_byte_cursor_eq(&s_rsa_encryption_oid_cur, &oid_cur)) {
392+
aws_raise_error(AWS_ERROR_CAL_UNSUPPORTED_KEY_FORMAT);
393+
goto on_done;
394+
}
395+
396+
/* skip additional params */
397+
if (!aws_der_decoder_next(decoder)) {
398+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
399+
goto on_done;
400+
}
401+
402+
/* key */
403+
struct aws_byte_cursor key;
404+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_string(decoder, &key)) {
405+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
406+
goto on_done;
407+
}
408+
409+
key_pair = aws_rsa_key_pair_new_from_private_key_pkcs1(allocator, key);
410+
411+
on_done:
412+
aws_der_decoder_destroy(decoder);
413+
return key_pair;
414+
}
415+
416+
#endif

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ add_test_case(rsa_decrypt_oaep256)
8989
add_test_case(rsa_decrypt_oaep512)
9090
add_test_case(rsa_signing_mismatch_pkcs1_sha256)
9191
add_test_case(rsa_signing_mismatch_pkcs1_sha1)
92+
add_test_case(rsa_encryption_roundtrip_pkcs15_from_user_pkcs8)
9293

9394
add_test_case(aes_cbc_NIST_CBCGFSbox256_case_1)
9495
add_test_case(aes_cbc_NIST_CBCVarKey256_case_254)

tests/rsa_test.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,33 @@ static const char *TEST_PKCS1_RSA_PRIVATE_KEY_2048 = "MIIEpQIBAAKCAQEAnt/K3mvMgP
102102
"tZI9TCH4Sq33MGEjf2MyW0XMXC56dA2VOPSTHGKaoKmyn7L9G4WfDFcYmCdvmLkR"
103103
"7wAz2Dyxr6ImChSWD/y2ddz1U+H39uqRxwIkwJ7TbDflYNXgsAOOlUg=";
104104

105+
static const char *TEST_PKCS8_RSA_PRIVATE_KEY_2048 = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDN0jYhXzsI1lSA"
106+
"j+3uxraUu/78uHJlCcTifQlVUftgEzJth7WNGvrJ8bDLUHwV04cTYSkydgsivjms"
107+
"XFgzuTCOLiHX0ik/RT1fOvOpk0gte2gCfPIACUAkTlGXKJ+v+1kqnWFmZFNvtkz2"
108+
"fmLIRq9ZrXWORVmySCeoSfvyygxkmfTUfGieFr8LMFSB6VoWKA/vOk0sMF/5PRxm"
109+
"JESS4pOMjWQ3QHpLdjsgUAnxsThLVMbaA5JXdHnFHlgkTbL/OSY06EUzuZ0dJpTU"
110+
"qSBbaz6qewlIO4eM9d2N+5d/mt5nn4rU7yuLGlJE/U9UpSutuvkCkqosST9yKBz7"
111+
"YUhK7WyPAgMBAAECggEAYrHEZyhFJK2yA5wA2hjLgHLNiN3hbPXMRVbz3MfdJGrQ"
112+
"KZmDw1AGpkORJU1I0yaFhRN4L8xO9rAE89OsL9FDqUoRzG3ofYB0N3ALW2tWlwiw"
113+
"DVFgsge9jCtKEJPYTwjV7wtcoz7Ei7L9IM3mDGdoujXlQv2aT1UuPxKLEBc27h3Q"
114+
"KZZL0QbA33SmO24BUx9Y741hIXsVvoce0MQM1TPwEGH18qYpffb3kCBTwFEySx+q"
115+
"JFwadZiK1JPQgpELrXZT0VuARD2Ze8Z7c0b6668wYiwJ2mmf4l0NTLk67mIIfG1w"
116+
"NNNzHme/UGQEjSjUU9TFWLVHU9enumPv5UeLx3wvgQKBgQD7k6pCk8kkUcjjq0sq"
117+
"92Coy1DMlZg9ictcusJ5pfe1ZdW01iX/S/88ZOxk1X27jTWcHarAeA+ci4LzDWPG"
118+
"I61Hy4kJFOJB6Sx3hDSnfNvuIC/7zrcWLNzvfAeDYmPhAqi1K4HYFN7Ipa/1+Uew"
119+
"kWVllcUHRz5zWU+oYVXDsjARrwKBgQDRcJowibhaG4UUMzRsyyEK1l/w+z75L8A7"
120+
"2ZA+wMqyUo9LPgJRYvEHHRQTHDGCzP81IUaAK2OfWrD5Jnn6r4Il7+cQ+xCHKiMD"
121+
"rBhnL9dG4lVAWgdZPyWXfrdT3mqpZ3UgfWysvph8s48QdLA3ku7PpTfchwtSdXQP"
122+
"cxhEItlrIQKBgQDTTB8AdCfIfXiA3+nuWH+yxbFDY5HOfeF0LNgSXDdFABcSH5si"
123+
"Za4mB44U0ssbr2qLiM9VgIF8NiDyCxj13hk359dc7VFrknBqoXuoANKnmhkzIVfd"
124+
"JCkca8vTqdvBrP4NzFDuL/k+BQtZSNnRjwze2X/2sPve3fBtt/LUvuBouQKBgQC9"
125+
"T1Cv6uxN1m410grjA8C8MQXLpu5HAxh5gLBXaKBPCz0mv8gMlKhUy73ngCZomq9b"
126+
"8NXu6ElGMw2gR10ecSHs9KohuS45XqcDnLz6GE44bkCsyDO4QdHS2+EN2A8FTNSc"
127+
"J4Lhqe3fWdZJA5B8yz09R5P0q8RaJnxfsqMOg4mOwQKBgQC+N5xLCRa/n1kJ11we"
128+
"rnOe2POS+zuThhi1aMn0LLPIDwVMktymV7F8JegbdYB5KjxyxzDPcpRDRKCXvAew"
129+
"QiaCZLRyigBqDpDP4l3uIp1OEzWLYuEAWwnErC7fuPm0TFAy5ecegxW7eXasDy2C"
130+
"dJJcK3yV8NRVOzr2voGRmr4d7w==";
131+
105132
static int s_rsa_encryption_roundtrip_from_user(
106133
struct aws_allocator *allocator,
107134
enum aws_rsa_encryption_algorithm algo) {
@@ -122,6 +149,26 @@ static int s_rsa_encryption_roundtrip_from_user(
122149
return AWS_OP_SUCCESS;
123150
}
124151

152+
static int s_rsa_encryption_roundtrip_from_user_pkcs8(
153+
struct aws_allocator *allocator,
154+
enum aws_rsa_encryption_algorithm algo) {
155+
struct aws_byte_buf key_buf;
156+
ASSERT_SUCCESS(s_byte_buf_decoded_from_base64_cur(
157+
allocator, aws_byte_cursor_from_c_str(TEST_PKCS8_RSA_PRIVATE_KEY_2048), &key_buf));
158+
159+
struct aws_rsa_key_pair *key_pair =
160+
aws_rsa_key_pair_new_from_private_key_pkcs8(allocator, aws_byte_cursor_from_buf(&key_buf));
161+
162+
ASSERT_NOT_NULL(key_pair);
163+
164+
s_rsa_encryption_roundtrip_helper(allocator, key_pair, algo);
165+
166+
aws_rsa_key_pair_release(key_pair);
167+
aws_byte_buf_clean_up_secure(&key_buf);
168+
169+
return AWS_OP_SUCCESS;
170+
}
171+
125172
static int s_rsa_encryption_roundtrip_pkcs1_from_user(struct aws_allocator *allocator, void *ctx) {
126173
(void)ctx;
127174

@@ -135,6 +182,40 @@ static int s_rsa_encryption_roundtrip_pkcs1_from_user(struct aws_allocator *allo
135182
}
136183
AWS_TEST_CASE(rsa_encryption_roundtrip_pkcs1_from_user, s_rsa_encryption_roundtrip_pkcs1_from_user);
137184

185+
static int s_rsa_encryption_roundtrip_pkcs15_from_user_pkcs8(struct aws_allocator *allocator, void *ctx) {
186+
(void)ctx;
187+
188+
aws_cal_library_test_init(allocator);
189+
190+
ASSERT_SUCCESS(s_rsa_encryption_roundtrip_from_user_pkcs8(allocator, AWS_CAL_RSA_ENCRYPTION_PKCS1_5));
191+
192+
aws_cal_library_clean_up();
193+
194+
return AWS_OP_SUCCESS;
195+
}
196+
AWS_TEST_CASE(rsa_encryption_roundtrip_pkcs15_from_user_pkcs8, s_rsa_encryption_roundtrip_pkcs15_from_user_pkcs8);
197+
198+
static int s_rsa_pkcs8_key_load_error(struct aws_allocator *allocator, void *ctx) {
199+
(void)ctx;
200+
201+
aws_cal_library_test_init(allocator);
202+
203+
struct aws_byte_buf key_buf;
204+
ASSERT_SUCCESS(s_byte_buf_decoded_from_base64_cur(
205+
allocator, aws_byte_cursor_from_c_str(TEST_PKCS1_RSA_PRIVATE_KEY_2048), &key_buf));
206+
207+
struct aws_rsa_key_pair *key_pair =
208+
aws_rsa_key_pair_new_from_private_key_pkcs8(allocator, aws_byte_cursor_from_buf(&key_buf));
209+
210+
ASSERT_NULL(key_pair);
211+
ASSERT_INT_EQUALS(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED, aws_last_error());
212+
213+
aws_cal_library_clean_up();
214+
215+
return AWS_OP_SUCCESS;
216+
}
217+
AWS_TEST_CASE(rsa_pkcs8_key_load_error, s_rsa_pkcs8_key_load_error);
218+
138219
static int s_rsa_encryption_roundtrip_oaep_sha256_from_user(struct aws_allocator *allocator, void *ctx) {
139220
(void)ctx;
140221

0 commit comments

Comments
 (0)