Commit fcf6eb95 authored by hbos's avatar hbos Committed by Commit bot

RTCPeerConnection.generateCertificate taking AlgorithmIdentifier and using WebCrypto

Latest WebRTC draft @ generateCertificate:
http://w3c.github.io/webrtc-pc/#widl-RTCPeerConnection-generateCertificate-Promise-RTCCertificate--AlgorithmIdentifier-keygenAlgorithm

Note the corrections that has since been made resolving WebRTC & WebCrypto spec discrepencies (https://github.com/w3c/webrtc-pc/issues/310, https://github.com/w3c/webrtc-pc/issues/322), the unstable github latest version looks slightly different:
https://github.com/w3c/webrtc-pc/blob/master/webrtc.html
(More readable version: http://htmlpreview.github.io/?https://github.com/w3c/webrtc-pc/blob/master/webrtc.html)

This implementation is using WebCrypto and supports the AlgorithmIdentifier values required by the github version of the WebRTC spec:
{ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }
{ name: "ECDSA", namedCurve: "P-256" }

NOPRESUBMIT=true
BUG=544917

Review URL: https://codereview.chromium.org/1418113002

Cr-Commit-Position: refs/heads/master@{#357076}
parent 2a71fe7b
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "crypto/scoped_openssl_types.h" #include "crypto/scoped_openssl_types.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
#include "third_party/WebKit/public/platform/WebCryptoUtil.h"
namespace webcrypto { namespace webcrypto {
...@@ -104,26 +105,6 @@ Status ReadRsaKeyJwk(const CryptoData& key_data, ...@@ -104,26 +105,6 @@ Status ReadRsaKeyJwk(const CryptoData& key_data,
return Status::Success(); return Status::Success();
} }
// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros,
// to unsigned int.
bool BigIntegerToUint(const uint8_t* data,
size_t data_size,
unsigned int* result) {
if (data_size == 0)
return false;
*result = 0;
for (size_t i = 0; i < data_size; ++i) {
size_t reverse_i = data_size - i - 1;
if (reverse_i >= sizeof(*result) && data[i])
return false; // Too large for a uint.
*result |= data[i] << 8 * reverse_i;
}
return true;
}
// Creates a blink::WebCryptoAlgorithm having the modulus length and public // Creates a blink::WebCryptoAlgorithm having the modulus length and public
// exponent of |key|. // exponent of |key|.
Status CreateRsaHashedKeyAlgorithm( Status CreateRsaHashedKeyAlgorithm(
...@@ -298,10 +279,8 @@ Status RsaHashedAlgorithm::GenerateKey( ...@@ -298,10 +279,8 @@ Status RsaHashedAlgorithm::GenerateKey(
} }
unsigned int public_exponent = 0; unsigned int public_exponent = 0;
if (!BigIntegerToUint(params->publicExponent().data(), if (!blink::bigIntegerToUint(params->publicExponent(), public_exponent))
params->publicExponent().size(), &public_exponent)) {
return Status::ErrorGenerateKeyPublicExponent(); return Status::ErrorGenerateKeyPublicExponent();
}
// OpenSSL hangs when given bad public exponents. Use a whitelist. // OpenSSL hangs when given bad public exponents. Use a whitelist.
if (public_exponent != 3 && public_exponent != 65537) if (public_exponent != 3 && public_exponent != 65537)
......
...@@ -136,7 +136,7 @@ void RTCCertificateGenerator::generateCertificate( ...@@ -136,7 +136,7 @@ void RTCCertificateGenerator::generateCertificate(
const blink::WebURL& url, const blink::WebURL& url,
const blink::WebURL& first_party_for_cookies, const blink::WebURL& first_party_for_cookies,
blink::WebCallbacks<blink::WebRTCCertificate*, void>* observer) { blink::WebCallbacks<blink::WebRTCCertificate*, void>* observer) {
DCHECK(isValidKeyParams(key_params)); DCHECK(isSupportedKeyParams(key_params));
#if defined(ENABLE_WEBRTC) #if defined(ENABLE_WEBRTC)
const scoped_refptr<base::SingleThreadTaskRunner> main_thread = const scoped_refptr<base::SingleThreadTaskRunner> main_thread =
...@@ -159,7 +159,7 @@ void RTCCertificateGenerator::generateCertificate( ...@@ -159,7 +159,7 @@ void RTCCertificateGenerator::generateCertificate(
#endif #endif
} }
bool RTCCertificateGenerator::isValidKeyParams( bool RTCCertificateGenerator::isSupportedKeyParams(
const blink::WebRTCKeyParams& key_params) { const blink::WebRTCKeyParams& key_params) {
return WebRTCKeyParamsToKeyParams(key_params).IsValid(); return WebRTCKeyParamsToKeyParams(key_params).IsValid();
} }
......
...@@ -26,7 +26,7 @@ class RTCCertificateGenerator : public blink::WebRTCCertificateGenerator { ...@@ -26,7 +26,7 @@ class RTCCertificateGenerator : public blink::WebRTCCertificateGenerator {
const blink::WebURL& url, const blink::WebURL& url,
const blink::WebURL& first_party_for_cookies, const blink::WebURL& first_party_for_cookies,
blink::WebCallbacks<blink::WebRTCCertificate*, void>* observer) override; blink::WebCallbacks<blink::WebRTCCertificate*, void>* observer) override;
bool isValidKeyParams(const blink::WebRTCKeyParams& key_params) override; bool isSupportedKeyParams(const blink::WebRTCKeyParams& key_params) override;
private: private:
DISALLOW_COPY_AND_ASSIGN(RTCCertificateGenerator); DISALLOW_COPY_AND_ASSIGN(RTCCertificateGenerator);
......
...@@ -3,7 +3,7 @@ Tests RTCPeerConnection.generateCertificate RSA/ECDSA. ...@@ -3,7 +3,7 @@ Tests RTCPeerConnection.generateCertificate RSA/ECDSA.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: 65537 }) PASS generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" })
PASS certRSA is non-null. PASS certRSA is non-null.
PASS generateCertificate({ name: "ECDSA", namedCurve: "P-256" }) PASS generateCertificate({ name: "ECDSA", namedCurve: "P-256" })
PASS certECDSA is non-null. PASS certECDSA is non-null.
......
...@@ -16,8 +16,8 @@ var certECDSA = null; ...@@ -16,8 +16,8 @@ var certECDSA = null;
// 1: RSA-2048 using public exponent = 65537. // 1: RSA-2048 using public exponent = 65537.
function generate1RSA() function generate1RSA()
{ {
generateCallString = 'generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: 65537 })'; generateCallString = 'generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" })';
webkitRTCPeerConnection.generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: 65537 }) webkitRTCPeerConnection.generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" })
.then(generate1RSASuccessful, generate1RSAFailed); .then(generate1RSASuccessful, generate1RSAFailed);
} }
function generate1RSASuccessful(certificate) function generate1RSASuccessful(certificate)
......
...@@ -67,9 +67,10 @@ shouldThrow("new webkitRTCPeerConnection({iceServers:[], certificates:[1337]}, n ...@@ -67,9 +67,10 @@ shouldThrow("new webkitRTCPeerConnection({iceServers:[], certificates:[1337]}, n
// Global certificate variables so that the "should..." methods can evaluate them. // Global certificate variables so that the "should..." methods can evaluate them.
var certRSA = null; var certRSA = null;
var certECDSA = null; var certECDSA = null;
function testCertificates1RSA() function testCertificates1RSA()
{ {
webkitRTCPeerConnection.generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: 65537 }) webkitRTCPeerConnection.generateCertificate({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" })
.then(function(certificate) { .then(function(certificate) {
certRSA = certificate; certRSA = certificate;
shouldNotThrow('new webkitRTCPeerConnection({iceServers:[], certificates:[certRSA]}, null);'); shouldNotThrow('new webkitRTCPeerConnection({iceServers:[], certificates:[certRSA]}, null);');
......
...@@ -6,6 +6,7 @@ include_rules = [ ...@@ -6,6 +6,7 @@ include_rules = [
"+modules/EventModules.h", "+modules/EventModules.h",
"+modules/EventTargetModules.h", "+modules/EventTargetModules.h",
"+modules/ModulesExport.h", "+modules/ModulesExport.h",
"+modules/crypto",
"+modules/mediastream", "+modules/mediastream",
"+platform", "+platform",
"+public/platform", "+public/platform",
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "core/html/VoidCallback.h" #include "core/html/VoidCallback.h"
#include "core/loader/FrameLoader.h" #include "core/loader/FrameLoader.h"
#include "core/loader/FrameLoaderClient.h" #include "core/loader/FrameLoaderClient.h"
#include "modules/crypto/CryptoResultImpl.h"
#include "modules/mediastream/MediaConstraintsImpl.h" #include "modules/mediastream/MediaConstraintsImpl.h"
#include "modules/mediastream/MediaStreamEvent.h" #include "modules/mediastream/MediaStreamEvent.h"
#include "modules/mediastream/RTCDTMFSender.h" #include "modules/mediastream/RTCDTMFSender.h"
...@@ -60,6 +61,8 @@ ...@@ -60,6 +61,8 @@
#include "platform/mediastream/RTCConfiguration.h" #include "platform/mediastream/RTCConfiguration.h"
#include "platform/mediastream/RTCOfferOptions.h" #include "platform/mediastream/RTCOfferOptions.h"
#include "public/platform/Platform.h" #include "public/platform/Platform.h"
#include "public/platform/WebCryptoAlgorithmParams.h"
#include "public/platform/WebCryptoUtil.h"
#include "public/platform/WebMediaStream.h" #include "public/platform/WebMediaStream.h"
#include "public/platform/WebRTCCertificate.h" #include "public/platform/WebRTCCertificate.h"
#include "public/platform/WebRTCCertificateGenerator.h" #include "public/platform/WebRTCCertificateGenerator.h"
...@@ -461,48 +464,57 @@ void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dict ...@@ -461,48 +464,57 @@ void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dict
exceptionState.throwDOMException(SyntaxError, "Could not update the ICE Agent with the given configuration."); exceptionState.throwDOMException(SyntaxError, "Could not update the ICE Agent with the given configuration.");
} }
ScriptPromise RTCPeerConnection::generateCertificate(ScriptState* scriptState, const Dictionary& keygenAlgorithm, ExceptionState& exceptionState) ScriptPromise RTCPeerConnection::generateCertificate(ScriptState* scriptState, const AlgorithmIdentifier& keygenAlgorithm, ExceptionState& exceptionState)
{ {
// Validate and interpret input |keygenAlgorithm|. // Normalize |keygenAlgorithm| with WebCrypto, making sure it is a recognized AlgorithmIdentifier.
// TODO(hbos): Use WebCrypto normalization process to validate and interpret |keygenAlgorithm|. WebCryptoAlgorithm cryptoAlgorithm;
// This may create a dependency between the Blink and WebCrypto modules? crbug.com/544917 AlgorithmError error;
if (!normalizeAlgorithm(keygenAlgorithm, WebCryptoOperationGenerateKey, cryptoAlgorithm, &error)) {
// Reject generateCertificate with the same error as was produced by WebCrypto.
// |result| is garbage collected, no need to delete.
CryptoResultImpl* result = CryptoResultImpl::create(scriptState);
ScriptPromise promise = result->promise();
result->completeWithError(error.errorType, error.errorDetails);
return promise;
}
// Convert from WebCrypto representation to recognized WebRTCKeyParams. WebRTC supports a small subset of what are valid AlgorithmIdentifiers.
const char* unsupportedParamsString = "The 1st argument provided is an AlgorithmIdentifier with a supported algorithm name, but the parameters are not supported.";
Nullable<WebRTCKeyParams> keyParams; Nullable<WebRTCKeyParams> keyParams;
String name; switch (cryptoAlgorithm.id()) {
if (DictionaryHelper::get(keygenAlgorithm, "name", name)) { case WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
if (name == "RSASSA-PKCS1-v1_5") { // name: "RSASSA-PKCS1-v1_5"
// RSA - Supported |keygenAlgorithm|: unsigned publicExponent;
// { name: "RSASSA-PKCS1-v1_5", modulusLength: <int>, publicExponent: 65537 } // "publicExponent" must fit in an unsigned int. The only recognized "hash" is "SHA-256".
int modulusLength = -1; if (bigIntegerToUint(cryptoAlgorithm.rsaHashedKeyGenParams()->publicExponent(), publicExponent)
int publicExponent = -1; && cryptoAlgorithm.rsaHashedKeyGenParams()->hash().id() == WebCryptoAlgorithmIdSha256) {
if (DictionaryHelper::get(keygenAlgorithm, "modulusLength", modulusLength) unsigned modulusLength = cryptoAlgorithm.rsaHashedKeyGenParams()->modulusLengthBits();
&& modulusLength >= 0 keyParams.set(blink::WebRTCKeyParams::createRSA(modulusLength, publicExponent));
&& DictionaryHelper::get(keygenAlgorithm, "publicExponent", publicExponent) } else {
&& publicExponent >= 0) { return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, unsupportedParamsString));
keyParams.set(blink::WebRTCKeyParams::createRSA(modulusLength, publicExponent));
}
} else if (name == "ECDSA") {
// ECDSA - Supported |keygenAlgorithm|:
// { name: "ECDSA", namedCurve: "P-256" }
String namedCurve;
DictionaryHelper::get(keygenAlgorithm, "namedCurve", namedCurve);
if (namedCurve == "P-256") {
keyParams.set(blink::WebRTCKeyParams::createECDSA(WebRTCECCurveNistP256));
}
} }
break;
case WebCryptoAlgorithmIdEcdsa:
// name: "ECDSA"
// The only recognized "namedCurve" is "P-256".
if (cryptoAlgorithm.ecKeyGenParams()->namedCurve() == WebCryptoNamedCurveP256) {
keyParams.set(blink::WebRTCKeyParams::createECDSA(blink::WebRTCECCurveNistP256));
} else {
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, unsupportedParamsString));
}
break;
default:
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "The 1st argument provided is an AlgorithmIdentifier, but the algorithm is not supported."));
break;
} }
if (keyParams.isNull()) { ASSERT(!keyParams.isNull());
// Invalid argument.
return ScriptPromise::rejectWithDOMException(
scriptState, DOMException::create(InvalidAccessError, ExceptionMessages::argumentNullOrIncorrectType(1, "AlgorithmIdentifier")));
}
OwnPtr<WebRTCCertificateGenerator> certificateGenerator = adoptPtr( OwnPtr<WebRTCCertificateGenerator> certificateGenerator = adoptPtr(
Platform::current()->createRTCCertificateGenerator()); Platform::current()->createRTCCertificateGenerator());
// Check validity of |keyParams|. // |keyParams| was successfully constructed, but does the certificate generator support these parameters?
if (!certificateGenerator->isValidKeyParams(keyParams.get())) { if (!certificateGenerator->isSupportedKeyParams(keyParams.get())) {
return ScriptPromise::rejectWithDOMException( return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, unsupportedParamsString));
scriptState, DOMException::create(NotSupportedError, "The 1st argument provided is an AlgorithmIdentifier, but it has unsupported parameter values."));
} }
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptPromise.h"
#include "core/dom/ActiveDOMObject.h" #include "core/dom/ActiveDOMObject.h"
#include "modules/EventTargetModules.h" #include "modules/EventTargetModules.h"
#include "modules/crypto/NormalizeAlgorithm.h"
#include "modules/mediastream/MediaStream.h" #include "modules/mediastream/MediaStream.h"
#include "modules/mediastream/RTCIceCandidate.h" #include "modules/mediastream/RTCIceCandidate.h"
#include "platform/AsyncMethodRunner.h" #include "platform/AsyncMethodRunner.h"
...@@ -81,7 +82,9 @@ public: ...@@ -81,7 +82,9 @@ public:
void updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState&); void updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState&);
static ScriptPromise generateCertificate(ScriptState*, const Dictionary& keygenAlgorithm, ExceptionState&); // Certificate management
// http://w3c.github.io/webrtc-pc/#sec.cert-mgmt
static ScriptPromise generateCertificate(ScriptState*, const AlgorithmIdentifier& keygenAlgorithm, ExceptionState&);
// DEPRECATED // DEPRECATED
void addIceCandidate(RTCIceCandidate*, ExceptionState&); void addIceCandidate(RTCIceCandidate*, ExceptionState&);
......
...@@ -131,8 +131,7 @@ enum RTCIceConnectionState { ...@@ -131,8 +131,7 @@ enum RTCIceConnectionState {
attribute EventHandler onaddstream; attribute EventHandler onaddstream;
attribute EventHandler onremovestream; attribute EventHandler onremovestream;
// Certificate Management // Certificate management
// http://w3c.github.io/webrtc-pc/#sec.cert-mgmt // http://w3c.github.io/webrtc-pc/#sec.cert-mgmt
// TODO(hbos): Use AlgorithmIdentifier and the WebCryptoAPI normalization process. crbug.com/544917 [RuntimeEnabled=RTCCertificate, RaisesException, CallWith=ScriptState] static Promise<RTCCertificate> generateCertificate(AlgorithmIdentifier keygenAlgorithm);
[RuntimeEnabled=RTCCertificate, RaisesException, CallWith=ScriptState] static Promise<RTCCertificate> generateCertificate(Dictionary keygenAlgorithm);
}; };
...@@ -275,6 +275,7 @@ ...@@ -275,6 +275,7 @@
'exported/WebCryptoKey.cpp', 'exported/WebCryptoKey.cpp',
'exported/WebCryptoKeyAlgorithm.cpp', 'exported/WebCryptoKeyAlgorithm.cpp',
'exported/WebCryptoResult.cpp', 'exported/WebCryptoResult.cpp',
'exported/WebCryptoUtil.cpp',
'exported/WebCursorInfo.cpp', 'exported/WebCursorInfo.cpp',
'exported/WebData.cpp', 'exported/WebData.cpp',
'exported/WebDataConsumerHandle.cpp', 'exported/WebDataConsumerHandle.cpp',
......
// Copyright 2015 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 "config.h"
#include "public/platform/WebCryptoUtil.h"
namespace blink {
bool bigIntegerToUint(const WebVector<unsigned char>& bigInteger, unsigned& result)
{
result = 0;
for (size_t i = 0; i < bigInteger.size(); ++i) {
size_t iReversed = bigInteger.size() - i - 1;
if (iReversed >= sizeof(result) && bigInteger[i])
return false; // Too large for unsigned int.
result |= bigInteger[i] << 8 * iReversed;
}
return true;
}
} // namespace blink
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
"platform/WebCryptoKey.h", "platform/WebCryptoKey.h",
"platform/WebCryptoKeyAlgorithm.h", "platform/WebCryptoKeyAlgorithm.h",
"platform/WebCryptoKeyAlgorithmParams.h", "platform/WebCryptoKeyAlgorithmParams.h",
"platform/WebCryptoUtil.h",
"platform/WebCursorInfo.h", "platform/WebCursorInfo.h",
"platform/WebData.h", "platform/WebData.h",
"platform/WebDataConsumerHandle.h", "platform/WebDataConsumerHandle.h",
......
// Copyright 2015 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 WebCryptoUtil_h
#define WebCryptoUtil_h
#include "WebCommon.h"
#include "WebVector.h"
namespace blink {
// Converts the (big-endian) BigInteger to unsigned int. Returns true on success (if its value is not too large).
BLINK_PLATFORM_EXPORT bool bigIntegerToUint(const WebVector<unsigned char>& bigInteger, unsigned& result);
} // namespace blink
#endif // WebCryptoUtil_h
...@@ -51,10 +51,10 @@ public: ...@@ -51,10 +51,10 @@ public:
const WebURL& firstPartyForCookies, const WebURL& firstPartyForCookies,
WebCallbacks<WebRTCCertificate*, void>* observer) = 0; WebCallbacks<WebRTCCertificate*, void>* observer) = 0;
// Determines if the parameters should be considered valid for certificate generation. // Determines if the parameters are supported by generateCertificate.
// For example, if the number of bits of some parameter is too small or too large we // For example, if the number of bits of some parameter is too small or too large we
// may want to reject it for security or performance reasons. // may want to reject it for security or performance reasons.
virtual bool isValidKeyParams(const WebRTCKeyParams&) = 0; virtual bool isSupportedKeyParams(const WebRTCKeyParams&) = 0;
}; };
} // namespace blink } // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment