mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Compare commits
6 Commits
reserve-co
...
derrick/te
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9eb730ed0 | ||
|
|
c8243d19f0 | ||
|
|
7db87fe0cb | ||
|
|
76f72517ff | ||
|
|
bb851b7e18 | ||
|
|
fe3131cf17 |
77
src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp
Normal file
77
src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "CommonCryptoDERUtilities.h"
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
size_t bytesUsedToEncodedLength(uint8_t octet)
|
||||
{
|
||||
if (octet < MaxLengthInOneByte)
|
||||
return 1;
|
||||
return octet - MaxLengthInOneByte + 1;
|
||||
}
|
||||
|
||||
size_t extraBytesNeededForEncodedLength(size_t length)
|
||||
{
|
||||
if (!length)
|
||||
return 0;
|
||||
size_t result = 1;
|
||||
while (result < sizeof(length) && length >= (1 << (result * 8)))
|
||||
result += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
void addEncodedASN1Length(Vector<uint8_t>& in, size_t length)
|
||||
{
|
||||
if (length < MaxLengthInOneByte) {
|
||||
in.append(length);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t extraBytes = extraBytesNeededForEncodedLength(length);
|
||||
in.append(128 + extraBytes); // 128 is used to set the first bit of this byte.
|
||||
|
||||
size_t lastPosition = in.size() + extraBytes - 1;
|
||||
in.grow(in.size() + extraBytes);
|
||||
for (size_t i = 0; i < extraBytes; i++) {
|
||||
in[lastPosition - i] = length & 0xff;
|
||||
length = length >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
size_t bytesNeededForEncodedLength(size_t length)
|
||||
{
|
||||
if (length < MaxLengthInOneByte)
|
||||
return 1;
|
||||
return 1 + extraBytesNeededForEncodedLength(length);
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
56
src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h
Normal file
56
src/bun.js/bindings/webcrypto/CommonCryptoDERUtilities.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wtf/Vector.h>
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
|
||||
// FIXME: <rdar://problem/31618371>
|
||||
// The following constants and functions are for customized DER implementations.
|
||||
// They are not intended to be used outside Crypto codes, and should be removed
|
||||
// once the above bug is fixed.
|
||||
namespace WebCore {
|
||||
|
||||
// Per X.690 08/2015: https://www.itu.int/rec/T-REC-X.680-X.693/en
|
||||
static const unsigned char BitStringMark = 0x03;
|
||||
static const unsigned char IntegerMark = 0x02;
|
||||
static const unsigned char OctetStringMark = 0x04;
|
||||
static const unsigned char SequenceMark = 0x30;
|
||||
// Version 0. Per https://tools.ietf.org/html/rfc5208#section-5
|
||||
static const unsigned char Version[] = {0x02, 0x01, 0x00};
|
||||
|
||||
static const unsigned char InitialOctet = 0x00;
|
||||
static const size_t MaxLengthInOneByte = 128;
|
||||
|
||||
size_t bytesUsedToEncodedLength(uint8_t);
|
||||
size_t extraBytesNeededForEncodedLength(size_t);
|
||||
void addEncodedASN1Length(Vector<uint8_t>&, size_t);
|
||||
size_t bytesNeededForEncodedLength(size_t);
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
210
src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp
Normal file
210
src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "CryptoAlgorithmEd25519.h"
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
|
||||
#include "CryptoKeyOKP.h"
|
||||
#include <JavaScriptCore/JSCJSValueInlines.h>
|
||||
#include <wtf/CrossThreadCopier.h>
|
||||
|
||||
// -- BUN --
|
||||
#include <openssl/curve25519.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
static ExceptionOr<Vector<uint8_t>> signEd25519(const Vector<uint8_t>& sk, size_t len, const Vector<uint8_t>& data)
|
||||
{
|
||||
uint8_t newSignature[64];
|
||||
|
||||
ED25519_sign(newSignature, data.data(), data.size(), sk.data());
|
||||
return Vector<uint8_t>(newSignature, 64);
|
||||
}
|
||||
|
||||
ExceptionOr<Vector<uint8_t>> CryptoAlgorithmEd25519::platformSign(const CryptoKeyOKP& key, const Vector<uint8_t>& data)
|
||||
{
|
||||
return signEd25519(key.platformKey(), key.keySizeInBytes(), data);
|
||||
}
|
||||
|
||||
static ExceptionOr<bool> verifyEd25519(const Vector<uint8_t>& key, size_t keyLengthInBytes, const Vector<uint8_t>& signature, const Vector<uint8_t> data)
|
||||
{
|
||||
if (signature.size() != keyLengthInBytes * 2)
|
||||
return false;
|
||||
|
||||
return ED25519_verify(data.data(), data.size(), signature.data(), key.data()) == 1;
|
||||
}
|
||||
|
||||
ExceptionOr<bool> CryptoAlgorithmEd25519::platformVerify(const CryptoKeyOKP& key, const Vector<uint8_t>& signature, const Vector<uint8_t>& data)
|
||||
{
|
||||
return verifyEd25519(key.platformKey(), key.keySizeInBytes(), signature, data);
|
||||
}
|
||||
|
||||
Ref<CryptoAlgorithm> CryptoAlgorithmEd25519::create()
|
||||
{
|
||||
return adoptRef(*new CryptoAlgorithmEd25519);
|
||||
}
|
||||
|
||||
void CryptoAlgorithmEd25519::generateKey(const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext&)
|
||||
{
|
||||
if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey)) {
|
||||
exceptionCallback(SyntaxError);
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = CryptoKeyOKP::generatePair(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, extractable, usages);
|
||||
if (result.hasException()) {
|
||||
exceptionCallback(result.releaseException().code());
|
||||
return;
|
||||
}
|
||||
|
||||
auto pair = result.releaseReturnValue();
|
||||
pair.publicKey->setUsagesBitmap(pair.publicKey->usagesBitmap() & CryptoKeyUsageVerify);
|
||||
pair.privateKey->setUsagesBitmap(pair.privateKey->usagesBitmap() & CryptoKeyUsageSign);
|
||||
callback(WTFMove(pair));
|
||||
}
|
||||
|
||||
void CryptoAlgorithmEd25519::sign(const CryptoAlgorithmParameters&, Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
|
||||
{
|
||||
if (key->type() != CryptoKeyType::Private) {
|
||||
exceptionCallback(InvalidAccessError);
|
||||
return;
|
||||
}
|
||||
dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
|
||||
[key = WTFMove(key), data = WTFMove(data)] {
|
||||
return platformSign(downcast<CryptoKeyOKP>(key.get()), data);
|
||||
});
|
||||
}
|
||||
|
||||
void CryptoAlgorithmEd25519::verify(const CryptoAlgorithmParameters&, Ref<CryptoKey>&& key, Vector<uint8_t>&& signature, Vector<uint8_t>&& data, BoolCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
|
||||
{
|
||||
if (key->type() != CryptoKeyType::Public) {
|
||||
exceptionCallback(InvalidAccessError);
|
||||
return;
|
||||
}
|
||||
dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
|
||||
[key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data)] {
|
||||
return platformVerify(downcast<CryptoKeyOKP>(key.get()), signature, data);
|
||||
});
|
||||
}
|
||||
|
||||
void CryptoAlgorithmEd25519::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback)
|
||||
{
|
||||
RefPtr<CryptoKeyOKP> result;
|
||||
switch (format) {
|
||||
case CryptoKeyFormat::Jwk: {
|
||||
JsonWebKey key = WTFMove(std::get<JsonWebKey>(data));
|
||||
if (usages && ((!key.d.isNull() && (usages ^ CryptoKeyUsageSign)) || (key.d.isNull() && (usages ^ CryptoKeyUsageVerify)))) {
|
||||
exceptionCallback(SyntaxError);
|
||||
return;
|
||||
}
|
||||
if (usages && !key.use.isNull() && key.use != "sig"_s) {
|
||||
exceptionCallback(DataError);
|
||||
return;
|
||||
}
|
||||
result = CryptoKeyOKP::importJwk(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(key), extractable, usages);
|
||||
break;
|
||||
}
|
||||
case CryptoKeyFormat::Raw:
|
||||
if (usages && (usages ^ CryptoKeyUsageVerify)) {
|
||||
exceptionCallback(SyntaxError);
|
||||
return;
|
||||
}
|
||||
result = CryptoKeyOKP::importRaw(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(std::get<Vector<uint8_t>>(data)), extractable, usages);
|
||||
break;
|
||||
case CryptoKeyFormat::Spki:
|
||||
if (usages && (usages ^ CryptoKeyUsageVerify)) {
|
||||
exceptionCallback(SyntaxError);
|
||||
return;
|
||||
}
|
||||
result = CryptoKeyOKP::importSpki(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(std::get<Vector<uint8_t>>(data)), extractable, usages);
|
||||
break;
|
||||
case CryptoKeyFormat::Pkcs8:
|
||||
if (usages && (usages ^ CryptoKeyUsageSign)) {
|
||||
exceptionCallback(SyntaxError);
|
||||
return;
|
||||
}
|
||||
result = CryptoKeyOKP::importPkcs8(CryptoAlgorithmIdentifier::Ed25519, CryptoKeyOKP::NamedCurve::Ed25519, WTFMove(std::get<Vector<uint8_t>>(data)), extractable, usages);
|
||||
break;
|
||||
}
|
||||
if (!result) {
|
||||
exceptionCallback(DataError);
|
||||
return;
|
||||
}
|
||||
callback(*result);
|
||||
}
|
||||
|
||||
void CryptoAlgorithmEd25519::exportKey(CryptoKeyFormat format, Ref<CryptoKey>&& key, KeyDataCallback&& callback, ExceptionCallback&& exceptionCallback)
|
||||
{
|
||||
const auto& okpKey = downcast<CryptoKeyOKP>(key.get());
|
||||
if (!okpKey.keySizeInBits()) {
|
||||
exceptionCallback(OperationError);
|
||||
return;
|
||||
}
|
||||
KeyData result;
|
||||
switch (format) {
|
||||
case CryptoKeyFormat::Jwk: {
|
||||
auto jwk = okpKey.exportJwk();
|
||||
if (jwk.hasException()) {
|
||||
exceptionCallback(jwk.releaseException().code());
|
||||
return;
|
||||
}
|
||||
result = jwk.releaseReturnValue();
|
||||
break;
|
||||
}
|
||||
case CryptoKeyFormat::Raw: {
|
||||
auto raw = okpKey.exportRaw();
|
||||
if (raw.hasException()) {
|
||||
exceptionCallback(raw.releaseException().code());
|
||||
return;
|
||||
}
|
||||
result = raw.releaseReturnValue();
|
||||
break;
|
||||
}
|
||||
case CryptoKeyFormat::Spki: {
|
||||
auto spki = okpKey.exportSpki();
|
||||
if (spki.hasException()) {
|
||||
exceptionCallback(spki.releaseException().code());
|
||||
return;
|
||||
}
|
||||
result = spki.releaseReturnValue();
|
||||
break;
|
||||
}
|
||||
case CryptoKeyFormat::Pkcs8: {
|
||||
auto pkcs8 = okpKey.exportPkcs8();
|
||||
if (pkcs8.hasException()) {
|
||||
exceptionCallback(pkcs8.releaseException().code());
|
||||
return;
|
||||
}
|
||||
result = pkcs8.releaseReturnValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback(format, WTFMove(result));
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
61
src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h
Normal file
61
src/bun.js/bindings/webcrypto/CryptoAlgorithmEd25519.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CryptoAlgorithm.h"
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
namespace WebCore {
|
||||
|
||||
class CryptoKeyOKP;
|
||||
|
||||
class CryptoAlgorithmEd25519 final : public CryptoAlgorithm {
|
||||
public:
|
||||
static constexpr ASCIILiteral s_name = "Ed25519"_s;
|
||||
static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::Ed25519;
|
||||
static Ref<CryptoAlgorithm> create();
|
||||
|
||||
private:
|
||||
CryptoAlgorithmEd25519() = default;
|
||||
CryptoAlgorithmIdentifier identifier() const final;
|
||||
|
||||
void generateKey(const CryptoAlgorithmParameters& , bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& , ExceptionCallback&& , ScriptExecutionContext&);
|
||||
void sign(const CryptoAlgorithmParameters&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final;
|
||||
void verify(const CryptoAlgorithmParameters&, Ref<CryptoKey>&&, Vector<uint8_t>&& signature, Vector<uint8_t>&&, BoolCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&) final;
|
||||
void importKey(CryptoKeyFormat, KeyData&&, const CryptoAlgorithmParameters&, bool extractable, CryptoKeyUsageBitmap, KeyCallback&&, ExceptionCallback&&) final;
|
||||
void exportKey(CryptoKeyFormat, Ref<CryptoKey>&&, KeyDataCallback&&, ExceptionCallback&&) final;
|
||||
|
||||
static ExceptionOr<Vector<uint8_t>> platformSign(const CryptoKeyOKP&, const Vector<uint8_t>&);
|
||||
static ExceptionOr<bool> platformVerify(const CryptoKeyOKP&, const Vector<uint8_t>&, const Vector<uint8_t>&);
|
||||
};
|
||||
|
||||
inline CryptoAlgorithmIdentifier CryptoAlgorithmEd25519::identifier() const
|
||||
{
|
||||
return s_identifier;
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
@@ -48,7 +48,8 @@ enum class CryptoAlgorithmIdentifier {
|
||||
SHA_384,
|
||||
SHA_512,
|
||||
HKDF,
|
||||
PBKDF2
|
||||
PBKDF2,
|
||||
Ed25519
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "CryptoAlgorithmAES_KW.h"
|
||||
#include "CryptoAlgorithmECDH.h"
|
||||
#include "CryptoAlgorithmECDSA.h"
|
||||
#include "CryptoAlgorithmEd25519.h"
|
||||
#include "CryptoAlgorithmHKDF.h"
|
||||
#include "CryptoAlgorithmHMAC.h"
|
||||
#include "CryptoAlgorithmPBKDF2.h"
|
||||
@@ -71,6 +72,7 @@ void CryptoAlgorithmRegistry::platformRegisterAlgorithms()
|
||||
registerAlgorithm<CryptoAlgorithmSHA256>();
|
||||
registerAlgorithm<CryptoAlgorithmSHA384>();
|
||||
registerAlgorithm<CryptoAlgorithmSHA512>();
|
||||
registerAlgorithm<CryptoAlgorithmEd25519>();
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
@@ -73,9 +73,6 @@ WebCoreOpaqueRoot root(CryptoKey* key)
|
||||
return WebCoreOpaqueRoot { key };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Vector<uint8_t> CryptoKey::randomData(size_t size)
|
||||
{
|
||||
Vector<uint8_t> result(size);
|
||||
|
||||
@@ -51,6 +51,7 @@ enum class CryptoKeyClass {
|
||||
AES,
|
||||
EC,
|
||||
HMAC,
|
||||
OKP,
|
||||
RSA,
|
||||
Raw,
|
||||
};
|
||||
@@ -94,8 +95,11 @@ WebCoreOpaqueRoot root(CryptoKey*);
|
||||
} // namespace WebCore
|
||||
|
||||
#define SPECIALIZE_TYPE_TRAITS_CRYPTO_KEY(ToClassName, KeyClass) \
|
||||
SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \
|
||||
static bool isType(const WebCore::CryptoKey& key) { return key.keyClass() == WebCore::KeyClass; } \
|
||||
SPECIALIZE_TYPE_TRAITS_END()
|
||||
SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \
|
||||
static bool isType(const WebCore::CryptoKey& key) \
|
||||
{ \
|
||||
return key.keyClass() == WebCore::KeyClass; \
|
||||
} \
|
||||
SPECIALIZE_TYPE_TRAITS_END()
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
|
||||
252
src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
Normal file
252
src/bun.js/bindings/webcrypto/CryptoKeyOKP.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "CryptoKeyOKP.h"
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
|
||||
#include "CryptoAlgorithmRegistry.h"
|
||||
#include "JsonWebKey.h"
|
||||
#include <wtf/text/Base64.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
static const ASCIILiteral X25519 { "X25519"_s };
|
||||
static const ASCIILiteral Ed25519 { "Ed25519"_s };
|
||||
|
||||
static constexpr size_t internalKeySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve, CryptoKeyType type)
|
||||
{
|
||||
switch (curve) {
|
||||
case CryptoKeyOKP::NamedCurve::X25519:
|
||||
return 32;
|
||||
case CryptoKeyOKP::NamedCurve::Ed25519:
|
||||
return type == CryptoKeyType::Private ? 64 : 32;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t externalKeySizeInBytesFromNamedCurve(CryptoKeyOKP::NamedCurve curve)
|
||||
{
|
||||
switch (curve) {
|
||||
case CryptoKeyOKP::NamedCurve::X25519:
|
||||
case CryptoKeyOKP::NamedCurve::Ed25519:
|
||||
return 32;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<CryptoKeyOKP> CryptoKeyOKP::create(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& platformKey, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
auto bytesExpectedInternal = internalKeySizeInBytesFromNamedCurve(curve, type);
|
||||
if (bytesExpectedInternal == -1)
|
||||
return nullptr;
|
||||
|
||||
if (platformKey.size() != bytesExpectedInternal) {
|
||||
if (type != CryptoKeyType::Private || curve != NamedCurve::Ed25519)
|
||||
return nullptr;
|
||||
|
||||
auto bytesExpectedExternal = externalKeySizeInBytesFromNamedCurve(curve);
|
||||
if (bytesExpectedExternal == -1)
|
||||
return nullptr;
|
||||
|
||||
// We need to match the internal format when importing a private key
|
||||
// Import format only consists of 32 bytes of private key
|
||||
// Internal format is private key + public key suffix
|
||||
if (platformKey.size() == bytesExpectedExternal) {
|
||||
auto&& privateKey = ed25519PrivateFromSeed(WTFMove(platformKey));
|
||||
if (!privateKey.data())
|
||||
return nullptr;
|
||||
|
||||
return adoptRef(*new CryptoKeyOKP(identifier, curve, type, WTFMove(privateKey), extractable, usages));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return adoptRef(*new CryptoKeyOKP(identifier, curve, type, WTFMove(platformKey), extractable, usages));
|
||||
}
|
||||
|
||||
CryptoKeyOKP::CryptoKeyOKP(CryptoAlgorithmIdentifier identifier, NamedCurve curve, CryptoKeyType type, KeyMaterial&& data, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
: CryptoKey(identifier, type, extractable, usages)
|
||||
, m_curve(curve)
|
||||
, m_data(data)
|
||||
, m_exportKey(curve == NamedCurve::Ed25519 && type == CryptoKeyType::Private ? std::optional<Vector<uint8_t>>(Vector<uint8_t>(data.data(), 32)) : std::nullopt)
|
||||
{
|
||||
}
|
||||
|
||||
ExceptionOr<CryptoKeyPair> CryptoKeyOKP::generatePair(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
if (!isPlatformSupportedCurve(namedCurve))
|
||||
return Exception { NotSupportedError };
|
||||
|
||||
auto result = platformGeneratePair(identifier, namedCurve, extractable, usages);
|
||||
if (!result)
|
||||
return Exception { OperationError };
|
||||
|
||||
return WTFMove(*result);
|
||||
}
|
||||
|
||||
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importRaw(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
if (!isPlatformSupportedCurve(namedCurve))
|
||||
return nullptr;
|
||||
|
||||
return create(identifier, namedCurve, usages & CryptoKeyUsageSign ? CryptoKeyType::Private : CryptoKeyType::Public, WTFMove(keyData), extractable, usages);
|
||||
}
|
||||
|
||||
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importJwk(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, JsonWebKey&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
if (!isPlatformSupportedCurve(namedCurve))
|
||||
return nullptr;
|
||||
|
||||
switch (namedCurve) {
|
||||
case NamedCurve::Ed25519:
|
||||
if (!keyData.d.isEmpty()) {
|
||||
if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageVerify | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey))
|
||||
return nullptr;
|
||||
} else {
|
||||
if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageSign | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey))
|
||||
return nullptr;
|
||||
}
|
||||
if (keyData.kty != "OKP"_s)
|
||||
return nullptr;
|
||||
if (keyData.crv != "Ed25519"_s)
|
||||
return nullptr;
|
||||
if (!keyData.alg.isEmpty() && keyData.alg != "EdDSA"_s)
|
||||
return nullptr;
|
||||
if (usages && !keyData.use.isEmpty() && keyData.use != "sig"_s)
|
||||
return nullptr;
|
||||
if (keyData.key_ops && ((keyData.usages & usages) != usages))
|
||||
return nullptr;
|
||||
if (keyData.ext && !keyData.ext.value() && extractable)
|
||||
return nullptr;
|
||||
break;
|
||||
case NamedCurve::X25519:
|
||||
if (keyData.crv != "X25519"_s)
|
||||
return nullptr;
|
||||
// FIXME: Add further checks.
|
||||
break;
|
||||
}
|
||||
|
||||
if (!keyData.d.isNull()) {
|
||||
// FIXME: Validate keyData.x is paired with keyData.d
|
||||
auto d = base64URLDecode(keyData.d);
|
||||
if (!d)
|
||||
return nullptr;
|
||||
return create(identifier, namedCurve, CryptoKeyType::Private, WTFMove(*d), extractable, usages);
|
||||
}
|
||||
|
||||
if (keyData.x.isNull())
|
||||
return nullptr;
|
||||
|
||||
auto x = base64URLDecode(keyData.x);
|
||||
if (!x)
|
||||
return nullptr;
|
||||
return create(identifier, namedCurve, CryptoKeyType::Public, WTFMove(*x), extractable, usages);
|
||||
}
|
||||
|
||||
ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportRaw() const
|
||||
{
|
||||
if (type() != CryptoKey::Type::Public)
|
||||
return Exception { InvalidAccessError };
|
||||
|
||||
auto&& result = platformExportRaw();
|
||||
if (result.isEmpty())
|
||||
return Exception { OperationError };
|
||||
return WTFMove(result);
|
||||
}
|
||||
|
||||
ExceptionOr<JsonWebKey> CryptoKeyOKP::exportJwk() const
|
||||
{
|
||||
JsonWebKey result;
|
||||
result.kty = "OKP"_s;
|
||||
switch (m_curve) {
|
||||
case NamedCurve::X25519:
|
||||
result.crv = X25519;
|
||||
break;
|
||||
case NamedCurve::Ed25519:
|
||||
result.crv = Ed25519;
|
||||
break;
|
||||
}
|
||||
|
||||
result.key_ops = usages();
|
||||
result.ext = extractable();
|
||||
|
||||
switch (type()) {
|
||||
case CryptoKeyType::Private:
|
||||
result.d = generateJwkD();
|
||||
result.x = generateJwkX();
|
||||
break;
|
||||
case CryptoKeyType::Public:
|
||||
result.x = generateJwkX();
|
||||
break;
|
||||
case CryptoKeyType::Secret:
|
||||
return Exception { OperationError };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String CryptoKeyOKP::namedCurveString() const
|
||||
{
|
||||
switch (m_curve) {
|
||||
case NamedCurve::X25519:
|
||||
return X25519;
|
||||
case NamedCurve::Ed25519:
|
||||
return Ed25519;
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
return emptyString();
|
||||
}
|
||||
|
||||
bool CryptoKeyOKP::isValidOKPAlgorithm(CryptoAlgorithmIdentifier algorithm)
|
||||
{
|
||||
return algorithm == CryptoAlgorithmIdentifier::Ed25519;
|
||||
}
|
||||
|
||||
auto CryptoKeyOKP::algorithm() const -> KeyAlgorithm
|
||||
{
|
||||
CryptoEcKeyAlgorithm result;
|
||||
result.name = CryptoAlgorithmRegistry::singleton().name(algorithmIdentifier());
|
||||
|
||||
switch (m_curve) {
|
||||
case NamedCurve::X25519:
|
||||
result.namedCurve = X25519;
|
||||
break;
|
||||
case NamedCurve::Ed25519:
|
||||
result.namedCurve = Ed25519;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
101
src/bun.js/bindings/webcrypto/CryptoKeyOKP.h
Normal file
101
src/bun.js/bindings/webcrypto/CryptoKeyOKP.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CryptoKey.h"
|
||||
#include "CryptoKeyPair.h"
|
||||
#include "ExceptionOr.h"
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
struct JsonWebKey;
|
||||
|
||||
class CryptoKeyOKP final : public CryptoKey {
|
||||
public:
|
||||
using KeyMaterial = Vector<uint8_t>;
|
||||
|
||||
enum class NamedCurve {
|
||||
X25519,
|
||||
Ed25519,
|
||||
};
|
||||
|
||||
static RefPtr<CryptoKeyOKP> create(CryptoAlgorithmIdentifier, NamedCurve, CryptoKeyType, KeyMaterial&&, bool extractable, CryptoKeyUsageBitmap);
|
||||
|
||||
WEBCORE_EXPORT static ExceptionOr<CryptoKeyPair> generatePair(CryptoAlgorithmIdentifier, NamedCurve, bool extractable, CryptoKeyUsageBitmap);
|
||||
WEBCORE_EXPORT static RefPtr<CryptoKeyOKP> importRaw(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap);
|
||||
static RefPtr<CryptoKeyOKP> importJwk(CryptoAlgorithmIdentifier, NamedCurve, JsonWebKey&&, bool extractable, CryptoKeyUsageBitmap);
|
||||
static RefPtr<CryptoKeyOKP> importSpki(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap);
|
||||
static RefPtr<CryptoKeyOKP> importPkcs8(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap);
|
||||
|
||||
WEBCORE_EXPORT ExceptionOr<Vector<uint8_t>> exportRaw() const;
|
||||
ExceptionOr<JsonWebKey> exportJwk() const;
|
||||
ExceptionOr<Vector<uint8_t>> exportSpki() const;
|
||||
ExceptionOr<Vector<uint8_t>> exportPkcs8() const;
|
||||
|
||||
NamedCurve namedCurve() const { return m_curve; }
|
||||
String namedCurveString() const;
|
||||
bool isEd25519PrivateKey() { return namedCurve() == NamedCurve::Ed25519 && type() == CryptoKeyType::Private; };
|
||||
|
||||
static bool isValidOKPAlgorithm(CryptoAlgorithmIdentifier);
|
||||
static KeyMaterial ed25519PublicFromPrivate(const KeyMaterial& privateKey);
|
||||
static KeyMaterial x25519PublicFromPrivate(const KeyMaterial& privateKey);
|
||||
static KeyMaterial ed25519PrivateFromSeed(KeyMaterial&& seed);
|
||||
|
||||
size_t keySizeInBits() const { return platformKey().size() * 8; }
|
||||
size_t keySizeInBytes() const { return platformKey().size(); }
|
||||
const KeyMaterial& platformKey() const { return m_data; }
|
||||
|
||||
size_t exportKeySizeInBits() const { return exportKey().size() * 8; }
|
||||
size_t exportKeySizeInBytes() const { return exportKey().size(); }
|
||||
const KeyMaterial& exportKey() const { return !m_exportKey ? m_data : *m_exportKey; };
|
||||
|
||||
private:
|
||||
CryptoKeyOKP(CryptoAlgorithmIdentifier, NamedCurve, CryptoKeyType, Vector<uint8_t>&&, bool extractable, CryptoKeyUsageBitmap);
|
||||
|
||||
CryptoKeyClass keyClass() const final { return CryptoKeyClass::OKP; }
|
||||
KeyAlgorithm algorithm() const final;
|
||||
|
||||
String generateJwkD() const;
|
||||
String generateJwkX() const;
|
||||
|
||||
static bool isPlatformSupportedCurve(NamedCurve);
|
||||
static std::optional<CryptoKeyPair> platformGeneratePair(CryptoAlgorithmIdentifier, NamedCurve, bool extractable, CryptoKeyUsageBitmap);
|
||||
Vector<uint8_t> platformExportRaw() const;
|
||||
Vector<uint8_t> platformExportSpki() const;
|
||||
Vector<uint8_t> platformExportPkcs8() const;
|
||||
|
||||
NamedCurve m_curve;
|
||||
KeyMaterial m_data;
|
||||
std::optional<KeyMaterial> m_exportKey;
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
SPECIALIZE_TYPE_TRAITS_CRYPTO_KEY(CryptoKeyOKP, CryptoKeyClass::OKP)
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
359
src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp
Normal file
359
src/bun.js/bindings/webcrypto/CryptoKeyOKPOpenSSL.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "CryptoKeyOKP.h"
|
||||
|
||||
#if ENABLE(WEB_CRYPTO)
|
||||
|
||||
#include "JsonWebKey.h"
|
||||
// #include "Logging.h"
|
||||
#include <wtf/text/Base64.h>
|
||||
#include <openssl/curve25519.h>
|
||||
#include "CommonCryptoDERUtilities.h"
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
bool CryptoKeyOKP::isPlatformSupportedCurve(NamedCurve namedCurve)
|
||||
{
|
||||
return namedCurve == NamedCurve::Ed25519;
|
||||
}
|
||||
|
||||
std::optional<CryptoKeyPair> CryptoKeyOKP::platformGeneratePair(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
if (namedCurve != NamedCurve::Ed25519)
|
||||
return {};
|
||||
|
||||
uint8_t public_key[ED25519_PUBLIC_KEY_LEN], private_key[ED25519_PRIVATE_KEY_LEN];
|
||||
|
||||
bool isEd25519 = identifier == CryptoAlgorithmIdentifier::Ed25519;
|
||||
if (isEd25519) {
|
||||
ED25519_keypair(public_key, private_key);
|
||||
} else {
|
||||
X25519_keypair(public_key, private_key);
|
||||
}
|
||||
|
||||
bool isPublicKeyExtractable = true;
|
||||
auto publicKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Public, Vector<uint8_t>(public_key), isPublicKeyExtractable, usages);
|
||||
ASSERT(publicKey);
|
||||
auto privateKey = CryptoKeyOKP::create(identifier, namedCurve, CryptoKeyType::Private, Vector<uint8_t>(private_key, isEd25519 ? ED25519_PRIVATE_KEY_LEN : X25519_PRIVATE_KEY_LEN), extractable, usages);
|
||||
ASSERT(privateKey);
|
||||
return CryptoKeyPair { WTFMove(publicKey), WTFMove(privateKey) };
|
||||
}
|
||||
|
||||
// Per https://www.ietf.org/rfc/rfc5280.txt
|
||||
// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
|
||||
// AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }
|
||||
// Per https://www.rfc-editor.org/rfc/rfc8410
|
||||
// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 }
|
||||
// id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 }
|
||||
// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
// id-Ed448 OBJECT IDENTIFIER ::= { 1 3 101 113 }
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importSpki(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
// FIXME: We should use the underlying crypto library to import PKCS8 OKP keys.
|
||||
|
||||
// Read SEQUENCE
|
||||
size_t index = 1;
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read length and SEQUENCE
|
||||
// FIXME: Check length is 5 + 1 + 1 + 1 + keyByteSize.
|
||||
index += bytesUsedToEncodedLength(keyData[index]) + 1;
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read length
|
||||
// FIXME: Check length is 5.
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
if (keyData.size() < index + 5)
|
||||
return nullptr;
|
||||
|
||||
// Read OID
|
||||
// FIXME: spec says this is 1 3 101 11X but WPT tests expect 6 3 43 101 11X.
|
||||
if (keyData[index++] != 6 || keyData[index++] != 3 || keyData[index++] != 43 || keyData[index++] != 101)
|
||||
return nullptr;
|
||||
|
||||
switch (namedCurve) {
|
||||
case NamedCurve::X25519:
|
||||
if (keyData[index++] != 110)
|
||||
return nullptr;
|
||||
break;
|
||||
case NamedCurve::Ed25519:
|
||||
if (keyData[index++] != 112)
|
||||
return nullptr;
|
||||
break;
|
||||
};
|
||||
|
||||
// Read BIT STRING
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
if (keyData[index++] != 3)
|
||||
return nullptr;
|
||||
|
||||
// Read length
|
||||
// FIXME: Check length is keyByteSize + 1.
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Initial octet
|
||||
if (!!keyData[index])
|
||||
return nullptr;
|
||||
++index;
|
||||
|
||||
return create(identifier, namedCurve, CryptoKeyType::Public, Span<const uint8_t> { keyData.data() + index, keyData.size() - index }, extractable, usages);
|
||||
}
|
||||
|
||||
constexpr uint8_t OKPOIDFirstByte = 6;
|
||||
constexpr uint8_t OKPOIDSecondByte = 3;
|
||||
constexpr uint8_t OKPOIDThirdByte = 43;
|
||||
constexpr uint8_t OKPOIDFourthByte = 101;
|
||||
constexpr uint8_t OKPOIDX25519Byte = 110;
|
||||
constexpr uint8_t OKPOIDEd25519Byte = 112;
|
||||
|
||||
static void writeOID(CryptoKeyOKP::NamedCurve namedCurve, Vector<uint8_t>& result)
|
||||
{
|
||||
result.append(OKPOIDFirstByte);
|
||||
result.append(OKPOIDSecondByte);
|
||||
result.append(OKPOIDThirdByte);
|
||||
result.append(OKPOIDFourthByte);
|
||||
|
||||
switch (namedCurve) {
|
||||
case CryptoKeyOKP::NamedCurve::X25519:
|
||||
result.append(OKPOIDX25519Byte);
|
||||
break;
|
||||
case CryptoKeyOKP::NamedCurve::Ed25519:
|
||||
result.append(OKPOIDEd25519Byte);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportSpki() const
|
||||
{
|
||||
if (type() != CryptoKeyType::Public)
|
||||
return Exception { InvalidAccessError };
|
||||
|
||||
size_t keySize = keySizeInBytes();
|
||||
|
||||
// SEQUENCE, length, SEQUENCE, length, OID, Bit String (Initial octet prepended)
|
||||
size_t totalSize = 1 + 1 + 1 + 1 + 5 + 1 + 1 + 1 + keySize;
|
||||
Vector<uint8_t> result;
|
||||
result.reserveInitialCapacity(totalSize);
|
||||
result.append(SequenceMark);
|
||||
addEncodedASN1Length(result, totalSize - 2);
|
||||
result.append(SequenceMark);
|
||||
addEncodedASN1Length(result, 5);
|
||||
|
||||
writeOID(namedCurve(), result);
|
||||
|
||||
result.append(BitStringMark);
|
||||
addEncodedASN1Length(result, keySize + 1);
|
||||
result.append(InitialOctet);
|
||||
result.append(platformKey().data(), platformKey().size());
|
||||
|
||||
ASSERT(result.size() == totalSize);
|
||||
|
||||
return WTFMove(result);
|
||||
}
|
||||
|
||||
// Per https://www.ietf.org/rfc/rfc5280.txt
|
||||
// PrivateKeyInfo ::= SEQUENCE { version INTEGER, privateKeyAlgorithm AlgorithmIdentifier, privateKey OCTET STRING }
|
||||
// AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }
|
||||
// Per https://www.rfc-editor.org/rfc/rfc8410
|
||||
// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 }
|
||||
// id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 }
|
||||
// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
// id-Ed448 OBJECT IDENTIFIER ::= { 1 3 101 113 }
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
RefPtr<CryptoKeyOKP> CryptoKeyOKP::importPkcs8(CryptoAlgorithmIdentifier identifier, NamedCurve namedCurve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
|
||||
{
|
||||
// FIXME: We should use the underlying crypto library to import PKCS8 OKP keys.
|
||||
|
||||
// Read SEQUENCE
|
||||
size_t index = 1;
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read length
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read version
|
||||
index += 3;
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read SEQUENCE
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read length
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read OID
|
||||
if (keyData[index++] != OKPOIDFirstByte || keyData[index++] != OKPOIDSecondByte || keyData[index++] != OKPOIDThirdByte || keyData[index++] != OKPOIDFourthByte)
|
||||
return nullptr;
|
||||
|
||||
switch (namedCurve) {
|
||||
case NamedCurve::X25519:
|
||||
if (keyData[index++] != OKPOIDX25519Byte)
|
||||
return nullptr;
|
||||
break;
|
||||
case NamedCurve::Ed25519:
|
||||
if (keyData[index++] != OKPOIDEd25519Byte)
|
||||
return nullptr;
|
||||
break;
|
||||
};
|
||||
|
||||
// Read OCTET STRING
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
if (keyData[index++] != 4)
|
||||
return nullptr;
|
||||
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
// Read OCTET STRING
|
||||
if (keyData[index++] != 4)
|
||||
return nullptr;
|
||||
|
||||
index += bytesUsedToEncodedLength(keyData[index]);
|
||||
if (keyData.size() < index + 1)
|
||||
return nullptr;
|
||||
|
||||
return create(identifier, namedCurve, CryptoKeyType::Private, Span<const uint8_t> { keyData.data() + index, keyData.size() - index }, extractable, usages);
|
||||
}
|
||||
|
||||
ExceptionOr<Vector<uint8_t>> CryptoKeyOKP::exportPkcs8() const
|
||||
{
|
||||
if (type() != CryptoKeyType::Private)
|
||||
return Exception { InvalidAccessError };
|
||||
|
||||
size_t keySize = exportKeySizeInBytes();
|
||||
|
||||
// SEQUENCE, length, version SEQUENCE, length, OID, Octet String Octet String
|
||||
size_t totalSize = 1 + 1 + 3 + 1 + 1 + 5 + 1 + 1 + 1 + 1 + keySize;
|
||||
Vector<uint8_t> result;
|
||||
result.reserveInitialCapacity(totalSize);
|
||||
result.append(SequenceMark);
|
||||
addEncodedASN1Length(result, totalSize - 2);
|
||||
|
||||
result.append(2);
|
||||
result.append(1);
|
||||
result.append(0);
|
||||
|
||||
result.append(SequenceMark);
|
||||
addEncodedASN1Length(result, 5);
|
||||
|
||||
writeOID(namedCurve(), result);
|
||||
|
||||
result.append(OctetStringMark);
|
||||
addEncodedASN1Length(result, keySize + 2);
|
||||
result.append(OctetStringMark);
|
||||
addEncodedASN1Length(result, keySize);
|
||||
result.append(exportKey().data(), exportKey().size());
|
||||
|
||||
ASSERT(result.size() == totalSize);
|
||||
|
||||
return WTFMove(result);
|
||||
}
|
||||
|
||||
String CryptoKeyOKP::generateJwkD() const
|
||||
{
|
||||
ASSERT(type() == CryptoKeyType::Private);
|
||||
if (namedCurve() == NamedCurve::Ed25519) {
|
||||
ASSERT(m_exportKey);
|
||||
return base64URLEncodeToString(*m_exportKey);
|
||||
}
|
||||
return base64URLEncodeToString(m_data);
|
||||
}
|
||||
|
||||
CryptoKeyOKP::KeyMaterial CryptoKeyOKP::ed25519PublicFromPrivate(const KeyMaterial& seed)
|
||||
{
|
||||
auto publicKey = KeyMaterial(ED25519_PUBLIC_KEY_LEN);
|
||||
uint8_t privateKey[ED25519_PRIVATE_KEY_LEN];
|
||||
|
||||
ED25519_keypair_from_seed(publicKey.data(), privateKey, seed.data());
|
||||
|
||||
return WTFMove(publicKey);
|
||||
}
|
||||
|
||||
CryptoKeyOKP::KeyMaterial CryptoKeyOKP::x25519PublicFromPrivate(const KeyMaterial& privateKey)
|
||||
{
|
||||
auto publicKey = KeyMaterial(X25519_PUBLIC_VALUE_LEN);
|
||||
|
||||
X25519_public_from_private(publicKey.data(), privateKey.data());
|
||||
|
||||
return WTFMove(publicKey);
|
||||
}
|
||||
|
||||
CryptoKeyOKP::KeyMaterial CryptoKeyOKP::ed25519PrivateFromSeed(KeyMaterial&& seed)
|
||||
{
|
||||
uint8_t publicKey[ED25519_PUBLIC_KEY_LEN];
|
||||
auto privateKey = KeyMaterial(ED25519_PRIVATE_KEY_LEN);
|
||||
|
||||
ED25519_keypair_from_seed(publicKey, privateKey.data(), seed.data());
|
||||
|
||||
return WTFMove(privateKey);
|
||||
}
|
||||
|
||||
String CryptoKeyOKP::generateJwkX() const
|
||||
{
|
||||
if (type() == CryptoKeyType::Public)
|
||||
return base64URLEncodeToString(m_data);
|
||||
|
||||
ASSERT(type() == CryptoKeyType::Private);
|
||||
|
||||
if (namedCurve() == NamedCurve::Ed25519)
|
||||
return base64URLEncodeToString(WTFMove(ed25519PublicFromPrivate(const_cast<KeyMaterial&>(m_data))));
|
||||
|
||||
ASSERT(namedCurve() == NamedCurve::X25519);
|
||||
return base64URLEncodeToString(WTFMove(x25519PublicFromPrivate(const_cast<KeyMaterial&>(m_data))));
|
||||
}
|
||||
|
||||
CryptoKeyOKP::KeyMaterial CryptoKeyOKP::platformExportRaw() const
|
||||
{
|
||||
if (namedCurve() == NamedCurve::Ed25519 && type() == CryptoKeyType::Private) {
|
||||
ASSERT(m_exportKey);
|
||||
const auto& exportKey = *m_exportKey;
|
||||
return WTFMove(Vector<uint8_t>(exportKey.data(), exportKey.size()));
|
||||
}
|
||||
return WTFMove(KeyMaterial(m_data.data(), m_data.size()));
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // ENABLE(WEB_CRYPTO)
|
||||
@@ -88,6 +88,22 @@ static ExceptionOr<CryptoAlgorithmIdentifier> toHashIdentifier(JSGlobalObject& s
|
||||
return digestParams.returnValue()->identifier;
|
||||
}
|
||||
|
||||
static bool isRSAESPKCSWebCryptoDeprecated(JSGlobalObject& state)
|
||||
{
|
||||
return true;
|
||||
// auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&state);
|
||||
// auto* context = globalObject.scriptExecutionContext();
|
||||
// return context && context->settingsValues().deprecateRSAESPKCSWebCryptoEnabled;
|
||||
}
|
||||
|
||||
static bool isSafeCurvesEnabled(JSGlobalObject& state)
|
||||
{
|
||||
return true;
|
||||
// auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&state);
|
||||
// auto* context = globalObject.scriptExecutionContext();
|
||||
// return context && context->settingsValues().webCryptoSafeCurvesEnabled;
|
||||
}
|
||||
|
||||
static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAlgorithmParameters(JSGlobalObject& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier, Operations operation)
|
||||
{
|
||||
VM& vm = state.vm();
|
||||
@@ -109,12 +125,17 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
if (UNLIKELY(!identifier))
|
||||
return Exception { NotSupportedError };
|
||||
|
||||
if (*identifier == CryptoAlgorithmIdentifier::Ed25519 && !isSafeCurvesEnabled(state))
|
||||
return Exception { NotSupportedError };
|
||||
|
||||
std::unique_ptr<CryptoAlgorithmParameters> result;
|
||||
switch (operation) {
|
||||
case Operations::Encrypt:
|
||||
case Operations::Decrypt:
|
||||
switch (*identifier) {
|
||||
case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
|
||||
if (isRSAESPKCSWebCryptoDeprecated(state))
|
||||
return Exception { NotSupportedError, "RSAES-PKCS1-v1_5 support is deprecated"_s };
|
||||
result = makeUnique<CryptoAlgorithmParameters>(params);
|
||||
break;
|
||||
case CryptoAlgorithmIdentifier::RSA_OAEP: {
|
||||
@@ -151,6 +172,7 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
switch (*identifier) {
|
||||
case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
|
||||
case CryptoAlgorithmIdentifier::HMAC:
|
||||
case CryptoAlgorithmIdentifier::Ed25519:
|
||||
result = makeUnique<CryptoAlgorithmParameters>(params);
|
||||
break;
|
||||
case CryptoAlgorithmIdentifier::ECDSA: {
|
||||
@@ -189,6 +211,8 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
case Operations::GenerateKey:
|
||||
switch (*identifier) {
|
||||
case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: {
|
||||
if (isRSAESPKCSWebCryptoDeprecated(state))
|
||||
return Exception { NotSupportedError, "RSAES-PKCS1-v1_5 support is deprecated"_s };
|
||||
auto params = convertDictionary<CryptoAlgorithmRsaKeyGenParams>(state, value.get());
|
||||
RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
|
||||
result = makeUnique<CryptoAlgorithmRsaKeyGenParams>(params);
|
||||
@@ -233,6 +257,9 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
result = makeUnique<CryptoAlgorithmEcKeyParams>(params);
|
||||
break;
|
||||
}
|
||||
case CryptoAlgorithmIdentifier::Ed25519:
|
||||
result = makeUnique<CryptoAlgorithmParameters>(params);
|
||||
break;
|
||||
default:
|
||||
return Exception { NotSupportedError };
|
||||
}
|
||||
@@ -279,6 +306,8 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
case Operations::ImportKey:
|
||||
switch (*identifier) {
|
||||
case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
|
||||
if (isRSAESPKCSWebCryptoDeprecated(state))
|
||||
return Exception { NotSupportedError, "RSAES-PKCS1-v1_5 support is deprecated"_s };
|
||||
result = makeUnique<CryptoAlgorithmParameters>(params);
|
||||
break;
|
||||
case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
|
||||
@@ -298,6 +327,7 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
case CryptoAlgorithmIdentifier::AES_GCM:
|
||||
case CryptoAlgorithmIdentifier::AES_CFB:
|
||||
case CryptoAlgorithmIdentifier::AES_KW:
|
||||
case CryptoAlgorithmIdentifier::Ed25519:
|
||||
result = makeUnique<CryptoAlgorithmParameters>(params);
|
||||
break;
|
||||
case CryptoAlgorithmIdentifier::HMAC: {
|
||||
@@ -321,7 +351,11 @@ static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAl
|
||||
case CryptoAlgorithmIdentifier::PBKDF2:
|
||||
result = makeUnique<CryptoAlgorithmParameters>(params);
|
||||
break;
|
||||
default:
|
||||
case CryptoAlgorithmIdentifier::SHA_1:
|
||||
case CryptoAlgorithmIdentifier::SHA_224:
|
||||
case CryptoAlgorithmIdentifier::SHA_256:
|
||||
case CryptoAlgorithmIdentifier::SHA_384:
|
||||
case CryptoAlgorithmIdentifier::SHA_512:
|
||||
return Exception { NotSupportedError };
|
||||
}
|
||||
break;
|
||||
@@ -482,10 +516,11 @@ static Vector<uint8_t> copyToVector(BufferSource&& data)
|
||||
return { data.data(), data.length() };
|
||||
}
|
||||
|
||||
static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier)
|
||||
static bool isSupportedExportKey(JSGlobalObject& state, CryptoAlgorithmIdentifier identifier)
|
||||
{
|
||||
switch (identifier) {
|
||||
case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
|
||||
return !isRSAESPKCSWebCryptoDeprecated(state);
|
||||
case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
|
||||
case CryptoAlgorithmIdentifier::RSA_PSS:
|
||||
case CryptoAlgorithmIdentifier::RSA_OAEP:
|
||||
@@ -497,6 +532,7 @@ static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier)
|
||||
case CryptoAlgorithmIdentifier::HMAC:
|
||||
case CryptoAlgorithmIdentifier::ECDSA:
|
||||
case CryptoAlgorithmIdentifier::ECDH:
|
||||
case CryptoAlgorithmIdentifier::Ed25519:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -932,7 +968,7 @@ void SubtleCrypto::importKey(JSC::JSGlobalObject& state, KeyFormat format, KeyDa
|
||||
|
||||
void SubtleCrypto::exportKey(KeyFormat format, CryptoKey& key, Ref<DeferredPromise>&& promise)
|
||||
{
|
||||
if (!isSupportedExportKey(key.algorithmIdentifier())) {
|
||||
if (!isSupportedExportKey(*promise->globalObject(), key.algorithmIdentifier())) {
|
||||
promise->reject(Exception { NotSupportedError });
|
||||
return;
|
||||
}
|
||||
@@ -1003,7 +1039,7 @@ void SubtleCrypto::wrapKey(JSC::JSGlobalObject& state, KeyFormat format, CryptoK
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSupportedExportKey(key.algorithmIdentifier())) {
|
||||
if (!isSupportedExportKey(state, key.algorithmIdentifier())) {
|
||||
promise->reject(Exception { NotSupportedError });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ namespace WebCore {
|
||||
const JSC::ConstructAbility s_processObjectInternalsGetStdioWriteStreamCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
|
||||
const JSC::ConstructorKind s_processObjectInternalsGetStdioWriteStreamCodeConstructorKind = JSC::ConstructorKind::None;
|
||||
const JSC::ImplementationVisibility s_processObjectInternalsGetStdioWriteStreamCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
|
||||
const int s_processObjectInternalsGetStdioWriteStreamCodeLength = 9968;
|
||||
const int s_processObjectInternalsGetStdioWriteStreamCodeLength = 9767;
|
||||
static const JSC::Intrinsic s_processObjectInternalsGetStdioWriteStreamCodeIntrinsic = JSC::NoIntrinsic;
|
||||
const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
"(function (fd_, rawRequire) {\n" \
|
||||
" var module = { path: \"node:process\", require: rawRequire };\n" \
|
||||
" var require = (path) => module.require(path);\n" \
|
||||
" var require = path => module.require(path);\n" \
|
||||
"\n" \
|
||||
" function createStdioWriteStream(fd_) {\n" \
|
||||
" var { Duplex, eos, destroy } = require(\"node:stream\");\n" \
|
||||
@@ -103,20 +103,11 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
" _destroy(err, callback) {\n" \
|
||||
" if (!err && this.#onClose !== null) {\n" \
|
||||
" var AbortError = class AbortError extends Error {\n" \
|
||||
" constructor(\n" \
|
||||
" message = \"The operation was aborted\",\n" \
|
||||
" options = void 0,\n" \
|
||||
" ) {\n" \
|
||||
" constructor(message = \"The operation was aborted\", options = void 0) {\n" \
|
||||
" if (options !== void 0 && typeof options !== \"object\") {\n" \
|
||||
" throw new Error(\n" \
|
||||
" `Invalid AbortError options:\\n" \
|
||||
" throw new Error(`Invalid AbortError options:\\n" \
|
||||
"\\n" \
|
||||
"${JSON.stringify(\n" \
|
||||
" options,\n" \
|
||||
" null,\n" \
|
||||
" 2,\n" \
|
||||
" )}`,\n" \
|
||||
" );\n" \
|
||||
"${JSON.stringify(options, null, 2)}`);\n" \
|
||||
" }\n" \
|
||||
" super(message, options);\n" \
|
||||
" this.code = \"ABORT_ERR\";\n" \
|
||||
@@ -158,7 +149,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
" }\n" \
|
||||
" });\n" \
|
||||
"\n" \
|
||||
" eos(stream, (err) => {\n" \
|
||||
" eos(stream, err => {\n" \
|
||||
" this.#writable = false;\n" \
|
||||
" if (err) {\n" \
|
||||
" destroy(stream, err);\n" \
|
||||
@@ -197,7 +188,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
" this.push(null);\n" \
|
||||
" });\n" \
|
||||
"\n" \
|
||||
" eos(readStream, (err) => {\n" \
|
||||
" eos(readStream, err => {\n" \
|
||||
" this.#readable = false;\n" \
|
||||
" if (err) {\n" \
|
||||
" destroy(readStream, err);\n" \
|
||||
@@ -230,12 +221,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
" if (!encoding) return true;\n" \
|
||||
"\n" \
|
||||
" var normalied = encoding.toLowerCase();\n" \
|
||||
" return (\n" \
|
||||
" normalied === \"utf8\" ||\n" \
|
||||
" normalied === \"utf-8\" ||\n" \
|
||||
" normalied === \"buffer\" ||\n" \
|
||||
" normalied === \"binary\"\n" \
|
||||
" );\n" \
|
||||
" return normalied === \"utf8\" || normalied === \"utf-8\" || normalied === \"buffer\" || normalied === \"binary\";\n" \
|
||||
" }\n" \
|
||||
"\n" \
|
||||
" var FastStdioWriteStream = class StdioWriteStream extends EventEmitter {\n" \
|
||||
@@ -389,7 +375,7 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
" this.#performCallback(callback);\n" \
|
||||
" this.emit(\"drain\");\n" \
|
||||
" },\n" \
|
||||
" (err) => this.#performCallback(callback, err),\n" \
|
||||
" err => this.#performCallback(callback, err),\n" \
|
||||
" );\n" \
|
||||
" return false;\n" \
|
||||
" }\n" \
|
||||
@@ -472,12 +458,12 @@ const char* const s_processObjectInternalsGetStdioWriteStreamCode =
|
||||
const JSC::ConstructAbility s_processObjectInternalsGetStdinStreamCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
|
||||
const JSC::ConstructorKind s_processObjectInternalsGetStdinStreamCodeConstructorKind = JSC::ConstructorKind::None;
|
||||
const JSC::ImplementationVisibility s_processObjectInternalsGetStdinStreamCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
|
||||
const int s_processObjectInternalsGetStdinStreamCodeLength = 4415;
|
||||
const int s_processObjectInternalsGetStdinStreamCodeLength = 4305;
|
||||
static const JSC::Intrinsic s_processObjectInternalsGetStdinStreamCodeIntrinsic = JSC::NoIntrinsic;
|
||||
const char* const s_processObjectInternalsGetStdinStreamCode =
|
||||
"(function (fd_, rawRequire, Bun) {\n" \
|
||||
" var module = { path: \"node:process\", require: rawRequire };\n" \
|
||||
" var require = (path) => module.require(path);\n" \
|
||||
" var require = path => module.require(path);\n" \
|
||||
"\n" \
|
||||
" var { Duplex, eos, destroy } = require(\"node:stream\");\n" \
|
||||
"\n" \
|
||||
@@ -526,15 +512,9 @@ const char* const s_processObjectInternalsGetStdinStreamCode =
|
||||
" var AbortError = class AbortError extends Error {\n" \
|
||||
" constructor(message = \"The operation was aborted\", options = void 0) {\n" \
|
||||
" if (options !== void 0 && typeof options !== \"object\") {\n" \
|
||||
" throw new Error(\n" \
|
||||
" `Invalid AbortError options:\\n" \
|
||||
" throw new Error(`Invalid AbortError options:\\n" \
|
||||
"\\n" \
|
||||
"${JSON.stringify(\n" \
|
||||
" options,\n" \
|
||||
" null,\n" \
|
||||
" 2,\n" \
|
||||
" )}`,\n" \
|
||||
" );\n" \
|
||||
"${JSON.stringify(options, null, 2)}`);\n" \
|
||||
" }\n" \
|
||||
" super(message, options);\n" \
|
||||
" this.code = \"ABORT_ERR\";\n" \
|
||||
@@ -652,7 +632,7 @@ const char* const s_processObjectInternalsGetStdinStreamCode =
|
||||
" }\n" \
|
||||
" });\n" \
|
||||
"\n" \
|
||||
" eos(writeStream, (err) => {\n" \
|
||||
" eos(writeStream, err => {\n" \
|
||||
" this.#writable = false;\n" \
|
||||
" if (err) {\n" \
|
||||
" destroy(writeStream, err);\n" \
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { describe, expect, it, beforeAll } from "bun:test";
|
||||
|
||||
type Ed25519KeyPair = {
|
||||
privateKey: CryptoKey;
|
||||
publicKey: CryptoKey;
|
||||
};
|
||||
|
||||
describe("Web Crypto", () => {
|
||||
it("has globals", () => {
|
||||
@@ -71,4 +76,104 @@ describe("Web Crypto", () => {
|
||||
const isSigValid = await verifySignature(msg, signature, SECRET);
|
||||
expect(isSigValid).toBe(true);
|
||||
});
|
||||
|
||||
describe("Ed25519", () => {
|
||||
let importedKeypair: Ed25519KeyPair;
|
||||
|
||||
describe("generateKey", () => {
|
||||
it("should generate key pair", async () => {
|
||||
const keyPair = (await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"])) as Ed25519KeyPair;
|
||||
expect(keyPair.privateKey).toBeDefined();
|
||||
expect(keyPair.privateKey instanceof CryptoKey).toBe(true);
|
||||
expect(keyPair.publicKey).toBeDefined();
|
||||
expect(keyPair.publicKey instanceof CryptoKey).toBe(true);
|
||||
});
|
||||
|
||||
it("should generate an extractable key pair", async () => {
|
||||
const keyPair = (await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"])) as Ed25519KeyPair;
|
||||
const privateKey = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
|
||||
const publicKey = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
|
||||
|
||||
expect(privateKey).toBeDefined();
|
||||
expect(privateKey.d).toBeDefined();
|
||||
expect(privateKey.x).toBeDefined();
|
||||
expect(privateKey.x!.length).toBe(43);
|
||||
expect(privateKey.d!.length).toBe(43);
|
||||
expect(privateKey.kty).toEqual("OKP");
|
||||
expect(privateKey.crv).toEqual("Ed25519");
|
||||
expect(privateKey.ext).toBe(true);
|
||||
expect(privateKey.key_ops).toStrictEqual(["sign"]);
|
||||
|
||||
expect(publicKey).toBeDefined();
|
||||
expect(publicKey.x).toBeDefined();
|
||||
expect(publicKey.x!.length).toBe(43);
|
||||
expect(publicKey.kty).toEqual("OKP");
|
||||
expect(publicKey.crv).toEqual("Ed25519");
|
||||
expect(publicKey.ext).toBe(true);
|
||||
expect(publicKey.key_ops).toStrictEqual(["verify"]);
|
||||
});
|
||||
|
||||
it("should generate an nonextractable private key", async done => {
|
||||
const keyPair = (await crypto.subtle.generateKey("Ed25519", false, ["sign", "verify"])) as Ed25519KeyPair;
|
||||
expect(keyPair.privateKey).toBeDefined();
|
||||
expect(keyPair.publicKey).toBeDefined();
|
||||
try {
|
||||
await crypto.subtle.exportKey("jwk", keyPair.privateKey);
|
||||
done(new Error("Should not be able to export private key"));
|
||||
} catch (e) {
|
||||
if (!(e instanceof Error)) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
expect(e.message).toBe("The CryptoKey is nonextractable");
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("should generate keys with correct usages", async () => {
|
||||
const keyPair1 = (await crypto.subtle.generateKey("Ed25519", false, ["sign"])) as Ed25519KeyPair;
|
||||
const keyPair2 = (await crypto.subtle.generateKey("Ed25519", false, ["sign", "verify"])) as Ed25519KeyPair;
|
||||
|
||||
expect(keyPair1.privateKey?.usages).toBeDefined();
|
||||
expect(keyPair1.publicKey?.usages).toBeDefined();
|
||||
expect(keyPair1.privateKey?.usages).toStrictEqual(["sign"]);
|
||||
expect(keyPair1.publicKey?.usages).toStrictEqual([]);
|
||||
|
||||
expect(keyPair2.privateKey?.usages).toBeDefined();
|
||||
expect(keyPair2.publicKey?.usages).toBeDefined();
|
||||
expect(keyPair2.privateKey?.usages).toEqual(["sign"]);
|
||||
expect(keyPair2.publicKey?.usages).toEqual(["verify"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("importKey", () => {
|
||||
it("should do raw import", async () => {
|
||||
const privateKey = "whvdISVptebNycNBnzsGltGsSWhThuD-mP2tcsBbNt8";
|
||||
const buf = Buffer.from(privateKey, "base64url");
|
||||
console.log(buf);
|
||||
const imported = await crypto.subtle.importKey("raw", buf, "Ed25519", false, ["sign", "verify"]);
|
||||
});
|
||||
|
||||
it("should do JWK import", async () => {
|
||||
const kp = await crypto.subtle.importKey(
|
||||
"jwk",
|
||||
{
|
||||
kty: "OKP",
|
||||
d: "whvdISVptebNycNBnzsGltGsSWhThuD-mP2tcsBbNt8",
|
||||
use: "sig",
|
||||
crv: "Ed25519",
|
||||
// kid: "sig-1675587884",
|
||||
x: "jZJN1eHyhwujYgS9btOxrSGJuVrWVmJMmkovz6vmmJQ",
|
||||
alg: "EdDSA",
|
||||
ext: true,
|
||||
},
|
||||
"Ed25519",
|
||||
true,
|
||||
["sign"],
|
||||
);
|
||||
});
|
||||
|
||||
it("should do PKCS8 import", () => {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user