зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1034851 - Add wrapKey and unwrapKey methods to WebCrypto API r=bz
This commit is contained in:
Родитель
e3578f6893
Коммит
58860abf23
|
@ -42,7 +42,7 @@ SubtleCrypto::WrapObject(JSContext* aCx)
|
|||
if (aRv.Failed()) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
nsRefPtr<WebCryptoTask> task = WebCryptoTask::Operation ## Task(__VA_ARGS__); \
|
||||
nsRefPtr<WebCryptoTask> task = WebCryptoTask::Create ## Operation ## Task(__VA_ARGS__); \
|
||||
task->DispatchWithPromise(p); \
|
||||
return p.forget();
|
||||
|
||||
|
@ -148,5 +148,32 @@ SubtleCrypto::DeriveBits(JSContext* cx,
|
|||
SUBTLECRYPTO_METHOD_BODY(DeriveBits, aRv, cx, algorithm, baseKey, length)
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
SubtleCrypto::WrapKey(JSContext* cx,
|
||||
const nsAString& format,
|
||||
CryptoKey& key,
|
||||
CryptoKey& wrappingKey,
|
||||
const ObjectOrString& wrapAlgorithm,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
SUBTLECRYPTO_METHOD_BODY(WrapKey, aRv, cx, format, key, wrappingKey, wrapAlgorithm)
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
SubtleCrypto::UnwrapKey(JSContext* cx,
|
||||
const nsAString& format,
|
||||
const ArrayBufferViewOrArrayBuffer& wrappedKey,
|
||||
CryptoKey& unwrappingKey,
|
||||
const ObjectOrString& unwrapAlgorithm,
|
||||
const ObjectOrString& unwrappedKeyAlgorithm,
|
||||
bool extractable,
|
||||
const Sequence<nsString>& keyUsages,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
SUBTLECRYPTO_METHOD_BODY(UnwrapKey, aRv, cx, format, wrappedKey, unwrappingKey,
|
||||
unwrapAlgorithm, unwrappedKeyAlgorithm,
|
||||
extractable, keyUsages)
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -102,6 +102,23 @@ public:
|
|||
uint32_t length,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> WrapKey(JSContext* cx,
|
||||
const nsAString& format,
|
||||
CryptoKey& key,
|
||||
CryptoKey& wrappingKey,
|
||||
const ObjectOrString& wrapAlgorithm,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> UnwrapKey(JSContext* cx,
|
||||
const nsAString& format,
|
||||
const ArrayBufferViewOrArrayBuffer& wrappedKey,
|
||||
CryptoKey& unwrappingKey,
|
||||
const ObjectOrString& unwrapAlgorithm,
|
||||
const ObjectOrString& unwrappedKeyAlgorithm,
|
||||
bool extractable,
|
||||
const Sequence<nsString>& keyUsages,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,14 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
uint8_t*
|
||||
CryptoBuffer::Assign(const CryptoBuffer& aData)
|
||||
{
|
||||
// Same as in nsTArray_Impl::operator=, but return the value
|
||||
// returned from ReplaceElementsAt to enable OOM detection
|
||||
return ReplaceElementsAt(0, Length(), aData.Elements(), aData.Length());
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
CryptoBuffer::Assign(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@ class OwningArrayBufferViewOrArrayBuffer;
|
|||
class CryptoBuffer : public FallibleTArray<uint8_t>
|
||||
{
|
||||
public:
|
||||
uint8_t* Assign(const CryptoBuffer& aData);
|
||||
uint8_t* Assign(const uint8_t* aData, uint32_t aLength);
|
||||
uint8_t* Assign(const SECItem* aItem);
|
||||
uint8_t* Assign(const ArrayBuffer& aData);
|
||||
|
|
|
@ -299,17 +299,48 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class AesTask : public ReturnArrayBufferViewTask
|
||||
class DeferredData
|
||||
{
|
||||
public:
|
||||
template<class T>
|
||||
void SetData(const T& aData) {
|
||||
mDataIsSet = mData.Assign(aData);
|
||||
}
|
||||
|
||||
protected:
|
||||
DeferredData()
|
||||
: mDataIsSet(false)
|
||||
{}
|
||||
|
||||
CryptoBuffer mData;
|
||||
bool mDataIsSet;
|
||||
};
|
||||
|
||||
class AesTask : public ReturnArrayBufferViewTask,
|
||||
public DeferredData
|
||||
{
|
||||
public:
|
||||
AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
: mSymKey(aKey.GetSymKey())
|
||||
, mEncrypt(aEncrypt)
|
||||
{
|
||||
Init(aCx, aAlgorithm, aKey, aEncrypt);
|
||||
}
|
||||
|
||||
AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, const CryptoOperationData& aData,
|
||||
bool aEncrypt)
|
||||
: mSymKey(aKey.GetSymKey())
|
||||
, mEncrypt(aEncrypt)
|
||||
{
|
||||
ATTEMPT_BUFFER_INIT(mData, aData);
|
||||
Init(aCx, aAlgorithm, aKey, aEncrypt);
|
||||
SetData(aData);
|
||||
}
|
||||
|
||||
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
{
|
||||
nsString algName;
|
||||
mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
|
@ -398,7 +429,6 @@ private:
|
|||
CK_MECHANISM_TYPE mMechanism;
|
||||
CryptoBuffer mSymKey;
|
||||
CryptoBuffer mIv; // Initialization vector
|
||||
CryptoBuffer mData;
|
||||
CryptoBuffer mAad; // Additional Authenticated Data
|
||||
uint8_t mTagLength;
|
||||
uint8_t mCounterLength;
|
||||
|
@ -408,6 +438,10 @@ private:
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
if (!mDataIsSet) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
// Construct the parameters object depending on algorithm
|
||||
SECItem param;
|
||||
ScopedSECItem cbcParam;
|
||||
|
@ -476,9 +510,19 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class RsaesPkcs1Task : public ReturnArrayBufferViewTask
|
||||
class RsaesPkcs1Task : public ReturnArrayBufferViewTask,
|
||||
public DeferredData
|
||||
{
|
||||
public:
|
||||
RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
: mPrivKey(aKey.GetPrivateKey())
|
||||
, mPubKey(aKey.GetPublicKey())
|
||||
, mEncrypt(aEncrypt)
|
||||
{
|
||||
Init(aCx, aAlgorithm, aKey, aEncrypt);
|
||||
}
|
||||
|
||||
RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, const CryptoOperationData& aData,
|
||||
bool aEncrypt)
|
||||
|
@ -486,9 +530,15 @@ public:
|
|||
, mPubKey(aKey.GetPublicKey())
|
||||
, mEncrypt(aEncrypt)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSAES_PKCS1);
|
||||
Init(aCx, aAlgorithm, aKey, aEncrypt);
|
||||
SetData(aData);
|
||||
}
|
||||
|
||||
ATTEMPT_BUFFER_INIT(mData, aData);
|
||||
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
{
|
||||
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSAES_PKCS1);
|
||||
|
||||
if (mEncrypt) {
|
||||
if (!mPubKey) {
|
||||
|
@ -496,14 +546,6 @@ public:
|
|||
return;
|
||||
}
|
||||
mStrength = SECKEY_PublicKeyStrength(mPubKey);
|
||||
|
||||
// Verify that the data input is not too big
|
||||
// (as required by PKCS#1 / RFC 3447, Section 7.2)
|
||||
// http://tools.ietf.org/html/rfc3447#section-7.2
|
||||
if (mData.Length() > mStrength - 11) {
|
||||
mEarlyRv = NS_ERROR_DOM_DATA_ERR;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!mPrivKey) {
|
||||
mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
|
@ -516,10 +558,25 @@ public:
|
|||
private:
|
||||
ScopedSECKEYPrivateKey mPrivKey;
|
||||
ScopedSECKEYPublicKey mPubKey;
|
||||
CryptoBuffer mData;
|
||||
uint32_t mStrength;
|
||||
bool mEncrypt;
|
||||
|
||||
virtual nsresult BeforeCrypto() MOZ_OVERRIDE
|
||||
{
|
||||
if (!mDataIsSet) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
// Verify that the data input is not too big
|
||||
// (as required by PKCS#1 / RFC 3447, Section 7.2)
|
||||
// http://tools.ietf.org/html/rfc3447#section-7.2
|
||||
if (mEncrypt && mData.Length() > mStrength - 11) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual nsresult DoCrypto() MOZ_OVERRIDE
|
||||
{
|
||||
nsresult rv;
|
||||
|
@ -549,9 +606,19 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class RsaOaepTask : public ReturnArrayBufferViewTask
|
||||
class RsaOaepTask : public ReturnArrayBufferViewTask,
|
||||
public DeferredData
|
||||
{
|
||||
public:
|
||||
RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
: mPrivKey(aKey.GetPrivateKey())
|
||||
, mPubKey(aKey.GetPublicKey())
|
||||
, mEncrypt(aEncrypt)
|
||||
{
|
||||
Init(aCx, aAlgorithm, aKey, aEncrypt);
|
||||
}
|
||||
|
||||
RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, const CryptoOperationData& aData,
|
||||
bool aEncrypt)
|
||||
|
@ -559,9 +626,14 @@ public:
|
|||
, mPubKey(aKey.GetPublicKey())
|
||||
, mEncrypt(aEncrypt)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
|
||||
Init(aCx, aAlgorithm, aKey, aEncrypt);
|
||||
SetData(aData);
|
||||
}
|
||||
|
||||
ATTEMPT_BUFFER_INIT(mData, aData);
|
||||
void Init(JSContext* aCx, const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey, bool aEncrypt)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
|
||||
|
||||
if (mEncrypt) {
|
||||
if (!mPubKey) {
|
||||
|
@ -620,7 +692,6 @@ private:
|
|||
ScopedSECKEYPrivateKey mPrivKey;
|
||||
ScopedSECKEYPublicKey mPubKey;
|
||||
CryptoBuffer mLabel;
|
||||
CryptoBuffer mData;
|
||||
uint32_t mStrength;
|
||||
bool mEncrypt;
|
||||
|
||||
|
@ -628,6 +699,10 @@ private:
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
if (!mDataIsSet) {
|
||||
return NS_ERROR_DOM_OPERATION_ERR;
|
||||
}
|
||||
|
||||
// Ciphertext is an integer mod the modulus, so it will be
|
||||
// no longer than mStrength octets
|
||||
if (!mResult.SetLength(mStrength)) {
|
||||
|
@ -895,10 +970,10 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class SimpleDigestTask : public ReturnArrayBufferViewTask
|
||||
class DigestTask : public ReturnArrayBufferViewTask
|
||||
{
|
||||
public:
|
||||
SimpleDigestTask(JSContext* aCx,
|
||||
DigestTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
const CryptoOperationData& aData)
|
||||
{
|
||||
|
@ -987,7 +1062,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void SetKeyData(const CryptoBuffer& aKeyData)
|
||||
{
|
||||
// An OOM will just result in an error in BeforeCrypto
|
||||
mKeyData = aKeyData;
|
||||
}
|
||||
|
||||
protected:
|
||||
CryptoBuffer mKeyData;
|
||||
nsRefPtr<CryptoKey> mKey;
|
||||
nsString mAlgName;
|
||||
|
||||
|
@ -1069,7 +1151,8 @@ public:
|
|||
if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
|
||||
if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT)) {
|
||||
if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
|
||||
CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
|
||||
|
@ -1102,14 +1185,7 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void SetKeyData(const CryptoBuffer& aKeyData)
|
||||
{
|
||||
// An OOM will just result in an error in BeforeCrypto
|
||||
mKeyData = aKeyData;
|
||||
}
|
||||
|
||||
private:
|
||||
CryptoBuffer mKeyData;
|
||||
nsString mHashName;
|
||||
};
|
||||
|
||||
|
@ -1158,7 +1234,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
CryptoBuffer mKeyData;
|
||||
nsString mFormat;
|
||||
nsString mHashName;
|
||||
uint32_t mModulusLength;
|
||||
|
@ -1215,9 +1290,9 @@ private:
|
|||
if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
|
||||
mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
|
||||
mKey->HasUsageOtherThan(CryptoKey::ENCRYPT)) ||
|
||||
mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
|
||||
(mKey->GetKeyType() == CryptoKey::PRIVATE &&
|
||||
mKey->HasUsageOtherThan(CryptoKey::DECRYPT))) {
|
||||
mKey->HasUsageOtherThan(CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY))) {
|
||||
return NS_ERROR_DOM_DATA_ERR;
|
||||
}
|
||||
} else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
|
||||
|
@ -1252,10 +1327,10 @@ private:
|
|||
};
|
||||
|
||||
|
||||
class UnifiedExportKeyTask : public ReturnArrayBufferViewTask
|
||||
class ExportKeyTask : public ReturnArrayBufferViewTask
|
||||
{
|
||||
public:
|
||||
UnifiedExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
|
||||
ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
|
||||
: mFormat(aFormat)
|
||||
, mSymKey(aKey.GetSymKey())
|
||||
, mPrivateKey(aKey.GetPrivateKey())
|
||||
|
@ -1779,11 +1854,69 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
template<class KeyEncryptTask>
|
||||
class WrapKeyTask : public ExportKeyTask
|
||||
{
|
||||
public:
|
||||
WrapKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
CryptoKey& aKey,
|
||||
CryptoKey& aWrappingKey,
|
||||
const ObjectOrString& aWrapAlgorithm)
|
||||
: ExportKeyTask(aFormat, aKey)
|
||||
{
|
||||
if (NS_FAILED(mEarlyRv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<KeyEncryptTask> mTask;
|
||||
|
||||
virtual void Resolve() MOZ_OVERRIDE {
|
||||
mTask->SetData(mResult);
|
||||
mTask->DispatchWithPromise(mResultPromise);
|
||||
}
|
||||
|
||||
virtual void Cleanup() MOZ_OVERRIDE
|
||||
{
|
||||
mTask = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<class KeyEncryptTask>
|
||||
class UnwrapKeyTask : public KeyEncryptTask
|
||||
{
|
||||
public:
|
||||
UnwrapKeyTask(JSContext* aCx,
|
||||
const ArrayBufferViewOrArrayBuffer& aWrappedKey,
|
||||
CryptoKey& aUnwrappingKey,
|
||||
const ObjectOrString& aUnwrapAlgorithm,
|
||||
ImportKeyTask* aTask)
|
||||
: KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey, false)
|
||||
, mTask(aTask)
|
||||
{}
|
||||
|
||||
private:
|
||||
nsRefPtr<ImportKeyTask> mTask;
|
||||
|
||||
virtual void Resolve() MOZ_OVERRIDE {
|
||||
mTask->SetKeyData(KeyEncryptTask::mResult);
|
||||
mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
|
||||
}
|
||||
|
||||
virtual void Cleanup() MOZ_OVERRIDE
|
||||
{
|
||||
mTask = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Task creation methods for WebCryptoTask
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::EncryptDecryptTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateEncryptDecryptTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aData,
|
||||
|
@ -1819,7 +1952,7 @@ WebCryptoTask::EncryptDecryptTask(JSContext* aCx,
|
|||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::SignVerifyTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateSignVerifyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aSignature,
|
||||
|
@ -1852,16 +1985,16 @@ WebCryptoTask::SignVerifyTask(JSContext* aCx,
|
|||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::DigestTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateDigestTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
const CryptoOperationData& aData)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
|
||||
return new SimpleDigestTask(aCx, aAlgorithm, aData);
|
||||
return new DigestTask(aCx, aAlgorithm, aData);
|
||||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::ImportKeyTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateImportKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
const KeyData& aKeyData,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
|
@ -1895,7 +2028,7 @@ WebCryptoTask::ImportKeyTask(JSContext* aCx,
|
|||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::ExportKeyTask(const nsAString& aFormat,
|
||||
WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
|
||||
CryptoKey& aKey)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
|
||||
|
@ -1903,12 +2036,12 @@ WebCryptoTask::ExportKeyTask(const nsAString& aFormat,
|
|||
if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
|
||||
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
} else {
|
||||
return new UnifiedExportKeyTask(aFormat, aKey);
|
||||
return new ExportKeyTask(aFormat, aKey);
|
||||
}
|
||||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::GenerateKeyTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
|
@ -1937,7 +2070,7 @@ WebCryptoTask::GenerateKeyTask(JSContext* aCx,
|
|||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::DeriveKeyTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aBaseKey,
|
||||
const ObjectOrString& aDerivedKeyType,
|
||||
|
@ -1961,7 +2094,7 @@ WebCryptoTask::DeriveKeyTask(JSContext* aCx,
|
|||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::DeriveBitsTask(JSContext* aCx,
|
||||
WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
uint32_t aLength)
|
||||
|
@ -1981,6 +2114,108 @@ WebCryptoTask::DeriveBitsTask(JSContext* aCx,
|
|||
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::CreateWrapKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
CryptoKey& aKey,
|
||||
CryptoKey& aWrappingKey,
|
||||
const ObjectOrString& aWrapAlgorithm)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
|
||||
|
||||
// Ensure key is usable for this operation
|
||||
if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
|
||||
return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
}
|
||||
|
||||
nsString wrapAlgName;
|
||||
nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
|
||||
if (NS_FAILED(rv)) {
|
||||
return new FailureTask(rv);
|
||||
}
|
||||
|
||||
if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
|
||||
wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
|
||||
wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
|
||||
return new WrapKeyTask<AesTask>(aCx, aFormat, aKey,
|
||||
aWrappingKey, aWrapAlgorithm);
|
||||
} else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
|
||||
return new WrapKeyTask<RsaesPkcs1Task>(aCx, aFormat, aKey,
|
||||
aWrappingKey, aWrapAlgorithm);
|
||||
} else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey,
|
||||
aWrappingKey, aWrapAlgorithm);
|
||||
}
|
||||
|
||||
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
}
|
||||
|
||||
WebCryptoTask*
|
||||
WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
const ArrayBufferViewOrArrayBuffer& aWrappedKey,
|
||||
CryptoKey& aUnwrappingKey,
|
||||
const ObjectOrString& aUnwrapAlgorithm,
|
||||
const ObjectOrString& aUnwrappedKeyAlgorithm,
|
||||
bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages)
|
||||
{
|
||||
Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
|
||||
|
||||
// Ensure key is usable for this operation
|
||||
if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
|
||||
return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
}
|
||||
|
||||
nsString keyAlgName;
|
||||
nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
|
||||
if (NS_FAILED(rv)) {
|
||||
return new FailureTask(rv);
|
||||
}
|
||||
|
||||
CryptoOperationData dummy;
|
||||
nsRefPtr<ImportKeyTask> importTask;
|
||||
if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
|
||||
keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
|
||||
keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
|
||||
keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
|
||||
importTask = new ImportSymmetricKeyTask(aCx, aFormat, dummy,
|
||||
aUnwrappedKeyAlgorithm,
|
||||
aExtractable, aKeyUsages);
|
||||
} else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) ||
|
||||
keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
|
||||
keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
importTask = new ImportRsaKeyTask(aCx, aFormat, dummy,
|
||||
aUnwrappedKeyAlgorithm,
|
||||
aExtractable, aKeyUsages);
|
||||
} else {
|
||||
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
}
|
||||
|
||||
nsString unwrapAlgName;
|
||||
rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
|
||||
if (NS_FAILED(rv)) {
|
||||
return new FailureTask(rv);
|
||||
}
|
||||
if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
|
||||
unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
|
||||
unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
|
||||
return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey,
|
||||
aUnwrappingKey, aUnwrapAlgorithm,
|
||||
importTask);
|
||||
} else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
|
||||
return new UnwrapKeyTask<RsaesPkcs1Task>(aCx, aWrappedKey,
|
||||
aUnwrappingKey, aUnwrapAlgorithm,
|
||||
importTask);
|
||||
} else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
|
||||
return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey,
|
||||
aUnwrappingKey, aUnwrapAlgorithm,
|
||||
importTask);
|
||||
}
|
||||
|
||||
return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -85,13 +85,13 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
static WebCryptoTask* EncryptDecryptTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateEncryptDecryptTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aData,
|
||||
bool aEncrypt);
|
||||
|
||||
static WebCryptoTask* SignVerifyTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateSignVerifyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aSignature,
|
||||
|
@ -99,69 +99,83 @@ protected:
|
|||
bool aSign);
|
||||
|
||||
public:
|
||||
static WebCryptoTask* EncryptTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateEncryptTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aData)
|
||||
{
|
||||
return EncryptDecryptTask(aCx, aAlgorithm, aKey, aData, true);
|
||||
return CreateEncryptDecryptTask(aCx, aAlgorithm, aKey, aData, true);
|
||||
}
|
||||
|
||||
static WebCryptoTask* DecryptTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateDecryptTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aData)
|
||||
{
|
||||
return EncryptDecryptTask(aCx, aAlgorithm, aKey, aData, false);
|
||||
return CreateEncryptDecryptTask(aCx, aAlgorithm, aKey, aData, false);
|
||||
}
|
||||
|
||||
static WebCryptoTask* SignTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateSignTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aData)
|
||||
{
|
||||
CryptoOperationData dummy;
|
||||
dummy.SetAsArrayBuffer(aCx);
|
||||
return SignVerifyTask(aCx, aAlgorithm, aKey, dummy, aData, true);
|
||||
return CreateSignVerifyTask(aCx, aAlgorithm, aKey, dummy, aData, true);
|
||||
}
|
||||
|
||||
static WebCryptoTask* VerifyTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateVerifyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
const CryptoOperationData& aSignature,
|
||||
const CryptoOperationData& aData)
|
||||
{
|
||||
return SignVerifyTask(aCx, aAlgorithm, aKey, aSignature, aData, false);
|
||||
return CreateSignVerifyTask(aCx, aAlgorithm, aKey, aSignature, aData, false);
|
||||
}
|
||||
|
||||
static WebCryptoTask* DigestTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateDigestTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
const CryptoOperationData& aData);
|
||||
|
||||
static WebCryptoTask* ImportKeyTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateImportKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
const KeyData& aKeyData,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages);
|
||||
static WebCryptoTask* ExportKeyTask(const nsAString& aFormat,
|
||||
static WebCryptoTask* CreateExportKeyTask(const nsAString& aFormat,
|
||||
CryptoKey& aKey);
|
||||
static WebCryptoTask* GenerateKeyTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateGenerateKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages);
|
||||
|
||||
static WebCryptoTask* DeriveKeyTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateDeriveKeyTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aBaseKey,
|
||||
const ObjectOrString& aDerivedKeyType,
|
||||
bool extractable,
|
||||
const Sequence<nsString>& aKeyUsages);
|
||||
static WebCryptoTask* DeriveBitsTask(JSContext* aCx,
|
||||
static WebCryptoTask* CreateDeriveBitsTask(JSContext* aCx,
|
||||
const ObjectOrString& aAlgorithm,
|
||||
CryptoKey& aKey,
|
||||
uint32_t aLength);
|
||||
|
||||
static WebCryptoTask* CreateWrapKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
CryptoKey& aKey,
|
||||
CryptoKey& aWrappingKey,
|
||||
const ObjectOrString& aWrapAlgorithm);
|
||||
static WebCryptoTask* CreateUnwrapKeyTask(JSContext* aCx,
|
||||
const nsAString& aFormat,
|
||||
const ArrayBufferViewOrArrayBuffer& aWrappedKey,
|
||||
CryptoKey& aUnwrappingKey,
|
||||
const ObjectOrString& aUnwrapAlgorithm,
|
||||
const ObjectOrString& aUnwrappedKeyAlgorithm,
|
||||
bool aExtractable,
|
||||
const Sequence<nsString>& aKeyUsages);
|
||||
|
||||
protected:
|
||||
nsRefPtr<Promise> mResultPromise;
|
||||
nsresult mEarlyRv;
|
||||
|
|
|
@ -355,6 +355,14 @@ tv = {
|
|||
),
|
||||
},
|
||||
|
||||
key_wrap_known_answer: {
|
||||
key: util.hex2abv("0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"),
|
||||
wrapping_key: util.hex2abv("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
|
||||
wrapping_iv: util.hex2abv("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"),
|
||||
wrapped_key: util.hex2abv("9ed0283a9a2b7e4292ebc5135e6342cc" +
|
||||
"8a7f65802a1f6fd41bd3251c4da0c138")
|
||||
},
|
||||
|
||||
// RFC 6070 <http://tools.ietf.org/html/rfc6070>
|
||||
pbkdf2_sha1: {
|
||||
password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"),
|
||||
|
|
|
@ -1274,3 +1274,158 @@ TestArray.addTest(
|
|||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Key wrap known answer, using AES-GCM",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = {
|
||||
name: "AES-GCM",
|
||||
iv: tv.key_wrap_known_answer.wrapping_iv,
|
||||
tagLength: 128
|
||||
};
|
||||
var key, wrappingKey;
|
||||
|
||||
function doImport(k) {
|
||||
wrappingKey = k;
|
||||
return crypto.subtle.importKey("raw", tv.key_wrap_known_answer.key,
|
||||
alg, true, ['encrypt', 'decrypt']);
|
||||
}
|
||||
function doWrap(k) {
|
||||
key = k;
|
||||
return crypto.subtle.wrapKey("raw", key, wrappingKey, alg);
|
||||
}
|
||||
|
||||
crypto.subtle.importKey("raw", tv.key_wrap_known_answer.wrapping_key,
|
||||
alg, false, ['wrapKey'])
|
||||
.then(doImport, error(that))
|
||||
.then(doWrap, error(that))
|
||||
.then(
|
||||
memcmp_complete(that, tv.key_wrap_known_answer.wrapped_key),
|
||||
error(that)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Key wrap failing on non-extractable key",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = {
|
||||
name: "AES-GCM",
|
||||
iv: tv.key_wrap_known_answer.wrapping_iv,
|
||||
tagLength: 128
|
||||
};
|
||||
var key, wrappingKey;
|
||||
|
||||
function doImport(k) {
|
||||
wrappingKey = k;
|
||||
return crypto.subtle.importKey("raw", tv.key_wrap_known_answer.key,
|
||||
alg, false, ['encrypt', 'decrypt']);
|
||||
}
|
||||
function doWrap(k) {
|
||||
key = k;
|
||||
return crypto.subtle.wrapKey("raw", key, wrappingKey, alg);
|
||||
}
|
||||
|
||||
crypto.subtle.importKey("raw", tv.key_wrap_known_answer.wrapping_key,
|
||||
alg, false, ['wrapKey'])
|
||||
.then(doImport, error(that))
|
||||
.then(doWrap, error(that))
|
||||
.then(
|
||||
error(that),
|
||||
complete(that)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Key unwrap known answer, using AES-GCM",
|
||||
function () {
|
||||
var that = this;
|
||||
var alg = {
|
||||
name: "AES-GCM",
|
||||
iv: tv.key_wrap_known_answer.wrapping_iv,
|
||||
tagLength: 128
|
||||
};
|
||||
var key, wrappingKey;
|
||||
|
||||
function doUnwrap(k) {
|
||||
wrappingKey = k;
|
||||
return crypto.subtle.unwrapKey(
|
||||
"raw", tv.key_wrap_known_answer.wrapped_key,
|
||||
wrappingKey, alg,
|
||||
"AES-GCM", true, ['encrypt', 'decrypt']
|
||||
);
|
||||
}
|
||||
function doExport(k) {
|
||||
return crypto.subtle.exportKey("raw", k);
|
||||
}
|
||||
|
||||
crypto.subtle.importKey("raw", tv.key_wrap_known_answer.wrapping_key,
|
||||
alg, false, ['unwrapKey'])
|
||||
.then(doUnwrap, error(that))
|
||||
.then(doExport, error(that))
|
||||
.then(
|
||||
memcmp_complete(that, tv.key_wrap_known_answer.key),
|
||||
error(that)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
TestArray.addTest(
|
||||
"Key wrap/unwrap round-trip, using RSA-OAEP",
|
||||
function () {
|
||||
var that = this;
|
||||
var oaep = {
|
||||
name: "RSA-OAEP",
|
||||
hash: "SHA-256"
|
||||
};
|
||||
var gcm = {
|
||||
name: "AES-GCM",
|
||||
iv: tv.aes_gcm_enc.iv,
|
||||
additionalData: tv.aes_gcm_enc.adata,
|
||||
tagLength: 128
|
||||
};
|
||||
var unwrapKey;
|
||||
|
||||
function doWrap(keys) {
|
||||
var originalKey = keys[0];
|
||||
var wrapKey = keys[1];
|
||||
unwrapKey = keys[2];
|
||||
return crypto.subtle.wrapKey("raw", originalKey, wrapKey, oaep);
|
||||
}
|
||||
function doUnwrap(wrappedKey) {
|
||||
return crypto.subtle.unwrapKey("raw", wrappedKey, unwrapKey, oaep,
|
||||
gcm, false, ['encrypt']);
|
||||
}
|
||||
function doEncrypt(aesKey) {
|
||||
return crypto.subtle.encrypt(gcm, aesKey, tv.aes_gcm_enc.data);
|
||||
}
|
||||
|
||||
// 1.Import:
|
||||
// -> HMAC key
|
||||
// -> OAEP wrap key (public)
|
||||
// -> OAEP unwrap key (private)
|
||||
// 2. Wrap the HMAC key
|
||||
// 3. Unwrap it
|
||||
// 4. Compute HMAC
|
||||
// 5. Check HMAC value
|
||||
Promise.all([
|
||||
crypto.subtle.importKey("raw", tv.aes_gcm_enc.key, gcm, true, ['encrypt']),
|
||||
crypto.subtle.importKey("spki", tv.rsaoaep.spki, oaep, true, ['wrapKey']),
|
||||
crypto.subtle.importKey("pkcs8", tv.rsaoaep.pkcs8, oaep, false, ['unwrapKey'])
|
||||
])
|
||||
.then(doWrap, error(that))
|
||||
.then(doUnwrap, error(that))
|
||||
.then(doEncrypt, error(that))
|
||||
.then(
|
||||
memcmp_complete(that, tv.aes_gcm_enc.result),
|
||||
error(that)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -176,5 +176,20 @@ interface SubtleCrypto {
|
|||
sequence<KeyUsage> keyUsages );
|
||||
[Throws]
|
||||
Promise exportKey(KeyFormat format, CryptoKey key);
|
||||
|
||||
[Throws]
|
||||
Promise wrapKey(KeyFormat format,
|
||||
CryptoKey key,
|
||||
CryptoKey wrappingKey,
|
||||
AlgorithmIdentifier wrapAlgorithm);
|
||||
|
||||
[Throws]
|
||||
Promise unwrapKey(KeyFormat format,
|
||||
CryptoOperationData wrappedKey,
|
||||
CryptoKey unwrappingKey,
|
||||
AlgorithmIdentifier unwrapAlgorithm,
|
||||
AlgorithmIdentifier unwrappedKeyAlgorithm,
|
||||
boolean extractable,
|
||||
sequence<KeyUsage> keyUsages );
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче