int _gnutls_mac_init(mac_hd_st * mac, const mac_entry_st * e, const void *key, int keylen) { int result; const gnutls_crypto_mac_st *cc = NULL; FAIL_IF_LIB_ERROR; if (unlikely(e == NULL || e->id == GNUTLS_MAC_NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); mac->e = e; mac->mac_len = _gnutls_mac_get_algo_len(e); /* check if a digest has been registered */ cc = _gnutls_get_crypto_mac(e->id); if (cc != NULL && cc->init != NULL) { if (cc->init(e->id, &mac->handle) < 0) { gnutls_assert(); return GNUTLS_E_HASH_FAILED; } if (cc->setkey(mac->handle, key, keylen) < 0) { gnutls_assert(); cc->deinit(mac->handle); return GNUTLS_E_HASH_FAILED; } mac->hash = cc->hash; mac->setnonce = cc->setnonce; mac->output = cc->output; mac->deinit = cc->deinit; return 0; } result = _gnutls_mac_ops.init(e->id, &mac->handle); if (result < 0) { gnutls_assert(); return result; } mac->hash = _gnutls_mac_ops.hash; mac->setnonce = _gnutls_mac_ops.setnonce; mac->output = _gnutls_mac_ops.output; mac->deinit = _gnutls_mac_ops.deinit; if (_gnutls_mac_ops.setkey(mac->handle, key, keylen) < 0) { gnutls_assert(); mac->deinit(mac->handle); return GNUTLS_E_HASH_FAILED; } return 0; }
int _gnutls_mac_deinit_ssl3_handshake(digest_hd_st * handle, void *digest, uint8_t * key, uint32_t key_size) { uint8_t ret[MAX_HASH_SIZE]; digest_hd_st td; uint8_t opad[48]; uint8_t ipad[48]; int padsize; int block, rc; padsize = get_padsize(handle->e->id); if (padsize == 0) { gnutls_assert(); rc = GNUTLS_E_INTERNAL_ERROR; goto cleanup; } memset(opad, 0x5C, padsize); memset(ipad, 0x36, padsize); rc = _gnutls_hash_init(&td, handle->e); if (rc < 0) { gnutls_assert(); goto cleanup; } if (key_size > 0) _gnutls_hash(&td, key, key_size); _gnutls_hash(&td, opad, padsize); block = _gnutls_mac_get_algo_len(handle->e); if (key_size > 0) _gnutls_hash(handle, key, key_size); _gnutls_hash(handle, ipad, padsize); _gnutls_hash_deinit(handle, ret); /* get the previous hash */ _gnutls_hash(&td, ret, block); _gnutls_hash_deinit(&td, digest); return 0; cleanup: _gnutls_hash_deinit(handle, NULL); return rc; }
int _gnutls_mac_output_ssl3(digest_hd_st * handle, void *digest) { uint8_t ret[MAX_HASH_SIZE]; digest_hd_st td; uint8_t opad[48]; int padsize; int block, rc; padsize = get_padsize(handle->e->id); if (padsize == 0) { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } memset(opad, 0x5C, padsize); rc = _gnutls_hash_init(&td, handle->e); if (rc < 0) { gnutls_assert(); return rc; } if (handle->keysize > 0) _gnutls_hash(&td, handle->key, handle->keysize); _gnutls_hash(&td, opad, padsize); block = _gnutls_mac_get_algo_len(handle->e); _gnutls_hash_output(handle, ret); /* get the previous hash */ _gnutls_hash(&td, ret, block); _gnutls_hash_deinit(&td, digest); /* reset handle */ memset(opad, 0x36, padsize); if (handle->keysize > 0) _gnutls_hash(handle, handle->key, handle->keysize); _gnutls_hash(handle, opad, padsize); return 0; }
/* ID should be: * 3 for MAC * 2 for IV * 1 for encryption key * * Note that this function produces different key for the * NULL password, and for the password with zero length. */ int _gnutls_pkcs12_string_to_key(const mac_entry_st * me, unsigned int id, const uint8_t * salt, unsigned int salt_size, unsigned int iter, const char *pw, unsigned int req_keylen, uint8_t * keybuf) { int rc; unsigned int i, j; digest_hd_st md; bigint_t num_b1 = NULL, num_ij = NULL; bigint_t mpi512 = NULL; unsigned int pwlen; uint8_t hash[MAX_HASH_SIZE], buf_b[64], buf_i[MAX_PASS_LEN * 2 + 64], *p; uint8_t d[64]; size_t cur_keylen; size_t n, m, p_size, i_size; unsigned mac_len; const uint8_t buf_512[] = /* 2^64 */ { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; cur_keylen = 0; if (pw == NULL) pwlen = 0; else pwlen = strlen(pw); if (pwlen > MAX_PASS_LEN) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if ((rc = _pkcs12_check_pass(pw, pwlen)) < 0) { gnutls_assert(); return rc; } rc = _gnutls_mpi_init_scan(&mpi512, buf_512, sizeof(buf_512)); if (rc < 0) { gnutls_assert(); return rc; } /* Store salt and password in BUF_I */ p_size = ((pwlen / 64) * 64) + 64; if (p_size > sizeof(buf_i) - 64) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); p = buf_i; for (i = 0; i < 64; i++) *p++ = salt[i % salt_size]; if (pw) { for (i = j = 0; i < p_size; i += 2) { *p++ = 0; *p++ = pw[j]; if (++j > pwlen) /* Note, that we include the trailing (0) */ j = 0; } } else memset(p, 0, p_size); i_size = 64 + p_size; mac_len = _gnutls_mac_get_algo_len(me); for (;;) { rc = _gnutls_hash_init(&md, me); if (rc < 0) { gnutls_assert(); goto cleanup; } memset(d, id & 0xff, 64); _gnutls_hash(&md, d, 64); _gnutls_hash(&md, buf_i, pw ? i_size : 64); _gnutls_hash_deinit(&md, hash); for (i = 1; i < iter; i++) { rc = _gnutls_hash_fast(me->id, hash, mac_len, hash); if (rc < 0) { gnutls_assert(); goto cleanup; } } for (i = 0; i < mac_len && cur_keylen < req_keylen; i++) keybuf[cur_keylen++] = hash[i]; if (cur_keylen == req_keylen) { rc = 0; /* ready */ goto cleanup; } /* need more bytes. */ for (i = 0; i < 64; i++) buf_b[i] = hash[i % mac_len]; n = 64; rc = _gnutls_mpi_init_scan(&num_b1, buf_b, n); if (rc < 0) { gnutls_assert(); goto cleanup; } rc = _gnutls_mpi_add_ui(num_b1, num_b1, 1); if (rc < 0) { gnutls_assert(); goto cleanup; } for (i = 0; i < 128; i += 64) { n = 64; rc = _gnutls_mpi_init_scan(&num_ij, buf_i + i, n); if (rc < 0) { gnutls_assert(); goto cleanup; } rc = _gnutls_mpi_addm(num_ij, num_ij, num_b1, mpi512); if (rc < 0) { gnutls_assert(); goto cleanup; } n = 64; #ifndef PKCS12_BROKEN_KEYGEN m = (_gnutls_mpi_get_nbits(num_ij) + 7) / 8; #else m = n; #endif memset(buf_i + i, 0, n - m); rc = _gnutls_mpi_print(num_ij, buf_i + i + n - m, &n); if (rc < 0) { gnutls_assert(); goto cleanup; } _gnutls_mpi_release(&num_ij); } } cleanup: _gnutls_mpi_release(&num_ij); _gnutls_mpi_release(&num_b1); _gnutls_mpi_release(&mpi512); return rc; }
/** * gnutls_pkcs12_generate_mac2: * @pkcs12: A pkcs12 type * @mac: the MAC algorithm to use * @pass: The password for the MAC * * This function will generate a MAC for the PKCS12 structure. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12, gnutls_mac_algorithm_t mac, const char *pass) { uint8_t salt[8], key[MAX_HASH_SIZE]; int result; const int iter = 10*1024; mac_hd_st td1; gnutls_datum_t tmp = { NULL, 0 }; unsigned mac_size, key_len; uint8_t mac_out[MAX_HASH_SIZE]; const mac_entry_st *me = mac_to_entry(mac); if (pkcs12 == NULL || me == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); if (me->oid == NULL) return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); mac_size = _gnutls_mac_get_algo_len(me); key_len = mac_size; /* Generate the salt. */ result = gnutls_rnd(GNUTLS_RND_NONCE, salt, sizeof(salt)); if (result < 0) { gnutls_assert(); return result; } /* Write the salt into the structure. */ result = asn1_write_value(pkcs12->pkcs12, "macData.macSalt", salt, sizeof(salt)); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } /* write the iterations */ if (iter > 1) { result = _gnutls_x509_write_uint32(pkcs12->pkcs12, "macData.iterations", iter); if (result < 0) { gnutls_assert(); goto cleanup; } } /* Generate the key. */ #if ENABLE_GOST if (me->id == GNUTLS_MAC_GOSTR_94 || me->id == GNUTLS_MAC_STREEBOG_256 || me->id == GNUTLS_MAC_STREEBOG_512) { key_len = 32; result = _gnutls_pkcs12_gost_string_to_key(me->id, salt, sizeof(salt), iter, pass, mac_size, key); } else #endif result = _gnutls_pkcs12_string_to_key(me, 3 /*MAC*/, salt, sizeof(salt), iter, pass, mac_size, key); if (result < 0) { gnutls_assert(); goto cleanup; } /* Get the data to be MACed */ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp); if (result < 0) { gnutls_assert(); goto cleanup; } /* MAC the data */ result = _gnutls_mac_init(&td1, me, key, key_len); if (result < 0) { gnutls_assert(); goto cleanup; } _gnutls_mac(&td1, tmp.data, tmp.size); _gnutls_free_datum(&tmp); _gnutls_mac_deinit(&td1, mac_out); result = asn1_write_value(pkcs12->pkcs12, "macData.mac.digest", mac_out, mac_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } result = asn1_write_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.parameters", NULL, 0); if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } result = asn1_write_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm", me->oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } return 0; cleanup: _gnutls_free_datum(&tmp); return result; }
/** * gnutls_pkcs12_verify_mac: * @pkcs12: should contain a gnutls_pkcs12_t type * @pass: The password for the MAC * * This function will verify the MAC for the PKCS12 structure. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass) { uint8_t key[MAX_HASH_SIZE]; char oid[MAX_OID_SIZE]; int result; unsigned int iter; int len; mac_hd_st td1; gnutls_datum_t tmp = { NULL, 0 }, salt = { NULL, 0}; uint8_t mac_output[MAX_HASH_SIZE]; uint8_t mac_output_orig[MAX_HASH_SIZE]; gnutls_mac_algorithm_t algo; unsigned mac_len, key_len; const mac_entry_st *entry; #if ENABLE_GOST int gost_retry = 0; #endif if (pkcs12 == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* read the iterations */ result = _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations", &iter); if (result < 0) { iter = 1; /* the default */ } len = sizeof(oid); result = asn1_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm", oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } algo = gnutls_oid_to_digest(oid); if (algo == GNUTLS_MAC_UNKNOWN) { unknown_mac: gnutls_assert(); return GNUTLS_E_UNKNOWN_HASH_ALGORITHM; } entry = mac_to_entry(algo); if (entry == NULL) goto unknown_mac; mac_len = _gnutls_mac_get_algo_len(entry); key_len = mac_len; /* Read the salt from the structure. */ result = _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt", &salt); if (result < 0) { gnutls_assert(); goto cleanup; } /* Generate the key. */ result = _gnutls_pkcs12_string_to_key(entry, 3 /*MAC*/, salt.data, salt.size, iter, pass, key_len, key); if (result < 0) { gnutls_assert(); goto cleanup; } /* Get the data to be MACed */ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp); if (result < 0) { gnutls_assert(); goto cleanup; } #if ENABLE_GOST /* GOST PKCS#12 files use either PKCS#12 scheme or proprietary * HMAC-based scheme to generate MAC key. */ pkcs12_try_gost: #endif /* MAC the data */ result = _gnutls_mac_init(&td1, entry, key, key_len); if (result < 0) { gnutls_assert(); goto cleanup; } _gnutls_mac(&td1, tmp.data, tmp.size); _gnutls_mac_deinit(&td1, mac_output); len = sizeof(mac_output_orig); result = asn1_read_value(pkcs12->pkcs12, "macData.mac.digest", mac_output_orig, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto cleanup; } if ((unsigned)len != mac_len || memcmp(mac_output_orig, mac_output, len) != 0) { #if ENABLE_GOST /* It is possible that GOST files use proprietary * key generation scheme */ if (!gost_retry && (algo == GNUTLS_MAC_GOSTR_94 || algo == GNUTLS_MAC_STREEBOG_256 || algo == GNUTLS_MAC_STREEBOG_512)) { gost_retry = 1; key_len = 32; result = _gnutls_pkcs12_gost_string_to_key(algo, salt.data, salt.size, iter, pass, key_len, key); if (result < 0) { gnutls_assert(); goto cleanup; } goto pkcs12_try_gost; } #endif gnutls_assert(); result = GNUTLS_E_MAC_VERIFY_FAILED; goto cleanup; } result = 0; cleanup: _gnutls_free_datum(&tmp); _gnutls_free_datum(&salt); return result; }
static int server_recv_params(gnutls_session_t session, const unsigned char *data, size_t len, const gnutls_psk_server_credentials_t pskcred) { int ret; const mac_entry_st *prf; gnutls_datum_t full_client_hello; uint8_t binder_value[MAX_HASH_SIZE]; uint16_t psk_index, i; gnutls_datum_t binder_recvd = { NULL, 0 }; gnutls_datum_t key = {NULL, 0}; psk_ext_parser_st psk_parser; psk_ext_iter_st psk_iter; struct psk_st psk; psk_auth_info_t info; tls13_ticket_st ticket_data; /* These values should be set properly when session ticket is accepted. */ uint32_t ticket_age = UINT32_MAX; struct timespec ticket_creation_time = { 0, 0 }; bool resuming; ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len); if (ret < 0) { /* No PSKs advertised by client */ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) return 0; return gnutls_assert_val(ret); } _gnutls13_psk_ext_iter_init(&psk_iter, &psk_parser); for (psk_index = 0; ; psk_index++) { ret = _gnutls13_psk_ext_iter_next_identity(&psk_iter, &psk); if (ret < 0) { /* We couldn't find any usable PSK */ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) return 0; return gnutls_assert_val(ret); } /* This will unpack the session ticket if it is well * formed and has the expected name */ if (!(session->internals.flags & GNUTLS_NO_TICKETS) && (ret = _gnutls13_unpack_session_ticket(session, &psk.identity, &ticket_data)) == 0) { prf = ticket_data.prf; session->internals.resumption_requested = 1; /* Check whether ticket is stale or not */ ticket_age = psk.ob_ticket_age - ticket_data.age_add; if (ticket_age / 1000 > ticket_data.lifetime) { gnutls_assert(); tls13_ticket_deinit(&ticket_data); continue; } ret = compute_psk_from_ticket(&ticket_data, &key); if (ret < 0) { gnutls_assert(); tls13_ticket_deinit(&ticket_data); continue; } memcpy(&ticket_creation_time, &ticket_data.creation_time, sizeof(struct timespec)); tls13_ticket_deinit(&ticket_data); resuming = 1; break; } else if (pskcred && psk.ob_ticket_age == 0 && psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) { /* _gnutls_psk_pwd_find_entry() expects 0-terminated identities */ char identity_str[MAX_USERNAME_SIZE + 1]; prf = pskcred->binder_algo; memcpy(identity_str, psk.identity.data, psk.identity.size); identity_str[psk.identity.size] = 0; /* this fails only on configuration errors; as such we always * return its error code in that case */ ret = _gnutls_psk_pwd_find_entry(session, identity_str, &key); if (ret < 0) return gnutls_assert_val(ret); resuming = 0; break; } } _gnutls13_psk_ext_iter_init(&psk_iter, &psk_parser); for (i = 0; i <= psk_index; i++) { ret = _gnutls13_psk_ext_iter_next_binder(&psk_iter, &binder_recvd); if (ret < 0) { gnutls_assert(); /* We couldn't extract binder */ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; goto fail; } } /* Get full ClientHello */ if (!_gnutls_ext_get_full_client_hello(session, &full_client_hello)) { ret = GNUTLS_E_INTERNAL_ERROR; gnutls_assert(); goto fail; } /* Compute the binder value for this PSK */ ret = compute_psk_binder(session, prf, psk_parser.binders_len+2, 0, 0, &key, &full_client_hello, resuming, binder_value); if (ret < 0) { gnutls_assert(); goto fail; } if (_gnutls_mac_get_algo_len(prf) != binder_recvd.size || safe_memcmp(binder_value, binder_recvd.data, binder_recvd.size)) { gnutls_assert(); ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; goto fail; } if (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) _gnutls_handshake_log("EXT[%p]: selected DHE-PSK mode\n", session); else { reset_cand_groups(session); _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session); } /* save the username in psk_auth_info to make it available * using gnutls_psk_server_get_username() */ if (!resuming) { assert(psk.identity.size < sizeof(info->username)); ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); if (ret < 0) { gnutls_assert(); goto fail; } info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); assert(info != NULL); memcpy(info->username, psk.identity.data, psk.identity.size); info->username[psk.identity.size] = 0; _gnutls_handshake_log("EXT[%p]: selected PSK identity: %s (%d)\n", session, info->username, psk_index); } else { if (session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED) { if (session->internals.anti_replay) { ret = _gnutls_anti_replay_check(session->internals.anti_replay, ticket_age, &ticket_creation_time, &binder_recvd); if (ret < 0) { session->internals.hsk_flags &= ~HSK_EARLY_DATA_ACCEPTED; _gnutls_handshake_log("EXT[%p]: replay detected; rejecting early data\n", session); } } else { _gnutls_handshake_log("EXT[%p]: anti-replay is not enabled; rejecting early data\n", session); session->internals.hsk_flags &= ~HSK_EARLY_DATA_ACCEPTED; } } session->internals.resumed = RESUME_TRUE; _gnutls_handshake_log("EXT[%p]: selected resumption PSK identity (%d)\n", session, psk_index); } session->internals.hsk_flags |= HSK_PSK_SELECTED; /* Reference the selected pre-shared key */ session->key.binders[0].psk.data = key.data; session->key.binders[0].psk.size = key.size; session->key.binders[0].idx = psk_index; session->key.binders[0].prf = prf; session->key.binders[0].resumption = resuming; ret = _gnutls_generate_early_secrets_for_psk(session); if (ret < 0) { gnutls_assert(); goto fail; } return 0; fail: gnutls_free(key.data); return ret; }
static int client_send_params(gnutls_session_t session, gnutls_buffer_t extdata, const gnutls_psk_client_credentials_t cred) { int ret, ext_offset = 0; uint8_t binder_value[MAX_HASH_SIZE]; size_t spos; gnutls_datum_t username = {NULL, 0}; gnutls_datum_t user_key = {NULL, 0}, rkey = {NULL, 0}; gnutls_datum_t client_hello; unsigned next_idx; const mac_entry_st *prf_res = NULL; const mac_entry_st *prf_psk = NULL; struct timespec cur_time; uint32_t ticket_age, ob_ticket_age; int free_username = 0; psk_auth_info_t info = NULL; unsigned psk_id_len = 0; unsigned binders_len, binders_pos; if (((session->internals.flags & GNUTLS_NO_TICKETS) || session->internals.tls13_ticket.ticket.data == NULL) && (!cred || !_gnutls_have_psk_credentials(cred, session))) { return 0; } binders_len = 0; /* placeholder to be filled later */ spos = extdata->length; ret = _gnutls_buffer_append_prefix(extdata, 16, 0); if (ret < 0) return gnutls_assert_val(ret); /* First, let's see if we have a session ticket to send */ if (!(session->internals.flags & GNUTLS_NO_TICKETS) && session->internals.tls13_ticket.ticket.data != NULL) { /* We found a session ticket */ if (unlikely(session->internals.tls13_ticket.prf == NULL)) { _gnutls13_session_ticket_unset(session); ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); goto cleanup; } prf_res = session->internals.tls13_ticket.prf; gnutls_gettime(&cur_time); if (unlikely(_gnutls_timespec_cmp(&cur_time, &session->internals. tls13_ticket. arrival_time) < 0)) { gnutls_assert(); _gnutls13_session_ticket_unset(session); goto ignore_ticket; } /* Check whether the ticket is stale */ ticket_age = timespec_sub_ms(&cur_time, &session->internals.tls13_ticket. arrival_time); if (ticket_age / 1000 > session->internals.tls13_ticket.lifetime) { _gnutls13_session_ticket_unset(session); goto ignore_ticket; } ret = compute_psk_from_ticket(&session->internals.tls13_ticket, &rkey); if (ret < 0) { _gnutls13_session_ticket_unset(session); goto ignore_ticket; } /* Calculate obfuscated ticket age, in milliseconds, mod 2^32 */ ob_ticket_age = ticket_age + session->internals.tls13_ticket.age_add; if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16, session->internals.tls13_ticket.ticket.data, session->internals.tls13_ticket.ticket.size)) < 0) { gnutls_assert(); goto cleanup; } /* Now append the obfuscated ticket age */ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, ob_ticket_age)) < 0) { gnutls_assert(); goto cleanup; } psk_id_len += 6 + session->internals.tls13_ticket.ticket.size; binders_len += 1 + _gnutls_mac_get_algo_len(prf_res); } ignore_ticket: if (cred && _gnutls_have_psk_credentials(cred, session)) { gnutls_datum_t tkey; if (cred->binder_algo == NULL) { gnutls_assert(); ret = gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); goto cleanup; } prf_psk = cred->binder_algo; ret = _gnutls_find_psk_key(session, cred, &username, &tkey, &free_username); if (ret < 0) { gnutls_assert(); goto cleanup; } if (username.size == 0 || username.size > UINT16_MAX) { ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD); goto cleanup; } if (!free_username) { /* we need to copy the key */ ret = _gnutls_set_datum(&user_key, tkey.data, tkey.size); if (ret < 0) { gnutls_assert(); goto cleanup; } } else { user_key.data = tkey.data; user_key.size = tkey.size; } ret = _gnutls_auth_info_init(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1); if (ret < 0) { gnutls_assert(); goto cleanup; } info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK); assert(info != NULL); memcpy(info->username, username.data, username.size); info->username[username.size] = 0; if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16, username.data, username.size)) < 0) { gnutls_assert(); goto cleanup; } /* Now append the obfuscated ticket age */ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) { gnutls_assert(); goto cleanup; } psk_id_len += 6 + username.size; binders_len += 1 + _gnutls_mac_get_algo_len(prf_psk); } /* if no tickets or identities to be sent */ if (psk_id_len == 0) { /* reset extensions buffer */ extdata->length = spos; return 0; } _gnutls_write_uint16(psk_id_len, &extdata->data[spos]); binders_pos = extdata->length-spos; ext_offset = _gnutls_ext_get_extensions_offset(session); /* Compute the binders. extdata->data points to the start * of this client hello. */ assert(extdata->length >= sizeof(mbuffer_st)); assert(ext_offset >= (ssize_t)sizeof(mbuffer_st)); ext_offset -= sizeof(mbuffer_st); client_hello.data = extdata->data+sizeof(mbuffer_st); client_hello.size = extdata->length-sizeof(mbuffer_st); next_idx = 0; ret = _gnutls_buffer_append_prefix(extdata, 16, binders_len); if (ret < 0) { gnutls_assert_val(ret); goto cleanup; } if (prf_res && rkey.size > 0) { ret = compute_psk_binder(session, prf_res, binders_len, binders_pos, ext_offset, &rkey, &client_hello, 1, binder_value); if (ret < 0) { gnutls_assert(); goto cleanup; } /* Associate the selected pre-shared key with the session */ gnutls_free(session->key.binders[next_idx].psk.data); session->key.binders[next_idx].psk.data = rkey.data; session->key.binders[next_idx].psk.size = rkey.size; rkey.data = NULL; session->key.binders[next_idx].prf = prf_res; session->key.binders[next_idx].resumption = 1; session->key.binders[next_idx].idx = next_idx; _gnutls_handshake_log("EXT[%p]: sent PSK resumption identity (%d)\n", session, next_idx); next_idx++; /* Add the binder */ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_res->output_size); if (ret < 0) { gnutls_assert(); goto cleanup; } session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT; } if (prf_psk && user_key.size > 0 && info) { ret = compute_psk_binder(session, prf_psk, binders_len, binders_pos, ext_offset, &user_key, &client_hello, 0, binder_value); if (ret < 0) { gnutls_assert(); goto cleanup; } /* Associate the selected pre-shared key with the session */ gnutls_free(session->key.binders[next_idx].psk.data); session->key.binders[next_idx].psk.data = user_key.data; session->key.binders[next_idx].psk.size = user_key.size; user_key.data = NULL; session->key.binders[next_idx].prf = prf_psk; session->key.binders[next_idx].resumption = 0; session->key.binders[next_idx].idx = next_idx; _gnutls_handshake_log("EXT[%p]: sent PSK identity '%s' (%d)\n", session, info->username, next_idx); next_idx++; /* Add the binder */ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_psk->output_size); if (ret < 0) { gnutls_assert(); goto cleanup; } } ret = 0; cleanup: if (free_username) _gnutls_free_datum(&username); _gnutls_free_temp_key_datum(&user_key); _gnutls_free_temp_key_datum(&rkey); return ret; }
static int wrap_padlock_hmac_fast(gnutls_mac_algorithm_t algo, const void *nonce, size_t nonce_size, const void *key, size_t key_size, const void *text, size_t text_size, void *digest) { if (algo == GNUTLS_MAC_SHA1 || algo == GNUTLS_MAC_SHA256) { unsigned char *pad; unsigned char pad2[SHA1_DATA_SIZE + MAX_SHA_DIGEST_SIZE]; unsigned char hkey[MAX_SHA_DIGEST_SIZE]; unsigned int digest_size = _gnutls_mac_get_algo_len(mac_to_entry(algo)); if (key_size > SHA1_DATA_SIZE) { wrap_padlock_hash_fast((gnutls_digest_algorithm_t) algo, key, key_size, hkey); key = hkey; key_size = digest_size; } pad = gnutls_malloc(text_size + SHA1_DATA_SIZE); if (pad == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); memset(pad, IPAD, SHA1_DATA_SIZE); memxor(pad, key, key_size); memcpy(&pad[SHA1_DATA_SIZE], text, text_size); wrap_padlock_hash_fast((gnutls_digest_algorithm_t) algo, pad, text_size + SHA1_DATA_SIZE, &pad2[SHA1_DATA_SIZE]); gnutls_free(pad); memset(pad2, OPAD, SHA1_DATA_SIZE); memxor(pad2, key, key_size); wrap_padlock_hash_fast((gnutls_digest_algorithm_t) algo, pad2, digest_size + SHA1_DATA_SIZE, digest); } else { struct padlock_hmac_ctx ctx; int ret; ret = _hmac_ctx_init(algo, &ctx); if (ret < 0) return gnutls_assert_val(ret); ctx.algo = algo; wrap_padlock_hmac_setkey(&ctx, key, key_size); wrap_padlock_hmac_update(&ctx, text, text_size); wrap_padlock_hmac_output(&ctx, digest, ctx.length); wrap_padlock_hmac_deinit(&ctx); } return 0; }
/* Auth_cipher API */ int _gnutls_auth_cipher_init(auth_cipher_hd_st * handle, const cipher_entry_st * e, const gnutls_datum_t * cipher_key, const gnutls_datum_t * iv, const mac_entry_st * me, const gnutls_datum_t * mac_key, unsigned etm, unsigned ssl_hmac, int enc) { int ret; if (unlikely(e == NULL)) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); FAIL_IF_LIB_ERROR; memset(handle, 0, sizeof(*handle)); handle->etm = etm; if (e->id != GNUTLS_CIPHER_NULL) { handle->non_null = 1; ret = _gnutls_cipher_init(&handle->cipher, e, cipher_key, iv, enc); if (ret < 0) return gnutls_assert_val(ret); } else handle->non_null = 0; if (me->id != GNUTLS_MAC_AEAD) { handle->is_mac = 1; handle->ssl_hmac = ssl_hmac; if (ssl_hmac) ret = _gnutls_mac_init_ssl3(&handle->mac.dig, me, mac_key->data, mac_key->size); else ret = _gnutls_mac_init(&handle->mac.mac, me, mac_key->data, mac_key->size); if (ret < 0) { gnutls_assert(); goto cleanup; } handle->tag_size = _gnutls_mac_get_algo_len(me); } else if (_gnutls_cipher_algo_is_aead(e)) { handle->tag_size = _gnutls_cipher_get_tag_size(e); } else { gnutls_assert(); ret = GNUTLS_E_INVALID_REQUEST; goto cleanup; } return 0; cleanup: if (handle->non_null != 0) _gnutls_cipher_deinit(&handle->cipher); return ret; }