-
Couldn't load subscription status.
- Fork 4
Implement ES256 Sign1 #1
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,30 @@ defmodule COSE.Keys.Symmetric do | |
| defstruct [:kty, :kid, :alg, :key_ops, :base_iv, :k] | ||
| end | ||
|
|
||
| defmodule ECDSASignature do | ||
| require Record | ||
|
|
||
| Record.defrecord( | ||
| :ecdsa_signature, | ||
| :"ECDSA-Sig-Value", | ||
| Record.extract(:"ECDSA-Sig-Value", from_lib: "public_key/include/OTP-PUB-KEY.hrl") | ||
| ) | ||
|
|
||
| def new(r, s) when is_integer(r) and is_integer(s) do | ||
| ecdsa_signature(r: r, s: s) | ||
| end | ||
|
|
||
| def new(raw) when is_binary(raw) do | ||
| size = raw |> byte_size() |> div(2) | ||
| <<r::size(size)-unit(8), s::size(size)-unit(8)>> = raw | ||
| new(r, s) | ||
| end | ||
|
|
||
| def to_der(ecdsa_signature() = signature) do | ||
| :public_key.der_encode(:"ECDSA-Sig-Value", signature) | ||
| end | ||
| end | ||
|
|
||
| defmodule COSE.Keys.OKP do | ||
| defstruct [:kty, :kid, :alg, :key_ops, :base_iv, :crv, :x, :d] | ||
|
|
||
|
|
@@ -36,3 +60,38 @@ defmodule COSE.Keys.OKP do | |
| :crypto.verify(:eddsa, :sha256, to_be_verified, signature, [ver_key.x, :ed25519]) | ||
| end | ||
| end | ||
|
|
||
| defmodule COSE.Keys.ECDSA do | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for not following defining the key struct here, as in the rest of the library? |
||
| def sign(:es256, to_be_signed_bytes, private_key) do | ||
| :crypto.sign(:ecdsa, :sha256, to_be_signed_bytes, [private_key, :secp256r1]) | ||
| |> encode_der_as_cose() | ||
| |> COSE.tag_as_byte() | ||
| end | ||
|
|
||
| def verify(:es256, to_be_verified_bytes, %CBOR.Tag{tag: :bytes, value: cose_encoded_signature}, public_key) do | ||
| signature_der_bytes = ECDSASignature.new(cose_encoded_signature) |> ECDSASignature.to_der() | ||
| :crypto.verify(:ecdsa, :sha256, to_be_verified_bytes, signature_der_bytes, [public_key, :secp256r1]) | ||
| end | ||
|
|
||
| defp encode_der_as_cose(der_signature) do | ||
| # The DER signature is a sequence of two integers, r and s, each of which is | ||
| # encoded as a signed big-endian integer. The COSE signature is a CBOR array | ||
| # of two integers, r and s, each of which is encoded as a positive big-endian | ||
| # integer. | ||
| {:"ECDSA-Sig-Value", r, s} = :public_key.der_decode(:"ECDSA-Sig-Value", der_signature) | ||
| # Convert the integers r and s into big endian binaries | ||
| r_bytes = :binary.encode_unsigned(r, :big) | ||
| s_bytes = :binary.encode_unsigned(s, :big) | ||
| # make both of these the same length by padding the shorter one with leading zeros | ||
| r_bytes = pad_leading(r_bytes, byte_size(s_bytes) - byte_size(r_bytes)) | ||
| s_bytes = pad_leading(s_bytes, byte_size(r_bytes) - byte_size(s_bytes)) | ||
| # concatenate the two integers | ||
| r_bytes <> s_bytes | ||
| end | ||
|
|
||
| defp pad_leading(binary, size) when is_binary(binary) do | ||
| padding_size = max(size - byte_size(binary), 0) | ||
| padding = String.duplicate(<<0>>, padding_size) | ||
| padding <> binary | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,28 @@ defmodule COSE.Messages.Sign1 do | |
| CBOR.encode(%CBOR.Tag{tag: 18, value: value}) | ||
| end | ||
|
|
||
| def sign_encode(:es256, msg, key) do | ||
| msg = sign(:es256, msg, key, <<>>) | ||
|
|
||
| value = [ | ||
| COSE.Headers.tag_phdr(msg.phdr), | ||
| msg.uhdr, | ||
| msg.payload, | ||
| msg.signature | ||
| ] | ||
|
|
||
| CBOR.encode(%CBOR.Tag{tag: 18, value: value}) | ||
| end | ||
|
|
||
| def sign(:es256, msg, private_key, external_aad) do | ||
| to_be_signed = CBOR.encode(sig_structure(msg, external_aad)) | ||
|
|
||
| %__MODULE__{ | ||
| msg | ||
| | signature: COSE.Keys.ECDSA.sign(:es256, to_be_signed, private_key) | ||
| } | ||
| end | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of the introduced functions duplicate a fair amount of the body, including |
||
| def sign(msg, key, external_aad \\ <<>>) do | ||
| to_be_signed = CBOR.encode(sig_structure(msg, external_aad)) | ||
|
|
||
|
|
@@ -38,6 +60,16 @@ defmodule COSE.Messages.Sign1 do | |
| end | ||
| end | ||
|
|
||
| def verify_decode(:es256, encoded_msg, key) do | ||
| msg = decode(encoded_msg) | ||
|
|
||
| if verify(:es256, msg, key, <<>>) do | ||
| msg | ||
| else | ||
| false | ||
| end | ||
| end | ||
|
|
||
| def decode(encoded_msg) do | ||
| {:ok, %CBOR.Tag{tag: 18, value: [phdr, uhdr, payload, signature]}, _} = | ||
| CBOR.decode(encoded_msg) | ||
|
|
@@ -50,6 +82,16 @@ defmodule COSE.Messages.Sign1 do | |
| } | ||
| end | ||
|
|
||
| def verify(:es256, msg, public_key, external_aad) do | ||
| to_be_verified = CBOR.encode(sig_structure(msg, external_aad)) | ||
|
|
||
| if COSE.Keys.ECDSA.verify(:es256, to_be_verified, msg.signature, public_key) do | ||
| msg | ||
| else | ||
| false | ||
| end | ||
| end | ||
|
|
||
| def verify(msg, ver_key, external_aad \\ <<>>) do | ||
| to_be_verified = CBOR.encode(sig_structure(msg, external_aad)) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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"}, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case we keep this module (see comment below about struct definition), the name should be
COSE.Keys. ECDSASignature.