Chromium Code Reviews| Index: net/socket/ssl_client_socket_openssl.cc |
| diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc |
| index bcf23eaf9c63c1411a4d982b13035e1bcc484b67..774b41666cec20be23be7b180d81b8fb18b2bb4b 100644 |
| --- a/net/socket/ssl_client_socket_openssl.cc |
| +++ b/net/socket/ssl_client_socket_openssl.cc |
| @@ -521,8 +521,129 @@ int SSLClientSocketOpenSSL::ExportKeyingMaterial( |
| } |
| int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { |
| - NOTIMPLEMENTED(); |
| - return ERR_NOT_IMPLEMENTED; |
| + uint8_t buf[64]; |
| + size_t len; |
| + if (!SSL_get_tls_unique(ssl_, buf, &len, arraysize(buf))) |
| + return ERR_FAILED; |
| + out->assign(reinterpret_cast<char*>(buf), len); |
| + return OK; |
| +} |
| + |
| +// BuildProvidedTokenBindingID builds the serialized TokenBindingID struct as |
| +// defined in draft-ietf-tokbind-protocol-02. It only builds provided token |
| +// bindings, and only supports ECDSA P256 keys. The TokenBindingID struct |
| +// definition (and related structs and enums) is provided below. |
| +// |
| +// enum { |
| +// rsa2048_pkcs1.5_sha256(0), |
| +// rsa2048_pss_sha256(1), |
| +// ecdsap256_sha256(2), (255) |
| +// } TokenBindingKeyParameters; |
| +// |
| +// enum { |
| +// secp256r1 (23), (0xFFFF) |
| +// } NamedCurve; |
| +// |
| +// struct { |
| +// opaque point <1..2^8-1>; |
| +// } ECPoint; |
| +// |
| +// struct { |
| +// NamedCurve namedcurve; |
| +// ECPoint point; // Uncompressed format |
| +// } ECDSAParams; |
| +// |
| +// enum { |
| +// provided_token_binding(0), referred_token_binding(1), (255) |
| +// } TokenBindingType; |
| +// |
| +// struct { |
| +// TokenBindingType tokenbinding_type; |
| +// TokenBindingKeyParameters key_parameters; |
| +// select (key_parameters) { |
| +// case rsa2048_pkcs1.5_sha256: |
| +// case rsa2048_pss_sha256: |
| +// RSAPublicKey rsapubkey; |
| +// case ecdsap256_sha256: |
| +// ECDSAParams ecdsaparams; |
| +// } |
| +// } TokenBindingID; |
| +bool BuildProvidedTokenBindingID(crypto::ECPrivateKey* key, CBB* out) { |
| + CBB ec_point; |
| + if (!CBB_add_u8(out, 0) || // provided_token_binding(0) |
| + !CBB_add_u8(out, TB_PARAM_ECDSAP256_SHA256) || |
| + !CBB_add_u16(out, 23) || // NamedCurve secp256r1(23) |
| + !CBB_add_u8_length_prefixed(out, &ec_point)) { |
| + return false; |
| + } |
| + std::string raw_key; |
| + if (!key->ExportRawPublicKey(&raw_key)) { |
| + return false; |
| + } |
| + if (!CBB_add_bytes(&ec_point, reinterpret_cast<uint8*>( |
| + const_cast<char*>(raw_key.data())), |
| + raw_key.size()) || |
| + !CBB_flush(out)) { |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +int SSLClientSocketOpenSSL::BuildProvidedTokenBinding() { |
| + size_t tb_ekm_size = 32; |
| + uint8_t tb_ekm_buf[32]; |
| + const char tb_ekm_label[] = "EXPORTER-Token-Binding"; |
| + // The EKM label does not include a null terminating byte. Calling arraysize |
| + // on a char array includes the null terminator in the length, so subtract 1 |
| + // to account for that. |
| + size_t ekm_label_length = arraysize(tb_ekm_label) - 1; |
| + if (!SSL_export_keying_material(ssl_, tb_ekm_buf, tb_ekm_size, tb_ekm_label, |
| + ekm_label_length, nullptr, 0, false)) { |
| + return ERR_FAILED; |
| + } |
| + |
| + crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); |
| + size_t sig_len; |
| + if (!EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, |
| + channel_id_key_->key()) || |
| + !EVP_DigestSignUpdate(ctx.get(), tb_ekm_buf, tb_ekm_size) || |
| + !EVP_DigestSignFinal(ctx.get(), nullptr, &sig_len)) { |
| + return ERR_FAILED; |
| + } |
| + std::vector<uint8> signature; |
| + signature.resize(sig_len); |
| + if (!EVP_DigestSignFinal(ctx.get(), &signature.front(), &sig_len)) |
| + return ERR_FAILED; |
| + |
| + // Build in |token_binding| the serialization of the TokenBinding struct, |
| + // defined as follows: |
| + // |
| + // struct { |
| + // TokenBindingID tokenbindingid; |
| + // opaque signature<0..2^16-1>;// Signature over the exported keying |
| + // material value |
| + // Extension extensions<0..2^16-1>; |
| + // } TokenBinding; |
| + CBB token_binding; |
| + uint8_t* out_data; |
| + size_t out_len; |
| + if (!CBB_init(&token_binding, 0) || |
| + !BuildProvidedTokenBindingID(channel_id_key_.get(), &token_binding) || |
| + !CBB_add_u16(&token_binding, sig_len) || |
| + !CBB_add_bytes(&token_binding, &signature.front(), sig_len) || |
| + // 0-length extensions |
| + !CBB_add_u16(&token_binding, 0) || |
| + !CBB_finish(&token_binding, &out_data, &out_len)) { |
| + CBB_cleanup(&token_binding); |
| + return ERR_FAILED; |
| + } |
| + provided_token_binding_.assign(reinterpret_cast<char*>(out_data), out_len); |
| + OPENSSL_free(out_data); |
| + return OK; |
| +} |
| + |
| +std::string SSLClientSocketOpenSSL::GetProvidedTokenBinding() { |
| + return provided_token_binding_; |
| } |
| int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) { |
| @@ -1114,16 +1235,19 @@ int SSLClientSocketOpenSSL::DoHandshakeComplete(int result) { |
| return ERR_SSL_FALLBACK_BEYOND_MINIMUM_VERSION; |
| } |
| - // Check that if token binding was negotiated, then extended master secret |
| - // must also be negotiated. |
| - if (token_binding_extension_.WasNegotiated() && |
| - !ssl_->session->extended_master_secret) { |
| - return ERR_SSL_PROTOCOL_ERROR; |
| - } |
| - |
| - if (token_binding_extension_.WasNegotiated() && !channel_id_key_) { |
| - GotoState(STATE_TOKEN_BINDING_LOOKUP); |
| - return OK; |
| + if (token_binding_extension_.WasNegotiated()) { |
| + // Check that if token binding was negotiated, then extended master secret |
| + // must also be negotiated. |
| + if (!ssl_->session->extended_master_secret) |
| + return ERR_SSL_PROTOCOL_ERROR; |
| + if (!channel_id_key_) { |
| + GotoState(STATE_TOKEN_BINDING_LOOKUP); |
| + return OK; |
| + } |
| + if (BuildProvidedTokenBinding() != OK) { |
| + NOTREACHED(); |
| + return ERR_FAILED; |
| + } |
| } |
| // SSL handshake is completed. If NPN wasn't negotiated, see if ALPN was. |
| @@ -2245,9 +2369,9 @@ void SSLClientSocketOpenSSL::TokenBindingExtension::SetParams( |
| bool SSLClientSocketOpenSSL::TokenBindingExtension::RegisterCallbacks( |
| SSL_CTX* ssl_ctx) { |
| return SSL_CTX_add_client_custom_ext( |
| - ssl_ctx, kExtNum, &TokenBindingExtension::ClientAddCallback, |
| - &TokenBindingExtension::ClientFreeCallback, nullptr, |
| - &TokenBindingExtension::ClientParseCallback, nullptr); |
| + ssl_ctx, kExtNum, &TokenBindingExtension::ClientAddCallback, |
| + &TokenBindingExtension::ClientFreeCallback, nullptr, |
| + &TokenBindingExtension::ClientParseCallback, nullptr) != 0; |
|
mattm
2015/10/01 01:53:01
should this change be in the parent CL?
nharper
2015/10/01 20:25:46
Done.
|
| } |
| int SSLClientSocketOpenSSL::TokenBindingExtension::ClientAdd( |