/* Parse the server's renegotiation binding and abort if it's not right */ int ssl_parse_serverhello_renegotiate_ext(SSL *s, unsigned char *d, int len, int *al) { int expected_len = s->s3->previous_client_finished_len + s->s3->previous_server_finished_len; int ilen; /* Check for logic errors */ OPENSSL_assert(!expected_len || s->s3->previous_client_finished_len); OPENSSL_assert(!expected_len || s->s3->previous_server_finished_len); /* Parse the length byte */ if (len < 1) { SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_ENCODING_ERR); *al = SSL_AD_ILLEGAL_PARAMETER; return 0; } ilen = *d; d++; /* Consistency check */ if (ilen + 1 != len) { SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_ENCODING_ERR); *al = SSL_AD_ILLEGAL_PARAMETER; return 0; } /* Check that the extension matches */ if (ilen != expected_len) { SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_MISMATCH); *al = SSL_AD_HANDSHAKE_FAILURE; return 0; } if (timingsafe_memcmp(d, s->s3->previous_client_finished, s->s3->previous_client_finished_len) != 0) { SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_MISMATCH); *al = SSL_AD_HANDSHAKE_FAILURE; return 0; } d += s->s3->previous_client_finished_len; if (timingsafe_memcmp(d, s->s3->previous_server_finished, s->s3->previous_server_finished_len)) { SSLerr(SSL_F_SSL_PARSE_SERVERHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_MISMATCH); *al = SSL_AD_ILLEGAL_PARAMETER; return 0; } s->s3->send_connection_binding = 1; return 1; }
int test_memcmp() { size_t i; struct timespec x,y; long diff[4]; memset(a,23,sizeof(a)); memset(b,23,sizeof(b)); assert(clock_gettime(CLOCK_MONOTONIC,&x)==0); for (i=0; i<100000; ++i) { assert(timingsafe_memcmp(a,b,sizeof(a))==0); } assert(clock_gettime(CLOCK_MONOTONIC,&y)==0); diff[0]=(y.tv_sec-x.tv_sec)*1000000000 + y.tv_nsec-x.tv_nsec; printf("timingsafe_memcmp: %lu.%09lu %lu.%09lu - %lu\n",x.tv_sec,x.tv_nsec,y.tv_sec,y.tv_nsec,diff[0]); assert(clock_gettime(CLOCK_MONOTONIC,&x)==0); for (i=0; i<100000; ++i) { assert(memcmp(a,b,sizeof(a))==0); } assert(clock_gettime(CLOCK_MONOTONIC,&y)==0); diff[1]=(y.tv_sec-x.tv_sec)*1000000000 + y.tv_nsec-x.tv_nsec; printf(" regular memcmp: %lu.%09lu %lu.%09lu - %lu\n",x.tv_sec,x.tv_nsec,y.tv_sec,y.tv_nsec,diff[1]); a[10]=24; // now give memcmp an opportunity to exit early assert(clock_gettime(CLOCK_MONOTONIC,&x)==0); for (i=0; i<100000; ++i) { assert(timingsafe_memcmp(a,b,sizeof(a))==1); } assert(clock_gettime(CLOCK_MONOTONIC,&y)==0); diff[2]=(y.tv_sec-x.tv_sec)*1000000000 + y.tv_nsec-x.tv_nsec; printf("timingsafe_memcmp: %lu.%09lu %lu.%09lu - %lu\n",x.tv_sec,x.tv_nsec,y.tv_sec,y.tv_nsec,diff[2]); assert(clock_gettime(CLOCK_MONOTONIC,&x)==0); for (i=0; i<100000; ++i) { assert(memcmp(a,b,sizeof(a))==1); } assert(clock_gettime(CLOCK_MONOTONIC,&y)==0); diff[3]=(y.tv_sec-x.tv_sec)*1000000000 + y.tv_nsec-x.tv_nsec; printf(" regular memcmp: %lu.%09lu %lu.%09lu - %lu\n",x.tv_sec,x.tv_nsec,y.tv_sec,y.tv_nsec,diff[3]); /* we expect the timingsafe_memcmp values to be roughly the same (+- 10% for measurement inaccuracies) */ long delta=diff[0]-diff[2]; if (delta<0) delta=-delta; assert(delta*10<diff[0]); }
static int aead_aes_gcm_open(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t *out_len, size_t max_out_len, const unsigned char *nonce, size_t nonce_len, const unsigned char *in, size_t in_len, const unsigned char *ad, size_t ad_len) { const struct aead_aes_gcm_ctx *gcm_ctx = ctx->aead_state; unsigned char tag[EVP_AEAD_AES_GCM_TAG_LEN]; GCM128_CONTEXT gcm; size_t plaintext_len; size_t bulk = 0; if (in_len < gcm_ctx->tag_len) { EVPerr(EVP_F_AEAD_AES_GCM_OPEN, EVP_R_BAD_DECRYPT); return 0; } plaintext_len = in_len - gcm_ctx->tag_len; if (max_out_len < plaintext_len) { EVPerr(EVP_F_AEAD_AES_GCM_OPEN, EVP_R_BUFFER_TOO_SMALL); return 0; } memcpy(&gcm, &gcm_ctx->gcm, sizeof(gcm)); CRYPTO_gcm128_setiv(&gcm, nonce, nonce_len); if (CRYPTO_gcm128_aad(&gcm, ad, ad_len)) return 0; if (gcm_ctx->ctr) { if (CRYPTO_gcm128_decrypt_ctr32(&gcm, in + bulk, out + bulk, in_len - bulk - gcm_ctx->tag_len, gcm_ctx->ctr)) return 0; } else { if (CRYPTO_gcm128_decrypt(&gcm, in + bulk, out + bulk, in_len - bulk - gcm_ctx->tag_len)) return 0; } CRYPTO_gcm128_tag(&gcm, tag, gcm_ctx->tag_len); if (timingsafe_memcmp(tag, in + plaintext_len, gcm_ctx->tag_len) != 0) { EVPerr(EVP_F_AEAD_AES_GCM_OPEN, EVP_R_BAD_DECRYPT); return 0; } *out_len = plaintext_len; return 1; }
/* Parse the client's renegotiation binding and abort if it's not right */ int ssl_parse_clienthello_renegotiate_ext(SSL *s, unsigned char *d, int len, int *al) { int ilen; /* Parse the length byte */ if (len < 1) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_ENCODING_ERR); *al = SSL_AD_ILLEGAL_PARAMETER; return 0; } ilen = *d; d++; /* Consistency check */ if ((ilen + 1) != len) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_ENCODING_ERR); *al = SSL_AD_ILLEGAL_PARAMETER; return 0; } /* Check that the extension matches */ if (ilen != s->s3->previous_client_finished_len) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_MISMATCH); *al = SSL_AD_HANDSHAKE_FAILURE; return 0; } if (timingsafe_memcmp(d, s->s3->previous_client_finished, s->s3->previous_client_finished_len) != 0) { SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_RENEGOTIATE_EXT, SSL_R_RENEGOTIATION_MISMATCH); *al = SSL_AD_HANDSHAKE_FAILURE; return 0; } s->s3->send_connection_binding = 1; return 1; }
/* tls_decrypt_ticket attempts to decrypt a session ticket. * * etick: points to the body of the session ticket extension. * eticklen: the length of the session tickets extenion. * sess_id: points at the session ID. * sesslen: the length of the session ID. * psess: (output) on return, if a ticket was decrypted, then this is set to * point to the resulting session. * * Returns: * -1: fatal error, either from parsing or decrypting the ticket. * 2: the ticket couldn't be decrypted. * 3: a ticket was successfully decrypted and *psess was set. * 4: same as 3, but the ticket needs to be renewed. */ static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen, const unsigned char *sess_id, int sesslen, SSL_SESSION **psess) { SSL_SESSION *sess; unsigned char *sdec; const unsigned char *p; int slen, mlen, renew_ticket = 0; unsigned char tick_hmac[EVP_MAX_MD_SIZE]; HMAC_CTX hctx; EVP_CIPHER_CTX ctx; SSL_CTX *tctx = s->initial_ctx; /* * The API guarantees EVP_MAX_IV_LENGTH bytes of space for * the iv to tlsext_ticket_key_cb(). Since the total space * required for a session cookie is never less than this, * this check isn't too strict. The exact check comes later. */ if (eticklen < 16 + EVP_MAX_IV_LENGTH) return 2; /* Initialize session ticket encryption and HMAC contexts */ HMAC_CTX_init(&hctx); EVP_CIPHER_CTX_init(&ctx); if (tctx->internal->tlsext_ticket_key_cb) { unsigned char *nctick = (unsigned char *)etick; int rv = tctx->internal->tlsext_ticket_key_cb(s, nctick, nctick + 16, &ctx, &hctx, 0); if (rv < 0) { HMAC_CTX_cleanup(&hctx); EVP_CIPHER_CTX_cleanup(&ctx); return -1; } if (rv == 0) { HMAC_CTX_cleanup(&hctx); EVP_CIPHER_CTX_cleanup(&ctx); return 2; } if (rv == 2) renew_ticket = 1; } else { /* Check key name matches */ if (timingsafe_memcmp(etick, tctx->internal->tlsext_tick_key_name, 16)) return 2; HMAC_Init_ex(&hctx, tctx->internal->tlsext_tick_hmac_key, 16, tlsext_tick_md(), NULL); EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, tctx->internal->tlsext_tick_aes_key, etick + 16); } /* * Attempt to process session ticket, first conduct sanity and * integrity checks on ticket. */ mlen = HMAC_size(&hctx); if (mlen < 0) { HMAC_CTX_cleanup(&hctx); EVP_CIPHER_CTX_cleanup(&ctx); return -1; } /* Sanity check ticket length: must exceed keyname + IV + HMAC */ if (eticklen <= 16 + EVP_CIPHER_CTX_iv_length(&ctx) + mlen) { HMAC_CTX_cleanup(&hctx); EVP_CIPHER_CTX_cleanup(&ctx); return 2; } eticklen -= mlen; /* Check HMAC of encrypted ticket */ if (HMAC_Update(&hctx, etick, eticklen) <= 0 || HMAC_Final(&hctx, tick_hmac, NULL) <= 0) { HMAC_CTX_cleanup(&hctx); EVP_CIPHER_CTX_cleanup(&ctx); return -1; } HMAC_CTX_cleanup(&hctx); if (timingsafe_memcmp(tick_hmac, etick + eticklen, mlen)) { EVP_CIPHER_CTX_cleanup(&ctx); return 2; } /* Attempt to decrypt session data */ /* Move p after IV to start of encrypted ticket, update length */ p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx); eticklen -= 16 + EVP_CIPHER_CTX_iv_length(&ctx); sdec = malloc(eticklen); if (sdec == NULL || EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen) <= 0) { free(sdec); EVP_CIPHER_CTX_cleanup(&ctx); return -1; } if (EVP_DecryptFinal_ex(&ctx, sdec + slen, &mlen) <= 0) { free(sdec); EVP_CIPHER_CTX_cleanup(&ctx); return 2; } slen += mlen; EVP_CIPHER_CTX_cleanup(&ctx); p = sdec; sess = d2i_SSL_SESSION(NULL, &p, slen); free(sdec); if (sess) { /* The session ID, if non-empty, is used by some clients to * detect that the ticket has been accepted. So we copy it to * the session structure. If it is empty set length to zero * as required by standard. */ if (sesslen) memcpy(sess->session_id, sess_id, sesslen); sess->session_id_length = sesslen; *psess = sess; if (renew_ticket) return 4; else return 3; } ERR_clear_error(); /* For session parse failure, indicate that we need to send a new * ticket. */ return 2; }
/** * Timing-safe version of memcmp. As memcmp, compare the <b>sz</b> bytes at * <b>a</b> with the <b>sz</b> bytes at <b>b</b>, and return less than 0 if * the bytes at <b>a</b> lexically precede those at <b>b</b>, 0 if the byte * ranges are equal, and greater than zero if the bytes at <b>a</b> lexically * follow those at <b>b</b>. * * This implementation differs from memcmp in that its timing behavior is not * data-dependent: it should return in the same amount of time regardless of * the contents of <b>a</b> and <b>b</b>. */ int tor_memcmp(const void *a, const void *b, size_t len) { #ifdef HAVE_TIMINGSAFE_MEMCMP return timingsafe_memcmp(a, b, len); #else const uint8_t *x = a; const uint8_t *y = b; size_t i = len; int retval = 0; /* This loop goes from the end of the arrays to the start. At the * start of every iteration, before we decrement i, we have set * "retval" equal to the result of memcmp(a+i,b+i,len-i). During the * loop, we update retval by leaving it unchanged if x[i]==y[i] and * setting it to x[i]-y[i] if x[i]!= y[i]. * * The following assumes we are on a system with two's-complement * arithmetic. We check for this at configure-time with the check * that sets USING_TWOS_COMPLEMENT. If we aren't two's complement, then * torint.h will stop compilation with an error. */ while (i--) { int v1 = x[i]; int v2 = y[i]; int equal_p = v1 ^ v2; /* The following sets bits 8 and above of equal_p to 'equal_p == * 0', and thus to v1 == v2. (To see this, note that if v1 == * v2, then v1^v2 == equal_p == 0, so equal_p-1 == -1, which is the * same as ~0 on a two's-complement machine. Then note that if * v1 != v2, then 0 < v1 ^ v2 < 256, so 0 <= equal_p - 1 < 255.) */ --equal_p; equal_p >>= 8; /* Thanks to (sign-preserving) arithmetic shift, equal_p is now * equal to -(v1 == v2), which is exactly what we need below. * (Since we're assuming two's-complement arithmetic, -1 is the * same as ~0 (all bits set).) * * (The result of an arithmetic shift on a negative value is * actually implementation-defined in standard C. So how do we * get away with assuming it? Easy. We check.) */ #if ((-60 >> 8) != -1) #error "According to cpp, right-shift doesn't perform sign-extension." #endif #ifndef RSHIFT_DOES_SIGN_EXTEND #error "According to configure, right-shift doesn't perform sign-extension." #endif /* If v1 == v2, equal_p is ~0, so this will leave retval * unchanged; otherwise, equal_p is 0, so this will zero it. */ retval &= equal_p; /* If v1 == v2, then this adds 0, and leaves retval unchanged. * Otherwise, we just zeroed retval, so this sets it to v1 - v2. */ retval += (v1 - v2); /* There. Now retval is equal to its previous value if v1 == v2, and * equal to v1 - v2 if v1 != v2. */ } return retval; #endif /* timingsafe_memcmp */ }
static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t *out_len, size_t max_out_len, const unsigned char *nonce, size_t nonce_len, const unsigned char *in, size_t in_len, const unsigned char *ad, size_t ad_len) { const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; unsigned char mac[POLY1305_TAG_LEN]; unsigned char poly1305_key[32]; const unsigned char *iv = nonce; poly1305_state poly1305; const uint64_t in_len_64 = in_len; size_t plaintext_len; uint64_t ctr = 0; if (in_len < c20_ctx->tag_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT); return 0; } /* The underlying ChaCha implementation may not overflow the block * counter into the second counter word. Therefore we disallow * individual operations that work on more than 2TB at a time. * in_len_64 is needed because, on 32-bit platforms, size_t is only * 32-bits and this produces a warning because it's always false. * Casting to uint64_t inside the conditional is not sufficient to stop * the warning. */ if (in_len_64 >= (1ULL << 32) * 64 - 64) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_TOO_LARGE); return 0; } if (nonce_len != ctx->aead->nonce_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_IV_TOO_LARGE); return 0; } plaintext_len = in_len - c20_ctx->tag_len; if (max_out_len < plaintext_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BUFFER_TOO_SMALL); return 0; } if (nonce_len == CHACHA20_NONCE_LEN_OLD) { /* Google's draft-agl-tls-chacha20poly1305-04, Nov 2013 */ memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0); CRYPTO_poly1305_init(&poly1305, poly1305_key); poly1305_update_with_length(&poly1305, ad, ad_len); poly1305_update_with_length(&poly1305, in, plaintext_len); } else if (nonce_len == CHACHA20_NONCE_LEN) { /* RFC 7539, May 2015 */ ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; iv = nonce + CHACHA20_CONSTANT_LEN; memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, iv, ctr); CRYPTO_poly1305_init(&poly1305, poly1305_key); poly1305_update_with_pad16(&poly1305, ad, ad_len); poly1305_update_with_pad16(&poly1305, in, plaintext_len); poly1305_update_with_length(&poly1305, NULL, ad_len); poly1305_update_with_length(&poly1305, NULL, plaintext_len); } CRYPTO_poly1305_finish(&poly1305, mac); if (timingsafe_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT); return 0; } CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, iv, ctr + 1); *out_len = plaintext_len; return 1; }
/* * ssl_get_prev attempts to find an SSL_SESSION to be used to resume this * connection. It is only called by servers. * * session_id: points at the session ID in the ClientHello. This code will * read past the end of this in order to parse out the session ticket * extension, if any. * len: the length of the session ID. * limit: a pointer to the first byte after the ClientHello. * * Returns: * -1: error * 0: a session may have been found. * * Side effects: * - If a session is found then s->session is pointed at it (after freeing * an existing session if need be) and s->verify_result is set from the * session. * - Both for new and resumed sessions, s->internal->tlsext_ticket_expected is set * to 1 if the server should issue a new session ticket (to 0 otherwise). */ int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len, const unsigned char *limit) { SSL_SESSION *ret = NULL; int fatal = 0; int try_session_cache = 1; int r; /* This is used only by servers. */ if (len > SSL_MAX_SSL_SESSION_ID_LENGTH) goto err; if (len == 0) try_session_cache = 0; /* Sets s->internal->tlsext_ticket_expected. */ r = tls1_process_ticket(s, session_id, len, limit, &ret); switch (r) { case -1: /* Error during processing */ fatal = 1; goto err; case 0: /* No ticket found */ case 1: /* Zero length ticket found */ break; /* Ok to carry on processing session id. */ case 2: /* Ticket found but not decrypted. */ case 3: /* Ticket decrypted, *ret has been set. */ try_session_cache = 0; break; default: abort(); } if (try_session_cache && ret == NULL && !(s->session_ctx->internal->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { SSL_SESSION data; data.ssl_version = s->version; data.session_id_length = len; memcpy(data.session_id, session_id, len); CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); ret = lh_SSL_SESSION_retrieve(s->session_ctx->internal->sessions, &data); if (ret != NULL) { /* Don't allow other threads to steal it. */ CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION); } CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); if (ret == NULL) s->session_ctx->internal->stats.sess_miss++; } if (try_session_cache && ret == NULL && s->session_ctx->internal->get_session_cb != NULL) { int copy = 1; if ((ret = s->session_ctx->internal->get_session_cb(s, session_id, len, ©))) { s->session_ctx->internal->stats.sess_cb_hit++; /* * Increment reference count now if the session * callback asks us to do so (note that if the session * structures returned by the callback are shared * between threads, it must handle the reference count * itself [i.e. copy == 0], or things won't be * thread-safe). */ if (copy) CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION); /* * Add the externally cached session to the internal * cache as well if and only if we are supposed to. */ if (!(s->session_ctx->internal->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE)) /* * The following should not return 1, * otherwise, things are very strange. */ SSL_CTX_add_session(s->session_ctx, ret); } } if (ret == NULL) goto err; /* Now ret is non-NULL and we own one of its reference counts. */ if (ret->sid_ctx_length != s->sid_ctx_length || timingsafe_memcmp(ret->sid_ctx, s->sid_ctx, ret->sid_ctx_length) != 0) { /* We have the session requested by the client, but we don't * want to use it in this context. */ goto err; /* treat like cache miss */ } if ((s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) { /* * We can't be sure if this session is being used out of * context, which is especially important for SSL_VERIFY_PEER. * The application should have used * SSL[_CTX]_set_session_id_context. * * For this error case, we generate an error instead of treating * the event like a cache miss (otherwise it would be easy for * applications to effectively disable the session cache by * accident without anyone noticing). */ SSLerror(s, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); fatal = 1; goto err; } if (ret->cipher == NULL) { ret->cipher = ssl3_get_cipher_by_id(ret->cipher_id); if (ret->cipher == NULL) goto err; } if (ret->timeout < (time(NULL) - ret->time)) { /* timeout */ s->session_ctx->internal->stats.sess_timeout++; if (try_session_cache) { /* session was from the cache, so remove it */ SSL_CTX_remove_session(s->session_ctx, ret); } goto err; } s->session_ctx->internal->stats.sess_hit++; if (s->session != NULL) SSL_SESSION_free(s->session); s->session = ret; s->verify_result = s->session->verify_result; return 1; err: if (ret != NULL) { SSL_SESSION_free(ret); if (!try_session_cache) { /* * The session was from a ticket, so we should * issue a ticket for the new session. */ s->internal->tlsext_ticket_expected = 1; } } if (fatal) return -1; else return 0; }