key_derivation.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/auth/private/key_derivation.h>
  6. #include <aws/auth/credentials.h>
  7. #include <aws/cal/ecc.h>
  8. #include <aws/cal/hash.h>
  9. #include <aws/cal/hmac.h>
  10. #include <aws/common/byte_buf.h>
  11. #include <aws/common/string.h>
  12. /*
  13. * The maximum number of iterations we will attempt to derive a valid ecc key for. The probability that this counter
  14. * value ever gets reached is vanishingly low -- with reasonable uniformity/independence assumptions, it's
  15. * approximately
  16. *
  17. * 2 ^ (-32 * 254)
  18. */
  19. #define MAX_KEY_DERIVATION_COUNTER_VALUE 254
  20. /*
  21. * The encoding (32-bit, big-endian) of the prefix to the FixedInputString when fed to the hmac function, per
  22. * the sigv4a key derivation specification.
  23. */
  24. AWS_STATIC_STRING_FROM_LITERAL(s_1_as_four_bytes_be, "\x00\x00\x00\x01");
  25. /*
  26. * The encoding (32-bit, big-endian) of the "Length" component of the sigv4a key derivation specification
  27. */
  28. AWS_STATIC_STRING_FROM_LITERAL(s_256_as_four_bytes_be, "\x00\x00\x01\x00");
  29. AWS_STRING_FROM_LITERAL(g_signature_type_sigv4a_http_request, "AWS4-ECDSA-P256-SHA256");
  30. AWS_STATIC_STRING_FROM_LITERAL(s_secret_buffer_prefix, "AWS4A");
  31. /*
  32. * This constructs the fixed input byte sequence of the Sigv4a key derivation specification. It also includes the
  33. * value (0x01 as a 32-bit big endian value) that is pre-pended to the fixed input before invoking the hmac to
  34. * generate the candidate key value.
  35. *
  36. * The final output looks like
  37. *
  38. * 0x00000001 || "AWS4-ECDSA-P256-SHA256" || 0x00 || AccessKeyId || CounterValue as uint8_t || 0x00000100 (Length)
  39. *
  40. * From this, we can determine the necessary buffer capacity when setting up the fixed input buffer:
  41. *
  42. * 4 + 22 + 1 + len(AccessKeyId) + 1 + 4 = 32 + len(AccessKeyId)
  43. */
  44. static int s_aws_build_fixed_input_buffer(
  45. struct aws_byte_buf *fixed_input,
  46. const struct aws_credentials *credentials,
  47. const uint8_t counter) {
  48. if (counter == 0 || counter > MAX_KEY_DERIVATION_COUNTER_VALUE) {
  49. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  50. }
  51. if (!aws_byte_buf_is_valid(fixed_input)) {
  52. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  53. }
  54. aws_byte_buf_reset(fixed_input, false);
  55. /*
  56. * A placeholder value that's not actually part of the fixed input string in the spec, but is always this value
  57. * and is always the first byte of the hmac-ed string.
  58. */
  59. struct aws_byte_cursor one_cursor = aws_byte_cursor_from_string(s_1_as_four_bytes_be);
  60. if (aws_byte_buf_append_dynamic(fixed_input, &one_cursor)) {
  61. return AWS_OP_ERR;
  62. }
  63. struct aws_byte_cursor sigv4a_algorithm_cursor = aws_byte_cursor_from_string(g_signature_type_sigv4a_http_request);
  64. if (aws_byte_buf_append(fixed_input, &sigv4a_algorithm_cursor)) {
  65. return AWS_OP_ERR;
  66. }
  67. if (aws_byte_buf_append_byte_dynamic(fixed_input, 0)) {
  68. return AWS_OP_ERR;
  69. }
  70. struct aws_byte_cursor access_key_cursor = aws_credentials_get_access_key_id(credentials);
  71. if (aws_byte_buf_append(fixed_input, &access_key_cursor)) {
  72. return AWS_OP_ERR;
  73. }
  74. if (aws_byte_buf_append_byte_dynamic(fixed_input, counter)) {
  75. return AWS_OP_ERR;
  76. }
  77. struct aws_byte_cursor encoded_bit_length_cursor = aws_byte_cursor_from_string(s_256_as_four_bytes_be);
  78. if (aws_byte_buf_append_dynamic(fixed_input, &encoded_bit_length_cursor)) {
  79. return AWS_OP_ERR;
  80. }
  81. return AWS_OP_SUCCESS;
  82. }
  83. /*
  84. * aws_be_bytes_compare_constant_time() and aws_be_bytes_add_one_constant_time() are constant-time arithmetic functions
  85. * that operate on raw bytes as if they were unbounded integers in a big-endian base 255 format.
  86. */
  87. /*
  88. * In the following function gt and eq are updated together. After each update, the variables will be
  89. * in one of the following states:
  90. *
  91. * (1) gt is 0, eq is 1, and from an ordering perspective, lhs == rhs, as checked "so far"
  92. * (2) gt is 1, eq is 0, (lhs > rhs)
  93. * (3) gt is 0, eq is 0, (lhs < rhs)
  94. *
  95. * States (2) and (3) are terminal states that cannot be exited since eq is 0 and is the and-wise mask of all
  96. * subsequent gt updates. Similarly, once eq is zero it cannot ever become non-zero.
  97. *
  98. * Intuitively these ideas match the standard way of comparing magnitude equality by considering digit count and
  99. * digits from most significant to least significant.
  100. *
  101. * Let l and r be the the two digits that we are
  102. * comparing between lhs and rhs. Assume 0 <= l, r <= 255 seated in 32-bit integers
  103. *
  104. * gt is maintained by the following bit trick:
  105. *
  106. * l > r <=>
  107. * (r - l) < 0 <=>
  108. * (r - l) as an int32 has the high bit set <=>
  109. * ((r - l) >> 31) & 0x01 == 1
  110. *
  111. * eq is maintained by the following bit trick:
  112. *
  113. * l == r <=>
  114. * l ^ r == 0 <=>
  115. * (l ^ r) - 1 == -1 <=>
  116. * (((l ^ r) - 1) >> 31) & 0x01 == 1
  117. *
  118. * We apply to the volatile type modifier to attempt to prevent all early-out optimizations that a compiler might
  119. * apply if it performed constraint-based reasoning on the logic. This is based on treating volatile
  120. * semantically as "this value can change underneath you at any time so you always have to re-read it and cannot
  121. * reason statically about program behavior when it reaches a certain value (like 0)"
  122. */
  123. /**
  124. * Compares two large unsigned integers in a raw byte format.
  125. * The two operands *must* be the same size (simplifies the problem significantly).
  126. *
  127. * The output parameter comparison_result is set to:
  128. * -1 if lhs_raw_be_bigint < rhs_raw_be_bigint
  129. * 0 if lhs_raw_be_bigint == rhs_raw_be_bigint
  130. * 1 if lhs_raw_be_bigint > rhs_raw_be_bigint
  131. */
  132. int aws_be_bytes_compare_constant_time(
  133. const struct aws_byte_buf *lhs_raw_be_bigint,
  134. const struct aws_byte_buf *rhs_raw_be_bigint,
  135. int *comparison_result) {
  136. AWS_FATAL_PRECONDITION(aws_byte_buf_is_valid(lhs_raw_be_bigint));
  137. AWS_FATAL_PRECONDITION(aws_byte_buf_is_valid(rhs_raw_be_bigint));
  138. /*
  139. * We only need to support comparing byte sequences of the same length here
  140. */
  141. const size_t lhs_len = lhs_raw_be_bigint->len;
  142. if (lhs_len != rhs_raw_be_bigint->len) {
  143. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  144. }
  145. volatile uint8_t gt = 0;
  146. volatile uint8_t eq = 1;
  147. const uint8_t *lhs_raw_bytes = lhs_raw_be_bigint->buffer;
  148. const uint8_t *rhs_raw_bytes = rhs_raw_be_bigint->buffer;
  149. for (size_t i = 0; i < lhs_len; ++i) {
  150. volatile int32_t lhs_digit = (int32_t)lhs_raw_bytes[i];
  151. volatile int32_t rhs_digit = (int32_t)rhs_raw_bytes[i];
  152. /*
  153. * For each digit, check for a state (1) => (2) ie lhs > rhs, or (1) => (3) ie lhs < rhs transition
  154. * based on comparing the two digits in constant time using the ideas explained in the giant comment
  155. * block above this function.
  156. */
  157. gt |= ((rhs_digit - lhs_digit) >> 31) & eq;
  158. eq &= (((lhs_digit ^ rhs_digit) - 1) >> 31) & 0x01;
  159. }
  160. *comparison_result = gt + gt + eq - 1;
  161. return AWS_OP_SUCCESS;
  162. }
  163. /**
  164. * Adds one to a large unsigned integer represented by a sequence of bytes.
  165. *
  166. * A maximal value will roll over to zero. This does not affect the correctness of the users
  167. * of this function.
  168. */
  169. void aws_be_bytes_add_one_constant_time(struct aws_byte_buf *raw_be_bigint) {
  170. AWS_FATAL_PRECONDITION(aws_byte_buf_is_valid(raw_be_bigint));
  171. const size_t byte_count = raw_be_bigint->len;
  172. volatile uint32_t carry = 1;
  173. uint8_t *raw_bytes = raw_be_bigint->buffer;
  174. for (size_t i = 0; i < byte_count; ++i) {
  175. const size_t index = byte_count - i - 1;
  176. volatile uint32_t current_digit = raw_bytes[index];
  177. current_digit += carry;
  178. carry = (current_digit >> 8) & 0x01;
  179. raw_bytes[index] = (uint8_t)(current_digit & 0xFF);
  180. }
  181. }
  182. /* clang-format off */
  183. /* In the spec, this is N-2 */
  184. static uint8_t s_n_minus_2[32] = {
  185. 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
  186. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  187. 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
  188. 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x4F,
  189. };
  190. /* clang-format on */
  191. enum aws_key_derivation_result {
  192. AKDR_SUCCESS,
  193. AKDR_NEXT_COUNTER,
  194. AKDR_FAILURE,
  195. };
  196. static enum aws_key_derivation_result s_aws_derive_ecc_private_key(
  197. struct aws_byte_buf *private_key_value,
  198. const struct aws_byte_buf *k0) {
  199. AWS_FATAL_ASSERT(k0->len == aws_ecc_key_coordinate_byte_size_from_curve_name(AWS_CAL_ECDSA_P256));
  200. aws_byte_buf_reset(private_key_value, false);
  201. struct aws_byte_buf s_n_minus_2_buf = {
  202. .allocator = NULL,
  203. .buffer = s_n_minus_2,
  204. .capacity = AWS_ARRAY_SIZE(s_n_minus_2),
  205. .len = AWS_ARRAY_SIZE(s_n_minus_2),
  206. };
  207. int comparison_result = 0;
  208. if (aws_be_bytes_compare_constant_time(k0, &s_n_minus_2_buf, &comparison_result)) {
  209. return AKDR_FAILURE;
  210. }
  211. if (comparison_result > 0) {
  212. return AKDR_NEXT_COUNTER;
  213. }
  214. struct aws_byte_cursor k0_cursor = aws_byte_cursor_from_buf(k0);
  215. if (aws_byte_buf_append(private_key_value, &k0_cursor)) {
  216. return AKDR_FAILURE;
  217. }
  218. aws_be_bytes_add_one_constant_time(private_key_value);
  219. return AKDR_SUCCESS;
  220. }
  221. static int s_init_secret_buf(
  222. struct aws_byte_buf *secret_buf,
  223. struct aws_allocator *allocator,
  224. const struct aws_credentials *credentials) {
  225. struct aws_byte_cursor secret_access_key_cursor = aws_credentials_get_secret_access_key(credentials);
  226. size_t secret_buffer_length = secret_access_key_cursor.len + s_secret_buffer_prefix->len;
  227. if (aws_byte_buf_init(secret_buf, allocator, secret_buffer_length)) {
  228. return AWS_OP_ERR;
  229. }
  230. struct aws_byte_cursor prefix_cursor = aws_byte_cursor_from_string(s_secret_buffer_prefix);
  231. if (aws_byte_buf_append(secret_buf, &prefix_cursor)) {
  232. return AWS_OP_ERR;
  233. }
  234. if (aws_byte_buf_append(secret_buf, &secret_access_key_cursor)) {
  235. return AWS_OP_ERR;
  236. }
  237. return AWS_OP_SUCCESS;
  238. }
  239. struct aws_ecc_key_pair *aws_ecc_key_pair_new_ecdsa_p256_key_from_aws_credentials(
  240. struct aws_allocator *allocator,
  241. const struct aws_credentials *credentials) {
  242. if (allocator == NULL || credentials == NULL) {
  243. aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  244. return NULL;
  245. }
  246. struct aws_ecc_key_pair *ecc_key_pair = NULL;
  247. struct aws_byte_buf fixed_input;
  248. AWS_ZERO_STRUCT(fixed_input);
  249. struct aws_byte_buf fixed_input_hmac_digest;
  250. AWS_ZERO_STRUCT(fixed_input_hmac_digest);
  251. struct aws_byte_buf private_key_buf;
  252. AWS_ZERO_STRUCT(private_key_buf);
  253. struct aws_byte_buf secret_buf;
  254. AWS_ZERO_STRUCT(secret_buf);
  255. size_t access_key_length = aws_credentials_get_access_key_id(credentials).len;
  256. /*
  257. * This value is calculated based on the format of the fixed input string as described above at
  258. * the definition of s_aws_build_fixed_input_buffer()
  259. */
  260. size_t required_fixed_input_capacity = 32 + access_key_length;
  261. if (aws_byte_buf_init(&fixed_input, allocator, required_fixed_input_capacity)) {
  262. goto done;
  263. }
  264. if (aws_byte_buf_init(&fixed_input_hmac_digest, allocator, AWS_SHA256_LEN)) {
  265. goto done;
  266. }
  267. size_t key_length = aws_ecc_key_coordinate_byte_size_from_curve_name(AWS_CAL_ECDSA_P256);
  268. AWS_FATAL_ASSERT(key_length == AWS_SHA256_LEN);
  269. if (aws_byte_buf_init(&private_key_buf, allocator, key_length)) {
  270. goto done;
  271. }
  272. if (s_init_secret_buf(&secret_buf, allocator, credentials)) {
  273. goto done;
  274. }
  275. struct aws_byte_cursor secret_cursor = aws_byte_cursor_from_buf(&secret_buf);
  276. uint8_t counter = 1;
  277. enum aws_key_derivation_result result = AKDR_NEXT_COUNTER;
  278. while ((result == AKDR_NEXT_COUNTER) && (counter <= MAX_KEY_DERIVATION_COUNTER_VALUE)) {
  279. if (s_aws_build_fixed_input_buffer(&fixed_input, credentials, counter++)) {
  280. break;
  281. }
  282. aws_byte_buf_reset(&fixed_input_hmac_digest, true);
  283. struct aws_byte_cursor fixed_input_cursor = aws_byte_cursor_from_buf(&fixed_input);
  284. if (aws_sha256_hmac_compute(allocator, &secret_cursor, &fixed_input_cursor, &fixed_input_hmac_digest, 0)) {
  285. break;
  286. }
  287. result = s_aws_derive_ecc_private_key(&private_key_buf, &fixed_input_hmac_digest);
  288. }
  289. if (result == AKDR_SUCCESS) {
  290. struct aws_byte_cursor private_key_cursor = aws_byte_cursor_from_buf(&private_key_buf);
  291. ecc_key_pair = aws_ecc_key_pair_new_from_private_key(allocator, AWS_CAL_ECDSA_P256, &private_key_cursor);
  292. }
  293. done:
  294. aws_byte_buf_clean_up_secure(&secret_buf);
  295. aws_byte_buf_clean_up_secure(&private_key_buf);
  296. aws_byte_buf_clean_up_secure(&fixed_input_hmac_digest);
  297. aws_byte_buf_clean_up(&fixed_input);
  298. return ecc_key_pair;
  299. }