Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): support AES-CBC for SubtleCrypto#encrypt & SubtleCrypto#decrypt #848

Merged
merged 2 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/strange-moles-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@lagon/cli': patch
'@lagon/runtime': patch
'@lagon/serverless': patch
'@lagon/docs': patch
---

Support AES-CBC for `SubtleCrypto#encrypt` & `SubtleCrypto#decrypt`
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 72 additions & 2 deletions crates/runtime/tests/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ async fn crypto_digest_object() {
}

#[tokio::test]
async fn crypto_encrypt() {
async fn crypto_encrypt_aes_gcm() {
utils::setup();
let (send, receiver) = utils::create_isolate(IsolateOptions::new(
"export async function handler() {
Expand Down Expand Up @@ -271,7 +271,7 @@ async fn crypto_encrypt() {
}

#[tokio::test]
async fn crypto_decrypt() {
async fn crypto_decrypt_aes_gcm() {
utils::setup();
let (send, receiver) = utils::create_isolate(IsolateOptions::new(
"export async function handler() {
Expand Down Expand Up @@ -308,6 +308,76 @@ async fn crypto_decrypt() {
);
}

#[tokio::test]
async fn crypto_encrypt_aes_cbc() {
utils::setup();
let (send, receiver) = utils::create_isolate(IsolateOptions::new(
"export async function handler() {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode('secret'),
{ name: 'AES-CBC' },
false,
['sign'],
);

const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv },
key,
new TextEncoder().encode('hello, world'),
);

return new Response(`${ciphertext instanceof Uint8Array} ${ciphertext.length}`);
}"
.into(),
));
send(Request::default());

assert_eq!(
receiver.recv_async().await.unwrap().as_response(),
Response::from("true 16")
);
}

#[tokio::test]
async fn crypto_decrypt_aes_cbc() {
utils::setup();
let (send, receiver) = utils::create_isolate(IsolateOptions::new(
"export async function handler() {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode('secret'),
{ name: 'AES-CBC' },
false,
['sign'],
);

const iv = crypto.getRandomValues(new Uint8Array(16));
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-CBC', iv },
key,
new TextEncoder().encode('hello, world'),
);

const text = await crypto.subtle.decrypt(
{ name: 'AES-CBC', iv },
key,
ciphertext,
);

return new Response(new TextDecoder().decode(text));
}"
.into(),
));
send(Request::default());

assert_eq!(
receiver.recv_async().await.unwrap().as_response(),
Response::from("hello, world")
);
}

#[tokio::test]
async fn crypto_hkdf_derive_bits() {
utils::setup();
Expand Down
1 change: 1 addition & 0 deletions crates/runtime_crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ sha1 = "0.10.5"
sha2 = "0.10.6"
aes = "0.8.2"
aes-gcm = "0.10.1"
cbc = { version = "0.1.2", features = ["std"] }
ring = { version = "0.16.20", features = ["std"] }
p256 = { version = "0.11.1", features = ["ecdh"] }
p384 = "0.11.1"
11 changes: 11 additions & 0 deletions crates/runtime_crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum Sha {
pub enum Algorithm {
Hmac,
AesGcm(Vec<u8>),
AesCbc(Vec<u8>),
}

pub enum CryptoNamedCurve {
Expand Down Expand Up @@ -61,6 +62,16 @@ pub fn extract_algorithm_object(

return Ok(Algorithm::AesGcm(iv));
}

if name == "AES-CBC" {
let iv_key = v8_string(scope, "iv");
let iv = match algorithm.get(scope, iv_key.into()) {
Some(iv) => extract_v8_uint8array(iv)?,
None => return Err(anyhow!("Algorithm iv not found")),
};

return Ok(Algorithm::AesCbc(iv));
}
}

Err(anyhow!("Algorithm not supported"))
Expand Down
11 changes: 11 additions & 0 deletions crates/runtime_crypto/src/methods/decrypt.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
use aes_gcm::{aead::Aead, KeyInit, Nonce};
use anyhow::{anyhow, Result};

use crate::{Aes256Gcm, Algorithm};

type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;

pub fn decrypt(algorithm: Algorithm, key_value: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>> {
match algorithm {
Algorithm::AesGcm(iv) => {
Expand All @@ -14,6 +17,14 @@ pub fn decrypt(algorithm: Algorithm, key_value: Vec<u8>, data: Vec<u8>) -> Resul
Err(_) => Err(anyhow!("Decryption failed")),
}
}
Algorithm::AesCbc(iv) => {
match Aes256CbcDec::new(key_value.as_slice().into(), iv.as_slice().into())
.decrypt_padded_vec_mut::<Pkcs7>(&data)
{
Ok(result) => Ok(result),
Err(_) => Err(anyhow!("Decryption failed")),
}
}
_ => Err(anyhow!("Algorithm not supported")),
}
}
8 changes: 8 additions & 0 deletions crates/runtime_crypto/src/methods/encrypt.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use aes::cipher::{block_padding::Pkcs7, BlockEncryptMut, KeyIvInit};
use aes_gcm::{aead::Aead, KeyInit, Nonce};
use anyhow::{anyhow, Result};

use crate::{Aes256Gcm, Algorithm};

type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;

pub fn encrypt(algorithm: Algorithm, key_value: Vec<u8>, data: Vec<u8>) -> Result<Vec<u8>> {
match algorithm {
Algorithm::AesGcm(iv) => {
Expand All @@ -14,6 +17,11 @@ pub fn encrypt(algorithm: Algorithm, key_value: Vec<u8>, data: Vec<u8>) -> Resul
Err(_) => Err(anyhow!("Encryption failed")),
}
}
Algorithm::AesCbc(iv) => Ok(Aes256CbcEnc::new(
key_value.as_slice().into(),
iv.as_slice().into(),
)
.encrypt_padded_vec_mut::<Pkcs7>(&data)),
_ => Err(anyhow!("Algorithm not supported")),
}
}
1 change: 1 addition & 0 deletions packages/docs/pages/runtime-apis.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ The standard `CryptoSubtle` object. [See the documentation on MDN](https://devel
| SHA-384 | | | ✅ | | |
| SHA-512 | | | ✅ | | |
| AES-GCM | | ✅ | | | ✅ |
| AES-CBC | | ✅ | | | ✅ |
| ECDH | | | | ✅ | |
| HKDF | | | | ✅ | |
| PBKDF2 | | | | ✅ | |
Expand Down