diff options
-rw-r--r-- | crypto/ec_signature_creator_unittest.cc | 16 | ||||
-rw-r--r-- | crypto/signature_verifier_openssl.cc | 15 | ||||
-rw-r--r-- | net/data/ssl/certificates/README | 5 | ||||
-rw-r--r-- | net/data/ssl/certificates/quic_intermediate.crt | 53 | ||||
-rw-r--r-- | net/data/ssl/certificates/quic_proof_verify.crt | 106 | ||||
-rw-r--r-- | net/data/ssl/certificates/quic_test.example.com.crt | 56 | ||||
-rw-r--r-- | net/data/ssl/certificates/quic_test_ecc.example.com.crt | 50 | ||||
-rw-r--r-- | net/net.gyp | 5 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake.cc | 12 | ||||
-rw-r--r-- | net/quic/crypto/crypto_handshake.h | 11 | ||||
-rw-r--r-- | net/quic/crypto/proof_test.cc | 348 | ||||
-rw-r--r-- | net/quic/crypto/proof_verifier.h | 20 | ||||
-rw-r--r-- | net/quic/crypto/proof_verifier_chromium.cc | 240 | ||||
-rw-r--r-- | net/quic/crypto/proof_verifier_chromium.h | 89 | ||||
-rw-r--r-- | net/quic/quic_crypto_client_stream.cc | 78 | ||||
-rw-r--r-- | net/quic/quic_crypto_client_stream.h | 14 | ||||
-rw-r--r-- | net/quic/quic_session.cc | 11 | ||||
-rw-r--r-- | net/quic/quic_session.h | 10 | ||||
-rw-r--r-- | net/quic/test_tools/crypto_test_utils_chromium.cc | 48 |
19 files changed, 1137 insertions, 50 deletions
diff --git a/crypto/ec_signature_creator_unittest.cc b/crypto/ec_signature_creator_unittest.cc index b34022b..bc0cb4a 100644 --- a/crypto/ec_signature_creator_unittest.cc +++ b/crypto/ec_signature_creator_unittest.cc @@ -54,12 +54,22 @@ TEST(ECSignatureCreatorTest, BasicTest) { std::vector<uint8> public_key_info; ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info)); - // This is the algorithm ID for SHA-256 with EC encryption. + // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT. + // RFC 5758: + // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } + // ... + // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or + // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field + // as an AlgorithmIdentifier, the encoding MUST omit the parameters + // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one + // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with- + // SHA384, or ecdsa-with-SHA512. + // See also RFC 5480, Appendix A. const uint8 kECDSAWithSHA256AlgorithmID[] = { - 0x30, 0x0c, + 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, - 0x05, 0x00 }; crypto::SignatureVerifier verifier; ASSERT_TRUE(verifier.VerifyInit( diff --git a/crypto/signature_verifier_openssl.cc b/crypto/signature_verifier_openssl.cc index 1e71339..a85f00b 100644 --- a/crypto/signature_verifier_openssl.cc +++ b/crypto/signature_verifier_openssl.cc @@ -53,7 +53,17 @@ bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, d2i_X509_ALGOR(NULL, &signature_algorithm, signature_algorithm_len)); if (!algorithm.get()) return false; - const EVP_MD* digest = EVP_get_digestbyobj(algorithm.get()->algorithm); + int nid = OBJ_obj2nid(algorithm.get()->algorithm); + const EVP_MD* digest; + if (nid == NID_ecdsa_with_SHA1) { + digest = EVP_sha1(); + } else if (nid == NID_ecdsa_with_SHA256) { + digest = EVP_sha256(); + } else { + // This works for PKCS #1 v1.5 RSA signatures, but not for ECDSA + // signatures. + digest = EVP_get_digestbyobj(algorithm.get()->algorithm); + } if (!digest) return false; @@ -104,7 +114,8 @@ bool SignatureVerifier::VerifyFinal() { int rv = EVP_DigestVerifyFinal(verify_context_->ctx.get(), vector_as_array(&signature_), signature_.size()); - DCHECK_GE(rv, 0); + // rv is -1 if a DER-encoded ECDSA signature cannot be decoded correctly. + DCHECK_GE(rv, -1); Reset(); return rv == 1; } diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README index 84844ea..84e65e9 100644 --- a/net/data/ssl/certificates/README +++ b/net/data/ssl/certificates/README @@ -214,3 +214,8 @@ unit tests. server for simulating HTTPS connections. They are generated by running the script net/data/ssl/scripts/generate-test-certs.sh. +- quic_intermediate.crt +- quic_test_ecc.example.com.crt +- quic_test.example.com.crt +- quic_proof_verify.crt + These certificates are used by the ProofVerifier's unit tests of QUIC. diff --git a/net/data/ssl/certificates/quic_intermediate.crt b/net/data/ssl/certificates/quic_intermediate.crt new file mode 100644 index 0000000..ca1e6f6 --- /dev/null +++ b/net/data/ssl/certificates/quic_intermediate.crt @@ -0,0 +1,53 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Acme Co, CN=Root CA + Validity + Not Before: Jan 1 10:00:00 2013 GMT + Not After : Dec 31 10:00:00 2023 GMT + Subject: O=Acme Co, CN=Intermediate CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:af:95:dd:a0:eb:d7:c3:ba:a6:ae:db:6e:05:68: + a0:00:15:a1:85:d1:89:ba:be:3a:7a:3b:8c:3b:41: + 07:76:63:71:28:f7:bf:a5:fb:b3:28:94:f9:9a:de: + 1d:03:00:ce:5e:25:06:6a:e6:c7:0a:6b:6d:d3:76: + 95:57:f5:16:f8:f0:43:de:b7:c7:1b:0b:83:f4:70: + e6:29:a1:8d:22:12:9a:df:4b:31:e8:9b:86:7d:95: + 29:97:18:c1:34:2f:b6:a7:c1:c7:46:d6:9c:c6:a6: + ae:6e:dd:8f:be:c2:ec:02:00:d2:54:f6:0f:a0:cc: + af:04:85:65:98:a1:ea:73:f1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 9a:68:79:17:6c:13:20:b3:5f:01:ca:ae:c0:bf:d2:7b:98:bf: + dd:4d:d1:c3:a5:ab:01:47:2e:c8:61:b4:f5:1d:55:04:f0:eb: + 5d:84:5a:78:09:b0:f1:42:64:14:e8:9e:ba:c3:38:32:d3:16: + fe:e1:65:1f:76:da:e4:c0:83:62:4a:ae:d0:4e:00:2e:38:52: + 91:81:62:94:b0:3d:69:b3:87:72:39:55:94:9e:ca:2c:ca:51: + 3c:d3:3f:d2:1c:92:d3:de:df:ba:bc:45:9b:30:99:b4:39:f8: + 17:55:94:7d:3a:ba:0e:e9:3f:2d:bc:f0:ea:6d:17:85:23:e4: + ca:94 +-----BEGIN CERTIFICATE----- +MIIB+DCCAWOgAwIBAgIBAjALBgkqhkiG9w0BAQUwJDEQMA4GA1UEChMHQWNtZSBD +bzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xMzAxMDExMDAwMDBaFw0yMzEyMzExMDAw +MDBaMCwxEDAOBgNVBAoTB0FjbWUgQ28xGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBD +QTCBnTALBgkqhkiG9w0BAQEDgY0AMIGJAoGBAK+V3aDr18O6pq7bbgVooAAVoYXR +ibq+Ono7jDtBB3ZjcSj3v6X7syiU+ZreHQMAzl4lBmrmxwprbdN2lVf1FvjwQ963 +xxsLg/Rw5imhjSISmt9LMeibhn2VKZcYwTQvtqfBx0bWnMamrm7dj77C7AIA0lT2 +D6DMrwSFZZih6nPxAgMBAAGjODA2MA4GA1UdDwEB/wQEAwIABDATBgNVHSUEDDAK +BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MAsGCSqGSIb3DQEBBQOBgQCaaHkX +bBMgs18Byq7Av9J7mL/dTdHDpasBRy7IYbT1HVUE8OtdhFp4CbDxQmQU6J66wzgy +0xb+4WUfdtrkwINiSq7QTgAuOFKRgWKUsD1ps4dyOVWUnsosylE80z/SHJLT3t+6 +vEWbMJm0OfgXVZR9OroO6T8tvPDqbReFI+TKlA== +-----END CERTIFICATE----- diff --git a/net/data/ssl/certificates/quic_proof_verify.crt b/net/data/ssl/certificates/quic_proof_verify.crt new file mode 100644 index 0000000..55502e6 --- /dev/null +++ b/net/data/ssl/certificates/quic_proof_verify.crt @@ -0,0 +1,106 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Acme Co, CN=Root CA + Validity + Not Before: Jan 1 10:00:00 2013 GMT + Not After : Dec 31 10:00:00 2023 GMT + Subject: O=Acme Co, CN=Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:f3:8f:b5:01:f7:8f:bf:0e:c3:bc:2e:43:f9:63: + 32:ae:e2:70:2d:77:70:bf:32:57:77:dd:00:f4:16: + 08:e2:f4:b8:b4:c9:bc:41:be:54:ba:44:3f:6f:77: + f9:d1:1b:52:25:16:7d:df:f9:29:79:3c:7c:8f:16: + e3:85:d5:7c:96:5e:2e:60:b3:80:e1:fc:09:b9:04: + 4d:ff:bc:05:25:55:96:b8:e7:7e:03:ed:f4:a1:93: + 54:66:b6:d5:e4:1f:92:94:52:7d:c3:60:89:5f:79: + f9:63:d1:f4:bb:4d:fa:da:4d:2e:d2:1d:ac:dc:7a: + 4f:52:67:3f:ad:eb:ed:ba:cd + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 7c:0f:20:54:85:ea:e9:68:c5:15:fc:10:8a:09:98:0e:51:53: + 7a:a3:37:c3:ec:8d:61:2c:49:66:d0:34:0b:8b:68:50:58:75: + 1f:fb:76:87:89:16:7d:56:d1:be:2f:bb:ef:95:26:92:55:37: + 6f:ca:82:e2:d4:93:33:80:1f:9c:b9:2d:1e:ee:3b:90:7d:13: + 2e:28:9b:17:8c:15:5f:12:eb:ed:f2:86:2f:a5:f5:59:e4:f3: + 07:a9:99:2d:32:70:d4:2a:d0:43:f2:1c:92:6d:75:f8:60:fa: + b5:8f:4f:07:6b:f6:c0:80:b3:4f:c8:9f:ed:11:bd:4d:d9:d7: + 4a:2c +-----BEGIN CERTIFICATE----- +MIIB8DCCAVugAwIBAgIBATALBgkqhkiG9w0BAQUwJDEQMA4GA1UEChMHQWNtZSBD +bzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xMzAxMDExMDAwMDBaFw0yMzEyMzExMDAw +MDBaMCQxEDAOBgNVBAoTB0FjbWUgQ28xEDAOBgNVBAMTB1Jvb3QgQ0EwgZ0wCwYJ +KoZIhvcNAQEBA4GNADCBiQKBgQDzj7UB94+/DsO8LkP5YzKu4nAtd3C/Mld33QD0 +Fgji9Li0ybxBvlS6RD9vd/nRG1IlFn3f+Sl5PHyPFuOF1XyWXi5gs4Dh/Am5BE3/ +vAUlVZa4534D7fShk1RmttXkH5KUUn3DYIlfeflj0fS7TfraTS7SHazcek9SZz+t +6+26zQIDAQABozgwNjAOBgNVHQ8BAf8EBAMCAAQwEwYDVR0lBAwwCgYIKwYBBQUH +AwEwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQUDgYEAfA8gVIXq6WjFFfwQ +igmYDlFTeqM3w+yNYSxJZtA0C4toUFh1H/t2h4kWfVbRvi+775UmklU3b8qC4tST +M4AfnLktHu47kH0TLiibF4wVXxLr7fKGL6X1WeTzB6mZLTJw1CrQQ/Ickm11+GD6 +tY9PB2v2wICzT8if7RG9TdnXSiw= +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Acme Co, CN=Root CA + Validity + Not Before: Jan 1 10:00:00 2013 GMT + Not After : Dec 31 10:00:00 2023 GMT + Subject: O=Acme Co, CN=Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:f3:8f:b5:01:f7:8f:bf:0e:c3:bc:2e:43:f9:63: + 32:ae:e2:70:2d:77:70:bf:32:57:77:dd:00:f4:16: + 08:e2:f4:b8:b4:c9:bc:41:be:54:ba:44:3f:6f:77: + f9:d1:1b:52:25:16:7d:df:f9:29:79:3c:7c:8f:16: + e3:85:d5:7c:96:5e:2e:60:b3:80:e1:fc:09:b9:04: + 4d:ff:bc:05:25:55:96:b8:e7:7e:03:ed:f4:a1:93: + 54:66:b6:d5:e4:1f:92:94:52:7d:c3:60:89:5f:79: + f9:63:d1:f4:bb:4d:fa:da:4d:2e:d2:1d:ac:dc:7a: + 4f:52:67:3f:ad:eb:ed:ba:cd + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 7c:0f:20:54:85:ea:e9:68:c5:15:fc:10:8a:09:98:0e:51:53: + 7a:a3:37:c3:ec:8d:61:2c:49:66:d0:34:0b:8b:68:50:58:75: + 1f:fb:76:87:89:16:7d:56:d1:be:2f:bb:ef:95:26:92:55:37: + 6f:ca:82:e2:d4:93:33:80:1f:9c:b9:2d:1e:ee:3b:90:7d:13: + 2e:28:9b:17:8c:15:5f:12:eb:ed:f2:86:2f:a5:f5:59:e4:f3: + 07:a9:99:2d:32:70:d4:2a:d0:43:f2:1c:92:6d:75:f8:60:fa: + b5:8f:4f:07:6b:f6:c0:80:b3:4f:c8:9f:ed:11:bd:4d:d9:d7: + 4a:2c +-----BEGIN CERTIFICATE----- +MIIB8DCCAVugAwIBAgIBATALBgkqhkiG9w0BAQUwJDEQMA4GA1UEChMHQWNtZSBD +bzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xMzAxMDExMDAwMDBaFw0yMzEyMzExMDAw +MDBaMCQxEDAOBgNVBAoTB0FjbWUgQ28xEDAOBgNVBAMTB1Jvb3QgQ0EwgZ0wCwYJ +KoZIhvcNAQEBA4GNADCBiQKBgQDzj7UB94+/DsO8LkP5YzKu4nAtd3C/Mld33QD0 +Fgji9Li0ybxBvlS6RD9vd/nRG1IlFn3f+Sl5PHyPFuOF1XyWXi5gs4Dh/Am5BE3/ +vAUlVZa4534D7fShk1RmttXkH5KUUn3DYIlfeflj0fS7TfraTS7SHazcek9SZz+t +6+26zQIDAQABozgwNjAOBgNVHQ8BAf8EBAMCAAQwEwYDVR0lBAwwCgYIKwYBBQUH +AwEwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQUDgYEAfA8gVIXq6WjFFfwQ +igmYDlFTeqM3w+yNYSxJZtA0C4toUFh1H/t2h4kWfVbRvi+775UmklU3b8qC4tST +M4AfnLktHu47kH0TLiibF4wVXxLr7fKGL6X1WeTzB6mZLTJw1CrQQ/Ickm11+GD6 +tY9PB2v2wICzT8if7RG9TdnXSiw= +-----END CERTIFICATE----- diff --git a/net/data/ssl/certificates/quic_test.example.com.crt b/net/data/ssl/certificates/quic_test.example.com.crt new file mode 100644 index 0000000..375cedd --- /dev/null +++ b/net/data/ssl/certificates/quic_test.example.com.crt @@ -0,0 +1,56 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Acme Co, CN=Intermediate CA + Validity + Not Before: Jan 1 10:00:00 2013 GMT + Not After : Dec 31 10:00:00 2023 GMT + Subject: O=Acme Co, CN=Leaf certificate + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:c1:31:32:b2:28:dc:0d:a4:e0:4b:54:d6:fa:b7: + d2:0c:45:29:bf:67:c7:d1:b8:a9:90:63:51:c4:96: + 9f:86:a9:47:d7:67:f6:f9:1d:37:29:c2:0a:55:a7: + 8c:29:97:dc:f2:7f:f4:97:d0:d5:44:c9:04:1c:48: + ea:cc:a9:48:5c:eb:69:11:75:6e:db:7d:1a:5a:c0: + 9f:ad:a7:b8:0e:3b:a1:61:24:24:6f:64:84:ad:bb: + 28:06:c2:4a:c8:07:7b:46:33:8a:c7:81:77:92:4f: + 9d:88:1c:52:04:23:61:12:97:c7:e4:af:90:67:7e: + fb:ac:3d:23:92:f0:c9:39:6d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Alternative Name: + DNS:test.example.com + Signature Algorithm: sha1WithRSAEncryption + ad:33:55:2a:80:4c:ab:bc:b3:34:f7:b3:7e:fb:05:a8:11:3f: + a1:35:56:4c:46:2f:8d:24:70:35:3a:66:8d:14:c4:fb:7f:d9: + 76:de:c4:52:a7:42:8f:70:1d:fd:d5:33:04:69:5d:3c:18:03: + 8f:db:19:d0:14:d8:1c:0b:b6:74:9c:cf:41:ba:24:d9:c4:c3: + cf:86:fb:15:3d:c4:99:ea:af:6a:29:34:ed:97:03:38:ed:38: + b3:21:39:a0:f0:16:ac:81:d3:88:52:d8:5e:a3:6d:e6:ec:3f: + e9:20:ac:d3:78:7b:ae:59:9e:5d:3b:5e:61:bb:43:88:cd:8e: + d0:0d +-----BEGIN CERTIFICATE----- +MIICGzCCAYagAwIBAgIBAzALBgkqhkiG9w0BAQUwLDEQMA4GA1UEChMHQWNtZSBD +bzEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlIENBMB4XDTEzMDEwMTEwMDAwMFoXDTIz +MTIzMTEwMDAwMFowLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UEAxMQTGVhZiBj +ZXJ0aWZpY2F0ZTCBnTALBgkqhkiG9w0BAQEDgY0AMIGJAoGBAMExMrIo3A2k4EtU +1vq30gxFKb9nx9G4qZBjUcSWn4apR9dn9vkdNynCClWnjCmX3PJ/9JfQ1UTJBBxI +6sypSFzraRF1btt9GlrAn62nuA47oWEkJG9khK27KAbCSsgHe0YziseBd5JPnYgc +UgQjYRKXx+SvkGd++6w9I5LwyTltAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIAoDAT +BgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBsGA1UdEQQUMBKCEHRl +c3QuZXhhbXBsZS5jb20wCwYJKoZIhvcNAQEFA4GBAK0zVSqATKu8szT3s377BagR +P6E1VkxGL40kcDU6Zo0UxPt/2XbexFKnQo9wHf3VMwRpXTwYA4/bGdAU2BwLtnSc +z0G6JNnEw8+G+xU9xJnqr2opNO2XAzjtOLMhOaDwFqyB04hS2F6jbebsP+kgrNN4 +e65Znl07XmG7Q4jNjtAN +-----END CERTIFICATE----- diff --git a/net/data/ssl/certificates/quic_test_ecc.example.com.crt b/net/data/ssl/certificates/quic_test_ecc.example.com.crt new file mode 100644 index 0000000..0e6bfba --- /dev/null +++ b/net/data/ssl/certificates/quic_test_ecc.example.com.crt @@ -0,0 +1,50 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4 (0x4) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Acme Co, CN=Intermediate CA + Validity + Not Before: Jan 1 10:00:00 2013 GMT + Not After : Dec 31 10:00:00 2023 GMT + Subject: O=Acme Co, CN=ECDSA Leaf certificate + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:6d:48:d0:30:76:bb:bf:91:b1:d7:03:c2:fc:95: + 9b:e0:ea:42:ed:43:2c:a6:b2:23:c4:52:33:93:95: + 25:fc:16:75:83:9e:0f:0f:91:a5:47:b1:21:91:d4: + 94:94:30:b8:00:dc:1c:79:2c:fa:72:99:62:b2:fa: + af:b0:ca:f2:42 + ASN1 OID: prime256v1 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Alternative Name: + DNS:test.example.com + Signature Algorithm: sha1WithRSAEncryption + 5d:23:47:b4:b5:0f:38:18:cd:63:90:24:37:f1:da:67:66:a4: + fe:8d:53:3f:c5:a7:10:e6:21:a3:1d:b7:42:b0:1a:e7:d7:83: + 3d:ea:7b:6b:89:85:bb:13:77:4d:45:ab:b2:e7:1e:ac:6e:74: + b6:9f:c4:e0:76:1c:e4:13:e9:6c:b1:20:a3:34:e8:1e:8a:71: + 51:cb:00:44:71:64:f6:4b:9e:9a:2d:d9:9a:44:62:f5:8c:3c: + c5:ec:c1:1c:d5:bb:05:53:33:af:70:44:1d:5b:aa:23:67:30: + 3e:d3:a9:5e:a2:57:84:86:aa:be:bd:7b:4f:74:d9:3b:cd:2e: + 7e:d1 +-----BEGIN CERTIFICATE----- +MIIB3DCCAUegAwIBAgIBBDALBgkqhkiG9w0BAQUwLDEQMA4GA1UEChMHQWNtZSBD +bzEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlIENBMB4XDTEzMDEwMTEwMDAwMFoXDTIz +MTIzMTEwMDAwMFowMzEQMA4GA1UEChMHQWNtZSBDbzEfMB0GA1UEAxMWRUNEU0Eg +TGVhZiBjZXJ0aWZpY2F0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABG1I0DB2 +u7+RsdcDwvyVm+DqQu1DLKayI8RSM5OVJfwWdYOeDw+RpUexIZHUlJQwuADcHHks ++nKZYrL6r7DK8kKjUjBQMA4GA1UdDwEB/wQEAwIAgDATBgNVHSUEDDAKBggrBgEF +BQcDATAMBgNVHRMBAf8EAjAAMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBsZS5jb20w +CwYJKoZIhvcNAQEFA4GBAF0jR7S1DzgYzWOQJDfx2mdmpP6NUz/FpxDmIaMdt0Kw +GufXgz3qe2uJhbsTd01Fq7LnHqxudLafxOB2HOQT6WyxIKM06B6KcVHLAERxZPZL +npot2ZpEYvWMPMXswRzVuwVTM69wRB1bqiNnMD7TqV6iV4SGqr69e0902TvNLn7R +-----END CERTIFICATE----- diff --git a/net/net.gyp b/net/net.gyp index f0991c7..5d5c131 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -758,6 +758,8 @@ 'quic/crypto/p256_key_exchange_nss.cc', 'quic/crypto/p256_key_exchange_openssl.cc', 'quic/crypto/proof_source.h', + 'quic/crypto/proof_verifier_chromium.cc', + 'quic/crypto/proof_verifier_chromium.h', 'quic/crypto/quic_decrypter.cc', 'quic/crypto/quic_decrypter.h', 'quic/crypto/quic_encrypter.cc', @@ -1680,6 +1682,7 @@ 'quic/crypto/strike_register_test.cc', 'quic/test_tools/crypto_test_utils.cc', 'quic/test_tools/crypto_test_utils.h', + 'quic/test_tools/crypto_test_utils_chromium.cc', 'quic/test_tools/crypto_test_utils_nss.cc', 'quic/test_tools/crypto_test_utils_openssl.cc', 'quic/test_tools/mock_clock.cc', @@ -2684,6 +2687,7 @@ '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', 'net', + 'net_test_support', 'quic_library', ], 'sources': [ @@ -2691,6 +2695,7 @@ 'quic/test_tools/quic_session_peer.h', 'quic/test_tools/crypto_test_utils.cc', 'quic/test_tools/crypto_test_utils.h', + 'quic/test_tools/crypto_test_utils_chromium.cc', 'quic/test_tools/crypto_test_utils_nss.cc', 'quic/test_tools/crypto_test_utils_openssl.cc', 'quic/test_tools/mock_clock.cc', diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index be296be..66a83de 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -357,7 +357,8 @@ QuicCryptoClientConfig::~QuicCryptoClientConfig() { } QuicCryptoClientConfig::CachedState::CachedState() - : server_config_valid_(false) {} + : server_config_valid_(false), + generation_counter_(0) {} QuicCryptoClientConfig::CachedState::~CachedState() {} @@ -430,6 +431,7 @@ QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig( if (!matches_existing) { server_config_ = server_config.as_string(); server_config_valid_ = false; + ++generation_counter_; scfg_.reset(new_scfg_storage.release()); } return QUIC_NO_ERROR; @@ -439,6 +441,7 @@ void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() { server_config_.clear(); scfg_.reset(); server_config_valid_ = false; + ++generation_counter_; } void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, @@ -461,6 +464,7 @@ void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs, // If the proof has changed then it needs to be revalidated. server_config_valid_ = false; + ++generation_counter_; certs_ = certs; server_config_sig_ = signature.as_string(); } @@ -490,6 +494,10 @@ bool QuicCryptoClientConfig::CachedState::proof_valid() const { return server_config_valid_; } +uint64 QuicCryptoClientConfig::CachedState::generation_counter() const { + return generation_counter_; +} + void QuicCryptoClientConfig::CachedState::set_source_address_token( StringPiece token) { source_address_token_ = token.as_string(); @@ -836,7 +844,7 @@ QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( return QUIC_NO_ERROR; } -const ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { +ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { return proof_verifier_.get(); } diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index 919f777..e702bb6 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -273,6 +273,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { const std::vector<std::string>& certs() const; const std::string& signature() const; bool proof_valid() const; + uint64 generation_counter() const; void set_source_address_token(base::StringPiece token); @@ -283,8 +284,12 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { std::vector<std::string> certs_; // A list of certificates in leaf-first // order. std::string server_config_sig_; // A signature of |server_config_|. - bool server_config_valid_; // true if |server_config_| is correctly signed - // and |certs_| has been validated. + bool server_config_valid_; // True if |server_config_| is correctly + // signed and |certs_| has been + // validated. + uint64 generation_counter_; // Generation counter associated with + // the |server_config_|, |certs_| and + // |server_config_sig_| combination. // scfg contains the cached, parsed value of |server_config|. mutable scoped_ptr<CryptoHandshakeMessage> scfg_; @@ -348,7 +353,7 @@ class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig { QuicCryptoNegotiatedParameters* out_params, std::string* error_details); - const ProofVerifier* proof_verifier() const; + ProofVerifier* proof_verifier() const; // SetProofVerifier takes ownership of a |ProofVerifier| that clients are // free to use in order to verify certificate chains from servers. If a diff --git a/net/quic/crypto/proof_test.cc b/net/quic/crypto/proof_test.cc index 3423b50..b30c9aa 100644 --- a/net/quic/crypto/proof_test.cc +++ b/net/quic/crypto/proof_test.cc @@ -2,11 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/files/file_path.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "net/base/test_data_directory.h" +#include "net/cert/x509_certificate.h" #include "net/quic/crypto/proof_source.h" #include "net/quic/crypto/proof_verifier.h" #include "net/quic/test_tools/crypto_test_utils.h" +#include "net/test/cert_test_util.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + using std::string; using std::vector; @@ -34,25 +44,341 @@ TEST(Proof, Verify) { ASSERT_EQ(first_certs, certs); ASSERT_EQ(signature, first_signature); - ASSERT_TRUE(verifier->VerifyProof(hostname, server_config, *certs, signature, - &error_details)); - ASSERT_FALSE(verifier->VerifyProof("foo.com", server_config, *certs, - signature, &error_details)); - ASSERT_FALSE( - verifier->VerifyProof(hostname, server_config.substr(1, string::npos), - *certs, signature, &error_details)); + int rv; + TestCompletionCallback callback; + rv = verifier->VerifyProof(hostname, server_config, *certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(OK, rv); + ASSERT_EQ("", error_details); + + rv = verifier->VerifyProof("foo.com", server_config, *certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + rv = verifier->VerifyProof(hostname, server_config.substr(1, string::npos), + *certs, signature, &error_details, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + const string corrupt_signature = "1" + signature; - ASSERT_FALSE(verifier->VerifyProof(hostname, server_config, *certs, - corrupt_signature, &error_details)); + rv = verifier->VerifyProof(hostname, server_config, *certs, corrupt_signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); vector<string> wrong_certs; for (size_t i = 1; i < certs->size(); i++) { wrong_certs.push_back((*certs)[i]); } - ASSERT_FALSE(verifier->VerifyProof("foo.com", server_config, wrong_certs, - signature, &error_details)); + rv = verifier->VerifyProof("foo.com", server_config, wrong_certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); #endif // 0 } +static string PEMCertFileToDER(const string& file_name) { + base::FilePath certs_dir = GetTestCertsDirectory(); + scoped_refptr<X509Certificate> cert = + ImportCertFromFile(certs_dir, file_name); + CHECK_NE(static_cast<X509Certificate*>(NULL), cert); + + string der_bytes; + CHECK(X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_bytes)); + return der_bytes; +} + +// A known answer test that allows us to test ProofVerifier without a working +// ProofSource. +TEST(Proof, VerifyRSAKnownAnswerTest) { + // These sample signatures were generated by running the Proof.Verify test + // and dumping the bytes of the |signature| output of ProofSource::GetProof(). +#if 0 + // In the future, we will change the |sLen| parameter of RSA-PSS to be the + // same as |hLen|, and these are the sample signatures with the parameter + // |sLen| equal to |hLen|. + static const unsigned char signature_data_0[] = { + 0x9e, 0xe6, 0x74, 0x3b, 0x8f, 0xb8, 0x66, 0x77, 0x57, 0x09, + 0x8a, 0x04, 0xe9, 0xf0, 0x7c, 0x91, 0xa9, 0x5c, 0xe9, 0xdf, + 0x12, 0x4d, 0x23, 0x82, 0x8c, 0x29, 0x72, 0x7f, 0xc2, 0x20, + 0xa7, 0xb3, 0xe5, 0xbc, 0xcf, 0x3c, 0x0d, 0x8f, 0xae, 0x46, + 0x6a, 0xb9, 0xee, 0x0c, 0xe1, 0x13, 0x21, 0xc0, 0x7e, 0x45, + 0x24, 0x24, 0x4b, 0x72, 0x43, 0x5e, 0xc4, 0x0d, 0xdf, 0x6c, + 0xd8, 0xaa, 0x35, 0x97, 0x05, 0x40, 0x76, 0xd3, 0x2c, 0xee, + 0x82, 0x16, 0x6a, 0x43, 0xf9, 0xa2, 0xd0, 0x41, 0x3c, 0xed, + 0x3f, 0x40, 0x10, 0x95, 0xc7, 0xa9, 0x1f, 0x04, 0xdb, 0xd5, + 0x98, 0x9f, 0xe2, 0xbf, 0x77, 0x3d, 0xc9, 0x9a, 0xaf, 0xf7, + 0xef, 0x63, 0x0b, 0x7d, 0xc8, 0x37, 0xda, 0x37, 0x23, 0x88, + 0x78, 0xc8, 0x8b, 0xf5, 0xb9, 0x36, 0x5d, 0x72, 0x1f, 0xfc, + 0x14, 0xff, 0xa7, 0x81, 0x27, 0x49, 0xae, 0xe1, + }; + static const unsigned char signature_data_1[] = { + 0x5e, 0xc2, 0xab, 0x6b, 0x16, 0xe6, 0x55, 0xf3, 0x16, 0x46, + 0x35, 0xdc, 0xcc, 0xde, 0xd0, 0xbd, 0x6c, 0x66, 0xb2, 0x3d, + 0xd3, 0x14, 0x78, 0xed, 0x47, 0x55, 0xfb, 0xdb, 0xe1, 0x7d, + 0xbf, 0x31, 0xf6, 0xf4, 0x10, 0x4c, 0x8d, 0x22, 0x17, 0xaa, + 0xe1, 0x85, 0xc7, 0x96, 0x4c, 0x42, 0xfb, 0xf4, 0x63, 0x53, + 0x8a, 0x79, 0x01, 0x63, 0x48, 0xa8, 0x3a, 0xbc, 0xc9, 0xd2, + 0xf5, 0xec, 0xe9, 0x09, 0x71, 0xaf, 0xce, 0x34, 0x56, 0xe5, + 0x00, 0xbe, 0xee, 0x3c, 0x1c, 0xc4, 0xa0, 0x07, 0xd5, 0x77, + 0xb8, 0x83, 0x57, 0x7d, 0x1a, 0xc9, 0xd0, 0xc0, 0x59, 0x9a, + 0x88, 0x19, 0x3f, 0xb9, 0xf0, 0x45, 0x37, 0xc3, 0x00, 0x8b, + 0xb3, 0x89, 0xf4, 0x89, 0x07, 0xa9, 0xc3, 0x26, 0xbf, 0x81, + 0xaf, 0x6b, 0x47, 0xbc, 0x16, 0x55, 0x37, 0x0a, 0xbe, 0x0e, + 0xc5, 0x75, 0x3f, 0x3d, 0x8e, 0xe8, 0x44, 0xe3, + }; + static const unsigned char signature_data_2[] = { + 0x8e, 0x5c, 0x78, 0x63, 0x74, 0x99, 0x2e, 0x96, 0xc0, 0x14, + 0x8d, 0xb5, 0x13, 0x74, 0xa3, 0xa4, 0xe0, 0x43, 0x3e, 0x85, + 0xba, 0x8f, 0x3c, 0x5e, 0x14, 0x64, 0x0e, 0x5e, 0xff, 0x89, + 0x88, 0x8a, 0x65, 0xe2, 0xa2, 0x79, 0xe4, 0xe9, 0x3a, 0x7f, + 0xf6, 0x9d, 0x3d, 0xe2, 0xb0, 0x8a, 0x35, 0x55, 0xed, 0x21, + 0xee, 0x20, 0xd8, 0x8a, 0x60, 0x47, 0xca, 0x52, 0x54, 0x91, + 0x99, 0x69, 0x8d, 0x16, 0x34, 0x69, 0xe1, 0x46, 0x56, 0x67, + 0x5f, 0x50, 0xf0, 0x94, 0xe7, 0x8b, 0xf2, 0x6a, 0x73, 0x0f, + 0x30, 0x30, 0xde, 0x59, 0xdc, 0xc7, 0xfe, 0xb6, 0x83, 0xe1, + 0x86, 0x1d, 0x88, 0xd3, 0x2f, 0x2f, 0x74, 0x68, 0xbd, 0x6c, + 0xd1, 0x46, 0x76, 0x06, 0xa9, 0xd4, 0x03, 0x3f, 0xda, 0x7d, + 0xa7, 0xff, 0x48, 0xe4, 0xb4, 0x42, 0x06, 0xac, 0x19, 0x12, + 0xe6, 0x05, 0xae, 0xbe, 0x29, 0x94, 0x8f, 0x99, + }; +#else + // sLen = special value -2 used by OpenSSL. + static const unsigned char signature_data_0[] = { + 0x4c, 0x68, 0x3c, 0xc2, 0x1f, 0x31, 0x73, 0xa5, 0x29, 0xd3, + 0x56, 0x75, 0xb1, 0xbf, 0xbd, 0x31, 0x17, 0xfb, 0x2e, 0x24, + 0xb3, 0xc4, 0x0d, 0xfa, 0x56, 0xb8, 0x65, 0x94, 0x12, 0x38, + 0x6e, 0xff, 0xb3, 0x10, 0x2e, 0xf8, 0x5c, 0xc1, 0x21, 0x9d, + 0x29, 0x0c, 0x3a, 0x0a, 0x1a, 0xbf, 0x6b, 0x1c, 0x63, 0x77, + 0xf7, 0x86, 0xd3, 0xa4, 0x36, 0xf2, 0xb1, 0x6f, 0xac, 0xc3, + 0x23, 0x8d, 0xda, 0xe6, 0xd5, 0x83, 0xba, 0xdf, 0x28, 0x3e, + 0x7f, 0x4e, 0x79, 0xfc, 0xba, 0xdb, 0xf7, 0xd0, 0x4b, 0xad, + 0x79, 0xd0, 0xeb, 0xcf, 0xfa, 0x6e, 0x84, 0x44, 0x7a, 0x26, + 0xb1, 0x29, 0xa3, 0x08, 0xa8, 0x63, 0xfd, 0xed, 0x85, 0xff, + 0x9a, 0xe6, 0x79, 0x8b, 0xb6, 0x81, 0x13, 0x2c, 0xde, 0xe2, + 0xd8, 0x31, 0x29, 0xa4, 0xe0, 0x1b, 0x75, 0x2d, 0x8a, 0xf8, + 0x27, 0x55, 0xbc, 0xc7, 0x3b, 0x1e, 0xc1, 0x42, + }; + static const unsigned char signature_data_1[] = { + 0xbb, 0xd1, 0x17, 0x43, 0xf3, 0x42, 0x16, 0xe9, 0xf9, 0x76, + 0xe6, 0xe3, 0xaa, 0x50, 0x47, 0x5f, 0x93, 0xb6, 0x7d, 0x35, + 0x03, 0x49, 0x0a, 0x07, 0x61, 0xd5, 0xf1, 0x9c, 0x6b, 0xaf, + 0xaa, 0xd7, 0x64, 0xe4, 0x0a, 0x0c, 0xab, 0x97, 0xfb, 0x4e, + 0x5c, 0x14, 0x08, 0xf6, 0xb9, 0xa9, 0x1d, 0xa9, 0xf8, 0x6d, + 0xb0, 0x2b, 0x2a, 0x0e, 0xc4, 0xd0, 0xd2, 0xe9, 0x96, 0x4f, + 0x44, 0x70, 0x90, 0x46, 0xb9, 0xd5, 0x89, 0x72, 0xb9, 0xa8, + 0xe4, 0xfb, 0x88, 0xbc, 0x69, 0x7f, 0xc9, 0xdc, 0x84, 0x87, + 0x18, 0x21, 0x9b, 0xde, 0x22, 0x33, 0xde, 0x16, 0x3f, 0xe6, + 0xfd, 0x27, 0x56, 0xd3, 0xa4, 0x97, 0x91, 0x65, 0x1a, 0xe7, + 0x5e, 0x80, 0x9a, 0xbf, 0xbf, 0x1a, 0x29, 0x8a, 0xbe, 0xa2, + 0x8c, 0x9c, 0x23, 0xf4, 0xcb, 0xba, 0x79, 0x31, 0x28, 0xab, + 0x77, 0x94, 0x92, 0xb2, 0xc2, 0x35, 0xb2, 0xfa, + }; + static const unsigned char signature_data_2[] = { + 0x7e, 0x17, 0x01, 0xcb, 0x76, 0x9e, 0x9f, 0xce, 0xeb, 0x66, + 0x3e, 0xaa, 0xc9, 0x36, 0x5b, 0x7e, 0x48, 0x25, 0x99, 0xf8, + 0x0d, 0xe1, 0xa8, 0x48, 0x93, 0x3c, 0xe8, 0x97, 0x2e, 0x98, + 0xd6, 0x73, 0x0f, 0xd0, 0x74, 0x9c, 0x17, 0xef, 0xee, 0xf8, + 0x0e, 0x2a, 0x27, 0x3f, 0xc6, 0x55, 0xc6, 0xb9, 0xfe, 0x17, + 0xcc, 0xeb, 0x5d, 0xa1, 0xdc, 0xbd, 0x64, 0xd9, 0x5e, 0xec, + 0x57, 0x9d, 0xc3, 0xdc, 0x11, 0xbf, 0x23, 0x02, 0x58, 0xc4, + 0xf1, 0x18, 0xc1, 0x6f, 0x3f, 0xef, 0x18, 0x4d, 0xa6, 0x1e, + 0xe8, 0x25, 0x32, 0x8f, 0x92, 0x1e, 0xad, 0xbc, 0xbe, 0xde, + 0x83, 0x2a, 0x92, 0xd5, 0x59, 0x6f, 0xe4, 0x95, 0x6f, 0xe6, + 0xb1, 0xf9, 0xaf, 0x3f, 0xdb, 0x69, 0x6f, 0xae, 0xa6, 0x36, + 0xd2, 0x50, 0x81, 0x78, 0x41, 0x13, 0x2c, 0x65, 0x9c, 0x9e, + 0xf4, 0xd2, 0xd5, 0x58, 0x5b, 0x8b, 0x87, 0xcf, + }; +#endif + + scoped_ptr<ProofVerifier> verifier( + CryptoTestUtils::ProofVerifierForTesting()); + + const string server_config = "server config bytes"; + const string hostname = "test.example.com"; + string error_details; + + vector<string> certs(2); + certs[0] = PEMCertFileToDER("quic_test.example.com.crt"); + certs[1] = PEMCertFileToDER("quic_intermediate.crt"); + + // Signatures are nondeterministic, so we test multiple signatures on the + // same server_config. + vector<string> signatures(3); + signatures[0].assign(reinterpret_cast<const char*>(signature_data_0), + sizeof(signature_data_0)); + signatures[1].assign(reinterpret_cast<const char*>(signature_data_1), + sizeof(signature_data_1)); + signatures[2].assign(reinterpret_cast<const char*>(signature_data_2), + sizeof(signature_data_2)); + + for (size_t i = 0; i < signatures.size(); i++) { + const string& signature = signatures[i]; + int rv; + TestCompletionCallback callback; + rv = verifier->VerifyProof(hostname, server_config, certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(OK, rv); + ASSERT_EQ("", error_details); + + rv = verifier->VerifyProof("foo.com", server_config, certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + rv = verifier->VerifyProof(hostname, server_config.substr(1, string::npos), + certs, signature, &error_details, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + const string corrupt_signature = "1" + signature; + rv = verifier->VerifyProof(hostname, server_config, certs, + corrupt_signature, &error_details, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + vector<string> wrong_certs; + for (size_t i = 1; i < certs.size(); i++) { + wrong_certs.push_back(certs[i]); + } + rv = verifier->VerifyProof("foo.com", server_config, wrong_certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + } +} + +// A known answer test that allows us to test ProofVerifier without a working +// ProofSource. +// TODO(rtenneti): Enable VerifyECDSAKnownAnswerTest on win_rel and XP. +#if defined(OS_WIN) +#define MAYBE_VerifyECDSAKnownAnswerTest DISABLED_VerifyECDSAKnownAnswerTest +#else +#define MAYBE_VerifyECDSAKnownAnswerTest VerifyECDSAKnownAnswerTest +#endif +TEST(Proof, MAYBE_VerifyECDSAKnownAnswerTest) { + // These sample signatures were generated by running the Proof.Verify test + // (modified to use ECDSA for signing proofs) and dumping the bytes of the + // |signature| output of ProofSource::GetProof(). + static const unsigned char signature_data_0[] = { + 0x30, 0x45, 0x02, 0x20, 0x15, 0xb7, 0x9f, 0xe3, 0xd9, 0x7a, + 0x3c, 0x3b, 0x18, 0xb0, 0xdb, 0x60, 0x23, 0x56, 0xa0, 0x06, + 0x4e, 0x70, 0xa3, 0xf7, 0x4b, 0xe5, 0x0d, 0x69, 0xf0, 0x35, + 0x8c, 0xae, 0xb5, 0x54, 0x32, 0xe9, 0x02, 0x21, 0x00, 0xf7, + 0xe3, 0x06, 0x99, 0x16, 0x56, 0x7e, 0xab, 0x33, 0x53, 0x0d, + 0xde, 0xbe, 0xef, 0x6d, 0xb0, 0xc7, 0xa6, 0x63, 0xaf, 0x8d, + 0xab, 0x34, 0xa9, 0xc0, 0x63, 0x88, 0x47, 0x17, 0x4c, 0x4c, + 0x04, + }; + static const unsigned char signature_data_1[] = { + 0x30, 0x44, 0x02, 0x20, 0x69, 0x60, 0x55, 0xbb, 0x11, 0x93, + 0x6a, 0xdc, 0x9b, 0x61, 0x2c, 0x60, 0x19, 0xbc, 0x15, 0x55, + 0xcf, 0xf2, 0x8e, 0x2e, 0x27, 0x0b, 0x69, 0xef, 0x33, 0x25, + 0x1e, 0x5d, 0x8c, 0x00, 0x11, 0xef, 0x02, 0x20, 0x0c, 0x26, + 0xfe, 0x0b, 0x06, 0x8f, 0xe8, 0xe2, 0x02, 0x63, 0xe5, 0x43, + 0x0d, 0xc9, 0x80, 0x4d, 0xe9, 0x6f, 0x6e, 0x18, 0xdb, 0xb0, + 0x04, 0x2a, 0x45, 0x37, 0x1a, 0x60, 0x0e, 0xc6, 0xc4, 0x8f, + }; + static const unsigned char signature_data_2[] = { + 0x30, 0x45, 0x02, 0x21, 0x00, 0xd5, 0x43, 0x36, 0x60, 0x50, + 0xce, 0xe0, 0x00, 0x51, 0x02, 0x84, 0x95, 0x51, 0x47, 0xaf, + 0xe4, 0xf9, 0xe1, 0x23, 0xae, 0x21, 0xb4, 0x98, 0xd1, 0xa3, + 0x5f, 0x3b, 0xf3, 0x6a, 0x65, 0x44, 0x6b, 0x02, 0x20, 0x30, + 0x7e, 0xb4, 0xea, 0xf0, 0xda, 0xdb, 0xbd, 0x38, 0xb9, 0x7a, + 0x5d, 0x12, 0x04, 0x0e, 0xc2, 0xf0, 0xb1, 0x0e, 0x25, 0xf8, + 0x0a, 0x27, 0xa3, 0x16, 0x94, 0xac, 0x1e, 0xb8, 0x6e, 0x00, + 0x05, + }; + + scoped_ptr<ProofVerifier> verifier( + CryptoTestUtils::ProofVerifierForTesting()); + + const string server_config = "server config bytes"; + const string hostname = "test.example.com"; + string error_details; + + vector<string> certs(2); + certs[0] = PEMCertFileToDER("quic_test_ecc.example.com.crt"); + certs[1] = PEMCertFileToDER("quic_intermediate.crt"); + + // Signatures are nondeterministic, so we test multiple signatures on the + // same server_config. + vector<string> signatures(3); + signatures[0].assign(reinterpret_cast<const char*>(signature_data_0), + sizeof(signature_data_0)); + signatures[1].assign(reinterpret_cast<const char*>(signature_data_1), + sizeof(signature_data_1)); + signatures[2].assign(reinterpret_cast<const char*>(signature_data_2), + sizeof(signature_data_2)); + + for (size_t i = 0; i < signatures.size(); i++) { + const string& signature = signatures[i]; + int rv; + TestCompletionCallback callback; + rv = verifier->VerifyProof(hostname, server_config, certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(OK, rv); + ASSERT_EQ("", error_details); + + rv = verifier->VerifyProof("foo.com", server_config, certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + rv = verifier->VerifyProof(hostname, server_config.substr(1, string::npos), + certs, signature, &error_details, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + // An ECDSA signature is DER-encoded. Corrupt the last byte so that the + // signature can still be DER-decoded correctly. + string corrupt_signature = signature; + corrupt_signature[corrupt_signature.size() - 1] += 1; + rv = verifier->VerifyProof(hostname, server_config, certs, + corrupt_signature, &error_details, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + // Prepending a "1" makes the DER invalid. + const string bad_der_signature1 = "1" + signature; + rv = verifier->VerifyProof(hostname, server_config, certs, + bad_der_signature1, &error_details, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + vector<string> wrong_certs; + for (size_t i = 1; i < certs.size(); i++) { + wrong_certs.push_back(certs[i]); + } + rv = verifier->VerifyProof("foo.com", server_config, wrong_certs, signature, + &error_details, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + } +} + } // namespace test } // namespace net diff --git a/net/quic/crypto/proof_verifier.h b/net/quic/crypto/proof_verifier.h index 29ed7ea..406f41e 100644 --- a/net/quic/crypto/proof_verifier.h +++ b/net/quic/crypto/proof_verifier.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "net/base/completion_callback.h" #include "net/base/net_export.h" namespace net { @@ -20,20 +21,23 @@ class NET_EXPORT_PRIVATE ProofVerifier { // VerifyProof checks that |signature| is a valid signature of // |server_config| by the public key in the leaf certificate of |certs|, and - // that |certs| is a valid chain for |hostname|. On success, it returns true. - // On failure, it returns false and sets |*error_details| to a description of - // the problem. + // that |certs| is a valid chain for |hostname|. On success, it returns OK. + // On failure, it returns ERR_FAILED and sets |*error_details| to a + // description of the problem. This function may also return ERR_IO_PENDING, + // in which case the |callback| will be run on the calling thread with the + // final OK/ERR_FAILED result when the proof is verified. // // The signature uses SHA-256 as the hash function and PSS padding in the // case of RSA. // // Note: this is just for testing. The CN of the certificate is ignored and // wildcards in the SANs are not supported. - virtual bool VerifyProof(const std::string& hostname, - const std::string& server_config, - const std::vector<std::string>& certs, - const std::string& signature, - std::string* error_details) const = 0; + virtual int VerifyProof(const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>& certs, + const std::string& signature, + std::string* error_details, + const CompletionCallback& callback) = 0; }; } // namespace net diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc new file mode 100644 index 0000000..7a764b6 --- /dev/null +++ b/net/quic/crypto/proof_verifier_chromium.cc @@ -0,0 +1,240 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/crypto/proof_verifier_chromium.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback_helpers.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "crypto/signature_verifier.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/cert/asn1_util.h" +#include "net/cert/cert_status_flags.h" +#include "net/cert/cert_verifier.h" +#include "net/cert/cert_verify_result.h" +#include "net/cert/single_request_cert_verifier.h" +#include "net/cert/x509_certificate.h" +#include "net/cert/x509_util.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/ssl/ssl_config_service.h" + +using base::StringPiece; +using base::StringPrintf; +using std::string; +using std::vector; + +namespace net { + +ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier, + const BoundNetLog& net_log) + : cert_verifier_(cert_verifier), + error_details_(NULL), + next_state_(STATE_NONE), + net_log_(net_log) { +} + +ProofVerifierChromium::~ProofVerifierChromium() { + verifier_.reset(); + + // Reset object state. + callback_.Reset(); + cert_verify_result_.Reset(); +} + +int ProofVerifierChromium::VerifyProof(const string& hostname, + const string& server_config, + const vector<string>& certs, + const string& signature, + std::string* error_details, + const CompletionCallback& callback) { + DCHECK(error_details); + error_details->clear(); + + DCHECK_EQ(STATE_NONE, next_state_); + if (STATE_NONE != next_state_) { + *error_details = "Certificate is already set and VerifyProof has begun"; + DLOG(WARNING) << *error_details; + return ERR_FAILED; + } + + if (certs.empty()) { + *error_details = "Failed to create certificate chain. Certs are empty."; + DLOG(WARNING) << *error_details; + return ERR_FAILED; + } + + // Convert certs to X509Certificate. + vector<StringPiece> cert_pieces(certs.size()); + for (unsigned i = 0; i < certs.size(); i++) { + cert_pieces[i] = base::StringPiece(certs[i]); + } + cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces); + if (!cert_.get()) { + cert_verify_result_.Reset(); + cert_verify_result_.cert_status = CERT_STATUS_INVALID; + *error_details = "Failed to create certificate chain"; + DLOG(WARNING) << *error_details; + return ERR_FAILED; + } + + // We call VerifySignature first to avoid copying of server_config and + // signature. + if (!VerifySignature(server_config, signature, certs[0])) { + *error_details = "Failed to verify signature of server config"; + DLOG(WARNING) << *error_details; + return ERR_FAILED; + } + + hostname_ = hostname; + callback_ = callback; + error_details_ = error_details; + + next_state_ = STATE_VERIFY_CERT; + return DoLoop(OK); +} + +int ProofVerifierChromium::DoLoop(int last_result) { + int rv = last_result; + do { + State state = next_state_; + next_state_ = STATE_NONE; + switch (state) { + case STATE_VERIFY_CERT: + DCHECK(rv == OK); + rv = DoVerifyCert(rv); + break; + case STATE_VERIFY_CERT_COMPLETE: + rv = DoVerifyCertComplete(rv); + break; + case STATE_NONE: + default: + rv = ERR_UNEXPECTED; + LOG(DFATAL) << "unexpected state " << state; + break; + } + } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); + return rv; +} + +void ProofVerifierChromium::OnIOComplete(int result) { + int rv = DoLoop(result); + if (rv != ERR_IO_PENDING) { + base::ResetAndReturn(&callback_).Run(rv); + } +} + +int ProofVerifierChromium::DoVerifyCert(int result) { + next_state_ = STATE_VERIFY_CERT_COMPLETE; + + int flags = 0; + verifier_.reset(new SingleRequestCertVerifier(cert_verifier_)); + return verifier_->Verify( + cert_.get(), + hostname_, + flags, + SSLConfigService::GetCRLSet().get(), + &cert_verify_result_, + base::Bind(&ProofVerifierChromium::OnIOComplete, + base::Unretained(this)), + net_log_); +} + +int ProofVerifierChromium::DoVerifyCertComplete(int result) { + verifier_.reset(); + + if (result <= ERR_FAILED) { + *error_details_ = StringPrintf("Failed to verify certificate chain: %s", + ErrorToString(result)); + DLOG(WARNING) << *error_details_; + result = ERR_FAILED; + } + + // Exit DoLoop and return the result to the caller to VerifyProof. + DCHECK_EQ(STATE_NONE, next_state_); + return result; +} + +bool ProofVerifierChromium::VerifySignature(const string& signed_data, + const string& signature, + const string& cert) { + StringPiece spki; + if (!asn1::ExtractSPKIFromDERCert(cert, &spki)) { + DLOG(WARNING) << "ExtractSPKIFromDERCert failed"; + return false; + } + + crypto::SignatureVerifier verifier; + + size_t size_bits; + X509Certificate::PublicKeyType type; + X509Certificate::GetPublicKeyInfo(cert_->os_cert_handle(), &size_bits, + &type); + if (type == X509Certificate::kPublicKeyTypeRSA) { + crypto::SignatureVerifier::HashAlgorithm hash_alg = + crypto::SignatureVerifier::SHA256; + crypto::SignatureVerifier::HashAlgorithm mask_hash_alg = hash_alg; + unsigned int hash_len = 32; // 32 is the length of a SHA-256 hash. + // TODO(wtc): change this to hash_len when we can change the wire format. + unsigned int salt_len = signature.size() - hash_len - 2; + + bool ok = verifier.VerifyInitRSAPSS( + hash_alg, mask_hash_alg, salt_len, + reinterpret_cast<const uint8*>(signature.data()), signature.size(), + reinterpret_cast<const uint8*>(spki.data()), spki.size()); + if (!ok) { + DLOG(WARNING) << "VerifyInitRSAPSS failed"; + return false; + } + } else if (type == X509Certificate::kPublicKeyTypeECDSA) { + // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT. + // RFC 5758: + // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } + // ... + // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or + // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field + // as an AlgorithmIdentifier, the encoding MUST omit the parameters + // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one + // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with- + // SHA384, or ecdsa-with-SHA512. + // See also RFC 5480, Appendix A. + static const uint8 kECDSAWithSHA256AlgorithmID[] = { + 0x30, 0x0a, + 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, + }; + + if (!verifier.VerifyInit( + kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID), + reinterpret_cast<const uint8*>(signature.data()), + signature.size(), + reinterpret_cast<const uint8*>(spki.data()), + spki.size())) { + DLOG(WARNING) << "VerifyInit failed"; + return false; + } + } else { + LOG(ERROR) << "Unsupported public key type " << type; + return false; + } + + verifier.VerifyUpdate(reinterpret_cast<const uint8*>(kProofSignatureLabel), + sizeof(kProofSignatureLabel)); + verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()), + signed_data.size()); + + if (!verifier.VerifyFinal()) { + DLOG(WARNING) << "VerifyFinal failed"; + return false; + } + + DLOG(INFO) << "VerifyFinal success"; + return true; +} + +} // namespace net diff --git a/net/quic/crypto/proof_verifier_chromium.h b/net/quic/crypto/proof_verifier_chromium.h new file mode 100644 index 0000000..2466507 --- /dev/null +++ b/net/quic/crypto/proof_verifier_chromium.h @@ -0,0 +1,89 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_ +#define NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" +#include "net/base/net_log.h" +#include "net/cert/cert_verify_result.h" +#include "net/cert/x509_certificate.h" +#include "net/quic/crypto/proof_verifier.h" + +namespace net { + +class BoundNetLog; +class CertVerifier; +class CertVerifyResult; +class SingleRequestCertVerifier; +class X509Certificate; + +// ProofVerifierChromium implements the QUIC ProofVerifier interface. +// TODO(rtenneti): Add support for multiple requests for one ProofVerifier. +class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier { + public: + explicit ProofVerifierChromium(CertVerifier* cert_verifier, + const BoundNetLog& net_log); + virtual ~ProofVerifierChromium(); + + // ProofVerifier interface + virtual int VerifyProof(const std::string& hostname, + const std::string& server_config, + const std::vector<std::string>& certs, + const std::string& signature, + std::string* error_details, + const CompletionCallback& callback) OVERRIDE; + + private: + enum State { + STATE_NONE, + STATE_VERIFY_CERT, + STATE_VERIFY_CERT_COMPLETE, + }; + + int DoLoop(int last_io_result); + void OnIOComplete(int result); + int DoVerifyCert(int result); + int DoVerifyCertComplete(int result); + + bool VerifySignature(const std::string& signed_data, + const std::string& signature, + const std::string& cert); + + // |cert_verifier_| and |verifier_| are used for verifying certificates. + CertVerifier* const cert_verifier_; + scoped_ptr<SingleRequestCertVerifier> verifier_; + + // |hostname| specifies the hostname for which |certs| is a valid chain. + std::string hostname_; + + CompletionCallback callback_; + + // The result of certificate verification. + CertVerifyResult cert_verify_result_; + std::string* error_details_; + + // X509Certificate from a chain of DER encoded certificates. + scoped_refptr<X509Certificate> cert_; + + // |generation_counter| passed to VerifyProof call. + uint64 generation_counter_; + + State next_state_; + + BoundNetLog net_log_; + + DISALLOW_COPY_AND_ASSIGN(ProofVerifierChromium); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_ diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index 674a441..f0ec9130 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_crypto_client_stream.h" +#include "net/base/completion_callback.h" +#include "net/base/net_errors.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_utils.h" #include "net/quic/crypto/null_encrypter.h" @@ -18,10 +20,12 @@ QuicCryptoClientStream::QuicCryptoClientStream( QuicSession* session, QuicCryptoClientConfig* crypto_config) : QuicCryptoStream(session), + weak_factory_(this), next_state_(STATE_IDLE), num_client_hellos_(0), crypto_config_(crypto_config), - server_hostname_(server_hostname) { + server_hostname_(server_hostname), + generation_counter_(0) { } QuicCryptoClientStream::~QuicCryptoClientStream() { @@ -29,12 +33,12 @@ QuicCryptoClientStream::~QuicCryptoClientStream() { void QuicCryptoClientStream::OnHandshakeMessage( const CryptoHandshakeMessage& message) { - DoHandshakeLoop(&message); + DoHandshakeLoop(&message, OK); } bool QuicCryptoClientStream::CryptoConnect() { next_state_ = STATE_SEND_CHLO; - DoHandshakeLoop(NULL); + DoHandshakeLoop(NULL, OK); return true; } @@ -50,7 +54,8 @@ int QuicCryptoClientStream::num_sent_client_hellos() const { static const int kMaxClientHellos = 3; void QuicCryptoClientStream::DoHandshakeLoop( - const CryptoHandshakeMessage* in) { + const CryptoHandshakeMessage* in, + int result) { CryptoHandshakeMessage out; QuicErrorCode error; string error_details; @@ -66,6 +71,8 @@ void QuicCryptoClientStream::DoHandshakeLoop( next_state_ = STATE_IDLE; switch (state) { case STATE_SEND_CHLO: { + // Send the subsequent client hello in plaintext. + session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE); if (num_client_hellos_ > kMaxClientHellos) { CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS); return; @@ -122,6 +129,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } case STATE_RECV_REJ: + DCHECK_EQ(OK, result); // We sent a dummy CHLO because we didn't have enough information to // perform a handshake, or we sent a full hello that the server // rejected. Here we hope to have a REJ that contains the information @@ -139,30 +147,54 @@ void QuicCryptoClientStream::DoHandshakeLoop( return; } if (!cached->proof_valid()) { - const ProofVerifier* verifier = crypto_config_->proof_verifier(); + ProofVerifier* verifier = session()->proof_verifier(); if (!verifier) { // If no verifier is set then we don't check the certificates. cached->SetProofValid(); } else if (!cached->signature().empty()) { - // TODO(rtenneti): In Chromium, we will need to make VerifyProof() - // asynchronous. - if (!verifier->VerifyProof(server_hostname_, - cached->server_config(), - cached->certs(), - cached->signature(), - &error_details)) { - CloseConnectionWithDetails(QUIC_PROOF_INVALID, - "Proof invalid: " + error_details); - return; - } - cached->SetProofValid(); + next_state_ = STATE_VERIFY_PROOF; + continue; } } - // Send the subsequent client hello in plaintext. - session()->connection()->SetDefaultEncryptionLevel( - ENCRYPTION_NONE); next_state_ = STATE_SEND_CHLO; break; + case STATE_VERIFY_PROOF: { + ProofVerifier* verifier = session()->proof_verifier(); + DCHECK(verifier); + next_state_ = STATE_VERIFY_PROOF_COMPLETED; + generation_counter_ = cached->generation_counter(); + result = verifier->VerifyProof( + server_hostname_, + cached->server_config(), + cached->certs(), + cached->signature(), + &error_details_, + base::Bind(&QuicCryptoClientStream::OnVerifyProofComplete, + weak_factory_.GetWeakPtr())); + if (result == ERR_IO_PENDING) { + DVLOG(1) << "Doing VerifyProof"; + return; + } + break; + } + case STATE_VERIFY_PROOF_COMPLETED: { + if (result != OK) { + CloseConnectionWithDetails( + QUIC_PROOF_INVALID, "Proof invalid: " + error_details_); + return; + } + ProofVerifier* verifier = session()->proof_verifier(); + DCHECK(verifier); + // Check if generation_counter has changed between STATE_VERIFY_PROOF + // and STATE_VERIFY_PROOF_COMPLETED state changes. + if (generation_counter_ != cached->generation_counter()) { + next_state_ = STATE_VERIFY_PROOF; + continue; + } + cached->SetProofValid(); + next_state_ = STATE_SEND_CHLO; + break; + } case STATE_RECV_SHLO: { // We sent a CHLO that we expected to be accepted and now we're hoping // for a SHLO from the server to confirm that. @@ -232,4 +264,10 @@ void QuicCryptoClientStream::DoHandshakeLoop( } } +void QuicCryptoClientStream::OnVerifyProofComplete(int result) { + DCHECK_EQ(STATE_VERIFY_PROOF_COMPLETED, next_state_); + DVLOG(1) << "VerifyProof completed: " << result; + DoHandshakeLoop(NULL, result); +} + } // namespace net diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h index e85a764..86633da 100644 --- a/net/quic/quic_crypto_client_stream.h +++ b/net/quic/quic_crypto_client_stream.h @@ -47,12 +47,18 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { STATE_IDLE, STATE_SEND_CHLO, STATE_RECV_REJ, + STATE_VERIFY_PROOF, + STATE_VERIFY_PROOF_COMPLETED, STATE_RECV_SHLO, }; // DoHandshakeLoop performs a step of the handshake state machine. Note that // |in| is NULL for the first call. - void DoHandshakeLoop(const CryptoHandshakeMessage* in); + void DoHandshakeLoop(const CryptoHandshakeMessage* in, int result); + + void OnVerifyProofComplete(int result); + + base::WeakPtrFactory<QuicCryptoClientStream> weak_factory_; State next_state_; // num_client_hellos_ contains the number of client hello messages that this @@ -66,6 +72,12 @@ class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { // Server's hostname std::string server_hostname_; + // Generation counter from QuicCryptoClientConfig's CachedState. + uint64 generation_counter_; + + // Error details for ProofVerifier's VerifyProof call. + std::string error_details_; + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream); }; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 3df8b0a..b6c3f10 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -5,6 +5,7 @@ #include "net/quic/quic_session.h" #include "base/stl_util.h" +#include "net/quic/crypto/proof_verifier.h" #include "net/quic/quic_connection.h" using base::StringPiece; @@ -257,6 +258,16 @@ void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) { } } +// TODO(rtenneti): Don't port proof_verifier code back to google3 until we have +// a way to have single ProofVerifier that can handle multiple requests. +ProofVerifier* QuicSession::proof_verifier() const { + return proof_verifier_.get(); +} + +void QuicSession::SetProofVerifier(ProofVerifier* verifier) { + proof_verifier_.reset(verifier); +} + QuicConfig* QuicSession::config() { return &config_; } diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h index 31a05fe..bd3f604 100644 --- a/net/quic/quic_session.h +++ b/net/quic/quic_session.h @@ -23,6 +23,7 @@ namespace net { +class ProofVerifier; class QuicCryptoStream; class ReliableQuicStream; class VisitorShim; @@ -104,6 +105,14 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { // Servers will simply call it once with HANDSHAKE_CONFIRMED. virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event); + virtual ProofVerifier* proof_verifier() const; + + // SetProofVerifier takes ownership of a |ProofVerifier| that clients are + // free to use in order to verify certificate chains from servers. If a + // ProofVerifier is set then the client will request a certificate chain from + // the server. + virtual void SetProofVerifier(ProofVerifier* verifier); + // Returns mutable config for this session. Returned config is owned // by QuicSession. QuicConfig* config(); @@ -214,6 +223,7 @@ class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface { QuicSpdyCompressor compressor_; QuicConfig config_; + scoped_ptr<ProofVerifier> proof_verifier_; // Returns the maximum number of streams this connection can open. size_t max_open_streams_; diff --git a/net/quic/test_tools/crypto_test_utils_chromium.cc b/net/quic/test_tools/crypto_test_utils_chromium.cc new file mode 100644 index 0000000..5a3212ae --- /dev/null +++ b/net/quic/test_tools/crypto_test_utils_chromium.cc @@ -0,0 +1,48 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/test_tools/crypto_test_utils.h" + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/test_data_directory.h" +#include "net/cert/cert_verifier.h" +#include "net/cert/test_root_certs.h" +#include "net/cert/x509_certificate.h" +#include "net/quic/crypto/proof_verifier_chromium.h" +#include "net/test/cert_test_util.h" + +namespace net { + +namespace test { + +class TestProofVerifierChromium : public ProofVerifierChromium { + public: + TestProofVerifierChromium(CertVerifier* cert_verifier, + const std::string& cert_file) + : ProofVerifierChromium(cert_verifier, BoundNetLog()), + cert_verifier_(cert_verifier) { + // Load and install the root for the validated chain. + scoped_refptr<X509Certificate> root_cert = + ImportCertFromFile(GetTestCertsDirectory(), cert_file); + scoped_root_.Reset(root_cert.get()); + } + virtual ~TestProofVerifierChromium() { } + + private: + ScopedTestRoot scoped_root_; + scoped_ptr<CertVerifier> cert_verifier_; +}; + +// static +ProofVerifier* CryptoTestUtils::ProofVerifierForTesting() { + TestProofVerifierChromium* proof_verifier = new TestProofVerifierChromium( + CertVerifier::CreateDefault(), "quic_proof_verify.crt"); + return proof_verifier; +} + +} // namespace test + +} // namespace net |