mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -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",
|
||||
"rand_core 0.6.3",
|
||||
"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]]
|
||||
|
@ -727,6 +738,8 @@ dependencies = [
|
|||
"deno_web",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"p256",
|
||||
"p384",
|
||||
"rand 0.8.4",
|
||||
"ring",
|
||||
"rsa",
|
||||
|
@ -1116,12 +1129,40 @@ dependencies = [
|
|||
"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]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.28"
|
||||
|
@ -1228,6 +1269,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "filetime"
|
||||
version = "0.2.15"
|
||||
|
@ -1544,6 +1595,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "h2"
|
||||
version = "0.3.4"
|
||||
|
@ -1608,6 +1670,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
|
@ -2336,6 +2408,26 @@ dependencies = [
|
|||
"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]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
|
@ -3221,6 +3313,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "siphasher"
|
||||
version = "0.3.6"
|
||||
|
|
|
@ -513,6 +513,31 @@ unitTest(async function testHkdfDeriveBits() {
|
|||
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() {
|
||||
// Test wrapKey
|
||||
const key = await crypto.subtle.generateKey(
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
"deriveBits": {
|
||||
"HKDF": "HkdfParams",
|
||||
"PBKDF2": "Pbkdf2Params",
|
||||
"ECDH": "EcdhKeyDeriveParams",
|
||||
},
|
||||
"encrypt": {
|
||||
"RSA-OAEP": "RsaOaepParams",
|
||||
|
@ -2138,6 +2139,58 @@
|
|||
|
||||
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": {
|
||||
// 1.
|
||||
if (length === null || length === 0 || length % 8 !== 0) {
|
||||
|
|
|
@ -385,4 +385,16 @@
|
|||
|
||||
webidl.converters.CryptoKeyPair = webidl
|
||||
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
||||
|
||||
const dictEcdhKeyDeriveParams = [
|
||||
...dictAlgorithm,
|
||||
{
|
||||
key: "public",
|
||||
converter: webidl.converters.CryptoKey,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
webidl.converters.EcdhKeyDeriveParams = webidl
|
||||
.createDictionaryConverter("EcdhKeyDeriveParams", dictEcdhKeyDeriveParams);
|
||||
})(this);
|
||||
|
|
|
@ -18,6 +18,8 @@ deno_core = { version = "0.102.0", path = "../../core" }
|
|||
deno_web = { version = "0.51.0", path = "../web" }
|
||||
lazy_static = "1.4.0"
|
||||
num-traits = "0.2.14"
|
||||
p256 = { version = "0.9.0", features = ["ecdh"] }
|
||||
p384 = "0.8.0"
|
||||
rand = "0.8.4"
|
||||
ring = { version = "0.16.20", 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;
|
||||
}
|
||||
|
||||
interface EcdhKeyDeriveParams extends Algorithm {
|
||||
public: CryptoKey;
|
||||
}
|
||||
|
||||
interface AesKeyGenParams extends Algorithm {
|
||||
length: number;
|
||||
}
|
||||
|
@ -219,7 +223,11 @@ interface SubtleCrypto {
|
|||
data: BufferSource,
|
||||
): Promise<ArrayBuffer>;
|
||||
deriveBits(
|
||||
algorithm: AlgorithmIdentifier | HkdfParams | Pbkdf2Params,
|
||||
algorithm:
|
||||
| AlgorithmIdentifier
|
||||
| HkdfParams
|
||||
| Pbkdf2Params
|
||||
| EcdhKeyDeriveParams,
|
||||
baseKey: CryptoKey,
|
||||
length: number,
|
||||
): Promise<ArrayBuffer>;
|
||||
|
|
|
@ -43,6 +43,7 @@ use rsa::pkcs1::der::Encodable;
|
|||
use rsa::pkcs1::FromRsaPrivateKey;
|
||||
use rsa::pkcs1::ToRsaPrivateKey;
|
||||
use rsa::pkcs8::der::asn1;
|
||||
use rsa::pkcs8::FromPrivateKey;
|
||||
use rsa::BigUint;
|
||||
use rsa::PublicKey;
|
||||
use rsa::RsaPrivateKey;
|
||||
|
@ -792,18 +793,23 @@ pub struct DeriveKeyArg {
|
|||
hash: Option<CryptoHash>,
|
||||
length: usize,
|
||||
iterations: Option<u32>,
|
||||
// ECDH
|
||||
public_key: Option<KeyData>,
|
||||
named_curve: Option<CryptoNamedCurve>,
|
||||
// HKDF
|
||||
info: Option<ZeroCopyBuf>,
|
||||
}
|
||||
|
||||
pub async fn op_crypto_derive_bits(
|
||||
_state: Rc<RefCell<OpState>>,
|
||||
args: DeriveKeyArg,
|
||||
zero_copy: ZeroCopyBuf,
|
||||
zero_copy: Option<ZeroCopyBuf>,
|
||||
) -> Result<ZeroCopyBuf, AnyError> {
|
||||
let salt = &*zero_copy;
|
||||
let algorithm = args.algorithm;
|
||||
match algorithm {
|
||||
Algorithm::Pbkdf2 => {
|
||||
let zero_copy = zero_copy.ok_or_else(not_supported)?;
|
||||
let salt = &*zero_copy;
|
||||
// The caller must validate these cases.
|
||||
assert!(args.length > 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);
|
||||
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 => {
|
||||
let zero_copy = zero_copy.ok_or_else(not_supported)?;
|
||||
let salt = &*zero_copy;
|
||||
let algorithm = match args.hash.ok_or_else(not_supported)? {
|
||||
CryptoHash::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY,
|
||||
CryptoHash::Sha256 => hkdf::HKDF_SHA256,
|
||||
|
|
Loading…
Reference in a new issue