summaryrefslogtreecommitdiff
path: root/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'extensions')
-rw-r--r--extensions/crypto/00_crypto.js111
-rw-r--r--extensions/crypto/lib.deno_crypto.d.ts28
-rw-r--r--extensions/crypto/lib.rs133
3 files changed, 272 insertions, 0 deletions
diff --git a/extensions/crypto/00_crypto.js b/extensions/crypto/00_crypto.js
index 936bf7cff..896a11570 100644
--- a/extensions/crypto/00_crypto.js
+++ b/extensions/crypto/00_crypto.js
@@ -65,6 +65,10 @@
"ECDSA": "EcdsaParams",
"HMAC": null,
},
+ "verify": {
+ "RSASSA-PKCS1-v1_5": null,
+ "RSA-PSS": "RsaPssParams",
+ },
};
// See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm
@@ -412,6 +416,113 @@
/**
* @param {string} algorithm
+ * @param {CryptoKey} key
+ * @param {BufferSource} signature
+ * @param {BufferSource} data
+ * @returns {Promise<boolean>}
+ */
+ async verify(algorithm, key, signature, data) {
+ webidl.assertBranded(this, SubtleCrypto);
+ const prefix = "Failed to execute 'verify' on 'SubtleCrypto'";
+ webidl.requiredArguments(arguments.length, 4, { prefix });
+ algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
+ prefix,
+ context: "Argument 1",
+ });
+ key = webidl.converters.CryptoKey(key, {
+ prefix,
+ context: "Argument 2",
+ });
+ signature = webidl.converters.BufferSource(signature, {
+ prefix,
+ context: "Argument 3",
+ });
+ data = webidl.converters.BufferSource(data, {
+ prefix,
+ context: "Argument 4",
+ });
+
+ // 2.
+ if (ArrayBuffer.isView(signature)) {
+ signature = new Uint8Array(
+ signature.buffer,
+ signature.byteOffset,
+ signature.byteLength,
+ );
+ } else {
+ signature = new Uint8Array(signature);
+ }
+ signature = signature.slice();
+
+ // 3.
+ if (ArrayBuffer.isView(data)) {
+ data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
+ } else {
+ data = new Uint8Array(data);
+ }
+ data = data.slice();
+
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, "verify");
+
+ const handle = key[_handle];
+ const keyData = KEY_STORE.get(handle);
+
+ if (normalizedAlgorithm.name !== key[_algorithm].name) {
+ throw new DOMException(
+ "Verifying algorithm doesn't match key algorithm.",
+ "InvalidAccessError",
+ );
+ }
+
+ if (!key[_usages].includes("verify")) {
+ throw new DOMException(
+ "Key does not support the 'verify' operation.",
+ "InvalidAccessError",
+ );
+ }
+
+ switch (normalizedAlgorithm.name) {
+ case "RSASSA-PKCS1-v1_5": {
+ if (key[_type] !== "public") {
+ throw new DOMException(
+ "Key type not supported",
+ "InvalidAccessError",
+ );
+ }
+
+ const hashAlgorithm = key[_algorithm].hash.name;
+ return await core.opAsync("op_crypto_verify_key", {
+ key: keyData,
+ algorithm: "RSASSA-PKCS1-v1_5",
+ hash: hashAlgorithm,
+ signature,
+ }, data);
+ }
+ case "RSA-PSS": {
+ if (key[_type] !== "public") {
+ throw new DOMException(
+ "Key type not supported",
+ "InvalidAccessError",
+ );
+ }
+
+ const hashAlgorithm = key[_algorithm].hash.name;
+ const saltLength = normalizedAlgorithm.saltLength;
+ return await core.opAsync("op_crypto_verify_key", {
+ key: keyData,
+ algorithm: "RSA-PSS",
+ hash: hashAlgorithm,
+ saltLength,
+ signature,
+ }, data);
+ }
+ }
+
+ throw new TypeError("unreachable");
+ }
+
+ /**
+ * @param {string} algorithm
* @param {boolean} extractable
* @param {KeyUsage[]} keyUsages
* @returns {Promise<any>}
diff --git a/extensions/crypto/lib.deno_crypto.d.ts b/extensions/crypto/lib.deno_crypto.d.ts
index 0798af59f..d4d42e581 100644
--- a/extensions/crypto/lib.deno_crypto.d.ts
+++ b/extensions/crypto/lib.deno_crypto.d.ts
@@ -111,6 +111,34 @@ interface SubtleCrypto {
| DataView
| ArrayBuffer,
): Promise<ArrayBuffer>;
+ verify(
+ algorithm: AlgorithmIdentifier | RsaPssParams,
+ key: CryptoKey,
+ signature:
+ | Int8Array
+ | Int16Array
+ | Int32Array
+ | Uint8Array
+ | Uint16Array
+ | Uint32Array
+ | Uint8ClampedArray
+ | Float32Array
+ | Float64Array
+ | DataView
+ | ArrayBuffer,
+ data:
+ | Int8Array
+ | Int16Array
+ | Int32Array
+ | Uint8Array
+ | Uint16Array
+ | Uint32Array
+ | Uint8ClampedArray
+ | Float32Array
+ | Float64Array
+ | DataView
+ | ArrayBuffer,
+ ): Promise<boolean>;
digest(
algorithm: AlgorithmIdentifier,
data:
diff --git a/extensions/crypto/lib.rs b/extensions/crypto/lib.rs
index 66e576c6a..d390afdab 100644
--- a/extensions/crypto/lib.rs
+++ b/extensions/crypto/lib.rs
@@ -34,7 +34,9 @@ use ring::signature::EcdsaSigningAlgorithm;
use rsa::padding::PaddingScheme;
use rsa::BigUint;
use rsa::PrivateKeyEncoding;
+use rsa::PublicKey;
use rsa::RSAPrivateKey;
+use rsa::RSAPublicKey;
use sha1::Sha1;
use sha2::Digest;
use sha2::Sha256;
@@ -70,6 +72,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension {
),
("op_crypto_generate_key", op_async(op_crypto_generate_key)),
("op_crypto_sign_key", op_async(op_crypto_sign_key)),
+ ("op_crypto_verify_key", op_async(op_crypto_verify_key)),
("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)),
("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)),
])
@@ -378,6 +381,136 @@ pub async fn op_crypto_sign_key(
Ok(signature.into())
}
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct VerifyArg {
+ key: KeyData,
+ algorithm: Algorithm,
+ salt_length: Option<u32>,
+ hash: Option<CryptoHash>,
+ signature: ZeroCopyBuf,
+}
+
+pub async fn op_crypto_verify_key(
+ _state: Rc<RefCell<OpState>>,
+ args: VerifyArg,
+ zero_copy: Option<ZeroCopyBuf>,
+) -> Result<bool, AnyError> {
+ let zero_copy = zero_copy.ok_or_else(null_opbuf)?;
+ let data = &*zero_copy;
+ let algorithm = args.algorithm;
+
+ let verification = match algorithm {
+ Algorithm::RsassaPkcs1v15 => {
+ let public_key: RSAPublicKey =
+ RSAPrivateKey::from_pkcs8(&*args.key.data)?.to_public_key();
+ let (padding, hashed) = match args
+ .hash
+ .ok_or_else(|| type_error("Missing argument hash".to_string()))?
+ {
+ CryptoHash::Sha1 => {
+ let mut hasher = Sha1::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::PKCS1v15Sign {
+ hash: Some(rsa::hash::Hash::SHA1),
+ },
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ CryptoHash::Sha256 => {
+ let mut hasher = Sha256::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::PKCS1v15Sign {
+ hash: Some(rsa::hash::Hash::SHA2_256),
+ },
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ CryptoHash::Sha384 => {
+ let mut hasher = Sha384::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::PKCS1v15Sign {
+ hash: Some(rsa::hash::Hash::SHA2_384),
+ },
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ CryptoHash::Sha512 => {
+ let mut hasher = Sha512::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::PKCS1v15Sign {
+ hash: Some(rsa::hash::Hash::SHA2_512),
+ },
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ };
+
+ public_key
+ .verify(padding, &hashed, &*args.signature)
+ .is_ok()
+ }
+ Algorithm::RsaPss => {
+ let salt_len = args
+ .salt_length
+ .ok_or_else(|| type_error("Missing argument saltLength".to_string()))?
+ as usize;
+ let public_key: RSAPublicKey =
+ RSAPrivateKey::from_pkcs8(&*args.key.data)?.to_public_key();
+
+ let rng = OsRng;
+ let (padding, hashed) = match args
+ .hash
+ .ok_or_else(|| type_error("Missing argument hash".to_string()))?
+ {
+ CryptoHash::Sha1 => {
+ let mut hasher = Sha1::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::new_pss_with_salt::<Sha1, _>(rng, salt_len),
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ CryptoHash::Sha256 => {
+ let mut hasher = Sha256::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::new_pss_with_salt::<Sha256, _>(rng, salt_len),
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ CryptoHash::Sha384 => {
+ let mut hasher = Sha384::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::new_pss_with_salt::<Sha384, _>(rng, salt_len),
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ CryptoHash::Sha512 => {
+ let mut hasher = Sha512::new();
+ hasher.update(&data);
+ (
+ PaddingScheme::new_pss_with_salt::<Sha512, _>(rng, salt_len),
+ hasher.finalize()[..].to_vec(),
+ )
+ }
+ };
+
+ public_key
+ .verify(padding, &hashed, &*args.signature)
+ .is_ok()
+ }
+ _ => return Err(type_error("Unsupported algorithm".to_string())),
+ };
+
+ Ok(verification)
+}
+
pub fn op_crypto_random_uuid(
state: &mut OpState,
_: (),