/** * gnutls_srtp_get_keys: * @session: is a #gnutls_session_t type. * @key_material: Space to hold the generated key material * @key_material_size: The maximum size of the key material * @client_key: The master client write key, pointing inside the key material * @server_key: The master server write key, pointing inside the key material * @client_salt: The master client write salt, pointing inside the key material * @server_salt: The master server write salt, pointing inside the key material * * This is a helper function to generate the keying material for SRTP. * It requires the space of the key material to be pre-allocated (should be at least * 2x the maximum key size and salt size). The @client_key, @client_salt, @server_key * and @server_salt are convenience datums that point inside the key material. They may * be %NULL. * * Returns: On success the size of the key material is returned, * otherwise, %GNUTLS_E_SHORT_MEMORY_BUFFER if the buffer given is not * sufficient, or a negative error code. * * Since 3.1.4 **/ int gnutls_srtp_get_keys(gnutls_session_t session, void *key_material, unsigned int key_material_size, gnutls_datum_t * client_key, gnutls_datum_t * client_salt, gnutls_datum_t * server_key, gnutls_datum_t * server_salt) { int ret; const srtp_profile_st *p; gnutls_srtp_profile_t profile; unsigned int msize; uint8_t *km = key_material; ret = gnutls_srtp_get_selected_profile(session, &profile); if (ret < 0) return gnutls_assert_val(ret); p = get_profile(profile); if (p == NULL) return gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM); msize = 2 * (p->key_length + p->salt_length); if (msize > key_material_size) return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); if (msize == 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ret = gnutls_prf(session, sizeof("EXTRACTOR-dtls_srtp") - 1, "EXTRACTOR-dtls_srtp", 0, 0, NULL, msize, key_material); if (ret < 0) return gnutls_assert_val(ret); if (client_key) { client_key->data = km; client_key->size = p->key_length; } if (server_key) { server_key->data = km + p->key_length; server_key->size = p->key_length; } if (client_salt) { client_salt->data = km + 2 * p->key_length; client_salt->size = p->salt_length; } if (server_salt) { server_salt->data = km + 2 * p->key_length + p->salt_length; server_salt->size = p->salt_length; } return msize; }
void session::prf (size_t label_size, const char *label, int server_random_first, size_t extra_size, const char *extra, size_t outsize, char *out) { RETWRAP (gnutls_prf (s, label_size, label, server_random_first, extra_size, extra, outsize, out)); }
int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, int skip_keyblock, u8 *out, size_t out_len) { if (conn == NULL || conn->session == NULL || skip_keyblock) return -1; return gnutls_prf(conn->session, os_strlen(label), label, server_random_first, 0, NULL, out_len, (char *) out); }
int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { #if LIBGNUTLS_VERSION_NUMBER >= 0x010302 if (conn == NULL || conn->session == NULL) return -1; return gnutls_prf(conn->session, os_strlen(label), label, server_random_first, 0, NULL, out_len, out); #else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ return -1; #endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ }
static void check_prfs(gnutls_session_t session) { unsigned char key_material[512]; unsigned char key_material2[512]; int ret; TRY(13, "key expansion", 0, NULL, 34, (uint8_t*)"\xcf\x3e\x1c\x03\x47\x1a\xdf\x4a\x8e\x74\xc6\xda\xcd\xda\x22\xa4\x8e\xa5\xf7\x62\xef\xd6\x47\xe7\x41\x20\xea\x44\xb8\x5d\x66\x87\x0a\x61"); TRY(6, "hello", 0, NULL, 31, (uint8_t*)"\x83\x6c\xc7\x8e\x1b\x62\xc7\x06\x17\x99\x37\x95\x2e\xb8\x42\x5c\x42\xcd\x75\x65\x2c\xa3\x16\x2b\xab\x0a\xcf\xfc\xc8\x90\x30"); TRY(7, "context", 5, "abcd\xfa", 31, (uint8_t*)"\x5b\xc7\x72\xe9\xda\xe4\x79\x3e\xfe\x9a\xc5\x6f\xf4\x8d\x5a\xfe\x4c\x8d\x16\xa7\xf0\x13\x13\xf1\x93\xdd\x4b\x43\x65\xc1\x94"); TRY(12, "null-context", 0, "", 31, (uint8_t*)"\xd7\xb6\xff\x3d\xf7\xbe\x0e\xf2\xd0\xbf\x55\x0b\x56\xac\xfb\x3c\x1d\x5c\xaa\xa8\x71\x45\xf5\xd5\x71\x35\xa2\x35\x83\xc2\xe0"); TRY_OLD(6, "hello", 0, NULL, 31, (uint8_t*)"\x53\x35\x9b\x1c\xbf\xf2\x50\x85\xa1\xbc\x42\xfb\x45\x92\xc3\xbe\x20\x24\x24\xe2\xeb\x6e\xf7\x4f\xc0\xee\xe3\xaa\x46\x36\xfd"); TRY_OLD(7, "context", 5, "abcd\xfa", 31, (uint8_t*)"\x5f\x75\xb7\x61\x76\x4c\x1e\x86\x4b\x7d\x2e\x6c\x09\x91\xfd\x1e\xe6\xe8\xee\xf9\x86\x6a\x80\xfe\xf3\xbe\x96\xd0\x47\xf5\x9e"); /* check whether gnutls_prf matches gnutls_prf_rfc5705 when no context is given */ ret = gnutls_prf(session, 4, "aaaa", 0, 0, NULL, 64, (void*)key_material); if (ret < 0) { fprintf(stderr, "gnutls_prf: error in %d\n", __LINE__); gnutls_perror(ret); exit(1); } ret = gnutls_prf_rfc5705(session, 4, "aaaa", 0, NULL, 64, (void*)key_material2); if (ret < 0) { fprintf(stderr, "gnutls_prf_rfc5705: error in %d\n", __LINE__); gnutls_perror(ret); exit(1); } if (memcmp(key_material, key_material2, 64) != 0) { fprintf(stderr, "gnutls_prf: output doesn't match in cross-check\n"); dump("got1 ", key_material, 64); dump("got2 ", key_material2, 64); exit(1); } }
static void client(int fd) { gnutls_session_t session; int ret; gnutls_anon_client_credentials_t anoncred; gnutls_datum_t mac_key, iv, cipher_key; gnutls_datum_t read_mac_key, read_iv, read_cipher_key; unsigned char rseq_number[8]; unsigned char wseq_number[8]; unsigned char key_material[512], *p; unsigned i; unsigned block_size, hash_size, key_size, iv_size; const char *err; /* Need to enable anonymous KX specifically. */ global_init(); if (debug) { gnutls_global_set_log_function(client_log_func); gnutls_global_set_log_level(4711); } gnutls_anon_allocate_client_credentials(&anoncred); /* Initialize TLS session */ gnutls_init(&session, GNUTLS_CLIENT); /* Use default priorities */ ret = gnutls_priority_set_direct(session, "NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+SIGN-ALL:+COMP-NULL:+ANON-DH:+ANON-ECDH:+CURVE-ALL", &err); if (ret < 0) { fail("client: priority set failed (%s): %s\n", gnutls_strerror(ret), err); exit(1); } /* put the anonymous credentials to the current session */ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred); gnutls_transport_set_int(session, fd); /* Perform the TLS handshake */ do { ret = gnutls_handshake(session); } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); if (ret < 0) { fail("client: Handshake failed: %s\n", strerror(ret)); terminate(); } else { if (debug) success("client: Handshake was completed\n"); } if (debug) success("client: TLS version is: %s\n", gnutls_protocol_get_name (gnutls_protocol_get_version(session))); ret = gnutls_cipher_get(session); if (ret != GNUTLS_CIPHER_AES_128_CBC) { fprintf(stderr, "negotiated unexpected cipher: %s\n", gnutls_cipher_get_name(ret)); terminate(); } ret = gnutls_mac_get(session); if (ret != GNUTLS_MAC_SHA1) { fprintf(stderr, "negotiated unexpected mac: %s\n", gnutls_mac_get_name(ret)); terminate(); } iv_size = 16; hash_size = 20; key_size = 16; block_size = 2*hash_size + 2*key_size + 2 *iv_size; ret = gnutls_prf(session, 13, "key expansion", 1, 0, NULL, block_size, (void*)key_material); if (ret < 0) { fprintf(stderr, "error in %d\n", __LINE__); gnutls_perror(ret); terminate(); } p = key_material; /* check whether the key material matches our calculations */ ret = gnutls_record_get_state(session, 0, &mac_key, &iv, &cipher_key, wseq_number); if (ret < 0) { fprintf(stderr, "error in %d\n", __LINE__); gnutls_perror(ret); terminate(); } if (memcmp(wseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) { dump("wseq:", wseq_number, 8); fprintf(stderr, "error in %d\n", __LINE__); terminate(); } ret = gnutls_record_get_state(session, 1, &read_mac_key, &read_iv, &read_cipher_key, rseq_number); if (ret < 0) { fprintf(stderr, "error in %d\n", __LINE__); gnutls_perror(ret); terminate(); } if (memcmp(rseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) { dump("rseq:", rseq_number, 8); fprintf(stderr, "error in %d\n", __LINE__); terminate(); } if (hash_size != mac_key.size || memcmp(p, mac_key.data, hash_size) != 0) { dump("MAC:", mac_key.data, mac_key.size); dump("Block:", key_material, block_size); fprintf(stderr, "error in %d\n", __LINE__); terminate(); } p+= hash_size; if (hash_size != read_mac_key.size || memcmp(p, read_mac_key.data, hash_size) != 0) { dump("MAC:", read_mac_key.data, read_mac_key.size); dump("Block:", key_material, block_size); fprintf(stderr, "error in %d\n", __LINE__); terminate(); } p+= hash_size; if (key_size != cipher_key.size || memcmp(p, cipher_key.data, key_size) != 0) { fprintf(stderr, "error in %d\n", __LINE__); terminate(); } p+= key_size; if (key_size != read_cipher_key.size || memcmp(p, read_cipher_key.data, key_size) != 0) { fprintf(stderr, "error in %d\n", __LINE__); terminate(); } p+= key_size; if (iv_size != iv.size || memcmp(p, iv.data, iv_size) != 0) { fprintf(stderr, "error in %d\n", __LINE__); terminate(); } p+=iv_size; if (iv_size != read_iv.size || memcmp(p, read_iv.data, iv_size) != 0) { fprintf(stderr, "error in %d\n", __LINE__); terminate(); } /* check sequence numbers */ for (i=0;i<5;i++) { ret = gnutls_record_send(session, "hello", 5); if (ret < 0) { fail("gnutls_record_send: %s\n", gnutls_strerror(ret)); } } ret = gnutls_record_get_state(session, 0, NULL, NULL, NULL, wseq_number); if (ret < 0) { fprintf(stderr, "error in %d\n", __LINE__); gnutls_perror(ret); terminate(); } if (memcmp(wseq_number, "\x00\x00\x00\x00\x00\x00\x00\x06", 8) != 0) { dump("wseq:", wseq_number, 8); fprintf(stderr, "error in %d\n", __LINE__); terminate(); } ret = gnutls_record_get_state(session, 1, NULL, NULL, NULL, rseq_number); if (ret < 0) { fprintf(stderr, "error in %d\n", __LINE__); gnutls_perror(ret); terminate(); } if (memcmp(rseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) { dump("wseq:", wseq_number, 8); fprintf(stderr, "error in %d\n", __LINE__); terminate(); } gnutls_bye(session, GNUTLS_SHUT_WR); close(fd); gnutls_deinit(session); gnutls_anon_free_client_credentials(anoncred); gnutls_global_deinit(); }