diff --git a/README.md b/README.md index 5e3f90c..909e0dd 100644 --- a/README.md +++ b/README.md @@ -24,17 +24,14 @@ gleam add munch@1 ## Example ```gleam -import gleam/bit_array import munch pub fn main() { // Hash a UTF-8 string and return its base16-encoded digest. let digest = munch.hash(munch.sha256, "hello") - // Authenticate binary data with HMAC-SHA-256. - let authentication_code = - munch.hmac(<<"message":utf8>>, munch.sha256, <<"secret":utf8>>) - |> bit_array.base16_encode + // Authenticate a UTF-8 string and return its base16-encoded HMAC. + let authentication_code = munch.hmac("message", munch.sha256, "secret") } ``` diff --git a/src/munch.gleam b/src/munch.gleam index 331ba28..3cc1175 100644 --- a/src/munch.gleam +++ b/src/munch.gleam @@ -71,7 +71,8 @@ //// #### Block API //// [hash](#hash "Hash a UTF-8 encoded string"), //// [hash_bits](#hash_bits "Hash a bit string"), -//// [hmac](#hmac "Authenticate a bit string") +//// [hmac](#hmac "Authenticate a UTF-8 encoded string"), +//// [hmac_bits](#hmac_bits "Authenticate a bit string") //// //// #### Stream API //// [new_hash](#new_hash "Start a streaming hash"), @@ -245,6 +246,27 @@ pub fn hash_bits(algorithm: HashAlgorithm, bits: BitArray) -> BitArray { |> digest_bits } +/// Computes the base16 encoded HMAC of UTF-8 encoded data using a UTF-8 +/// encoded key. +/// +/// ## Examples +/// +/// ```gleam +/// import munch +/// +/// assert munch.hmac("message", munch.sha256, "key") +/// == "6E9EF29B75FFFC5B7ABAE527D58FDADB2FE42E7219011976917343065F58ED4A" +/// ``` +/// +/// To authenticate content in multiple chunks, see the `new_hmac` function. +/// +pub fn hmac(data: String, algorithm: HashAlgorithm, key: String) -> String { + algorithm + |> new_hmac(<>) + |> update(data) + |> digest +} + /// Computes the binary HMAC of a bit string. /// /// If the key does not contain a whole number of bytes, its final byte is @@ -256,14 +278,14 @@ pub fn hash_bits(algorithm: HashAlgorithm, bits: BitArray) -> BitArray { /// import gleam/bit_array /// import munch /// -/// assert munch.hmac(<<"message":utf8>>, munch.sha256, <<"key":utf8>>) +/// assert munch.hmac_bits(<<"message":utf8>>, munch.sha256, <<"key":utf8>>) /// |> bit_array.base16_encode /// == "6E9EF29B75FFFC5B7ABAE527D58FDADB2FE42E7219011976917343065F58ED4A" /// ``` /// /// To authenticate content in multiple chunks, see the `new_hmac` function. /// -pub fn hmac( +pub fn hmac_bits( data: BitArray, algorithm: HashAlgorithm, key: BitArray, @@ -389,7 +411,7 @@ pub fn sign_message( algorithm: HashAlgorithm, ) -> String { let input = signing_input(algorithm, message) - let signature = hmac(<>, algorithm, secret) + let signature = hmac_bits(<>, algorithm, secret) string.concat([input, ".", bit_array.base64_url_encode(signature, False)]) } @@ -419,7 +441,7 @@ pub fn verify_signed_message( case secure_compare(protected, <>) { True -> { - let challenge = hmac(<>, algorithm, secret) + let challenge = hmac_bits(<>, algorithm, secret) case secure_compare(challenge, signature) { True -> Ok(payload) diff --git a/test/hmac_test.gleam b/test/hmac_test.gleam index b5ec770..ebe0687 100644 --- a/test/hmac_test.gleam +++ b/test/hmac_test.gleam @@ -11,7 +11,7 @@ fn repeated_byte(byte: Int, count: Int) -> BitArray { pub fn hmac_md5_test() { let key = repeated_byte(0x0B, 16) - assert munch.hmac(<<"Hi There":utf8>>, munch.md5, key) + assert munch.hmac_bits(<<"Hi There":utf8>>, munch.md5, key) |> bit_array.base16_encode |> string.lowercase == "9294727a3638bb1c13f48ef8158bfc9d" @@ -19,7 +19,7 @@ pub fn hmac_md5_test() { pub fn hmac_sha1_test() { let key = repeated_byte(0x0B, 20) - assert munch.hmac(<<"Hi There":utf8>>, munch.sha1, key) + assert munch.hmac_bits(<<"Hi There":utf8>>, munch.sha1, key) |> bit_array.base16_encode |> string.lowercase == "b617318655057264e28bc0b6fb378c8ef146be00" @@ -27,7 +27,7 @@ pub fn hmac_sha1_test() { pub fn hmac_sha224_test() { let key = repeated_byte(0x0B, 20) - assert munch.hmac(<<"Hi There":utf8>>, munch.sha224, key) + assert munch.hmac_bits(<<"Hi There":utf8>>, munch.sha224, key) |> bit_array.base16_encode |> string.lowercase == "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22" @@ -35,7 +35,7 @@ pub fn hmac_sha224_test() { pub fn hmac_sha256_test() { let key = repeated_byte(0x0B, 20) - assert munch.hmac(<<"Hi There":utf8>>, munch.sha256, key) + assert munch.hmac_bits(<<"Hi There":utf8>>, munch.sha256, key) |> bit_array.base16_encode |> string.lowercase == "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7" @@ -43,7 +43,7 @@ pub fn hmac_sha256_test() { pub fn hmac_sha384_test() { let key = repeated_byte(0x0B, 20) - assert munch.hmac(<<"Hi There":utf8>>, munch.sha384, key) + assert munch.hmac_bits(<<"Hi There":utf8>>, munch.sha384, key) |> bit_array.base16_encode |> string.lowercase == "afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6" @@ -51,7 +51,7 @@ pub fn hmac_sha384_test() { pub fn hmac_sha512_test() { let key = repeated_byte(0x0B, 20) - assert munch.hmac(<<"Hi There":utf8>>, munch.sha512, key) + assert munch.hmac_bits(<<"Hi There":utf8>>, munch.sha512, key) |> bit_array.base16_encode |> string.lowercase == "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854" @@ -60,7 +60,7 @@ pub fn hmac_sha512_test() { pub fn hmac_long_key_test() { let key = repeated_byte(0xAA, 131) let message = "Test Using Larger Than Block-Size Key - Hash Key First" - assert munch.hmac(<>, munch.sha256, key) + assert munch.hmac_bits(<>, munch.sha256, key) |> bit_array.base16_encode |> string.lowercase == "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54" @@ -72,11 +72,11 @@ pub fn hmac_partial_byte_key_test() { let expected = "ff2d7f701c3b91e8ee495114c2f00b7e01c497e23c60ee82425925542b110506" - assert munch.hmac(<<"message":utf8>>, munch.sha256, partial_key) + assert munch.hmac_bits(<<"message":utf8>>, munch.sha256, partial_key) |> bit_array.base16_encode |> string.lowercase == expected - assert munch.hmac(<<"message":utf8>>, munch.sha256, padded_key) + assert munch.hmac_bits(<<"message":utf8>>, munch.sha256, padded_key) |> bit_array.base16_encode |> string.lowercase == expected @@ -89,16 +89,22 @@ pub fn hmac_long_partial_byte_key_test() { let expected = "54a984bf1f360f3c92be3c7ffa46640f7aa21d88d8fff8454d8317119b400a62" - assert munch.hmac(<<"message":utf8>>, munch.sha256, partial_key) + assert munch.hmac_bits(<<"message":utf8>>, munch.sha256, partial_key) |> bit_array.base16_encode |> string.lowercase == expected - assert munch.hmac(<<"message":utf8>>, munch.sha256, padded_key) + assert munch.hmac_bits(<<"message":utf8>>, munch.sha256, padded_key) |> bit_array.base16_encode |> string.lowercase == expected } +pub fn hmac_string_test() { + assert munch.hmac("message", munch.sha256, "key") + |> string.lowercase + == "6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a" +} + pub fn hmac_streaming_test() { let digest = munch.new_hmac(munch.sha256, <<"key":utf8>>)