From 3da71f7de6ad612b3fbb571c1d90c5be8558feb8 Mon Sep 17 00:00:00 2001 From: Francesco Noacco Date: Mon, 24 Nov 2025 17:16:40 +0100 Subject: [PATCH 1/2] chore: update lock --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index bb4c1a4..2dccf58 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "b58": {:git, "https://github.com/dwyl/base58.git", "711b29b62f50394eef3602a2b52dc28d536a5894", []}, + "b58": {:hex, :b58, "1.0.3", "d300d6ae5a3de956a54b9e8220e924e4fee1a349de983df2340fe61e0e464202", [:mix], [], "hexpm", "af62a98a8661fd89978cf3a3a4b5b2ebe82209de6ac6164f0b112e36af72fc59"}, "cbor": {:hex, :cbor, "1.0.0", "35d33a26f6420ce3d2d01c0b1463a748b34c537d5609fc40116daf3666700d36", [:mix], [], "hexpm", "cc5e21e0fa5a0330715a3806c67bc294f8b65d07160f751b5bd6058bed1962ac"}, "hkdf_erlang": {:hex, :hkdf_erlang, "0.1.1", "b35538edfffefc44d6855b2bfe2dc00909c2a4365f41e47d2fe493811006955e", [:rebar3], [], "hexpm", "eb784bda0df4e964fd69f622310752198fb0218543e1a7393912abc7a5ce926e"}, } From e182d7ffd4270a781ef31510e4623e48401d04b9 Mon Sep 17 00:00:00 2001 From: Francesco Noacco Date: Mon, 24 Nov 2025 17:16:40 +0100 Subject: [PATCH 2/2] feat: add encrypt0 message --- lib/cose/messages/encrypt0.ex | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 lib/cose/messages/encrypt0.ex diff --git a/lib/cose/messages/encrypt0.ex b/lib/cose/messages/encrypt0.ex new file mode 100644 index 0000000..4cf0ff7 --- /dev/null +++ b/lib/cose/messages/encrypt0.ex @@ -0,0 +1,82 @@ +defmodule COSE.Messages.Encrypt0 do + defstruct [:phdr, :uhdr, :ciphertext, :payload, :aad] + + @spec build(binary, map, map) :: map + def build(payload, phdr \\ %{}, uhdr \\ %{}) do + %__MODULE__{ + phdr: phdr, + uhdr: uhdr, + payload: payload, + aad: COSE.tag_as_byte(<<>>) + } + end + + def encrypt_encode(msg, cipher_suite, key, iv) do + msg + |> encrypt(cipher_suite, key, iv) + |> encode() + end + + def encrypt(msg, cipher_suite, key, iv, external_aad \\ <<>>) do + aad = msg |> enc_structure(external_aad) |> CBOR.encode() + + {encrypted, tag} = + :crypto.crypto_one_time_aead(cipher_suite, key.k, iv, msg.payload, aad, 8, true) + + Map.put(msg, :ciphertext, COSE.tag_as_byte(encrypted <> tag)) + end + + def encode(msg) do + cose_values = [ + COSE.Headers.tag_phdr(msg.phdr), + COSE.Headers.translate(msg.uhdr), + msg.ciphertext + ] + + CBOR.encode(%CBOR.Tag{tag: 16, value: cose_values}) + end + + def decrypt_decode(msg_cbor, cipher_suite, key) do + msg = decode(msg_cbor) + decrypt(msg, cipher_suite, key, msg.uhdr.iv.value) + end + + def decrypt(msg, cipher_suite, key, iv, external_aad \\ <<>>) do + aad = msg |> enc_structure(external_aad) |> CBOR.encode() + {encrypted, tag} = split_encrypted_tag(msg.ciphertext.value) + + :crypto.crypto_one_time_aead(cipher_suite, key.k, iv, encrypted, aad, tag, false) + |> case do + payload when is_binary(payload) -> + {:ok, Map.put(msg, :payload, payload)} + + error -> + error + end + end + + def decode(encoded_msg) do + {:ok, %CBOR.Tag{tag: 16, value: [phdr, uhdr, ciphertext]}, _} = + CBOR.decode(encoded_msg) + + %__MODULE__{ + phdr: COSE.Headers.decode_phdr(phdr), + uhdr: COSE.Headers.translate(uhdr), + ciphertext: ciphertext + } + end + + def enc_structure(msg, external_aad \\ <<>>) do + [ + "Encrypt0", + (msg.phdr == %{} && COSE.tag_as_byte(<<>>)) || COSE.Headers.tag_phdr(msg.phdr), + external_aad + ] + end + + def split_encrypted_tag(ciphertext, tag_len \\ 8) do + encrypted_len = byte_size(ciphertext) - tag_len + <> = ciphertext + {encrypted, tag} + end +end