mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1172785 - RTCCertificate implementation, r=rbarnes
This commit is contained in:
parent
3313286ea1
commit
35c3c246af
1
config/external/nss/nss.def
vendored
1
config/external/nss/nss.def
vendored
@ -23,6 +23,7 @@ CERT_CacheOCSPResponseFromSideChannel
|
||||
CERT_CertChainFromCert
|
||||
CERT_CertificateRequestTemplate DATA
|
||||
CERT_CertificateTemplate DATA
|
||||
CERT_CertListFromCert
|
||||
CERT_ChangeCertTrust
|
||||
CERT_CheckCertUsage
|
||||
CERT_CheckCertValidTimes
|
||||
|
@ -43,6 +43,8 @@ enum StructuredCloneTags {
|
||||
|
||||
SCTAG_DOM_NFC_NDEF,
|
||||
|
||||
SCTAG_DOM_RTC_CERTIFICATE,
|
||||
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
||||
|
@ -61,6 +61,8 @@
|
||||
#ifdef MOZ_NFC
|
||||
#include "mozilla/dom/MozNDEFRecord.h"
|
||||
#endif // MOZ_NFC
|
||||
#include "mozilla/dom/RTCCertificate.h"
|
||||
#include "mozilla/dom/RTCCertificateBinding.h"
|
||||
#include "mozilla/dom/StructuredClone.h"
|
||||
#include "mozilla/dom/SubtleCryptoBinding.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
@ -2547,6 +2549,25 @@ NS_DOMReadStructuredClone(JSContext* cx,
|
||||
#endif
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
|
||||
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
|
||||
if (!global) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Prevent the return value from being trashed by a GC during ~nsRefPtr.
|
||||
JS::Rooted<JSObject*> result(cx);
|
||||
{
|
||||
nsRefPtr<RTCCertificate> cert = new RTCCertificate(global);
|
||||
if (!cert->ReadStructuredClone(reader)) {
|
||||
result = nullptr;
|
||||
} else {
|
||||
result = cert->WrapObject(cx, nullptr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Don't know what this is. Bail.
|
||||
xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return nullptr;
|
||||
@ -2571,6 +2592,13 @@ NS_DOMWriteStructuredClone(JSContext* cx,
|
||||
key->WriteStructuredClone(writer);
|
||||
}
|
||||
|
||||
// Handle WebRTC Certificate cloning
|
||||
RTCCertificate* cert;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, obj, cert))) {
|
||||
return JS_WriteUint32Pair(writer, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
|
||||
cert->WriteStructuredClone(writer);
|
||||
}
|
||||
|
||||
if (xpc::IsReflector(obj)) {
|
||||
nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
|
||||
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
|
||||
|
@ -161,15 +161,21 @@ ReadBuffer(JSStructuredCloneReader* aReader, CryptoBuffer& aBuffer)
|
||||
}
|
||||
|
||||
inline bool
|
||||
WriteBuffer(JSStructuredCloneWriter* aWriter, const CryptoBuffer& aBuffer)
|
||||
WriteBuffer(JSStructuredCloneWriter* aWriter, const uint8_t* aBuffer, size_t aLength)
|
||||
{
|
||||
bool ret = JS_WriteUint32Pair(aWriter, aBuffer.Length(), 0);
|
||||
if (ret && aBuffer.Length() > 0) {
|
||||
ret = JS_WriteBytes(aWriter, aBuffer.Elements(), aBuffer.Length());
|
||||
bool ret = JS_WriteUint32Pair(aWriter, aLength, 0);
|
||||
if (ret && aLength > 0) {
|
||||
ret = JS_WriteBytes(aWriter, aBuffer, aLength);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool
|
||||
WriteBuffer(JSStructuredCloneWriter* aWriter, const CryptoBuffer& aBuffer)
|
||||
{
|
||||
return WriteBuffer(aWriter, aBuffer.Elements(), aBuffer.Length());
|
||||
}
|
||||
|
||||
inline CK_MECHANISM_TYPE
|
||||
MapAlgorithmNameToMechanism(const nsString& aName)
|
||||
{
|
||||
|
@ -2163,251 +2163,239 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class GenerateAsymmetricKeyTask : public WebCryptoTask
|
||||
GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
|
||||
JSContext* aCx, const ObjectOrString& aAlgorithm, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
{
|
||||
public:
|
||||
GenerateAsymmetricKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
{
|
||||
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
if (!global) {
|
||||
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
if (!global) {
|
||||
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
mArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||||
if (!mArena) {
|
||||
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
mArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||||
if (!mArena) {
|
||||
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an empty key and set easy attributes
|
||||
mKeyPair.mPrivateKey = new CryptoKey(global);
|
||||
mKeyPair.mPublicKey = new CryptoKey(global);
|
||||
// Create an empty key and set easy attributes
|
||||
mKeyPair.mPrivateKey = new CryptoKey(global);
|
||||
mKeyPair.mPublicKey = new CryptoKey(global);
|
||||
|
||||
// Extract algorithm name
|
||||
nsString algName;
|
||||
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
|
||||
// Extract algorithm name
|
||||
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct an appropriate KeyAlorithm
|
||||
uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
|
||||
if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
RootedDictionary<RsaHashedKeyGenParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct an appropriate KeyAlorithm
|
||||
uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
|
||||
if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
|
||||
algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
RootedDictionary<RsaHashedKeyGenParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
// Pull relevant info
|
||||
uint32_t modulusLength = params.mModulusLength;
|
||||
CryptoBuffer publicExponent;
|
||||
ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
|
||||
nsString hashName;
|
||||
mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull relevant info
|
||||
uint32_t modulusLength = params.mModulusLength;
|
||||
CryptoBuffer publicExponent;
|
||||
ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
|
||||
nsString hashName;
|
||||
mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
// Create algorithm
|
||||
if (!mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(mAlgName,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hashName)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(mAlgName,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hashName)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
|
||||
|
||||
// Create algorithm
|
||||
if (!mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(algName,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hashName)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(algName,
|
||||
modulusLength,
|
||||
publicExponent,
|
||||
hashName)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
|
||||
// Set up params struct
|
||||
mRsaParams.keySizeInBits = modulusLength;
|
||||
bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
|
||||
if (!converted) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return;
|
||||
}
|
||||
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
|
||||
RootedDictionary<EcKeyGenParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up params struct
|
||||
mRsaParams.keySizeInBits = modulusLength;
|
||||
bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
|
||||
if (!converted) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
return;
|
||||
}
|
||||
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
|
||||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
|
||||
RootedDictionary<EcKeyGenParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
|
||||
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create algorithm.
|
||||
mKeyPair.mPublicKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
|
||||
mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve);
|
||||
mMechanism = CKM_EC_KEY_PAIR_GEN;
|
||||
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
|
||||
RootedDictionary<DhKeyGenParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoBuffer prime;
|
||||
ATTEMPT_BUFFER_INIT(prime, params.mPrime);
|
||||
|
||||
CryptoBuffer generator;
|
||||
ATTEMPT_BUFFER_INIT(generator, params.mGenerator);
|
||||
|
||||
// Set up params.
|
||||
if (!prime.ToSECItem(mArena, &mDhParams.prime) ||
|
||||
!generator.ToSECItem(mArena, &mDhParams.base)) {
|
||||
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create algorithm.
|
||||
if (!mKeyPair.mPublicKey.get()->Algorithm().MakeDh(algName,
|
||||
prime,
|
||||
generator)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeDh(algName,
|
||||
prime,
|
||||
generator)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
mMechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
|
||||
} else {
|
||||
if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
|
||||
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set key usages.
|
||||
if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
|
||||
algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
|
||||
privateAllowedUsages = CryptoKey::SIGN;
|
||||
publicAllowedUsages = CryptoKey::VERIFY;
|
||||
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
|
||||
publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
|
||||
} else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
|
||||
algName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
|
||||
privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
|
||||
publicAllowedUsages = 0;
|
||||
// Create algorithm.
|
||||
mKeyPair.mPublicKey.get()->Algorithm().MakeEc(mAlgName, mNamedCurve);
|
||||
mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(mAlgName, mNamedCurve);
|
||||
mMechanism = CKM_EC_KEY_PAIR_GEN;
|
||||
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
|
||||
RootedDictionary<DhKeyGenParams> params(aCx);
|
||||
mEarlyRv = Coerce(aCx, params, aAlgorithm);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable);
|
||||
mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
|
||||
CryptoBuffer prime;
|
||||
ATTEMPT_BUFFER_INIT(prime, params.mPrime);
|
||||
|
||||
mKeyPair.mPublicKey.get()->SetExtractable(true);
|
||||
mKeyPair.mPublicKey.get()->SetType(CryptoKey::PUBLIC);
|
||||
CryptoBuffer generator;
|
||||
ATTEMPT_BUFFER_INIT(generator, params.mGenerator);
|
||||
|
||||
mKeyPair.mPrivateKey.get()->ClearUsages();
|
||||
mKeyPair.mPublicKey.get()->ClearUsages();
|
||||
for (uint32_t i=0; i < aKeyUsages.Length(); ++i) {
|
||||
mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i],
|
||||
privateAllowedUsages);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
|
||||
publicAllowedUsages);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
// Set up params.
|
||||
if (!prime.ToSECItem(mArena, &mDhParams.prime) ||
|
||||
!generator.ToSECItem(mArena, &mDhParams.base)) {
|
||||
mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// If no usages ended up being allowed, DataError
|
||||
if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
|
||||
!mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
|
||||
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
|
||||
// Create algorithm.
|
||||
if (!mKeyPair.mPublicKey.get()->Algorithm().MakeDh(mAlgName,
|
||||
prime,
|
||||
generator)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
if (!mKeyPair.mPrivateKey.get()->Algorithm().MakeDh(mAlgName,
|
||||
prime,
|
||||
generator)) {
|
||||
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
|
||||
return;
|
||||
}
|
||||
mMechanism = CKM_DH_PKCS_KEY_PAIR_GEN;
|
||||
} else {
|
||||
mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set key usages.
|
||||
if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
|
||||
privateAllowedUsages = CryptoKey::SIGN;
|
||||
publicAllowedUsages = CryptoKey::VERIFY;
|
||||
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
|
||||
publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
|
||||
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_DH)) {
|
||||
privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
|
||||
publicAllowedUsages = 0;
|
||||
}
|
||||
|
||||
mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable);
|
||||
mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
|
||||
|
||||
mKeyPair.mPublicKey.get()->SetExtractable(true);
|
||||
mKeyPair.mPublicKey.get()->SetType(CryptoKey::PUBLIC);
|
||||
|
||||
mKeyPair.mPrivateKey.get()->ClearUsages();
|
||||
mKeyPair.mPublicKey.get()->ClearUsages();
|
||||
for (uint32_t i=0; i < aKeyUsages.Length(); ++i) {
|
||||
mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i],
|
||||
privateAllowedUsages);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i],
|
||||
publicAllowedUsages);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPLArenaPool mArena;
|
||||
CryptoKeyPair mKeyPair;
|
||||
CK_MECHANISM_TYPE mMechanism;
|
||||
PK11RSAGenParams mRsaParams;
|
||||
SECKEYDHParams mDhParams;
|
||||
ScopedSECKEYPublicKey mPublicKey;
|
||||
ScopedSECKEYPrivateKey mPrivateKey;
|
||||
nsString mNamedCurve;
|
||||
|
||||
virtual void ReleaseNSSResources() override
|
||||
{
|
||||
mPublicKey.dispose();
|
||||
mPrivateKey.dispose();
|
||||
// If no usages ended up being allowed, DataError
|
||||
if (!mKeyPair.mPublicKey.get()->HasAnyUsage() &&
|
||||
!mKeyPair.mPrivateKey.get()->HasAnyUsage()) {
|
||||
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
virtual nsresult DoCrypto() override
|
||||
{
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
MOZ_ASSERT(slot.get());
|
||||
void
|
||||
GenerateAsymmetricKeyTask::ReleaseNSSResources()
|
||||
{
|
||||
mPublicKey.dispose();
|
||||
mPrivateKey.dispose();
|
||||
}
|
||||
|
||||
void* param;
|
||||
switch (mMechanism) {
|
||||
case CKM_RSA_PKCS_KEY_PAIR_GEN:
|
||||
param = &mRsaParams;
|
||||
break;
|
||||
case CKM_DH_PKCS_KEY_PAIR_GEN:
|
||||
param = &mDhParams;
|
||||
break;
|
||||
case CKM_EC_KEY_PAIR_GEN: {
|
||||
param = CreateECParamsForCurve(mNamedCurve, mArena);
|
||||
if (!param) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
break;
|
||||
nsresult
|
||||
GenerateAsymmetricKeyTask::DoCrypto()
|
||||
{
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
MOZ_ASSERT(slot.get());
|
||||
|
||||
void* param;
|
||||
switch (mMechanism) {
|
||||
case CKM_RSA_PKCS_KEY_PAIR_GEN:
|
||||
param = &mRsaParams;
|
||||
break;
|
||||
case CKM_DH_PKCS_KEY_PAIR_GEN:
|
||||
param = &mDhParams;
|
||||
break;
|
||||
case CKM_EC_KEY_PAIR_GEN: {
|
||||
param = CreateECParamsForCurve(mNamedCurve, mArena);
|
||||
if (!param) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
default:
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
break;
|
||||
}
|
||||
|
||||
SECKEYPublicKey* pubKey = nullptr;
|
||||
mPrivateKey = PK11_GenerateKeyPair(slot.get(), mMechanism, param, &pubKey,
|
||||
PR_FALSE, PR_FALSE, nullptr);
|
||||
mPublicKey = pubKey;
|
||||
if (!mPrivateKey.get() || !mPublicKey.get()) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey);
|
||||
mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey);
|
||||
|
||||
// PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
|
||||
// private key, we need this later when exporting to PKCS8 and JWK though.
|
||||
if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
|
||||
nsresult rv = mKeyPair.mPrivateKey->AddPublicKeyData(mPublicKey);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
default:
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
virtual void Resolve() override
|
||||
{
|
||||
mResultPromise->MaybeResolve(mKeyPair);
|
||||
SECKEYPublicKey* pubKey = nullptr;
|
||||
mPrivateKey = PK11_GenerateKeyPair(slot.get(), mMechanism, param, &pubKey,
|
||||
PR_FALSE, PR_FALSE, nullptr);
|
||||
mPublicKey = pubKey;
|
||||
if (!mPrivateKey.get() || !mPublicKey.get()) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
};
|
||||
|
||||
mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey);
|
||||
mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey);
|
||||
|
||||
// PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
|
||||
// private key, we need this later when exporting to PKCS8 and JWK though.
|
||||
if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
|
||||
nsresult rv = mKeyPair.mPrivateKey->AddPublicKeyData(mPublicKey);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GenerateAsymmetricKeyTask::Resolve()
|
||||
{
|
||||
mResultPromise->MaybeResolve(mKeyPair);
|
||||
}
|
||||
|
||||
class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask
|
||||
{
|
||||
|
@ -205,6 +205,31 @@ protected:
|
||||
virtual void CallCallback(nsresult rv) override final;
|
||||
};
|
||||
|
||||
// XXX This class is declared here (unlike others) to enable reuse by WebRTC.
|
||||
class GenerateAsymmetricKeyTask : public WebCryptoTask
|
||||
{
|
||||
public:
|
||||
GenerateAsymmetricKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm, bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages);
|
||||
protected:
|
||||
ScopedPLArenaPool mArena;
|
||||
CryptoKeyPair mKeyPair;
|
||||
nsString mAlgName;
|
||||
CK_MECHANISM_TYPE mMechanism;
|
||||
PK11RSAGenParams mRsaParams;
|
||||
SECKEYDHParams mDhParams;
|
||||
nsString mNamedCurve;
|
||||
|
||||
virtual void ReleaseNSSResources() override;
|
||||
virtual nsresult DoCrypto() override;
|
||||
virtual void Resolve() override;
|
||||
|
||||
private:
|
||||
ScopedSECKEYPublicKey mPublicKey;
|
||||
ScopedSECKEYPrivateKey mPrivateKey;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
443
dom/media/webrtc/RTCCertificate.cpp
Normal file
443
dom/media/webrtc/RTCCertificate.cpp
Normal file
@ -0,0 +1,443 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/RTCCertificate.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "cert.h"
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/dom/CryptoKey.h"
|
||||
#include "mozilla/dom/RTCCertificateBinding.h"
|
||||
#include "mozilla/dom/WebCryptoCommon.h"
|
||||
#include "mozilla/dom/WebCryptoTask.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#define RTCCERTIFICATE_SC_VERSION 0x00000001
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCCertificate, mGlobal)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCCertificate)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCCertificate)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCCertificate)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// Note: explicit casts necessary to avoid
|
||||
// warning C4307: '*' : integral constant overflow
|
||||
#define ONE_DAY PRTime(PR_USEC_PER_SEC) * PRTime(60) /*sec*/ \
|
||||
* PRTime(60) /*min*/ * PRTime(24) /*hours*/
|
||||
#define EXPIRATION_DEFAULT ONE_DAY * PRTime(30)
|
||||
#define EXPIRATION_SLACK ONE_DAY
|
||||
#define EXPIRATION_MAX ONE_DAY * PRTime(365) /*year*/
|
||||
|
||||
const size_t RTCCertificateCommonNameLength = 16;
|
||||
const size_t RTCCertificateMinRsaSize = 1024;
|
||||
|
||||
class GenerateRTCCertificateTask : public GenerateAsymmetricKeyTask
|
||||
{
|
||||
public:
|
||||
GenerateRTCCertificateTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
: GenerateAsymmetricKeyTask(aCx, aAlgorithm, true, aKeyUsages),
|
||||
mExpires(0),
|
||||
mAuthType(ssl_kea_null),
|
||||
mCertificate(nullptr),
|
||||
mSignatureAlg(SEC_OID_UNKNOWN)
|
||||
{
|
||||
// Expiry is 30 days after by default.
|
||||
// This is a sort of arbitrary range designed to be valid
|
||||
// now with some slack in case the other side expects
|
||||
// some before expiry.
|
||||
//
|
||||
|
||||
mExpires = EXPIRATION_DEFAULT;
|
||||
if (!aAlgorithm.IsObject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the "expires" attribute from the algorithm dictionary. This is
|
||||
// (currently) non-standard; it exists to support testing of certificate
|
||||
// expiration, since one month is too long to wait for a test to run.
|
||||
JS::Rooted<JS::Value> exp(aCx, JS::UndefinedValue());
|
||||
JS::Rooted<JSObject*> jsval(aCx, aAlgorithm.GetAsObject());
|
||||
bool ok = JS_GetProperty(aCx, jsval, "expires", &exp);
|
||||
int64_t expval;
|
||||
if (ok) {
|
||||
ok = JS::ToInt64(aCx, exp, &expval);
|
||||
}
|
||||
if (ok && expval > 0) {
|
||||
mExpires = std::min(expval, EXPIRATION_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
PRTime mExpires;
|
||||
SSLKEAType mAuthType;
|
||||
ScopedCERTCertificate mCertificate;
|
||||
SECOidTag mSignatureAlg;
|
||||
|
||||
static CERTName* GenerateRandomName(PK11SlotInfo* aSlot)
|
||||
{
|
||||
uint8_t randomName[RTCCertificateCommonNameLength];
|
||||
SECStatus rv = PK11_GenerateRandomOnSlot(aSlot, randomName,
|
||||
sizeof(randomName));
|
||||
if (rv != SECSuccess) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char buf[sizeof(randomName) * 2 + 4];
|
||||
PL_strncpy(buf, "CN=", 3);
|
||||
for (size_t i = 0; i < sizeof(randomName); ++i) {
|
||||
PR_snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]);
|
||||
}
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
return CERT_AsciiToName(buf);
|
||||
}
|
||||
|
||||
nsresult GenerateCertificate()
|
||||
{
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
MOZ_ASSERT(slot.get());
|
||||
|
||||
ScopedCERTName subjectName(GenerateRandomName(slot.get()));
|
||||
if (!subjectName) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
ScopedSECKEYPublicKey publicKey(mKeyPair.mPublicKey.get()->GetPublicKey());
|
||||
ScopedCERTSubjectPublicKeyInfo spki(
|
||||
SECKEY_CreateSubjectPublicKeyInfo(publicKey));
|
||||
if (!spki) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
ScopedCERTCertificateRequest certreq(
|
||||
CERT_CreateCertificateRequest(subjectName, spki, nullptr));
|
||||
if (!certreq) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
PRTime now = PR_Now();
|
||||
PRTime notBefore = now - EXPIRATION_SLACK;
|
||||
mExpires += now;
|
||||
|
||||
ScopedCERTValidity validity(CERT_CreateValidity(notBefore, mExpires));
|
||||
if (!validity) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
unsigned long serial;
|
||||
// Note: This serial in principle could collide, but it's unlikely, and we
|
||||
// don't expect anyone to be validating certificates anyway.
|
||||
SECStatus rv =
|
||||
PK11_GenerateRandomOnSlot(slot,
|
||||
reinterpret_cast<unsigned char *>(&serial),
|
||||
sizeof(serial));
|
||||
if (rv != SECSuccess) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
CERTCertificate* cert = CERT_CreateCertificate(serial, subjectName,
|
||||
validity, certreq);
|
||||
if (!cert) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
mCertificate.reset(cert);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SignCertificate()
|
||||
{
|
||||
MOZ_ASSERT(mSignatureAlg != SEC_OID_UNKNOWN);
|
||||
PLArenaPool *arena = mCertificate->arena;
|
||||
|
||||
SECStatus rv = SECOID_SetAlgorithmID(arena, &mCertificate->signature,
|
||||
mSignatureAlg, nullptr);
|
||||
if (rv != SECSuccess) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
// Set version to X509v3.
|
||||
*(mCertificate->version.data) = SEC_CERTIFICATE_VERSION_3;
|
||||
mCertificate->version.len = 1;
|
||||
|
||||
SECItem innerDER = { siBuffer, nullptr, 0 };
|
||||
if (!SEC_ASN1EncodeItem(arena, &innerDER, mCertificate,
|
||||
SEC_ASN1_GET(CERT_CertificateTemplate))) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
SECItem *signedCert = PORT_ArenaZNew(arena, SECItem);
|
||||
if (!signedCert) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
ScopedSECKEYPrivateKey privateKey(mKeyPair.mPrivateKey.get()->GetPrivateKey());
|
||||
rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
|
||||
privateKey, mSignatureAlg);
|
||||
if (rv != SECSuccess) {
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR;
|
||||
}
|
||||
mCertificate->derCert = *signedCert;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BeforeCrypto() override
|
||||
{
|
||||
if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
|
||||
// Double check that size is OK.
|
||||
auto sz = static_cast<size_t>(mRsaParams.keySizeInBits);
|
||||
if (sz < RTCCertificateMinRsaSize) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
mSignatureAlg = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
|
||||
mAuthType = ssl_kea_rsa;
|
||||
|
||||
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
|
||||
// We only support good curves in WebCrypto.
|
||||
// If that ever changes, check that a good one was chosen.
|
||||
|
||||
mSignatureAlg = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE;
|
||||
mAuthType = ssl_kea_ecdh;
|
||||
} else {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult DoCrypto() override
|
||||
{
|
||||
nsresult rv = GenerateAsymmetricKeyTask::DoCrypto();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = GenerateCertificate();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = SignCertificate();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void Resolve() override
|
||||
{
|
||||
// Make copies of the private key and certificate, otherwise, when this
|
||||
// object is deleted, the structures they reference will be deleted too.
|
||||
SECKEYPrivateKey* key = mKeyPair.mPrivateKey.get()->GetPrivateKey();
|
||||
CERTCertificate* cert = CERT_DupCertificate(mCertificate);
|
||||
nsRefPtr<RTCCertificate> result =
|
||||
new RTCCertificate(mResultPromise->GetParentObject(),
|
||||
key, cert, mAuthType, mExpires);
|
||||
mResultPromise->MaybeResolve(result);
|
||||
}
|
||||
};
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RTCCertificate::GenerateCertificate(
|
||||
const GlobalObject& aGlobal, const ObjectOrString& aKeygenAlgorithm,
|
||||
ErrorResult& aRv, JSCompartment* aCompartment)
|
||||
{
|
||||
nsIGlobalObject* global = xpc::NativeGlobal(aGlobal.Get());
|
||||
nsRefPtr<Promise> p = Promise::Create(global, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
Sequence<nsString> usages;
|
||||
if (!usages.AppendElement(NS_LITERAL_STRING("sign"), fallible)) {
|
||||
return nullptr;
|
||||
}
|
||||
nsRefPtr<WebCryptoTask> task =
|
||||
new GenerateRTCCertificateTask(aGlobal.Context(),
|
||||
aKeygenAlgorithm, usages);
|
||||
task->DispatchWithPromise(p);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal)
|
||||
: mGlobal(aGlobal),
|
||||
mPrivateKey(nullptr),
|
||||
mCertificate(nullptr),
|
||||
mAuthType(ssl_kea_null),
|
||||
mExpires(0)
|
||||
{
|
||||
}
|
||||
|
||||
RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal,
|
||||
SECKEYPrivateKey* aPrivateKey,
|
||||
CERTCertificate* aCertificate,
|
||||
SSLKEAType aAuthType,
|
||||
PRTime aExpires)
|
||||
: mGlobal(aGlobal),
|
||||
mPrivateKey(aPrivateKey),
|
||||
mCertificate(aCertificate),
|
||||
mAuthType(aAuthType),
|
||||
mExpires(aExpires)
|
||||
{
|
||||
}
|
||||
|
||||
RTCCertificate::~RTCCertificate()
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return;
|
||||
}
|
||||
destructorSafeDestroyNSSReference();
|
||||
shutdown(calledFromObject);
|
||||
}
|
||||
|
||||
// This creates some interesting lifecycle consequences, since the DtlsIdentity
|
||||
// holds NSS objects, but does not implement nsNSSShutDownObject.
|
||||
|
||||
// Unfortunately, the code that uses DtlsIdentity cannot always use that lock
|
||||
// due to external linkage requirements. Therefore, the lock is held on this
|
||||
// object instead. Consequently, the DtlsIdentity that this method returns must
|
||||
// have a lifetime that is strictly shorter than the RTCCertificate.
|
||||
//
|
||||
// RTCPeerConnection provides this guarantee by holding a strong reference to
|
||||
// the RTCCertificate. It will cleanup any DtlsIdentity instances that it
|
||||
// creates before the RTCCertificate reference is released.
|
||||
RefPtr<DtlsIdentity>
|
||||
RTCCertificate::CreateDtlsIdentity() const
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown() || !mPrivateKey || !mCertificate) {
|
||||
return nullptr;
|
||||
}
|
||||
SECKEYPrivateKey* key = SECKEY_CopyPrivateKey(mPrivateKey);
|
||||
CERTCertificate* cert = CERT_DupCertificate(mCertificate);
|
||||
RefPtr<DtlsIdentity> id = new DtlsIdentity(key, cert, mAuthType);
|
||||
return id;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
RTCCertificate::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return RTCCertificateBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
RTCCertificate::virtualDestroyNSSReference()
|
||||
{
|
||||
destructorSafeDestroyNSSReference();
|
||||
}
|
||||
|
||||
void
|
||||
RTCCertificate::destructorSafeDestroyNSSReference()
|
||||
{
|
||||
mPrivateKey.dispose();
|
||||
mCertificate.dispose();
|
||||
}
|
||||
|
||||
bool
|
||||
RTCCertificate::WritePrivateKey(JSStructuredCloneWriter* aWriter,
|
||||
const nsNSSShutDownPreventionLock& aLockProof) const
|
||||
{
|
||||
JsonWebKey jwk;
|
||||
nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey, jwk, aLockProof);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
nsString json;
|
||||
if (!jwk.ToJSON(json)) {
|
||||
return false;
|
||||
}
|
||||
return WriteString(aWriter, json);
|
||||
}
|
||||
|
||||
bool
|
||||
RTCCertificate::WriteCertificate(JSStructuredCloneWriter* aWriter,
|
||||
const nsNSSShutDownPreventionLock& /*proof*/) const
|
||||
{
|
||||
ScopedCERTCertificateList certs(CERT_CertListFromCert(mCertificate.get()));
|
||||
if (!certs || certs->len <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (!JS_WriteUint32Pair(aWriter, certs->certs[0].len, 0)) {
|
||||
return false;
|
||||
}
|
||||
return JS_WriteBytes(aWriter, certs->certs[0].data, certs->certs[0].len);
|
||||
}
|
||||
|
||||
bool
|
||||
RTCCertificate::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown() || !mPrivateKey || !mCertificate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JS_WriteUint32Pair(aWriter, RTCCERTIFICATE_SC_VERSION, mAuthType) &&
|
||||
JS_WriteUint32Pair(aWriter, (mExpires >> 32) & 0xffffffff,
|
||||
mExpires & 0xffffffff) &&
|
||||
WritePrivateKey(aWriter, locker) &&
|
||||
WriteCertificate(aWriter, locker);
|
||||
}
|
||||
|
||||
bool
|
||||
RTCCertificate::ReadPrivateKey(JSStructuredCloneReader* aReader,
|
||||
const nsNSSShutDownPreventionLock& aLockProof)
|
||||
{
|
||||
nsString json;
|
||||
if (!ReadString(aReader, json)) {
|
||||
return false;
|
||||
}
|
||||
JsonWebKey jwk;
|
||||
if (!jwk.Init(json)) {
|
||||
return false;
|
||||
}
|
||||
mPrivateKey = CryptoKey::PrivateKeyFromJwk(jwk, aLockProof);
|
||||
return !!mPrivateKey;
|
||||
}
|
||||
|
||||
bool
|
||||
RTCCertificate::ReadCertificate(JSStructuredCloneReader* aReader,
|
||||
const nsNSSShutDownPreventionLock& /*proof*/)
|
||||
{
|
||||
CryptoBuffer cert;
|
||||
if (!ReadBuffer(aReader, cert) || cert.Length() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SECItem der = { siBuffer, cert.Elements(),
|
||||
static_cast<unsigned int>(cert.Length()) };
|
||||
mCertificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
|
||||
&der, nullptr, true, true);
|
||||
return !!mCertificate;
|
||||
}
|
||||
|
||||
bool
|
||||
RTCCertificate::ReadStructuredClone(JSStructuredCloneReader* aReader)
|
||||
{
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t version, authType;
|
||||
if (!JS_ReadUint32Pair(aReader, &version, &authType) ||
|
||||
version != RTCCERTIFICATE_SC_VERSION) {
|
||||
return false;
|
||||
}
|
||||
mAuthType = static_cast<SSLKEAType>(authType);
|
||||
|
||||
uint32_t high, low;
|
||||
if (!JS_ReadUint32Pair(aReader, &high, &low)) {
|
||||
return false;
|
||||
}
|
||||
mExpires = static_cast<PRTime>(high) << 32 | low;
|
||||
|
||||
return ReadPrivateKey(aReader, locker) &&
|
||||
ReadCertificate(aReader, locker);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
95
dom/media/webrtc/RTCCertificate.h
Normal file
95
dom/media/webrtc/RTCCertificate.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_RTCCertificate_h
|
||||
#define mozilla_dom_RTCCertificate_h
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsNSSShutDown.h"
|
||||
#include "prtime.h"
|
||||
#include "sslt.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/Date.h"
|
||||
#include "mozilla/dom/CryptoKey.h"
|
||||
#include "mtransport/dtlsidentity.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ObjectOrString;
|
||||
|
||||
class RTCCertificate final
|
||||
: public nsISupports,
|
||||
public nsWrapperCache,
|
||||
public nsNSSShutDownObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(RTCCertificate)
|
||||
|
||||
// WebIDL method that implements RTCPeerConnection.generateCertificate.
|
||||
static already_AddRefed<Promise> GenerateCertificate(
|
||||
const GlobalObject& global, const ObjectOrString& keygenAlgorithm,
|
||||
ErrorResult& aRv, JSCompartment* aCompartment = nullptr);
|
||||
|
||||
explicit RTCCertificate(nsIGlobalObject* aGlobal);
|
||||
RTCCertificate(nsIGlobalObject* aGlobal, SECKEYPrivateKey* aPrivateKey,
|
||||
CERTCertificate* aCertificate, SSLKEAType aAuthType,
|
||||
PRTime aExpires);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return mGlobal; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// WebIDL expires attribute. Note: JS dates are milliseconds since epoch;
|
||||
// NSPR PRTime is in microseconds since the same epoch.
|
||||
int64_t Expires() const { return mExpires / PR_USEC_PER_MSEC; }
|
||||
|
||||
// Accessors for use by PeerConnectionImpl.
|
||||
RefPtr<DtlsIdentity> CreateDtlsIdentity() const;
|
||||
CERTCertificate* Certificate() const { return mCertificate; }
|
||||
|
||||
// For nsNSSShutDownObject
|
||||
virtual void virtualDestroyNSSReference() override;
|
||||
void destructorSafeDestroyNSSReference();
|
||||
|
||||
// Structured clone methods
|
||||
bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const;
|
||||
bool ReadStructuredClone(JSStructuredCloneReader* aReader);
|
||||
|
||||
private:
|
||||
~RTCCertificate();
|
||||
void operator=(const RTCCertificate&) = delete;
|
||||
RTCCertificate(const RTCCertificate&) = delete;
|
||||
|
||||
bool ReadCertificate(JSStructuredCloneReader* aReader,
|
||||
const nsNSSShutDownPreventionLock& /*lockproof*/);
|
||||
bool ReadPrivateKey(JSStructuredCloneReader* aReader,
|
||||
const nsNSSShutDownPreventionLock& aLockProof);
|
||||
bool WriteCertificate(JSStructuredCloneWriter* aWriter,
|
||||
const nsNSSShutDownPreventionLock& /*lockproof*/) const;
|
||||
bool WritePrivateKey(JSStructuredCloneWriter* aWriter,
|
||||
const nsNSSShutDownPreventionLock& aLockProof) const;
|
||||
|
||||
nsRefPtr<nsIGlobalObject> mGlobal;
|
||||
ScopedSECKEYPrivateKey mPrivateKey;
|
||||
ScopedCERTCertificate mCertificate;
|
||||
SSLKEAType mAuthType;
|
||||
PRTime mExpires;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_RTCCertificate_h
|
Loading…
Reference in New Issue
Block a user