CBOR Object Signing and Encryption (COSE) [RFC8152] in Elixir.
Currently supports:
Sign1
messages with algorithmeddsa
Encrypt
messages with algorithmecdh_ss_hkdf_256
and keyx25519
Additionally, there is some support for CBOR Web Tokens (CWT
) [RFC8392].
Add cose
to your list of dependencies in mix.exs
:
def deps do
[
{:cose, git: "https://github.com/geonnave/cose-elixir.git"}
]
end
alias COSE.{Messages, Keys}
key = Keys.OKP.generate(:sig)
msg = Messages.Sign1.build("content to sign", %{alg: :eddsa})
# sign and COSE-encode
encoded_msg = Messages.Sign1.sign_encode(msg, key)
# COSE-decode to obtain parameters (e.g. key used) + verify signature
decoded_msg = Messages.Sign1.decode(encoded_msg)
verified_msg = Messages.Sign1.verify(decoded_msg, key)
alias COSE.{Keys, Headers, Messages}
# generate keys
sender_key = Keys.OKP.generate(:enc)
receiver_key = Keys.OKP.generate(:enc)
# create recipient metadata
recipient = %Messages.Recipient{phdr: %{alg: :ecdh_ss_hkdf_256}}
s = %Messages.SuppPubInfo{key_data_length: 128, protected: Headers.tag_phdr(recipient.phdr)}
context = Messages.ContextKDF.build(:aes_ccm_16_64_128, %Messages.PartyInfo{}, %Messages.PartyInfo{}, s)
# create content-encryption key (same as key-encryption key, as we are using a `direct` algorithm)
kek_bytes = Messages.Recipient.derive_kek(sender_key, receiver_key, context)
cek = %Keys.Symmetric{k: kek_bytes, alg: :aes_ccm_16_64_128}
# prepare message
msg_phdr = %{alg: :aes_ccm_16_64_128}
msg_uhdr = %{iv: COSE.tag_as_byte(:crypto.strong_rand_bytes(12))}
msg = Messages.Encrypt.build("content to encrypt", recipient, msg_phdr, msg_uhdr)
# encrypt + COSE-encode
encoded_msg = Messages.Encrypt.encrypt_encode(msg, cek, msg.uhdr.iv.value)
# COSE-decode to obtain parameters (headers, recipients, etc.) + decrypt
decoded_msg = Messages.Encrypt.decode(encoded_msg)
{:ok, decrypted_msg} = Messages.Encrypt.decrypt(decoded_msg, cek, decoded_msg.uhdr.iv.value)
alias COSE.{Messages, Keys, CWT}
key = Keys.OKP.generate(:sig)
# create claims, including claims that are application-specific
claims = %{
expiration: (DateTime.utc_now() |> DateTime.to_unix()) + 60, # one minute
issuer: "alice",
subject: "bob",
custom_claim: "this claim is application-specific",
}
custom_claims = %{custom_claim: 22}
# encode + sign, verify + decode
encoded_token = CWT.sign_encode(claims, key, custom_claims)
verified_token = CWT.verify_decode(encoded_token, key, custom_claims)