mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 08:39:09 -05:00
feat(ext/crypto): implement deriveBits for ECDH (p256) (#11873)
This commit is contained in:
parent
c49a057599
commit
a5d3c8b06c
7 changed files with 240 additions and 3 deletions
102
Cargo.lock
generated
102
Cargo.lock
generated
|
@ -482,6 +482,17 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"rand_core 0.6.3",
|
"rand_core 0.6.3",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-mac"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -727,6 +738,8 @@ dependencies = [
|
||||||
"deno_web",
|
"deno_web",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"p256",
|
||||||
|
"p384",
|
||||||
"rand 0.8.4",
|
"rand 0.8.4",
|
||||||
"ring",
|
"ring",
|
||||||
"rsa",
|
"rsa",
|
||||||
|
@ -1116,12 +1129,40 @@ dependencies = [
|
||||||
"text_lines",
|
"text_lines",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"elliptic-curve",
|
||||||
|
"hmac",
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-bigint",
|
||||||
|
"ff",
|
||||||
|
"generic-array",
|
||||||
|
"group",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core 0.6.3",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.28"
|
version = "0.8.28"
|
||||||
|
@ -1228,6 +1269,16 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.3",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1544,6 +1595,17 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.6.3",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -1608,6 +1670,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-mac",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hostname"
|
name = "hostname"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -2336,6 +2408,26 @@ dependencies = [
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p256"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186"
|
||||||
|
dependencies = [
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p384"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f23bc88c404ccc881c8a1ad62ba5cd7d336a64ecbf46de4874f2ad955f67b157"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -3221,6 +3313,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"rand_core 0.6.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
|
|
@ -513,6 +513,31 @@ unitTest(async function testHkdfDeriveBits() {
|
||||||
assertEquals(result.byteLength, 128 / 8);
|
assertEquals(result.byteLength, 128 / 8);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO(@littledivy): Enable WPT when we have importKey support
|
||||||
|
unitTest(async function testECDH() {
|
||||||
|
const namedCurve = "P-256";
|
||||||
|
const keyPair = await crypto.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "ECDH",
|
||||||
|
namedCurve,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["deriveBits"],
|
||||||
|
);
|
||||||
|
|
||||||
|
const derivedKey = await crypto.subtle.deriveBits(
|
||||||
|
{
|
||||||
|
name: "ECDH",
|
||||||
|
public: keyPair.publicKey,
|
||||||
|
},
|
||||||
|
keyPair.privateKey,
|
||||||
|
256,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(derivedKey instanceof ArrayBuffer);
|
||||||
|
assertEquals(derivedKey.byteLength, 256 / 8);
|
||||||
|
});
|
||||||
|
|
||||||
unitTest(async function testWrapKey() {
|
unitTest(async function testWrapKey() {
|
||||||
// Test wrapKey
|
// Test wrapKey
|
||||||
const key = await crypto.subtle.generateKey(
|
const key = await crypto.subtle.generateKey(
|
||||||
|
|
|
@ -113,6 +113,7 @@
|
||||||
"deriveBits": {
|
"deriveBits": {
|
||||||
"HKDF": "HkdfParams",
|
"HKDF": "HkdfParams",
|
||||||
"PBKDF2": "Pbkdf2Params",
|
"PBKDF2": "Pbkdf2Params",
|
||||||
|
"ECDH": "EcdhKeyDeriveParams",
|
||||||
},
|
},
|
||||||
"encrypt": {
|
"encrypt": {
|
||||||
"RSA-OAEP": "RsaOaepParams",
|
"RSA-OAEP": "RsaOaepParams",
|
||||||
|
@ -2138,6 +2139,58 @@
|
||||||
|
|
||||||
return buf.buffer;
|
return buf.buffer;
|
||||||
}
|
}
|
||||||
|
case "ECDH": {
|
||||||
|
// 1.
|
||||||
|
if (baseKey[_type] !== "private") {
|
||||||
|
throw new DOMException("Invalid key type", "InvalidAccessError");
|
||||||
|
}
|
||||||
|
// 2.
|
||||||
|
const publicKey = normalizedAlgorithm.public;
|
||||||
|
// 3.
|
||||||
|
if (publicKey[_type] !== "public") {
|
||||||
|
throw new DOMException("Invalid key type", "InvalidAccessError");
|
||||||
|
}
|
||||||
|
// 4.
|
||||||
|
if (publicKey[_algorithm].name !== baseKey[_algorithm].name) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Algorithm mismatch",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 5.
|
||||||
|
if (
|
||||||
|
publicKey[_algorithm].namedCurve !== baseKey[_algorithm].namedCurve
|
||||||
|
) {
|
||||||
|
throw new DOMException(
|
||||||
|
"namedCurve mismatch",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 6.
|
||||||
|
if (
|
||||||
|
ArrayPrototypeIncludes(
|
||||||
|
supportedNamedCurves,
|
||||||
|
publicKey[_algorithm].namedCurve,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const baseKeyhandle = baseKey[_handle];
|
||||||
|
const baseKeyData = WeakMapPrototypeGet(KEY_STORE, baseKeyhandle);
|
||||||
|
const publicKeyhandle = baseKey[_handle];
|
||||||
|
const publicKeyData = WeakMapPrototypeGet(KEY_STORE, publicKeyhandle);
|
||||||
|
|
||||||
|
const buf = await core.opAsync("op_crypto_derive_bits", {
|
||||||
|
key: baseKeyData,
|
||||||
|
publicKey: publicKeyData,
|
||||||
|
algorithm: "ECDH",
|
||||||
|
namedCurve: publicKey[_algorithm].namedCurve,
|
||||||
|
length,
|
||||||
|
});
|
||||||
|
|
||||||
|
return buf.buffer;
|
||||||
|
} else {
|
||||||
|
throw new DOMException("Not implemented", "NotSupportedError");
|
||||||
|
}
|
||||||
|
}
|
||||||
case "HKDF": {
|
case "HKDF": {
|
||||||
// 1.
|
// 1.
|
||||||
if (length === null || length === 0 || length % 8 !== 0) {
|
if (length === null || length === 0 || length % 8 !== 0) {
|
||||||
|
|
|
@ -385,4 +385,16 @@
|
||||||
|
|
||||||
webidl.converters.CryptoKeyPair = webidl
|
webidl.converters.CryptoKeyPair = webidl
|
||||||
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
||||||
|
|
||||||
|
const dictEcdhKeyDeriveParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "public",
|
||||||
|
converter: webidl.converters.CryptoKey,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.EcdhKeyDeriveParams = webidl
|
||||||
|
.createDictionaryConverter("EcdhKeyDeriveParams", dictEcdhKeyDeriveParams);
|
||||||
})(this);
|
})(this);
|
||||||
|
|
|
@ -18,6 +18,8 @@ deno_core = { version = "0.102.0", path = "../../core" }
|
||||||
deno_web = { version = "0.51.0", path = "../web" }
|
deno_web = { version = "0.51.0", path = "../web" }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
|
p256 = { version = "0.9.0", features = ["ecdh"] }
|
||||||
|
p384 = "0.8.0"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
ring = { version = "0.16.20", features = ["std"] }
|
ring = { version = "0.16.20", features = ["std"] }
|
||||||
rsa = { version = "0.5.0", default-features = false, features = ["std"] }
|
rsa = { version = "0.5.0", default-features = false, features = ["std"] }
|
||||||
|
|
10
ext/crypto/lib.deno_crypto.d.ts
vendored
10
ext/crypto/lib.deno_crypto.d.ts
vendored
|
@ -125,6 +125,10 @@ interface Pbkdf2Params extends Algorithm {
|
||||||
salt: BufferSource;
|
salt: BufferSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EcdhKeyDeriveParams extends Algorithm {
|
||||||
|
public: CryptoKey;
|
||||||
|
}
|
||||||
|
|
||||||
interface AesKeyGenParams extends Algorithm {
|
interface AesKeyGenParams extends Algorithm {
|
||||||
length: number;
|
length: number;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +223,11 @@ interface SubtleCrypto {
|
||||||
data: BufferSource,
|
data: BufferSource,
|
||||||
): Promise<ArrayBuffer>;
|
): Promise<ArrayBuffer>;
|
||||||
deriveBits(
|
deriveBits(
|
||||||
algorithm: AlgorithmIdentifier | HkdfParams | Pbkdf2Params,
|
algorithm:
|
||||||
|
| AlgorithmIdentifier
|
||||||
|
| HkdfParams
|
||||||
|
| Pbkdf2Params
|
||||||
|
| EcdhKeyDeriveParams,
|
||||||
baseKey: CryptoKey,
|
baseKey: CryptoKey,
|
||||||
length: number,
|
length: number,
|
||||||
): Promise<ArrayBuffer>;
|
): Promise<ArrayBuffer>;
|
||||||
|
|
|
@ -43,6 +43,7 @@ use rsa::pkcs1::der::Encodable;
|
||||||
use rsa::pkcs1::FromRsaPrivateKey;
|
use rsa::pkcs1::FromRsaPrivateKey;
|
||||||
use rsa::pkcs1::ToRsaPrivateKey;
|
use rsa::pkcs1::ToRsaPrivateKey;
|
||||||
use rsa::pkcs8::der::asn1;
|
use rsa::pkcs8::der::asn1;
|
||||||
|
use rsa::pkcs8::FromPrivateKey;
|
||||||
use rsa::BigUint;
|
use rsa::BigUint;
|
||||||
use rsa::PublicKey;
|
use rsa::PublicKey;
|
||||||
use rsa::RsaPrivateKey;
|
use rsa::RsaPrivateKey;
|
||||||
|
@ -792,18 +793,23 @@ pub struct DeriveKeyArg {
|
||||||
hash: Option<CryptoHash>,
|
hash: Option<CryptoHash>,
|
||||||
length: usize,
|
length: usize,
|
||||||
iterations: Option<u32>,
|
iterations: Option<u32>,
|
||||||
|
// ECDH
|
||||||
|
public_key: Option<KeyData>,
|
||||||
|
named_curve: Option<CryptoNamedCurve>,
|
||||||
|
// HKDF
|
||||||
info: Option<ZeroCopyBuf>,
|
info: Option<ZeroCopyBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn op_crypto_derive_bits(
|
pub async fn op_crypto_derive_bits(
|
||||||
_state: Rc<RefCell<OpState>>,
|
_state: Rc<RefCell<OpState>>,
|
||||||
args: DeriveKeyArg,
|
args: DeriveKeyArg,
|
||||||
zero_copy: ZeroCopyBuf,
|
zero_copy: Option<ZeroCopyBuf>,
|
||||||
) -> Result<ZeroCopyBuf, AnyError> {
|
) -> Result<ZeroCopyBuf, AnyError> {
|
||||||
let salt = &*zero_copy;
|
|
||||||
let algorithm = args.algorithm;
|
let algorithm = args.algorithm;
|
||||||
match algorithm {
|
match algorithm {
|
||||||
Algorithm::Pbkdf2 => {
|
Algorithm::Pbkdf2 => {
|
||||||
|
let zero_copy = zero_copy.ok_or_else(not_supported)?;
|
||||||
|
let salt = &*zero_copy;
|
||||||
// The caller must validate these cases.
|
// The caller must validate these cases.
|
||||||
assert!(args.length > 0);
|
assert!(args.length > 0);
|
||||||
assert!(args.length % 8 == 0);
|
assert!(args.length % 8 == 0);
|
||||||
|
@ -823,7 +829,36 @@ pub async fn op_crypto_derive_bits(
|
||||||
pbkdf2::derive(algorithm, iterations, salt, &secret, &mut out);
|
pbkdf2::derive(algorithm, iterations, salt, &secret, &mut out);
|
||||||
Ok(out.into())
|
Ok(out.into())
|
||||||
}
|
}
|
||||||
|
Algorithm::Ecdh => {
|
||||||
|
let named_curve = args
|
||||||
|
.named_curve
|
||||||
|
.ok_or_else(|| type_error("Missing argument namedCurve".to_string()))?;
|
||||||
|
|
||||||
|
let public_key = args
|
||||||
|
.public_key
|
||||||
|
.ok_or_else(|| type_error("Missing argument publicKey".to_string()))?;
|
||||||
|
|
||||||
|
match named_curve {
|
||||||
|
CryptoNamedCurve::P256 => {
|
||||||
|
let secret_key = p256::SecretKey::from_pkcs8_der(&args.key.data)?;
|
||||||
|
let public_key =
|
||||||
|
p256::SecretKey::from_pkcs8_der(&public_key.data)?.public_key();
|
||||||
|
|
||||||
|
let shared_secret = p256::elliptic_curve::ecdh::diffie_hellman(
|
||||||
|
secret_key.to_secret_scalar(),
|
||||||
|
public_key.as_affine(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(shared_secret.as_bytes().to_vec().into())
|
||||||
|
}
|
||||||
|
// TODO(@littledivy): support for P384
|
||||||
|
// https://github.com/RustCrypto/elliptic-curves/issues/240
|
||||||
|
_ => Err(type_error("Unsupported namedCurve".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
Algorithm::Hkdf => {
|
Algorithm::Hkdf => {
|
||||||
|
let zero_copy = zero_copy.ok_or_else(not_supported)?;
|
||||||
|
let salt = &*zero_copy;
|
||||||
let algorithm = match args.hash.ok_or_else(not_supported)? {
|
let algorithm = match args.hash.ok_or_else(not_supported)? {
|
||||||
CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY,
|
CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY,
|
||||||
CryptoHash::Sha256 => hkdf::HKDF_SHA256,
|
CryptoHash::Sha256 => hkdf::HKDF_SHA256,
|
||||||
|
|
Loading…
Reference in a new issue