/* A TLS CBC record looks like .. * * [ Payload data ] [ HMAC ] [ Padding ] [ Padding length byte ] * * Each byte in the padding is expected to be set to the same value * as the padding length byte. So if the padding length byte is '2' * then the padding will be [ '2', '2' ] (there'll be three bytes * set to that value if you include the padding length byte). * * The goal of s2n_verify_cbc() is to verify that the padding and hmac * are correct, without leaking (via timing) how much padding there * actually is: as this is considered secret. * * In addition to our efforts here though, s2n also wraps any CBC * verification error (or record parsing error in general) with * a randomized delay of between 1ms and 10 seconds. See s2n_connection.c. * This amount of delay randomization is sufficient to increase the * complexity of attack for even a 1 microsecond timing leak (which * is quite large) by a factor of around 83 trillion. */ int s2n_verify_cbc(struct s2n_connection *conn, struct s2n_hmac_state *hmac, struct s2n_blob *decrypted) { struct s2n_hmac_state copy; int mac_digest_size = s2n_hmac_digest_size(hmac->alg); /* The record has to be at least big enough to contain the MAC, * plus the padding length byte */ gt_check(decrypted->size, mac_digest_size); int payload_and_padding_size = decrypted->size - mac_digest_size; /* Determine what the padding length is */ uint8_t padding_length = decrypted->data[decrypted->size - 1]; int payload_length = MAX(payload_and_padding_size - padding_length - 1, 0); /* Update the MAC */ GUARD(s2n_hmac_update(hmac, decrypted->data, payload_length)); GUARD(s2n_hmac_copy(©, hmac)); /* Check the MAC */ uint8_t check_digest[S2N_MAX_DIGEST_LEN]; lte_check(mac_digest_size, sizeof(check_digest)); GUARD(s2n_hmac_digest_two_compression_rounds(hmac, check_digest, mac_digest_size)); int mismatches = s2n_constant_time_equals(decrypted->data + payload_length, check_digest, mac_digest_size) ^ 1; /* Compute a MAC on the rest of the data so that we perform the same number of hash operations */ GUARD(s2n_hmac_update(©, decrypted->data + payload_length + mac_digest_size, decrypted->size - payload_length - mac_digest_size - 1)); /* SSLv3 doesn't specify what the padding should actually be */ if (conn->actual_protocol_version == S2N_SSLv3) { return 0 - mismatches; } /* Check the maximum amount that could theoritically be padding */ int check = MIN(255, (payload_and_padding_size - 1)); int cutoff = check - padding_length; for (int i = 0, j = decrypted->size - 1 - check; i < check && j < decrypted->size; i++, j++) { uint8_t mask = ~(0xff << ((i >= cutoff) * 8)); mismatches |= (decrypted->data[j] ^ padding_length) & mask; } if (mismatches) { S2N_ERROR(S2N_ERR_CBC_VERIFY); } return 0; }
int main(int argc, char **argv) { uint8_t digest_pad[256]; uint8_t check_pad[256]; uint8_t output_pad[256]; struct s2n_stuffer output; uint8_t sekrit[] = "sekrit"; uint8_t longsekrit[] = "This is a really really really long key on purpose to make sure that it's longer than the block size"; uint8_t hello[] = "Hello world!"; struct s2n_hmac_state hmac, copy; struct s2n_hmac_state cmac; struct s2n_blob out = {.data = output_pad,.size = sizeof(output_pad) }; BEGIN_TEST(); /* Initialise our output stuffers */ EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_MD5), 16); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_MD5, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_copy(©, &hmac)); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "3ad68c53dc1a3cf35f6469877fae4585", 16 * 2), 0); /* Check the copy */ EXPECT_SUCCESS(s2n_hmac_digest(©, digest_pad, 16)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "3ad68c53dc1a3cf35f6469877fae4585", 16 * 2), 0); /* Test that a reset works */ EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "3ad68c53dc1a3cf35f6469877fae4585", 16 * 2), 0); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_MD5, longsekrit, strlen((char *)longsekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "2ce569d61f4ee6ad9ceebe02a112ace7", 16 * 2), 0); /* Test that a reset works */ EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "2ce569d61f4ee6ad9ceebe02a112ace7", 16 * 2), 0); /* Verify that _verify works */ EXPECT_SUCCESS(s2n_hmac_init(&cmac, S2N_HMAC_MD5, longsekrit, strlen((char *)longsekrit))); EXPECT_SUCCESS(s2n_hmac_update(&cmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&cmac, check_pad, 16)); EXPECT_SUCCESS(s2n_hmac_digest_verify(digest_pad, 16, check_pad, 16)); /* Try SHA1 */ EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_SHA1), 20); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA1, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 20; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "6d301861b599938eca94f6de917362886d97882f", 20 * 2), 0); /* Try SHA256 */ EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_SHA256), 32); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA256, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 32)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 32; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "adc20b12d236e6d1824d690622e33ead4f67ba5a2be9606fe762b2dd859a78a9", 32 * 2), 0); /* Try SHA384 */ EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_SHA384), 48); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA384, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 48)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 48; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "8552563cadd583b79dcc7225bb79bc6483c63f259187162e1c9d4283eb6299ef1bc3ca81c0c40fc7b22f7a1f3b93adb4", 48 * 2), 0); /* Try SHA512 */ EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_SHA512), 64); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA512, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 64)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 64; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from python */ EXPECT_EQUAL(memcmp(output_pad, "0a834a1ed265042e2897405edb4fdd9818950cd5bea10b828f2fed45a1cb6dbd2107e4b04eb20f211998cd4e8c7e11ebdcb0103ac63882481e1bb8083d07f4be", 64 * 2), 0); /* Try SSLv3 MD5 */ EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_SSLv3_MD5), 16); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SSLv3_MD5, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from Go */ EXPECT_EQUAL(memcmp(output_pad, "d4f0d06b9765de23e6c3e33a24c5ded0", 16 * 2), 0); /* Test that a reset works */ EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 16; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } EXPECT_EQUAL(memcmp(output_pad, "d4f0d06b9765de23e6c3e33a24c5ded0", 16 * 2), 0); /* Try SSLv3 SHA1 */ EXPECT_EQUAL(s2n_hmac_digest_size(S2N_HMAC_SSLv3_SHA1), 20); EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SSLv3_SHA1, sekrit, strlen((char *)sekrit))); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 20; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } /* Reference value from Go */ EXPECT_EQUAL(memcmp(output_pad, "b0c66179f6eb5a46b4b7c4fca84b3ea5161b7326", 20 * 2), 0); /* Test that a reset works */ EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *)hello))); EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); for (int i = 0; i < 20; i++) { EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); } EXPECT_EQUAL(memcmp(output_pad, "b0c66179f6eb5a46b4b7c4fca84b3ea5161b7326", 20 * 2), 0); END_TEST(); }