forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement ECDH for WebCrypto using BoringSSL (chromium-side).
BUG=399093 Review URL: https://codereview.chromium.org/749873002 Cr-Commit-Position: refs/heads/master@{#306112}
- Loading branch information
eroman
authored and
Commit bot
committed
Nov 28, 2014
1 parent
271679c
commit ed48e81
Showing
10 changed files
with
661 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// Copyright 2014 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 <openssl/ec.h> | ||
#include <openssl/ecdh.h> | ||
#include <openssl/evp.h> | ||
|
||
#include "base/logging.h" | ||
#include "content/child/webcrypto/algorithm_implementation.h" | ||
#include "content/child/webcrypto/crypto_data.h" | ||
#include "content/child/webcrypto/generate_key_result.h" | ||
#include "content/child/webcrypto/openssl/ec_key_openssl.h" | ||
#include "content/child/webcrypto/openssl/key_openssl.h" | ||
#include "content/child/webcrypto/openssl/util_openssl.h" | ||
#include "content/child/webcrypto/status.h" | ||
#include "content/child/webcrypto/webcrypto_util.h" | ||
#include "crypto/openssl_util.h" | ||
#include "crypto/scoped_openssl_types.h" | ||
#include "crypto/secure_util.h" | ||
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | ||
#include "third_party/WebKit/public/platform/WebCryptoKey.h" | ||
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | ||
|
||
namespace content { | ||
|
||
namespace webcrypto { | ||
|
||
namespace { | ||
|
||
// TODO(eroman): Support the "raw" format for ECDH key import + export, as | ||
// specified by WebCrypto spec. | ||
|
||
// TODO(eroman): Allow id-ecDH in SPKI and PKCS#8 import | ||
// (http://crbug.com/389400) | ||
|
||
class EcdhImplementation : public EcAlgorithm { | ||
public: | ||
EcdhImplementation() | ||
: EcAlgorithm(0, | ||
blink::WebCryptoKeyUsageDeriveKey | | ||
blink::WebCryptoKeyUsageDeriveBits) {} | ||
|
||
const char* GetJwkAlgorithm( | ||
const blink::WebCryptoNamedCurve curve) const override { | ||
// JWK import for ECDH does not enforce any required value for "alg". | ||
return ""; | ||
} | ||
|
||
Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm, | ||
const blink::WebCryptoKey& base_key, | ||
unsigned int length_bits, | ||
std::vector<uint8_t>* derived_bytes) const override { | ||
if (base_key.type() != blink::WebCryptoKeyTypePrivate) | ||
return Status::ErrorUnexpectedKeyType(); | ||
|
||
// Verify the "publicKey" parameter. The only guarantee from Blink is that | ||
// it is a valid WebCryptoKey, but it could be any type. | ||
const blink::WebCryptoKey& public_key = | ||
algorithm.ecdhKeyDeriveParams()->publicKey(); | ||
|
||
if (public_key.type() != blink::WebCryptoKeyTypePublic) | ||
return Status::ErrorEcdhPublicKeyWrongType(); | ||
|
||
// Make sure it is an EC key. | ||
if (!public_key.algorithm().ecParams()) | ||
return Status::ErrorEcdhPublicKeyWrongType(); | ||
|
||
// TODO(eroman): This is not described by the spec: | ||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27404 | ||
if (public_key.algorithm().id() != blink::WebCryptoAlgorithmIdEcdh) | ||
return Status::ErrorEcdhPublicKeyWrongAlgorithm(); | ||
|
||
// The public and private keys come from different key pairs, however their | ||
// curves must match. | ||
if (public_key.algorithm().ecParams()->namedCurve() != | ||
base_key.algorithm().ecParams()->namedCurve()) { | ||
return Status::ErrorEcdhCurveMismatch(); | ||
} | ||
|
||
// Handle the empty length case now to avoid calling an undefined | ||
// |&derived_bytes->front()| later. | ||
if (length_bits == 0) { | ||
derived_bytes->clear(); | ||
return Status::Success(); | ||
} | ||
|
||
crypto::ScopedEC_KEY public_key_ec( | ||
EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(public_key)->key())); | ||
|
||
const EC_POINT* public_key_point = | ||
EC_KEY_get0_public_key(public_key_ec.get()); | ||
|
||
crypto::ScopedEC_KEY private_key_ec( | ||
EVP_PKEY_get1_EC_KEY(AsymKeyOpenSsl::Cast(base_key)->key())); | ||
|
||
// The size of the shared secret is the field size in bytes (rounded up). | ||
// Note that, if rounding was required, the most significant bits of the | ||
// secret are zero. So for P-521, the maximum length is 528 bits, not 521. | ||
int field_size_bytes = | ||
(EC_GROUP_get_degree(EC_KEY_get0_group(private_key_ec.get())) + 7) / 8; | ||
|
||
if (length_bits > static_cast<unsigned int>(field_size_bytes * 8)) | ||
return Status::ErrorEcdhLengthTooBig(field_size_bytes * 8); | ||
|
||
// Resize to target length in bytes (BoringSSL can operate on a shorter | ||
// buffer than field_size_bytes). | ||
derived_bytes->resize((length_bits + 7) / 8); | ||
|
||
int result = | ||
ECDH_compute_key(&derived_bytes->front(), derived_bytes->size(), | ||
public_key_point, private_key_ec.get(), 0); | ||
if (result < 0 || static_cast<size_t>(result) != derived_bytes->size()) | ||
return Status::OperationError(); | ||
|
||
// Zero any "unused bits" in the resulting byte array. | ||
// TODO(eroman): This is not described by the spec: | ||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27402 | ||
unsigned int remainder_bits = length_bits % 8; | ||
if (remainder_bits) { | ||
(*derived_bytes)[derived_bytes->size() - 1] &= | ||
~((0xFF) >> remainder_bits); | ||
} | ||
|
||
return Status::Success(); | ||
} | ||
}; | ||
|
||
} // namespace | ||
|
||
AlgorithmImplementation* CreatePlatformEcdhImplementation() { | ||
return new EcdhImplementation; | ||
} | ||
|
||
} // namespace webcrypto | ||
|
||
} // namespace content |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2014 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 "base/stl_util.h" | ||
#include "content/child/webcrypto/algorithm_dispatch.h" | ||
#include "content/child/webcrypto/crypto_data.h" | ||
#include "content/child/webcrypto/jwk.h" | ||
#include "content/child/webcrypto/status.h" | ||
#include "content/child/webcrypto/test/test_helpers.h" | ||
#include "content/child/webcrypto/webcrypto_util.h" | ||
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | ||
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | ||
|
||
namespace content { | ||
|
||
namespace webcrypto { | ||
|
||
namespace { | ||
|
||
bool SupportsEcdh() { | ||
#if defined(USE_OPENSSL) | ||
return true; | ||
#else | ||
LOG(ERROR) << "Skipping ECDH test because unsupported"; | ||
return false; | ||
#endif | ||
} | ||
|
||
// TODO(eroman): Test passing an RSA public key instead of ECDH key. | ||
// TODO(eroman): Test passing an ECDSA public key | ||
|
||
blink::WebCryptoAlgorithm CreateEcdhImportAlgorithm( | ||
blink::WebCryptoNamedCurve named_curve) { | ||
return CreateEcImportAlgorithm(blink::WebCryptoAlgorithmIdEcdh, named_curve); | ||
} | ||
|
||
blink::WebCryptoAlgorithm CreateEcdhDeriveParams( | ||
const blink::WebCryptoKey& public_key) { | ||
return blink::WebCryptoAlgorithm::adoptParamsAndCreate( | ||
blink::WebCryptoAlgorithmIdEcdh, | ||
new blink::WebCryptoEcdhKeyDeriveParams(public_key)); | ||
} | ||
|
||
TEST(WebCryptoEcdhTest, VerifyKnownAnswer) { | ||
if (!SupportsEcdh()) | ||
return; | ||
|
||
scoped_ptr<base::ListValue> tests; | ||
ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests)); | ||
|
||
for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) { | ||
SCOPED_TRACE(test_index); | ||
|
||
const base::DictionaryValue* test; | ||
ASSERT_TRUE(tests->GetDictionary(test_index, &test)); | ||
|
||
// Import the public key. | ||
const base::DictionaryValue* public_key_json = NULL; | ||
EXPECT_TRUE(test->GetDictionary("public_key", &public_key_json)); | ||
blink::WebCryptoNamedCurve curve = | ||
GetCurveNameFromDictionary(public_key_json, "crv"); | ||
blink::WebCryptoKey public_key; | ||
ASSERT_EQ( | ||
Status::Success(), | ||
ImportKey(blink::WebCryptoKeyFormatJwk, | ||
CryptoData(MakeJsonVector(*public_key_json)), | ||
CreateEcdhImportAlgorithm(curve), true, 0, &public_key)); | ||
|
||
// Import the private key. | ||
const base::DictionaryValue* private_key_json = NULL; | ||
EXPECT_TRUE(test->GetDictionary("private_key", &private_key_json)); | ||
curve = GetCurveNameFromDictionary(private_key_json, "crv"); | ||
blink::WebCryptoKey private_key; | ||
ASSERT_EQ(Status::Success(), | ||
ImportKey(blink::WebCryptoKeyFormatJwk, | ||
CryptoData(MakeJsonVector(*private_key_json)), | ||
CreateEcdhImportAlgorithm(curve), true, | ||
blink::WebCryptoKeyUsageDeriveBits, &private_key)); | ||
|
||
// Now try to derive bytes. | ||
std::vector<uint8_t> derived_bytes; | ||
int length_bits = 0; | ||
ASSERT_TRUE(test->GetInteger("length_bits", &length_bits)); | ||
|
||
// If the test didn't specify an error, that implies it expects success. | ||
std::string expected_error = "Success"; | ||
test->GetString("error", &expected_error); | ||
|
||
Status status = DeriveBits(CreateEcdhDeriveParams(public_key), private_key, | ||
length_bits, &derived_bytes); | ||
ASSERT_EQ(expected_error, StatusToString(status)); | ||
if (status.IsError()) | ||
continue; | ||
|
||
std::vector<uint8_t> expected_bytes = | ||
GetBytesFromHexString(test, "derived_bytes"); | ||
|
||
EXPECT_EQ(CryptoData(expected_bytes), CryptoData(derived_bytes)); | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
} // namespace webcrypto | ||
|
||
} // namespace content |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.