int s2n_hmac_reset(struct s2n_hmac_state *state) { state->currently_in_hash_block = 0; memcpy_check(&state->inner, &state->inner_just_key, sizeof(state->inner)); return 0; }
/* * For each name in the cert. Iterate them. Call the callback. If one returns true, then consider it validated, * if none of them return true, the cert is considered invalid. */ static uint8_t s2n_verify_host_information(struct s2n_x509_validator *validator, struct s2n_connection *conn, X509 *public_cert) { uint8_t verified = 0; uint8_t san_found = 0; /* Check SubjectAltNames before CommonName as per RFC 6125 6.4.4 */ STACK_OF(GENERAL_NAME) *names_list = X509_get_ext_d2i(public_cert, NID_subject_alt_name, NULL, NULL); int n = sk_GENERAL_NAME_num(names_list); for (int i = 0; i < n && !verified; i++) { GENERAL_NAME *current_name = sk_GENERAL_NAME_value(names_list, i); if (current_name->type == GEN_DNS) { san_found = 1; const char *name = (const char *) ASN1_STRING_data(current_name->d.ia5); size_t name_len = (size_t) ASN1_STRING_length(current_name->d.ia5); verified = conn->verify_host_fn(name, name_len, conn->data_for_verify_host); } } GENERAL_NAMES_free(names_list); /* if no SubjectAltNames of type DNS found, go to the common name. */ if (!san_found) { X509_NAME *subject_name = X509_get_subject_name(public_cert); if (subject_name) { int next_idx = 0, curr_idx = -1; while ((next_idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, curr_idx)) >= 0) { curr_idx = next_idx; } if (curr_idx >= 0) { ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, curr_idx)); if (common_name) { char peer_cn[255]; static size_t peer_cn_size = sizeof(peer_cn); memset_check(&peer_cn, 0, peer_cn_size); // X520CommonName allows the following ANSI string types per RFC 5280 Appendix A.1 if (ASN1_STRING_type(common_name) == V_ASN1_TELETEXSTRING || ASN1_STRING_type(common_name) == V_ASN1_PRINTABLESTRING || ASN1_STRING_type(common_name) == V_ASN1_UNIVERSALSTRING || ASN1_STRING_type(common_name) == V_ASN1_UTF8STRING || ASN1_STRING_type(common_name) == V_ASN1_BMPSTRING ) { size_t len = (size_t) ASN1_STRING_length(common_name); lte_check(len, sizeof(peer_cn) - 1); memcpy_check(peer_cn, ASN1_STRING_data(common_name), len); verified = conn->verify_host_fn(peer_cn, len, conn->data_for_verify_host); } } } } } return verified; }
int s2n_realloc(struct s2n_blob *b, uint32_t size) { if (size == 0) { return s2n_free(b); } /* blob already has space for the request */ if (size < b->allocated) { b->size = size; return 0; } void *data; if (!use_mlock) { data = realloc(b->data, size); if (!data) { S2N_ERROR(S2N_ERR_ALLOC); } b->data = data; b->size = size; b->allocated = size; return 0; } /* Page aligned allocation required for mlock */ uint32_t allocate = page_size * (((size - 1) / page_size) + 1); if (posix_memalign(&data, page_size, allocate)) { S2N_ERROR(S2N_ERR_ALLOC); } if (b->size) { memcpy_check(data, b->data, b->size); GUARD(s2n_free(b)); } b->data = data; b->size = size; b->allocated = allocate; #ifdef MADV_DONTDUMP if (madvise(b->data, size, MADV_DONTDUMP) < 0) { GUARD(s2n_free(b)); S2N_ERROR(S2N_ERR_MADVISE); } #endif if (mlock(b->data, size) < 0) { GUARD(s2n_free(b)); S2N_ERROR(S2N_ERR_MLOCK); } b->mlocked = 1; return 0; }
int s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) { GUARD(s2n_stuffer_skip_read(stuffer, size)); void *ptr = stuffer->blob.data + stuffer->read_cursor - size; notnull_check(ptr); memcpy_check(data, ptr, size); return 0; }
int s2n_dup(struct s2n_blob *from, struct s2n_blob *to) { eq_check(to->size, 0); eq_check(to->data, NULL); GUARD(s2n_alloc(to, from->size)); memcpy_check(to->data, from->data, to->size); return 0; }
static int s2n_stream_cipher_null_endecrypt(struct s2n_session_key *key, struct s2n_blob *in, struct s2n_blob *out) { if (out->data < in->data) { S2N_ERROR(S2N_ERR_SIZE_MISMATCH); } if (in->data != out->data) { memcpy_check(in->data, out->data, out->size); } return 0; }
static int s2n_sslv3_mac_digest(struct s2n_hmac_state *state, void *out, uint32_t size) { for (int i = 0; i < state->block_size; i++) { state->xor_pad[i] = 0x5c; } GUARD(s2n_hash_digest(&state->inner, state->digest_pad, state->digest_size)); memcpy_check(&state->inner, &state->outer, sizeof(state->inner)); GUARD(s2n_hash_update(&state->inner, state->digest_pad, state->digest_size)); return s2n_hash_digest(&state->inner, out, size); }
static int s2n_sslv3_prf(struct s2n_prf_working_space *ws, struct s2n_blob *secret, struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) { struct s2n_hash_state *md5 = &ws->ssl3.md5; struct s2n_hash_state *sha1 = &ws->ssl3.sha1; uint32_t outputlen = out->size; uint8_t *output = out->data; uint8_t iteration = 1; uint8_t A = 'A'; while (outputlen) { GUARD(s2n_hash_reset(sha1)); for (int i = 0; i < iteration; i++) { GUARD(s2n_hash_update(sha1, &A, 1)); } GUARD(s2n_hash_update(sha1, secret->data, secret->size)); GUARD(s2n_hash_update(sha1, seed_a->data, seed_a->size)); if (seed_b) { GUARD(s2n_hash_update(sha1, seed_b->data, seed_b->size)); if (seed_c) { GUARD(s2n_hash_update(sha1, seed_c->data, seed_c->size)); } } GUARD(s2n_hash_digest(sha1, ws->ssl3.sha1_digest, sizeof(ws->ssl3.sha1_digest))); GUARD(s2n_hash_reset(md5)); GUARD(s2n_hash_update(md5, secret->data, secret->size)); GUARD(s2n_hash_update(md5, ws->ssl3.sha1_digest, sizeof(ws->ssl3.sha1_digest))); GUARD(s2n_hash_digest(md5, ws->ssl3.md5_digest, sizeof(ws->ssl3.md5_digest))); uint32_t bytes_to_copy = MIN(outputlen, sizeof(ws->ssl3.md5_digest)); memcpy_check(output, ws->ssl3.md5_digest, bytes_to_copy); outputlen -= bytes_to_copy; output += bytes_to_copy; /* Increment the letter */ A++; iteration++; } GUARD(s2n_hash_reset(md5)); GUARD(s2n_hash_reset(sha1)); return 0; }
int s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) { GUARD(s2n_stuffer_skip_read(stuffer, out->size)); void *ptr = stuffer->blob.data + stuffer->read_cursor - out->size; if (ptr == NULL) { return -1; } memcpy_check(out->data, ptr, out->size); memset(ptr, 0, out->size); return 0; }
int s2n_set_server_name(struct s2n_connection *conn, const char *server_name) { if (conn->mode != S2N_CLIENT) { S2N_ERROR(S2N_ERR_CLIENT_MODE); } int len = strlen(server_name); if (len > 255) { S2N_ERROR(S2N_ERR_SERVER_NAME_TOO_LONG); } memcpy_check(conn->server_name, server_name, len); return 0; }
int s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *data, const uint32_t size) { GUARD(s2n_stuffer_skip_write(stuffer, size)); void *ptr = stuffer->blob.data + stuffer->write_cursor - size; if (ptr == NULL) { return -1; } if (ptr == data) { return 0; } memcpy_check(ptr, data, size); return 0; }
int s2n_server_status_recv(struct s2n_connection *conn) { uint8_t type; struct s2n_blob status = { .data = NULL, .size = 0 }; GUARD(s2n_stuffer_read_uint8(&conn->handshake.io, &type)); GUARD(s2n_stuffer_read_uint24(&conn->handshake.io, &status.size)); status.data = s2n_stuffer_raw_read(&conn->handshake.io, status.size); notnull_check(status.data); if (type == S2N_STATUS_REQUEST_OCSP) { GUARD(s2n_alloc(&conn->status_response, status.size)); memcpy_check(conn->status_response.data, status.data, status.size); conn->status_response.size = status.size; } return 0; }
int s2n_realloc(struct s2n_blob *b, uint32_t size) { if (size == 0) { return s2n_free(b); } if (size < b->allocated) { b->size = size; return 0; } uint32_t allocate = page_size * ((size + (page_size - 1)) / page_size); // preventing unsigned overflow occurs at (size + (page_size - 1)) uint32_t allocate = page_size * (((size - 1) / page_size) + 1); void *data; if (posix_memalign(&data, page_size, allocate)) { S2N_ERROR(S2N_ERR_ALLOC); } if (b->size) { memcpy_check(data, b->data, b->size); GUARD(s2n_free(b)); } b->data = data; b->size = size; b->allocated = allocate; #ifdef MADV_DONTDUMP if (madvise(b->data, size, MADV_DONTDUMP) < 0) { GUARD(s2n_free(b)); S2N_ERROR(S2N_ERR_MADVISE); } #endif if (use_mlock == 0) { return 0; } if (mlock(b->data, size) < 0) { GUARD(s2n_free(b)); S2N_ERROR(S2N_ERR_MLOCK); } b->mlocked = 1; return 0; }
int s2n_recv_server_alpn(struct s2n_connection *conn, struct s2n_stuffer *extension) { uint16_t size_of_all; GUARD(s2n_stuffer_read_uint16(extension, &size_of_all)); if (size_of_all > s2n_stuffer_data_available(extension) || size_of_all < 3) { /* ignore invalid extension size */ return 0; } uint8_t protocol_len; GUARD(s2n_stuffer_read_uint8(extension, &protocol_len)); uint8_t *protocol = s2n_stuffer_raw_read(extension, protocol_len); notnull_check(protocol); /* copy the first protocol name */ memcpy_check(conn->application_protocol, protocol, protocol_len); conn->application_protocol[protocol_len] = '\0'; return 0; }
static int s2n_recv_client_server_name(struct s2n_connection *conn, struct s2n_stuffer *extension) { uint16_t size_of_all; uint8_t server_name_type; uint16_t server_name_len; uint8_t *server_name; GUARD(s2n_stuffer_read_uint16(extension, &size_of_all)); if (size_of_all > s2n_stuffer_data_available(extension) || size_of_all < 3) { /* the size of all server names is incorrect, ignore the extension */ return 0; } GUARD(s2n_stuffer_read_uint8(extension, &server_name_type)); if (server_name_type != 0) { /* unknown server name type, ignore the extension */ return 0; } GUARD(s2n_stuffer_read_uint16(extension, &server_name_len)); if (server_name_len + 3 > size_of_all) { /* the server name length is incorrect, ignore the extension */ return 0; } if (server_name_len > sizeof(conn->server_name) - 1) { /* the server name is too long, ignore the extension */ return 0; } notnull_check(server_name = s2n_stuffer_raw_read(extension, server_name_len)); /* copy the first server name */ memcpy_check(conn->server_name, server_name, server_name_len); return 0; }
int s2n_server_status_recv(struct s2n_connection *conn) { uint8_t type; struct s2n_blob status = { .data = NULL, .size = 0 }; GUARD(s2n_stuffer_read_uint8(&conn->handshake.io, &type)); GUARD(s2n_stuffer_read_uint24(&conn->handshake.io, &status.size)); status.data = s2n_stuffer_raw_read(&conn->handshake.io, status.size); notnull_check(status.data); if (type == S2N_STATUS_REQUEST_OCSP) { GUARD(s2n_alloc(&conn->status_response, status.size)); memcpy_check(conn->status_response.data, status.data, status.size); conn->status_response.size = status.size; } conn->handshake.next_state = SERVER_HELLO_DONE; if (conn->pending.cipher_suite->key_exchange_alg->flags & S2N_KEY_EXCHANGE_EPH) { conn->handshake.next_state = SERVER_KEY; } return 0; }
static int s2n_recv_client_alpn(struct s2n_connection *conn, struct s2n_stuffer *extension) { uint16_t size_of_all; struct s2n_stuffer client_protos; struct s2n_stuffer server_protos; if (!conn->config->application_protocols.size) { /* No protocols configured, nothing to do */ return 0; } GUARD(s2n_stuffer_read_uint16(extension, &size_of_all)); if (size_of_all > s2n_stuffer_data_available(extension) || size_of_all < 3) { /* Malformed length, ignore the extension */ return 0; } struct s2n_blob application_protocols = { .data = s2n_stuffer_raw_read(extension, size_of_all), .size = size_of_all }; notnull_check(application_protocols.data); /* Find a matching protocol */ GUARD(s2n_stuffer_init(&client_protos, &application_protocols)); GUARD(s2n_stuffer_write(&client_protos, &application_protocols)); GUARD(s2n_stuffer_init(&server_protos, &conn->config->application_protocols)); GUARD(s2n_stuffer_write(&server_protos, &conn->config->application_protocols)); while (s2n_stuffer_data_available(&server_protos)) { uint8_t length; uint8_t protocol[255]; GUARD(s2n_stuffer_read_uint8(&server_protos, &length)); GUARD(s2n_stuffer_read_bytes(&server_protos, protocol, length)); while (s2n_stuffer_data_available(&client_protos)) { uint8_t client_length; GUARD(s2n_stuffer_read_uint8(&client_protos, &client_length)); if (client_length > s2n_stuffer_data_available(&client_protos)) { S2N_ERROR(S2N_ERR_BAD_MESSAGE); } if (client_length != length) { GUARD(s2n_stuffer_skip_read(&client_protos, client_length)); } else { uint8_t client_protocol[255]; GUARD(s2n_stuffer_read_bytes(&client_protos, client_protocol, client_length)); if (memcmp(client_protocol, protocol, client_length) == 0) { memcpy_check(conn->application_protocol, client_protocol, client_length); conn->application_protocol[client_length] = '\0'; return 0; } } } GUARD(s2n_stuffer_reread(&client_protos)); } S2N_ERROR(S2N_ERR_NO_APPLICATION_PROTOCOL); } static int s2n_recv_client_status_request(struct s2n_connection *conn, struct s2n_stuffer *extension) { if (s2n_stuffer_data_available(extension) < 5) { /* Malformed length, ignore the extension */ return 0; } uint8_t type; GUARD(s2n_stuffer_read_uint8(extension, &type)); if (type != (uint8_t) S2N_STATUS_REQUEST_OCSP) { /* We only support OCSP (type 1), ignore the extension */ return 0; } conn->status_type = (s2n_status_request_type) type; return 0; } static int s2n_recv_client_elliptic_curves(struct s2n_connection *conn, struct s2n_stuffer *extension) { uint16_t size_of_all; struct s2n_blob proposed_curves; GUARD(s2n_stuffer_read_uint16(extension, &size_of_all)); if (size_of_all > s2n_stuffer_data_available(extension) || size_of_all % 2) { /* Malformed length, ignore the extension */ return 0; } proposed_curves.size = size_of_all; proposed_curves.data = s2n_stuffer_raw_read(extension, proposed_curves.size); notnull_check(proposed_curves.data); if (s2n_ecc_find_supported_curve(&proposed_curves, &conn->secure.server_ecc_params.negotiated_curve) != 0) { /* Can't agree on a curve, ECC is not allowed. Return success to proceed with the handshake. */ conn->secure.server_ecc_params.negotiated_curve = NULL; } return 0; }
int s2n_record_write(struct s2n_connection *conn, uint8_t content_type, struct s2n_blob *in) { struct s2n_blob out, iv, aad; uint8_t padding = 0; uint16_t block_size = 0; uint8_t aad_gen[S2N_TLS_MAX_AAD_LEN] = { 0 }; uint8_t aad_iv[S2N_TLS_MAX_IV_LEN] = { 0 }; uint8_t *sequence_number = conn->server->server_sequence_number; struct s2n_hmac_state *mac = &conn->server->server_record_mac; struct s2n_session_key *session_key = &conn->server->server_key; const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; uint8_t *implicit_iv = conn->server->server_implicit_iv; if (conn->mode == S2N_CLIENT) { sequence_number = conn->client->client_sequence_number; mac = &conn->client->client_record_mac; session_key = &conn->client->client_key; cipher_suite = conn->client->cipher_suite; implicit_iv = conn->client->client_implicit_iv; } S2N_ERROR_IF(s2n_stuffer_data_available(&conn->out), S2N_ERR_BAD_MESSAGE); uint8_t mac_digest_size; GUARD(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); /* Before we do anything, we need to figure out what the length of the * fragment is going to be. */ uint16_t data_bytes_to_take = MIN(in->size, s2n_record_max_write_payload_size(conn)); uint16_t extra = overhead(conn); /* If we have padding to worry about, figure that out too */ if (cipher_suite->record_alg->cipher->type == S2N_CBC) { block_size = cipher_suite->record_alg->cipher->io.cbc.block_size; if (((data_bytes_to_take + extra) % block_size)) { padding = block_size - ((data_bytes_to_take + extra) % block_size); } } else if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { block_size = cipher_suite->record_alg->cipher->io.comp.block_size; } /* Start the MAC with the sequence number */ GUARD(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); /* Now that we know the length, start writing the record */ GUARD(s2n_stuffer_write_uint8(&conn->out, content_type)); GUARD(s2n_record_write_protocol_version(conn)); /* First write a header that has the payload length, this is for the MAC */ GUARD(s2n_stuffer_write_uint16(&conn->out, data_bytes_to_take)); if (conn->actual_protocol_version > S2N_SSLv3) { GUARD(s2n_hmac_update(mac, conn->out.blob.data, S2N_TLS_RECORD_HEADER_LENGTH)); } else { /* SSLv3 doesn't include the protocol version in the MAC */ GUARD(s2n_hmac_update(mac, conn->out.blob.data, 1)); GUARD(s2n_hmac_update(mac, conn->out.blob.data + 3, 2)); } /* Compute non-payload parts of the MAC(seq num, type, proto vers, fragment length) for composite ciphers. * Composite "encrypt" will MAC the payload data and fill in padding. */ if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { /* Only fragment length is needed for MAC, but the EVP ctrl function needs fragment length + eiv len. */ uint16_t payload_and_eiv_len = data_bytes_to_take; if (conn->actual_protocol_version > S2N_TLS10) { payload_and_eiv_len += block_size; } /* Outputs number of extra bytes required for MAC and padding */ int pad_and_mac_len; GUARD(cipher_suite->record_alg->cipher->io.comp.initial_hmac(session_key, sequence_number, content_type, conn->actual_protocol_version, payload_and_eiv_len, &pad_and_mac_len)); extra += pad_and_mac_len; } /* Rewrite the length to be the actual fragment length */ uint16_t actual_fragment_length = data_bytes_to_take + padding + extra; GUARD(s2n_stuffer_wipe_n(&conn->out, 2)); GUARD(s2n_stuffer_write_uint16(&conn->out, actual_fragment_length)); /* If we're AEAD, write the sequence number as an IV, and generate the AAD */ if (cipher_suite->record_alg->cipher->type == S2N_AEAD) { struct s2n_stuffer iv_stuffer = {{0}}; iv.data = aad_iv; iv.size = sizeof(aad_iv); GUARD(s2n_stuffer_init(&iv_stuffer, &iv)); if (cipher_suite->record_alg->flags & S2N_TLS12_AES_GCM_AEAD_NONCE) { /* Partially explicit nonce. See RFC 5288 Section 3 */ GUARD(s2n_stuffer_write_bytes(&conn->out, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); GUARD(s2n_stuffer_write_bytes(&iv_stuffer, implicit_iv, cipher_suite->record_alg->cipher->io.aead.fixed_iv_size)); GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); } else if (cipher_suite->record_alg->flags & S2N_TLS12_CHACHA_POLY_AEAD_NONCE) { /* Fully implicit nonce. See RFC7905 Section 2 */ uint8_t four_zeroes[4] = { 0 }; GUARD(s2n_stuffer_write_bytes(&iv_stuffer, four_zeroes, 4)); GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); for(int i = 0; i < cipher_suite->record_alg->cipher->io.aead.fixed_iv_size; i++) { aad_iv[i] = aad_iv[i] ^ implicit_iv[i]; } } else { S2N_ERROR(S2N_ERR_INVALID_NONCE_TYPE); } /* Set the IV size to the amount of data written */ iv.size = s2n_stuffer_data_available(&iv_stuffer); aad.data = aad_gen; aad.size = sizeof(aad_gen); struct s2n_stuffer ad_stuffer = {{0}}; GUARD(s2n_stuffer_init(&ad_stuffer, &aad)); GUARD(s2n_aead_aad_init(conn, sequence_number, content_type, data_bytes_to_take, &ad_stuffer)); } else if (cipher_suite->record_alg->cipher->type == S2N_CBC || cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { iv.size = block_size; iv.data = implicit_iv; /* For TLS1.1/1.2; write the IV with random data */ if (conn->actual_protocol_version > S2N_TLS10) { GUARD(s2n_get_public_random_data(&iv)); GUARD(s2n_stuffer_write(&conn->out, &iv)); } } /* We are done with this sequence number, so we can increment it */ struct s2n_blob seq = {.data = sequence_number,.size = S2N_TLS_SEQUENCE_NUM_LEN }; GUARD(s2n_increment_sequence_number(&seq)); /* Write the plaintext data */ out.data = in->data; out.size = data_bytes_to_take; GUARD(s2n_stuffer_write(&conn->out, &out)); GUARD(s2n_hmac_update(mac, out.data, out.size)); /* Write the digest */ uint8_t *digest = s2n_stuffer_raw_write(&conn->out, mac_digest_size); notnull_check(digest); GUARD(s2n_hmac_digest(mac, digest, mac_digest_size)); GUARD(s2n_hmac_reset(mac)); if (cipher_suite->record_alg->cipher->type == S2N_CBC) { /* Include padding bytes, each with the value 'p', and * include an extra padding length byte, also with the value 'p'. */ for (int i = 0; i <= padding; i++) { GUARD(s2n_stuffer_write_uint8(&conn->out, padding)); } } /* Rewind to rewrite/encrypt the packet */ GUARD(s2n_stuffer_rewrite(&conn->out)); /* Skip the header */ GUARD(s2n_stuffer_skip_write(&conn->out, S2N_TLS_RECORD_HEADER_LENGTH)); uint16_t encrypted_length = data_bytes_to_take + mac_digest_size; switch (cipher_suite->record_alg->cipher->type) { case S2N_AEAD: GUARD(s2n_stuffer_skip_write(&conn->out, cipher_suite->record_alg->cipher->io.aead.record_iv_size)); encrypted_length += cipher_suite->record_alg->cipher->io.aead.tag_size; break; case S2N_CBC: if (conn->actual_protocol_version > S2N_TLS10) { /* Leave the IV alone and unencrypted */ GUARD(s2n_stuffer_skip_write(&conn->out, iv.size)); } /* Encrypt the padding and the padding length byte too */ encrypted_length += padding + 1; break; case S2N_COMPOSITE: /* Composite CBC expects a pointer starting at explicit IV: [Explicit IV | fragment | MAC | padding | padding len ] * extra will account for the explicit IV len(if applicable), MAC digest len, padding len + padding byte. */ encrypted_length += extra; break; default: break; } /* Do the encryption */ struct s2n_blob en = {0}; en.size = encrypted_length; en.data = s2n_stuffer_raw_write(&conn->out, en.size); notnull_check(en.data); switch (cipher_suite->record_alg->cipher->type) { case S2N_STREAM: GUARD(cipher_suite->record_alg->cipher->io.stream.encrypt(session_key, &en, &en)); break; case S2N_CBC: GUARD(cipher_suite->record_alg->cipher->io.cbc.encrypt(session_key, &iv, &en, &en)); /* Copy the last encrypted block to be the next IV */ if (conn->actual_protocol_version < S2N_TLS11) { gte_check(en.size, block_size); memcpy_check(implicit_iv, en.data + en.size - block_size, block_size); } break; case S2N_AEAD: GUARD(cipher_suite->record_alg->cipher->io.aead.encrypt(session_key, &iv, &aad, &en, &en)); break; case S2N_COMPOSITE: /* This will: compute mac, append padding, append padding length, and encrypt */ GUARD(cipher_suite->record_alg->cipher->io.comp.encrypt(session_key, &iv, &en, &en)); /* Copy the last encrypted block to be the next IV */ gte_check(en.size, block_size); memcpy_check(implicit_iv, en.data + en.size - block_size, block_size); break; default: S2N_ERROR(S2N_ERR_CIPHER_TYPE); break; } conn->wire_bytes_out += actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH; return data_bytes_to_take; }
int s2n_config_free_cert_chain_and_key(struct s2n_config *config) { struct s2n_blob b = { .data = (uint8_t *) config->cert_and_key_pairs, .size = sizeof(struct s2n_cert_chain_and_key) }; /* If there were cert and key pairs set, walk the chain and free the certs */ if (config->cert_and_key_pairs) { struct s2n_cert_chain *node = config->cert_and_key_pairs->head; while (node) { struct s2n_blob n = { .data = (uint8_t *)node, .size = sizeof(struct s2n_cert_chain) }; /* Free the cert */ GUARD(s2n_free(&node->cert)); /* Advance to next */ node = node->next; /* Free the node */ GUARD(s2n_free(&n)); } GUARD(s2n_rsa_private_key_free(&config->cert_and_key_pairs->private_key)); GUARD(s2n_free(&config->cert_and_key_pairs->ocsp_status)); } GUARD(s2n_free(&b)); return 0; } int s2n_config_free_dhparams(struct s2n_config *config) { struct s2n_blob b = { .data = (uint8_t *) config->dhparams, .size = sizeof(struct s2n_dh_params) }; if (config->dhparams) { GUARD(s2n_dh_params_free(config->dhparams)); } GUARD(s2n_free(&b)); return 0; } int s2n_config_free(struct s2n_config *config) { struct s2n_blob b = {.data = (uint8_t *) config,.size = sizeof(struct s2n_config) }; GUARD(s2n_config_free_cert_chain_and_key(config)); GUARD(s2n_config_free_dhparams(config)); GUARD(s2n_free(&config->application_protocols)); GUARD(s2n_free(&b)); return 0; } int s2n_config_set_cipher_preferences(struct s2n_config *config, const char *version) { for (int i = 0; selection[i].version != NULL; i++) { if (!strcasecmp(version, selection[i].version)) { config->cipher_preferences = selection[i].preferences; return 0; } } s2n_errno = S2N_ERR_INVALID_CIPHER_PREFERENCES; return -1; } int s2n_config_set_protocol_preferences(struct s2n_config *config, const char * const *protocols, int protocol_count) { struct s2n_stuffer protocol_stuffer; GUARD(s2n_free(&config->application_protocols)); if (protocols == NULL || protocol_count == 0) { /* NULL value indicates no prference, so nothing to do */ return 0; } GUARD(s2n_stuffer_growable_alloc(&protocol_stuffer, 256)); for (int i = 0; i < protocol_count; i++) { size_t length = strlen(protocols[i]); uint8_t protocol[255]; if (length > 255 || (s2n_stuffer_data_available(&protocol_stuffer) + length + 1) > 65535) { return S2N_ERR_APPLICATION_PROTOCOL_TOO_LONG; } memcpy_check(protocol, protocols[i], length); GUARD(s2n_stuffer_write_uint8(&protocol_stuffer, length)); GUARD(s2n_stuffer_write_bytes(&protocol_stuffer, protocol, length)); } uint32_t size = s2n_stuffer_data_available(&protocol_stuffer); /* config->application_protocols blob now owns this data */ config->application_protocols.size = size; config->application_protocols.data = s2n_stuffer_raw_read(&protocol_stuffer, size); notnull_check(config->application_protocols.data); return 0; } int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type) { config->status_request_type = type; return 0; } int s2n_config_add_cert_chain_and_key_with_status(struct s2n_config *config, char *cert_chain_pem, char *private_key_pem, const uint8_t *status, uint32_t length) { struct s2n_stuffer chain_in_stuffer, cert_out_stuffer, key_in_stuffer, key_out_stuffer; struct s2n_blob key_blob; struct s2n_blob mem; /* Allocate the memory for the chain and key struct */ GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert_chain_and_key))); config->cert_and_key_pairs = (struct s2n_cert_chain_and_key *)(void *)mem.data; config->cert_and_key_pairs->ocsp_status.data = NULL; config->cert_and_key_pairs->ocsp_status.size = 0; /* Put the private key pem in a stuffer */ GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); /* Convert pem to asn1 and asn1 to the private key */ GUARD(s2n_stuffer_rsa_private_key_from_pem(&key_in_stuffer, &key_out_stuffer)); GUARD(s2n_stuffer_free(&key_in_stuffer)); key_blob.size = s2n_stuffer_data_available(&key_out_stuffer); key_blob.data = s2n_stuffer_raw_read(&key_out_stuffer, key_blob.size); notnull_check(key_blob.data); GUARD(s2n_asn1der_to_rsa_private_key(&config->cert_and_key_pairs->private_key, &key_blob)); GUARD(s2n_stuffer_free(&key_out_stuffer)); /* Turn the chain into a stuffer */ GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); struct s2n_cert_chain **insert = &config->cert_and_key_pairs->head; uint32_t chain_size = 0; do { struct s2n_cert_chain *new_node; if (s2n_stuffer_certificate_from_pem(&chain_in_stuffer, &cert_out_stuffer) < 0) { if (chain_size == 0) { S2N_ERROR(S2N_ERR_NO_CERTIFICATE_IN_PEM); } break; } GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert_chain))); new_node = (struct s2n_cert_chain *)(void *)mem.data; GUARD(s2n_alloc(&new_node->cert, s2n_stuffer_data_available(&cert_out_stuffer))); GUARD(s2n_stuffer_read(&cert_out_stuffer, &new_node->cert)); /* Additional 3 bytes for the length field in the protocol */ chain_size += new_node->cert.size + 3; new_node->next = NULL; *insert = new_node; insert = &new_node->next; } while (s2n_stuffer_data_available(&chain_in_stuffer)); GUARD(s2n_stuffer_free(&chain_in_stuffer)); GUARD(s2n_stuffer_free(&cert_out_stuffer)); config->cert_and_key_pairs->chain_size = chain_size; if (status && length > 0) { GUARD(s2n_alloc(&config->cert_and_key_pairs->ocsp_status, length)); memcpy_check(config->cert_and_key_pairs->ocsp_status.data, status, length); } return 0; } int s2n_config_add_cert_chain_and_key(struct s2n_config *config, char *cert_chain_pem, char *private_key_pem) { GUARD(s2n_config_add_cert_chain_and_key_with_status(config, cert_chain_pem, private_key_pem, NULL, 0)); return 0; } int s2n_config_add_dhparams(struct s2n_config *config, char *dhparams_pem) { struct s2n_stuffer dhparams_in_stuffer, dhparams_out_stuffer; struct s2n_blob dhparams_blob; struct s2n_blob mem; /* Allocate the memory for the chain and key struct */ GUARD(s2n_alloc(&mem, sizeof(struct s2n_dh_params))); config->dhparams = (struct s2n_dh_params *)(void *)mem.data; GUARD(s2n_stuffer_alloc_ro_from_string(&dhparams_in_stuffer, dhparams_pem)); GUARD(s2n_stuffer_growable_alloc(&dhparams_out_stuffer, strlen(dhparams_pem))); /* Convert pem to asn1 and asn1 to the private key */ GUARD(s2n_stuffer_dhparams_from_pem(&dhparams_in_stuffer, &dhparams_out_stuffer)); GUARD(s2n_stuffer_free(&dhparams_in_stuffer)); dhparams_blob.size = s2n_stuffer_data_available(&dhparams_out_stuffer); dhparams_blob.data = s2n_stuffer_raw_read(&dhparams_out_stuffer, dhparams_blob.size); notnull_check(dhparams_blob.data); GUARD(s2n_pkcs3_to_dh_params(config->dhparams, &dhparams_blob)); GUARD(s2n_free(&dhparams_blob)); return 0; } int s2n_config_set_nanoseconds_since_epoch_callback(struct s2n_config *config, int (*nanoseconds_since_epoch)(void *, uint64_t *), void * data) { notnull_check(nanoseconds_since_epoch); config->nanoseconds_since_epoch = nanoseconds_since_epoch; config->data_for_nanoseconds_since_epoch = data; return 0; }
static int s2n_drbg_bits(struct s2n_drbg *drbg, struct s2n_blob *out) { struct s2n_blob value = {.data = drbg->v, .size = sizeof(drbg->v) }; int block_aligned_size = out->size - (out->size % S2N_DRBG_BLOCK_SIZE); /* Per NIST SP800-90A 10.2.1.2: */ for (int i = 0; i < block_aligned_size; i += S2N_DRBG_BLOCK_SIZE) { GUARD(s2n_increment_sequence_number(&value)); GUARD(s2n_drbg_block_encrypt(&drbg->ctx, drbg->v, out->data + i)); drbg->bytes_used += S2N_DRBG_BLOCK_SIZE; } if (out->size <= block_aligned_size) { return 0; } uint8_t spare_block[S2N_DRBG_BLOCK_SIZE]; GUARD(s2n_increment_sequence_number(&value)); GUARD(s2n_drbg_block_encrypt(&drbg->ctx, drbg->v, spare_block)); drbg->bytes_used += S2N_DRBG_BLOCK_SIZE; memcpy_check(out->data + block_aligned_size, spare_block, out->size - block_aligned_size); return 0; } static int s2n_drbg_update(struct s2n_drbg *drbg, struct s2n_blob *provided_data) { uint8_t temp[32]; struct s2n_blob temp_blob = {.data = temp, .size = sizeof(temp) }; eq_check(provided_data->size, sizeof(temp)); GUARD(s2n_drbg_bits(drbg, &temp_blob)); /* XOR in the provided data */ for (int i = 0; i < provided_data->size; i++) { temp[i] ^= provided_data->data[i]; } /* Update the key and value */ if (EVP_EncryptInit_ex(&drbg->ctx, EVP_aes_128_ecb(), NULL, temp, NULL) != 1) { S2N_ERROR(S2N_ERR_DRBG); } memcpy_check(drbg->v, temp + S2N_DRBG_BLOCK_SIZE, S2N_DRBG_BLOCK_SIZE); return 0; } int s2n_drbg_seed(struct s2n_drbg *drbg) { uint8_t seed[32]; struct s2n_blob blob = {.data = seed, .size = sizeof(seed) }; if (drbg->entropy_generator) { GUARD(drbg->entropy_generator(&blob)); } else { GUARD(s2n_get_urandom_data(&blob)); } for (int i = 0; i < sizeof(drbg->ps); i++) { blob.data[i] ^= drbg->ps[i]; } GUARD(s2n_drbg_update(drbg, &blob)); drbg->bytes_used = 0; drbg->generation += 1; return 0; } int s2n_drbg_instantiate(struct s2n_drbg *drbg, struct s2n_blob *personalization_string) { struct s2n_blob value = {.data = drbg->v, .size = sizeof(drbg->v) }; struct s2n_blob ps = {.data = drbg->ps, .size = sizeof(drbg->ps) }; /* Start off with zerod data, per 10.2.1.3.1 item 4 */ GUARD(s2n_blob_zero(&value)); /* Start off with zerod key, per 10.2.1.3.1 item 5 */ (void) EVP_CIPHER_CTX_init(&drbg->ctx); if (EVP_EncryptInit_ex(&drbg->ctx, EVP_aes_128_ecb(), NULL, drbg->v, NULL) != 1) { S2N_ERROR(S2N_ERR_DRBG); } /* Copy the personalization string */ GUARD(s2n_blob_zero(&ps)); memcpy_check(ps.data, personalization_string->data, MIN(ps.size, personalization_string->size)); /* Seed / update the DRBG */ GUARD(s2n_drbg_seed(drbg)); return 0; } int s2n_drbg_generate(struct s2n_drbg *drbg, struct s2n_blob *blob) { uint8_t all_zeros[32] = { 0 }; struct s2n_blob zeros = {.data = all_zeros, .size = sizeof(all_zeros) }; if (blob->size > S2N_DRBG_GENERATE_LIMIT) { S2N_ERROR(S2N_ERR_DRBG_REQUEST_SIZE); } if (drbg->bytes_used + blob->size + S2N_DRBG_BLOCK_SIZE >= S2N_DRBG_RESEED_LIMIT) { GUARD(s2n_drbg_seed(drbg)); } GUARD(s2n_drbg_bits(drbg, blob)); GUARD(s2n_drbg_update(drbg, &zeros)); return 0; } int s2n_drbg_wipe(struct s2n_drbg *drbg) { struct s2n_blob state = {.data = (void *) drbg, .size = sizeof(struct s2n_drbg) }; if (EVP_CIPHER_CTX_cleanup(&drbg->ctx) != 1) { S2N_ERROR(S2N_ERR_DRBG); } GUARD(s2n_blob_zero(&state)); return 0; } int s2n_drbg_bytes_used(struct s2n_drbg *drbg) { return drbg->bytes_used; }
int s2n_hmac_copy(struct s2n_hmac_state *to, struct s2n_hmac_state *from) { memcpy_check(to, from, sizeof(struct s2n_hmac_state)); return 0; }
int s2n_connection_wipe(struct s2n_connection *conn) { /* First make a copy of everything we'd like to save, which isn't very * much. */ int mode = conn->mode; struct s2n_config *config = conn->config; struct s2n_stuffer alert_in; struct s2n_stuffer reader_alert_out; struct s2n_stuffer writer_alert_out; struct s2n_stuffer handshake_io; struct s2n_stuffer header_in; struct s2n_stuffer in; struct s2n_stuffer out; /* Session keys will be wiped. Preserve structs to avoid reallocation */ struct s2n_session_key initial_client_key; struct s2n_session_key initial_server_key; struct s2n_session_key secure_client_key; struct s2n_session_key secure_server_key; /* Wipe all of the sensitive stuff */ GUARD(s2n_connection_wipe_keys(conn)); GUARD(s2n_stuffer_wipe(&conn->alert_in)); GUARD(s2n_stuffer_wipe(&conn->reader_alert_out)); GUARD(s2n_stuffer_wipe(&conn->writer_alert_out)); GUARD(s2n_stuffer_wipe(&conn->handshake.io)); GUARD(s2n_stuffer_wipe(&conn->header_in)); GUARD(s2n_stuffer_wipe(&conn->in)); GUARD(s2n_stuffer_wipe(&conn->out)); /* Restore the socket option values */ GUARD(s2n_socket_read_restore(conn)); GUARD(s2n_socket_write_restore(conn)); GUARD(s2n_free(&conn->status_response)); /* Allocate or resize to their original sizes */ GUARD(s2n_stuffer_resize(&conn->in, S2N_LARGE_FRAGMENT_LENGTH)); /* Allocate memory for handling handshakes */ GUARD(s2n_stuffer_resize(&conn->handshake.io, S2N_LARGE_RECORD_LENGTH)); /* Clone the stuffers */ /* ignore gcc 4.7 address warnings because dest is allocated on the stack */ /* pragma gcc diagnostic was added in gcc 4.6 */ #if defined(__GNUC__) && GCC_VERSION >= 40600 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" #endif memcpy_check(&alert_in, &conn->alert_in, sizeof(struct s2n_stuffer)); memcpy_check(&reader_alert_out, &conn->reader_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&writer_alert_out, &conn->writer_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); memcpy_check(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); memcpy_check(&in, &conn->in, sizeof(struct s2n_stuffer)); memcpy_check(&out, &conn->out, sizeof(struct s2n_stuffer)); memcpy_check(&initial_client_key, &conn->initial.client_key, sizeof(struct s2n_session_key)); memcpy_check(&initial_server_key, &conn->initial.server_key, sizeof(struct s2n_session_key)); memcpy_check(&secure_client_key, &conn->secure.client_key, sizeof(struct s2n_session_key)); memcpy_check(&secure_server_key, &conn->secure.server_key, sizeof(struct s2n_session_key)); #if defined(__GNUC__) && GCC_VERSION >= 40600 #pragma GCC diagnostic pop #endif /* Zero the whole connection structure */ memset_check(conn, 0, sizeof(struct s2n_connection)); conn->readfd = -1; conn->writefd = -1; conn->mode = mode; conn->config = config; conn->close_notify_queued = 0; conn->current_user_data_consumed = 0; conn->initial.cipher_suite = &s2n_null_cipher_suite; conn->secure.cipher_suite = &s2n_null_cipher_suite; conn->server = &conn->initial; conn->client = &conn->initial; conn->max_fragment_length = S2N_SMALL_FRAGMENT_LENGTH; conn->handshake.handshake_type = INITIAL; conn->handshake.message_number = 0; GUARD(s2n_hash_init(&conn->handshake.md5, S2N_HASH_MD5)); GUARD(s2n_hash_init(&conn->handshake.sha1, S2N_HASH_SHA1)); GUARD(s2n_hash_init(&conn->handshake.sha256, S2N_HASH_SHA256)); GUARD(s2n_hash_init(&conn->handshake.sha384, S2N_HASH_SHA384)); GUARD(s2n_hmac_init(&conn->client->client_record_mac, S2N_HMAC_NONE, NULL, 0)); GUARD(s2n_hmac_init(&conn->server->server_record_mac, S2N_HMAC_NONE, NULL, 0)); memcpy_check(&conn->alert_in, &alert_in, sizeof(struct s2n_stuffer)); memcpy_check(&conn->reader_alert_out, &reader_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&conn->writer_alert_out, &writer_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); memcpy_check(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); memcpy_check(&conn->in, &in, sizeof(struct s2n_stuffer)); memcpy_check(&conn->out, &out, sizeof(struct s2n_stuffer)); memcpy_check(&conn->initial.client_key, &initial_client_key, sizeof(struct s2n_session_key)); memcpy_check(&conn->initial.server_key, &initial_server_key, sizeof(struct s2n_session_key)); memcpy_check(&conn->secure.client_key, &secure_client_key, sizeof(struct s2n_session_key)); memcpy_check(&conn->secure.server_key, &secure_server_key, sizeof(struct s2n_session_key)); if (conn->mode == S2N_SERVER) { conn->server_protocol_version = s2n_highest_protocol_version; conn->client_protocol_version = s2n_unknown_protocol_version; } else { conn->server_protocol_version = s2n_unknown_protocol_version; conn->client_protocol_version = s2n_highest_protocol_version; } conn->actual_protocol_version = s2n_unknown_protocol_version; return 0; }
static int s2n_drbg_bits(struct s2n_drbg *drbg, struct s2n_blob *out) { struct s2n_blob value = {.data = drbg->v,.size = sizeof(drbg->v) }; int block_aligned_size = out->size - (out->size % S2N_DRBG_BLOCK_SIZE); /* Per NIST SP800-90A 10.2.1.2: */ for (int i = 0; i < block_aligned_size; i += S2N_DRBG_BLOCK_SIZE) { GUARD(s2n_increment_sequence_number(&value)); GUARD(s2n_drbg_block_encrypt(drbg->ctx, drbg->v, out->data + i)); drbg->bytes_used += S2N_DRBG_BLOCK_SIZE; } if (out->size <= block_aligned_size) { return 0; } uint8_t spare_block[S2N_DRBG_BLOCK_SIZE]; GUARD(s2n_increment_sequence_number(&value)); GUARD(s2n_drbg_block_encrypt(drbg->ctx, drbg->v, spare_block)); drbg->bytes_used += S2N_DRBG_BLOCK_SIZE; memcpy_check(out->data + block_aligned_size, spare_block, out->size - block_aligned_size); return 0; } static int s2n_drbg_update(struct s2n_drbg *drbg, struct s2n_blob *provided_data) { uint8_t temp[32]; struct s2n_blob temp_blob = {.data = temp,.size = sizeof(temp) }; eq_check(provided_data->size, sizeof(temp)); GUARD(s2n_drbg_bits(drbg, &temp_blob)); /* XOR in the provided data */ for (int i = 0; i < provided_data->size; i++) { temp[i] ^= provided_data->data[i]; } /* Update the key and value */ GUARD_OSSL(EVP_EncryptInit_ex(drbg->ctx, EVP_aes_128_ecb(), NULL, temp, NULL), S2N_ERR_DRBG); memcpy_check(drbg->v, temp + S2N_DRBG_BLOCK_SIZE, S2N_DRBG_BLOCK_SIZE); return 0; } int s2n_drbg_seed(struct s2n_drbg *drbg, struct s2n_blob *ps) { uint8_t seed[32]; struct s2n_blob blob = {.data = seed,.size = sizeof(seed) }; lte_check(ps->size, sizeof(seed)); if (drbg->entropy_generator) { GUARD(drbg->entropy_generator(&blob)); } else { GUARD(s2n_get_urandom_data(&blob)); } for (int i = 0; i < ps->size; i++) { blob.data[i] ^= ps->data[i]; } GUARD(s2n_drbg_update(drbg, &blob)); drbg->bytes_used = 0; drbg->generation += 1; return 0; } int s2n_drbg_instantiate(struct s2n_drbg *drbg, struct s2n_blob *personalization_string) { struct s2n_blob value = {.data = drbg->v,.size = sizeof(drbg->v) }; uint8_t ps_prefix[32]; struct s2n_blob ps = {.data = ps_prefix,.size = sizeof(ps_prefix) }; /* Start off with zeroed data, per 10.2.1.3.1 item 4 */ GUARD(s2n_blob_zero(&value)); drbg->ctx = EVP_CIPHER_CTX_new(); S2N_ERROR_IF(!drbg->ctx, S2N_ERR_DRBG); (void)EVP_CIPHER_CTX_init(drbg->ctx); /* Start off with zeroed key, per 10.2.1.3.1 item 5 */ GUARD_OSSL(EVP_EncryptInit_ex(drbg->ctx, EVP_aes_128_ecb(), NULL, drbg->v, NULL), S2N_ERR_DRBG); /* Copy the personalization string */ GUARD(s2n_blob_zero(&ps)); memcpy_check(ps.data, personalization_string->data, MIN(ps.size, personalization_string->size)); /* Seed / update the DRBG */ GUARD(s2n_drbg_seed(drbg, &ps)); /* After initial seeding, pivot to RDRAND if available and not overridden */ if (drbg->entropy_generator == NULL && s2n_cpu_supports_rdrand()) { drbg->entropy_generator = s2n_get_rdrand_data; } return 0; } int s2n_drbg_generate(struct s2n_drbg *drbg, struct s2n_blob *blob) { uint8_t all_zeros[32] = { 0 }; struct s2n_blob zeros = {.data = all_zeros,.size = sizeof(all_zeros) }; S2N_ERROR_IF(blob->size > S2N_DRBG_GENERATE_LIMIT, S2N_ERR_DRBG_REQUEST_SIZE); /* If either the entropy generator is set, for prediction resistance, * or if we reach the definitely-need-to-reseed limit, then reseed. */ if (drbg->entropy_generator || drbg->bytes_used + blob->size + S2N_DRBG_BLOCK_SIZE >= S2N_DRBG_RESEED_LIMIT) { GUARD(s2n_drbg_seed(drbg, &zeros)); } GUARD(s2n_drbg_bits(drbg, blob)); GUARD(s2n_drbg_update(drbg, &zeros)); return 0; } int s2n_drbg_wipe(struct s2n_drbg *drbg) { struct s2n_blob state = {.data = (void *)drbg,.size = sizeof(struct s2n_drbg) }; if (drbg->ctx) { GUARD_OSSL(EVP_CIPHER_CTX_cleanup(drbg->ctx), S2N_ERR_DRBG); EVP_CIPHER_CTX_free(drbg->ctx); drbg->ctx = NULL; } GUARD(s2n_blob_zero(&state)); return 0; } int s2n_drbg_bytes_used(struct s2n_drbg *drbg) { return drbg->bytes_used; }
int s2n_connection_wipe(struct s2n_connection *conn) { /* First make a copy of everything we'd like to save, which isn't very * much. */ int mode = conn->mode; struct s2n_config *config = conn->config; struct s2n_stuffer alert_in; struct s2n_stuffer reader_alert_out; struct s2n_stuffer writer_alert_out; struct s2n_stuffer handshake_io; struct s2n_stuffer header_in; struct s2n_stuffer in; struct s2n_stuffer out; /* Wipe all of the sensitive stuff */ GUARD(s2n_connection_free_keys(conn)); GUARD(s2n_stuffer_wipe(&conn->alert_in)); GUARD(s2n_stuffer_wipe(&conn->reader_alert_out)); GUARD(s2n_stuffer_wipe(&conn->writer_alert_out)); GUARD(s2n_stuffer_wipe(&conn->handshake.io)); GUARD(s2n_stuffer_wipe(&conn->header_in)); GUARD(s2n_stuffer_wipe(&conn->in)); GUARD(s2n_stuffer_wipe(&conn->out)); /* Allocate or resize to their original sizes */ GUARD(s2n_stuffer_resize(&conn->in, S2N_DEFAULT_FRAGMENT_LENGTH)); /* Allocate memory for handling handshakes */ GUARD(s2n_stuffer_resize(&conn->handshake.io, S2N_DEFAULT_RECORD_LENGTH)); /* Clone the stuffers */ /* ignore gcc 4.7 address warnings because dest is allocated on the stack */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" memcpy_check(&alert_in, &conn->alert_in, sizeof(struct s2n_stuffer)); memcpy_check(&reader_alert_out, &conn->reader_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&writer_alert_out, &conn->writer_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); memcpy_check(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); memcpy_check(&in, &conn->in, sizeof(struct s2n_stuffer)); memcpy_check(&out, &conn->out, sizeof(struct s2n_stuffer)); #pragma GCC diagnostic pop /* Zero the whole connection structure */ memset_check(conn, 0, sizeof(struct s2n_connection)); conn->mode = mode; conn->config = config; conn->active.cipher_suite = &s2n_null_cipher_suite; conn->pending.cipher_suite = &s2n_null_cipher_suite; conn->server = &conn->active; conn->client = &conn->active; conn->max_fragment_length = S2N_DEFAULT_FRAGMENT_LENGTH; conn->handshake.state = CLIENT_HELLO; GUARD(s2n_hash_init(&conn->handshake.client_md5, S2N_HASH_MD5)); GUARD(s2n_hash_init(&conn->handshake.client_sha1, S2N_HASH_SHA1)); GUARD(s2n_hash_init(&conn->handshake.client_sha256, S2N_HASH_SHA256)); GUARD(s2n_hash_init(&conn->handshake.server_md5, S2N_HASH_MD5)); GUARD(s2n_hash_init(&conn->handshake.server_sha1, S2N_HASH_SHA1)); GUARD(s2n_hash_init(&conn->handshake.server_sha256, S2N_HASH_SHA256)); memcpy_check(&conn->alert_in, &alert_in, sizeof(struct s2n_stuffer)); memcpy_check(&conn->reader_alert_out, &reader_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&conn->writer_alert_out, &writer_alert_out, sizeof(struct s2n_stuffer)); memcpy_check(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); memcpy_check(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); memcpy_check(&conn->in, &in, sizeof(struct s2n_stuffer)); memcpy_check(&conn->out, &out, sizeof(struct s2n_stuffer)); /* Set everything to the highest version at first */ conn->server_protocol_version = s2n_highest_protocol_version; conn->client_protocol_version = s2n_highest_protocol_version; conn->actual_protocol_version = s2n_highest_protocol_version; return 0; }
int s2n_hmac_init(struct s2n_hmac_state *state, s2n_hmac_algorithm alg, const void *key, uint32_t klen) { s2n_hash_algorithm hash_alg = S2N_HASH_NONE; state->currently_in_hash_block = 0; state->digest_size = 0; state->block_size = 64; state->hash_block_size = 64; switch (alg) { case S2N_HMAC_NONE: break; case S2N_HMAC_SSLv3_MD5: state->block_size = 48; /* Fall through ... */ case S2N_HMAC_MD5: hash_alg = S2N_HASH_MD5; state->digest_size = MD5_DIGEST_LENGTH; break; case S2N_HMAC_SSLv3_SHA1: state->block_size = 40; /* Fall through ... */ case S2N_HMAC_SHA1: hash_alg = S2N_HASH_SHA1; state->digest_size = SHA_DIGEST_LENGTH; break; case S2N_HMAC_SHA224: hash_alg = S2N_HASH_SHA224; state->digest_size = SHA224_DIGEST_LENGTH; break; case S2N_HMAC_SHA256: hash_alg = S2N_HASH_SHA256; state->digest_size = SHA256_DIGEST_LENGTH; break; case S2N_HMAC_SHA384: hash_alg = S2N_HASH_SHA384; state->digest_size = SHA384_DIGEST_LENGTH; state->block_size = 128; state->hash_block_size = 128; break; case S2N_HMAC_SHA512: hash_alg = S2N_HASH_SHA512; state->digest_size = SHA512_DIGEST_LENGTH; state->block_size = 128; state->hash_block_size = 128; break; default: S2N_ERROR(S2N_ERR_HMAC_INVALID_ALGORITHM); } gte_check(sizeof(state->xor_pad), state->block_size); gte_check(sizeof(state->digest_pad), state->digest_size); state->alg = alg; if (alg == S2N_HMAC_SSLv3_SHA1 || alg == S2N_HMAC_SSLv3_MD5) { return s2n_sslv3_mac_init(state, alg, key, klen); } GUARD(s2n_hash_init(&state->inner_just_key, hash_alg)); GUARD(s2n_hash_init(&state->outer, hash_alg)); uint32_t copied = klen; if (klen > state->block_size) { GUARD(s2n_hash_update(&state->outer, key, klen)); GUARD(s2n_hash_digest(&state->outer, state->digest_pad, state->digest_size)); memcpy_check(state->xor_pad, state->digest_pad, state->digest_size); copied = state->digest_size; } else { memcpy_check(state->xor_pad, key, klen); } for (int i = 0; i < copied; i++) { state->xor_pad[i] ^= 0x36; } for (int i = copied; i < state->block_size; i++) { state->xor_pad[i] = 0x36; } GUARD(s2n_hash_update(&state->inner_just_key, state->xor_pad, state->block_size)); /* 0x36 xor 0x5c == 0x6a */ for (int i = 0; i < state->block_size; i++) { state->xor_pad[i] ^= 0x6a; } return s2n_hmac_reset(state); }