1
0
Fork 0
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:
Divy Srivastava 2021-10-08 20:59:36 +05:30 committed by GitHub
parent c49a057599
commit a5d3c8b06c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 240 additions and 3 deletions

102
Cargo.lock generated
View file

@ -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"

View file

@ -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(

View file

@ -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) {

View file

@ -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);

View file

@ -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"] }

View file

@ -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>;

View file

@ -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,