Skip to content

Commit d4d51d1

Browse files
Add helpers for encoding/decoding der ecdsa signatures to raw (#230)
1 parent 3c6d901 commit d4d51d1

File tree

5 files changed

+172
-25
lines changed

5 files changed

+172
-25
lines changed

include/aws/cal/ecc.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,25 @@ AWS_CAL_API void aws_ecc_key_pair_get_private_key(
179179

180180
AWS_CAL_API size_t aws_ecc_key_coordinate_byte_size_from_curve_name(enum aws_ecc_curve_name curve_name);
181181

182+
/*
183+
* Helper to decode ECDSA signature from DER format to base components R and S.
184+
*/
185+
AWS_CAL_API int aws_ecc_decode_signature_der_to_raw(
186+
struct aws_allocator *allocator,
187+
struct aws_byte_cursor signature,
188+
struct aws_byte_cursor *out_r,
189+
struct aws_byte_cursor *out_s);
190+
191+
/*
192+
* Helper to encode ECDSA signature from raw format (R and S) to DER.
193+
* out_signature must be initialized and must be able to fit signature
194+
*/
195+
AWS_CAL_API int aws_ecc_encode_signature_raw_to_der(
196+
struct aws_allocator *allocator,
197+
struct aws_byte_cursor r,
198+
struct aws_byte_cursor s,
199+
struct aws_byte_buf *out_signature);
200+
182201
AWS_EXTERN_C_END
183202
AWS_POP_SANE_WARNING_LEVEL
184203

source/ecc.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,102 @@ struct aws_ecc_key_pair *aws_ecc_key_new_from_hex_coordinates(
336336

337337
return key;
338338
}
339+
340+
int aws_ecc_decode_signature_der_to_raw(
341+
struct aws_allocator *allocator,
342+
struct aws_byte_cursor signature,
343+
struct aws_byte_cursor *out_r,
344+
struct aws_byte_cursor *out_s) {
345+
346+
AWS_ERROR_PRECONDITION(allocator);
347+
AWS_ERROR_PRECONDITION(out_r);
348+
AWS_ERROR_PRECONDITION(out_s);
349+
350+
struct aws_der_decoder *decoder = aws_der_decoder_new(allocator, signature);
351+
352+
if (!decoder) {
353+
return AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED;
354+
}
355+
356+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_type(decoder) != AWS_DER_SEQUENCE) {
357+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
358+
goto on_error;
359+
}
360+
361+
struct aws_byte_cursor r;
362+
AWS_ZERO_STRUCT(r);
363+
struct aws_byte_cursor s;
364+
AWS_ZERO_STRUCT(s);
365+
366+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_unsigned_integer(decoder, &r)) {
367+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
368+
goto on_error;
369+
}
370+
if (!aws_der_decoder_next(decoder) || aws_der_decoder_tlv_unsigned_integer(decoder, &s)) {
371+
aws_raise_error(AWS_ERROR_CAL_MALFORMED_ASN1_ENCOUNTERED);
372+
goto on_error;
373+
}
374+
375+
aws_der_decoder_destroy(decoder);
376+
*out_r = r;
377+
*out_s = s;
378+
return AWS_OP_SUCCESS;
379+
380+
on_error:
381+
aws_der_decoder_destroy(decoder);
382+
return AWS_OP_ERR;
383+
}
384+
385+
static bool s_trim_zeros_predicate(uint8_t value) {
386+
return value == 0;
387+
}
388+
389+
int aws_ecc_encode_signature_raw_to_der(
390+
struct aws_allocator *allocator,
391+
struct aws_byte_cursor r,
392+
struct aws_byte_cursor s,
393+
struct aws_byte_buf *out_signature) {
394+
395+
AWS_ERROR_PRECONDITION(allocator);
396+
AWS_ERROR_PRECONDITION(out_signature);
397+
398+
struct aws_der_encoder *encoder = aws_der_encoder_new(allocator, out_signature->capacity - out_signature->len);
399+
400+
if (aws_der_encoder_begin_sequence(encoder)) {
401+
aws_raise_error(AWS_ERROR_UNKNOWN);
402+
goto on_error;
403+
}
404+
405+
r = aws_byte_cursor_left_trim_pred(&r, s_trim_zeros_predicate);
406+
if (aws_der_encoder_write_unsigned_integer(encoder, r)) {
407+
aws_raise_error(AWS_ERROR_UNKNOWN);
408+
goto on_error;
409+
}
410+
411+
s = aws_byte_cursor_left_trim_pred(&s, s_trim_zeros_predicate);
412+
if (aws_der_encoder_write_unsigned_integer(encoder, s)) {
413+
aws_raise_error(AWS_ERROR_UNKNOWN);
414+
goto on_error;
415+
}
416+
417+
if (aws_der_encoder_end_sequence(encoder)) {
418+
aws_raise_error(AWS_ERROR_UNKNOWN);
419+
goto on_error;
420+
}
421+
422+
struct aws_byte_cursor contents;
423+
AWS_ZERO_STRUCT(contents);
424+
if (aws_der_encoder_get_contents(encoder, &contents)) {
425+
aws_raise_error(AWS_ERROR_UNKNOWN);
426+
goto on_error;
427+
}
428+
429+
aws_byte_buf_append(out_signature, &contents);
430+
431+
aws_der_encoder_destroy(encoder);
432+
return AWS_OP_SUCCESS;
433+
434+
on_error:
435+
aws_der_encoder_destroy(encoder);
436+
return AWS_OP_ERR;
437+
}

source/windows/bcrypt_ecc.c

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,6 @@ static size_t s_signature_length(const struct aws_ecc_key_pair *key_pair) {
8282
return s_der_overhead + aws_ecc_key_coordinate_byte_size_from_curve_name(key_pair->curve_name) * 2;
8383
}
8484

85-
static bool s_trim_zeros_predicate(uint8_t value) {
86-
return value == 0;
87-
}
88-
8985
static int s_sign_message(
9086
const struct aws_ecc_key_pair *key_pair,
9187
const struct aws_byte_cursor *message,
@@ -120,29 +116,12 @@ static int s_sign_message(
120116
size_t coordinate_len = temp_signature_buf.len / 2;
121117

122118
/* okay. Windows doesn't DER encode this to ASN.1, so we need to do it manually. */
123-
struct aws_der_encoder *encoder =
124-
aws_der_encoder_new(key_pair->allocator, signature_output->capacity - signature_output->len);
125-
if (!encoder) {
126-
return AWS_OP_ERR;
119+
struct aws_byte_cursor s = aws_byte_cursor_from_buf(&temp_signature_buf);
120+
struct aws_byte_cursor r = aws_byte_cursor_advance(&s, coordinate_len);
121+
if (aws_ecc_encode_signature_raw_to_der(key_pair->allocator, r, s, signature_output)) {
122+
return aws_raise_error(AWS_ERROR_CAL_CRYPTO_OPERATION_FAILED);
127123
}
128124

129-
aws_der_encoder_begin_sequence(encoder);
130-
struct aws_byte_cursor integer_cur = aws_byte_cursor_from_array(temp_signature_buf.buffer, coordinate_len);
131-
/* trim off the leading zero padding for DER encoding */
132-
integer_cur = aws_byte_cursor_left_trim_pred(&integer_cur, s_trim_zeros_predicate);
133-
aws_der_encoder_write_unsigned_integer(encoder, integer_cur);
134-
integer_cur = aws_byte_cursor_from_array(temp_signature_buf.buffer + coordinate_len, coordinate_len);
135-
/* trim off the leading zero padding for DER encoding */
136-
integer_cur = aws_byte_cursor_left_trim_pred(&integer_cur, s_trim_zeros_predicate);
137-
aws_der_encoder_write_unsigned_integer(encoder, integer_cur);
138-
aws_der_encoder_end_sequence(encoder);
139-
140-
struct aws_byte_cursor signature_out_cur;
141-
AWS_ZERO_STRUCT(signature_out_cur);
142-
aws_der_encoder_get_contents(encoder, &signature_out_cur);
143-
aws_byte_buf_append(signature_output, &signature_out_cur);
144-
aws_der_encoder_destroy(encoder);
145-
146125
return AWS_OP_SUCCESS;
147126
}
148127

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ add_test_case(ecdsa_test_import_asn1_key_pair_public_only)
8383
add_test_case(ecdsa_test_import_asn1_key_pair_invalid_fails)
8484
add_test_case(ecdsa_test_signature_format)
8585
add_test_case(ecdsa_p256_test_small_coordinate_verification)
86+
add_test_case(ecdsa_signature_encode_helper_roundtrip)
8687

8788
add_test_case(rsa_encryption_roundtrip_pkcs1_from_user)
8889
add_test_case(rsa_encryption_roundtrip_oaep_sha256_from_user)

tests/ecc_test.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,55 @@ static int s_ecdsa_p256_test_small_coordinate_verification(struct aws_allocator
950950
}
951951
AWS_TEST_CASE(ecdsa_p256_test_small_coordinate_verification, s_ecdsa_p256_test_small_coordinate_verification);
952952

953+
static int s_ecdsa_signature_encode_helper_roundtrip(struct aws_allocator *allocator, void *ctx) {
954+
(void)ctx;
955+
aws_cal_library_test_init(allocator);
956+
957+
struct aws_byte_cursor signature_value_cursor = aws_byte_cursor_from_string(s_signature_value);
958+
size_t binary_length = 0;
959+
if (aws_hex_compute_decoded_len(signature_value_cursor.len, &binary_length)) {
960+
return AWS_OP_ERR;
961+
}
962+
struct aws_byte_buf binary_signature;
963+
AWS_ZERO_STRUCT(binary_signature);
964+
965+
aws_byte_buf_init(&binary_signature, allocator, binary_length);
966+
967+
ASSERT_SUCCESS(aws_hex_decode(&signature_value_cursor, &binary_signature));
968+
969+
struct aws_byte_cursor r;
970+
AWS_ZERO_STRUCT(r);
971+
struct aws_byte_cursor s;
972+
AWS_ZERO_STRUCT(s);
973+
struct aws_byte_cursor bin_cursor = aws_byte_cursor_from_buf(&binary_signature);
974+
ASSERT_SUCCESS(aws_ecc_decode_signature_der_to_raw(allocator, bin_cursor, &r, &s));
975+
976+
uint8_t expected_r[] = {0x7c, 0xfd, 0x51, 0xaf, 0x2b, 0x72, 0x2f, 0x8d, 0x1f, 0xa1, 0xaf,
977+
0xb6, 0x5b, 0x4d, 0x54, 0x86, 0xed, 0x59, 0xa6, 0x7b, 0xcf, 0x9f,
978+
0x3a, 0xcc, 0x62, 0xaa, 0xd6, 0xdd, 0xd3, 0x7d, 0xb1};
979+
struct aws_byte_cursor expected_r_cur = aws_byte_cursor_from_array(expected_r, sizeof(expected_r));
980+
uint8_t expected_s[] = {0x9d, 0x4c, 0x9f, 0x9a, 0x37, 0x10, 0x4f, 0xc0, 0x1a, 0x8d, 0xaf,
981+
0xfc, 0x9a, 0x6b, 0xd1, 0x05, 0x6b, 0x7b, 0x43, 0xc1, 0x19, 0x6e,
982+
0xdd, 0xe0, 0xb5, 0x28, 0x78, 0xb7, 0x59, 0x62, 0x8f, 0x8c};
983+
struct aws_byte_cursor expected_s_cur = aws_byte_cursor_from_array(expected_s, sizeof(expected_s));
984+
985+
ASSERT_BIN_ARRAYS_EQUALS(expected_r_cur.ptr, expected_r_cur.len, r.ptr, r.len);
986+
ASSERT_BIN_ARRAYS_EQUALS(expected_s_cur.ptr, expected_s_cur.len, s.ptr, s.len);
987+
988+
struct aws_byte_buf encoded_sig;
989+
aws_byte_buf_init(&encoded_sig, allocator, 128);
990+
ASSERT_SUCCESS(aws_ecc_encode_signature_raw_to_der(allocator, r, s, &encoded_sig));
991+
992+
ASSERT_BIN_ARRAYS_EQUALS(binary_signature.buffer, binary_signature.len, encoded_sig.buffer, encoded_sig.len);
993+
994+
aws_byte_buf_clean_up(&binary_signature);
995+
aws_byte_buf_clean_up(&encoded_sig);
996+
997+
aws_cal_library_clean_up();
998+
return AWS_OP_SUCCESS;
999+
}
1000+
AWS_TEST_CASE(ecdsa_signature_encode_helper_roundtrip, s_ecdsa_signature_encode_helper_roundtrip);
1001+
9531002
#ifdef AWS_OS_APPLE
9541003

9551004
static int s_test_key_gen_from_private_fuzz(

0 commit comments

Comments
 (0)