Skip to content

Commit f933a0f

Browse files
committed
add createHmac method
Signed-off-by: karthik Ganeshram <[email protected]>
1 parent 425add9 commit f933a0f

File tree

5 files changed

+99
-11
lines changed

5 files changed

+99
-11
lines changed

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/spin-js-engine/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ crate-type = [ "cdylib" ]
1010
anyhow = "1"
1111
bytes = { version = "1.2.1", features = ["serde"] }
1212
glob = "0.3.0"
13+
hmac = "0.12.1"
1314
http = "0.2"
1415
quickjs-wasm-rs = { git = "https://github.com/shopify/javy" }
1516
quickjs-wasm-sys = { git = "https://github.com/shopify/javy" }

crates/spin-js-engine/src/js_sdk/modules/crypto.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ declare var _random: {
77
math_rand: () => number
88
get_rand: () => number
99
get_hash: (algorithm: string, content: ArrayBuffer) => ArrayBuffer
10+
get_hmac: (algorithm: string, key: ArrayBuffer, content: ArrayBuffer) => ArrayBuffer
1011
}
1112

1213
/** @internal */
@@ -37,14 +38,16 @@ export const crypto = {
3738
createHash: function (algorithm: string) {
3839
let data = new Uint8Array()
3940
return {
40-
update(content: string | Uint8Array, inputEncoding: string = "string") {
41-
if (inputEncoding == "string") {
42-
// @ts-ignore
43-
content = encoder.encode(content)
41+
update(content: string | Uint8Array, inputEncoding: string = "utf8") {
42+
if (typeof (content) == "string") {
43+
if (inputEncoding == "utf8") {
44+
content = encoder.encode(content)
45+
} else {
46+
throw new Error("Currently only utf8 strings are supported")
47+
}
4448
}
45-
let mergedArray = new Uint8Array(data.length + content.length);
49+
let mergedArray = new Uint8Array(data.length + content.length);
4650
mergedArray.set(data);
47-
// @ts-ignore
4851
mergedArray.set(content, data.length);
4952
data = mergedArray
5053
},
@@ -57,6 +60,31 @@ export const crypto = {
5760
throw new Error("sha256 and sha512 are the only supported algorithms");
5861
}
5962
}
63+
},
64+
createHmac: function (algorithm: string, key: ArrayBuffer) {
65+
let data = new Uint8Array()
66+
return {
67+
update(content: string | Uint8Array, inputEncoding: string = "utf8") {
68+
if (typeof (content) == "string") {
69+
if (inputEncoding == "utf8") {
70+
content = encoder.encode(content)
71+
} else {
72+
throw new Error("Currently only utf8 strings are supported")
73+
}
74+
}
75+
let mergedArray = new Uint8Array(data.length + content.length);
76+
mergedArray.set(data);
77+
mergedArray.set(content, data.length);
78+
data = mergedArray
79+
},
80+
digest() {
81+
if (algorithm == "sha256") {
82+
return _random.get_hmac("sha256", key, data.buffer)
83+
} else if (algorithm == "sha512") {
84+
return _random.get_hmac("sha512", key, data.buffer)
85+
} else
86+
throw new Error("sha256 and sha512 are the only supported algorithms");
87+
}
88+
}
6089
}
61-
62-
}
90+
}

crates/spin-js-engine/src/lib.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
use {
44
anyhow::{anyhow, bail, Result},
5+
hmac::{Hmac, Mac},
56
http::{header::HeaderName, request, HeaderValue},
67
once_cell::sync::{Lazy, OnceCell},
78
quickjs_wasm_rs::{Context, Deserializer, Exception, Serializer, Value},
89
rand::{thread_rng, Rng},
910
send_wrapper::SendWrapper,
1011
serde::{Deserialize, Serialize},
1112
serde_bytes::ByteBuf,
12-
sha2::Digest,
13+
sha2::{Digest, Sha256, Sha512},
1314
spin_sdk::{
1415
config,
1516
http::{Request, Response},
@@ -281,6 +282,45 @@ fn get_hash(context: &Context, _this: &Value, args: &[Value]) -> Result<Value> {
281282
}
282283
}
283284

285+
286+
fn get_hmac(context: &Context, _this: &Value, args: &[Value]) -> Result<Value> {
287+
match args {
288+
[algorithm, key, content] => {
289+
let algorithm = &deserialize_helper(algorithm)?;
290+
let key_deserializer = &mut Deserializer::from(key.clone());
291+
let key = ByteBuf::deserialize(key_deserializer)?;
292+
let content_deserializer = &mut Deserializer::from(content.clone());
293+
let content = ByteBuf::deserialize(content_deserializer)?;
294+
295+
if algorithm == "sha256" {
296+
let mut mac = Hmac::<Sha256>::new_from_slice(&key).expect("HMAC can take key of any size");
297+
mac.update(&content);
298+
let result = mac.finalize();
299+
let code_bytes = result.into_bytes();
300+
let mut serializer = Serializer::from_context(context)?;
301+
code_bytes.serialize(&mut serializer)?;
302+
Ok(serializer.value)
303+
} else if algorithm == "sha512" {
304+
let mut mac = Hmac::<Sha512>::new_from_slice(&key).expect("HMAC can take key of any size");
305+
mac.update(&content);
306+
let result = mac.finalize();
307+
let code_bytes = result.into_bytes();
308+
let mut serializer = Serializer::from_context(context)?;
309+
code_bytes.serialize(&mut serializer)?;
310+
Ok(serializer.value)
311+
} else {
312+
bail!("Invalid algorithm, only sha256 and sha512 are supported")
313+
}
314+
}
315+
_ => {
316+
bail!(
317+
"expected a three arguments (algorithm, key, content), got {} arguments",
318+
args.len()
319+
)
320+
}
321+
}
322+
}
323+
284324
fn math_rand(context: &Context, _this: &Value, _args: &[Value]) -> Result<Value> {
285325
context.value_from_f64(thread_rng().gen_range(0.0_f64..1.0))
286326
}
@@ -488,6 +528,7 @@ fn do_init() -> Result<()> {
488528
_random.set_property("math_rand", context.wrap_callback(math_rand)?)?;
489529
_random.set_property("get_rand", context.wrap_callback(get_rand)?)?;
490530
_random.set_property("get_hash", context.wrap_callback(get_hash)?)?;
531+
_random.set_property("get_hmac", context.wrap_callback(get_hmac)?)?;
491532

492533
global.set_property("_random", _random)?;
493534
global.set_property("spinSdk", spin_sdk)?;

types/lib/modules/overrides.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ declare global {
3737
toString(): string
3838
values(): string[]
3939
}
40-
interface Hash {
40+
interface HashAndHmac {
4141
update(content: string | Uint8Array, inputEncoding?: string): void
4242
digest(): ArrayBuffer
4343
}
@@ -46,7 +46,8 @@ declare global {
4646
subtle: {
4747
digest(algorithm: string, content: ArrayBuffer): Promise<ArrayBuffer>
4848
}
49-
createHash(algorithm: string): Hash
49+
createHash(algorithm: string): HashAndHmac
50+
createHmac(algorithm: string, key: ArrayBuffer): HashAndHmac
5051
}
5152
}
5253

0 commit comments

Comments
 (0)