Packages

Gleam implementation of JOSE (JWS, JWE, JWK, JWT) and COSE (COSE_Sign, COSE_Encrypt, COSE_Mac0, CWT) standards

Retired package: Deprecated

Current section

Files

Jump to
gose src gose@cose@key.erl
Raw

src/gose@cose@key.erl

-module(gose@cose@key).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gose/cose/key.gleam").
-export([ec_curve_to_cose/1, ec_curve_from_cose/1, xdh_curve_to_cose/1, xdh_curve_from_cose/1, to_cbor_map/1, to_cbor/1, from_cbor_map/1, from_cbor/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" COSE_Key encoding and decoding ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html)).\n"
"\n"
" Converts between `gose/key.Key` and COSE_Key CBOR representations.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import gose/cose/key as cose_key\n"
" import gose/key\n"
" import kryptos/ec\n"
"\n"
" // Generate an EC key and attach a binary kid (COSE uses BitArray kids)\n"
" let k =\n"
" key.generate_ec(ec.P256)\n"
" |> key.with_kid_bits(<<\"my-key\":utf8>>)\n"
"\n"
" // Serialize to CBOR\n"
" let assert Ok(bytes) = cose_key.to_cbor(k)\n"
"\n"
" // Parse from CBOR\n"
" let assert Ok(parsed) = cose_key.from_cbor(bytes)\n"
" let assert Ok(kid) = key.kid(parsed)\n"
" assert kid == <<\"my-key\":utf8>>\n"
" ```\n"
).
-file("src/gose/cose/key.gleam", 173).
-spec encode_rsa(gose@key:rsa_key_material()) -> list({gose@cbor:value(),
gose@cbor:value()}).
encode_rsa(Mat) ->
case Mat of
{rsa_private, Priv, Public_key} ->
N = gose@internal@utils:strip_leading_zeros(
kryptos_ffi:rsa_public_key_modulus(Public_key)
),
E = gose@internal@utils:strip_leading_zeros(
kryptos_ffi:rsa_public_key_exponent_bytes(Public_key)
),
[{{int, 1}, {int, 3}},
{{int, -1}, {bytes, N}},
{{int, -2}, {bytes, E}},
{{int, -3},
{bytes,
kryptos_ffi:rsa_private_key_private_exponent_bytes(Priv)}},
{{int, -4}, {bytes, kryptos_ffi:rsa_private_key_prime1(Priv)}},
{{int, -5}, {bytes, kryptos_ffi:rsa_private_key_prime2(Priv)}},
{{int, -6},
{bytes, kryptos_ffi:rsa_private_key_exponent1(Priv)}},
{{int, -7},
{bytes, kryptos_ffi:rsa_private_key_exponent2(Priv)}},
{{int, -8},
{bytes, kryptos_ffi:rsa_private_key_coefficient(Priv)}}];
{rsa_public, Public_key@1} ->
N@1 = gose@internal@utils:strip_leading_zeros(
kryptos_ffi:rsa_public_key_modulus(Public_key@1)
),
E@1 = gose@internal@utils:strip_leading_zeros(
kryptos_ffi:rsa_public_key_exponent_bytes(Public_key@1)
),
[{{int, 1}, {int, 3}},
{{int, -1}, {bytes, N@1}},
{{int, -2}, {bytes, E@1}}]
end.
-file("src/gose/cose/key.gleam", 204).
-spec encode_symmetric(bitstring()) -> list({gose@cbor:value(),
gose@cbor:value()}).
encode_symmetric(Secret) ->
[{{int, 1}, {int, 4}}, {{int, -1}, {bytes, Secret}}].
-file("src/gose/cose/key.gleam", 240).
-spec encode_alg_metadata(gose@key:alg()) -> {ok,
list({gose@cbor:value(), gose@cbor:value()})} |
{error, gose:gose_error()}.
encode_alg_metadata(Alg) ->
case Alg of
{signing_alg, Signing_alg} ->
{ok,
[{{int, 3},
{int,
gose@cose@algorithm:signing_alg_to_int(Signing_alg)}}]};
{key_encryption_alg, Ke_alg} ->
gleam@result:map(
gose@cose@algorithm:key_encryption_alg_to_int(Ke_alg),
fun(Id) -> [{{int, 3}, {int, Id}}] end
);
{content_alg, Content_alg} ->
gleam@result:map(
gose@cose@algorithm:content_alg_to_int(Content_alg),
fun(Id@1) -> [{{int, 3}, {int, Id@1}}] end
)
end.
-file("src/gose/cose/key.gleam", 231).
-spec resolve_alg_metadata(gose@key:key(any())) -> {ok,
list({gose@cbor:value(), gose@cbor:value()})} |
{error, gose:gose_error()}.
resolve_alg_metadata(K) ->
case gose@key:alg(K) of
{ok, Alg} ->
encode_alg_metadata(Alg);
{error, _} ->
{ok, []}
end.
-file("src/gose/cose/key.gleam", 474).
-spec decode_alg(integer()) -> {ok, gose@key:alg()} | {error, gose:gose_error()}.
decode_alg(Id) ->
case gose@cose@algorithm:signing_alg_from_int(Id) of
{ok, Alg} ->
{ok, {signing_alg, Alg}};
{error, _} ->
case gose@cose@algorithm:key_encryption_alg_from_int(Id) of
{ok, Alg@1} ->
{ok, {key_encryption_alg, Alg@1}};
{error, _} ->
_pipe = gose@cose@algorithm:content_alg_from_int(Id),
_pipe@1 = gleam@result:map(
_pipe,
fun(Field@0) -> {content_alg, Field@0} end
),
gleam@result:replace_error(
_pipe@1,
{parse_error,
<<"unknown COSE algorithm: "/utf8,
(erlang:integer_to_binary(Id))/binary>>}
)
end
end.
-file("src/gose/cose/key.gleam", 502).
?DOC(false).
-spec ec_curve_to_cose(kryptos@ec:curve()) -> integer().
ec_curve_to_cose(Curve) ->
case Curve of
p256 ->
1;
p384 ->
2;
p521 ->
3;
secp256k1 ->
8
end.
-file("src/gose/cose/key.gleam", 93).
-spec encode_ec(gose@key:ec_key_material()) -> list({gose@cbor:value(),
gose@cbor:value()}).
encode_ec(Mat) ->
{Curve, Public, Private_d} = case Mat of
{ec_private, Priv, Public_key, C} ->
{C, Public_key, {some, Priv}};
{ec_public, Public_key@1, C@1} ->
{C@1, Public_key@1, none}
end,
Crv_id = ec_curve_to_cose(Curve),
Raw_point = kryptos_ffi:ec_public_key_to_raw_point(Public),
Coord_size = kryptos@ec:coordinate_size(Curve),
X@1 = case gleam_stdlib:bit_array_slice(Raw_point, 1, Coord_size) of
{ok, X} -> X;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gose/cose/key"/utf8>>,
function => <<"encode_ec"/utf8>>,
line => 105,
value => _assert_fail,
start => 3099,
'end' => 3159,
pattern_start => 3110,
pattern_end => 3115})
end,
Y@1 = case gleam_stdlib:bit_array_slice(
Raw_point,
1 + Coord_size,
Coord_size
) of
{ok, Y} -> Y;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gose/cose/key"/utf8>>,
function => <<"encode_ec"/utf8>>,
line => 106,
value => _assert_fail@1,
start => 3162,
'end' => 3235,
pattern_start => 3173,
pattern_end => 3178})
end,
Pairs = [{{int, 1}, {int, 2}},
{{int, -1}, {int, Crv_id}},
{{int, -2}, {bytes, X@1}},
{{int, -3}, {bytes, Y@1}}],
case Private_d of
{some, Priv@1} ->
[{{int, -4}, {bytes, kryptos_ffi:ec_private_key_to_bytes(Priv@1)}} |
Pairs];
none ->
Pairs
end.
-file("src/gose/cose/key.gleam", 512).
?DOC(false).
-spec ec_curve_from_cose(integer()) -> {ok, kryptos@ec:curve()} |
{error, gose:gose_error()}.
ec_curve_from_cose(Id) ->
case Id of
1 ->
{ok, p256};
2 ->
{ok, p384};
3 ->
{ok, p521};
8 ->
{ok, secp256k1};
_ ->
{error,
{parse_error,
<<"unsupported COSE EC curve: "/utf8,
(erlang:integer_to_binary(Id))/binary>>}}
end.
-file("src/gose/cose/key.gleam", 523).
-spec eddsa_curve_to_cose(kryptos@eddsa:curve()) -> integer().
eddsa_curve_to_cose(Curve) ->
case Curve of
ed25519 ->
6;
ed448 ->
7
end.
-file("src/gose/cose/key.gleam", 123).
-spec encode_eddsa(gose@key:eddsa_key_material()) -> list({gose@cbor:value(),
gose@cbor:value()}).
encode_eddsa(Mat) ->
{Curve, Public_bytes, Private_d} = case Mat of
{eddsa_private, Priv, Public_key, C} ->
{C,
kryptos_ffi:eddsa_public_key_to_bytes(Public_key),
{some, kryptos_ffi:eddsa_private_key_to_bytes(Priv)}};
{eddsa_public, Public_key@1, C@1} ->
{C@1, kryptos_ffi:eddsa_public_key_to_bytes(Public_key@1), none}
end,
Crv_id = eddsa_curve_to_cose(Curve),
Pairs = [{{int, 1}, {int, 1}},
{{int, -1}, {int, Crv_id}},
{{int, -2}, {bytes, Public_bytes}}],
case Private_d of
{some, D} ->
[{{int, -4}, {bytes, D}} | Pairs];
none ->
Pairs
end.
-file("src/gose/cose/key.gleam", 531).
?DOC(false).
-spec xdh_curve_to_cose(kryptos@xdh:curve()) -> integer().
xdh_curve_to_cose(Curve) ->
case Curve of
x25519 ->
4;
x448 ->
5
end.
-file("src/gose/cose/key.gleam", 148).
-spec encode_xdh(gose@key:xdh_key_material()) -> list({gose@cbor:value(),
gose@cbor:value()}).
encode_xdh(Mat) ->
{Curve, Public_bytes, Private_d} = case Mat of
{xdh_private, Priv, Public_key, C} ->
{C,
kryptos_ffi:xdh_public_key_to_bytes(Public_key),
{some, kryptos_ffi:xdh_private_key_to_bytes(Priv)}};
{xdh_public, Public_key@1, C@1} ->
{C@1, kryptos_ffi:xdh_public_key_to_bytes(Public_key@1), none}
end,
Crv_id = xdh_curve_to_cose(Curve),
Pairs = [{{int, 1}, {int, 1}},
{{int, -1}, {int, Crv_id}},
{{int, -2}, {bytes, Public_bytes}}],
case Private_d of
{some, D} ->
[{{int, -4}, {bytes, D}} | Pairs];
none ->
Pairs
end.
-file("src/gose/cose/key.gleam", 81).
-spec encode_key_material(gose@key:key_material()) -> {ok,
list({gose@cbor:value(), gose@cbor:value()})} |
{error, gose:gose_error()}.
encode_key_material(Mat) ->
case Mat of
{ec, Ec_mat} ->
{ok, encode_ec(Ec_mat)};
{eddsa, Eddsa_mat} ->
{ok, encode_eddsa(Eddsa_mat)};
{xdh, Xdh_mat} ->
{ok, encode_xdh(Xdh_mat)};
{rsa, Rsa_mat} ->
{ok, encode_rsa(Rsa_mat)};
{octet_key, Secret} ->
{ok, encode_symmetric(Secret)}
end.
-file("src/gose/cose/key.gleam", 539).
?DOC(false).
-spec xdh_curve_from_cose(integer()) -> {ok, kryptos@xdh:curve()} |
{error, gose:gose_error()}.
xdh_curve_from_cose(Id) ->
case Id of
4 ->
{ok, x25519};
5 ->
{ok, x448};
_ ->
{error,
{parse_error,
<<"unsupported COSE XDH curve: "/utf8,
(erlang:integer_to_binary(Id))/binary>>}}
end.
-file("src/gose/cose/key.gleam", 548).
-spec key_op_to_cose(gose@key:key_op()) -> integer().
key_op_to_cose(Op) ->
case Op of
sign ->
1;
verify ->
2;
encrypt ->
3;
decrypt ->
4;
wrap_key ->
5;
unwrap_key ->
6;
derive_key ->
7;
derive_bits ->
8
end.
-file("src/gose/cose/key.gleam", 211).
-spec encode_metadata(gose@key:key(bitstring())) -> {ok,
list({gose@cbor:value(), gose@cbor:value()})} |
{error, gose:gose_error()}.
encode_metadata(K) ->
Kid_pair = case gose@key:kid(K) of
{ok, Kid} ->
[{{int, 2}, {bytes, Kid}}];
{error, _} ->
[]
end,
gleam@result:'try'(
resolve_alg_metadata(K),
fun(Alg_pair) ->
Ops_pair = case gose@key:key_ops(K) of
{ok, Ops} ->
[{{int, 4},
{array,
gleam@list:map(
Ops,
fun(Op) -> {int, key_op_to_cose(Op)} end
)}}];
{error, _} ->
[]
end,
{ok, lists:append([Kid_pair, Alg_pair, Ops_pair])}
end
).
-file("src/gose/cose/key.gleam", 63).
?DOC(" Encode to CBOR map entries (for embedding in larger CBOR structures).\n").
-spec to_cbor_map(gose@key:key(bitstring())) -> {ok,
list({gose@cbor:value(), gose@cbor:value()})} |
{error, gose:gose_error()}.
to_cbor_map(K) ->
Mat = gose@key:material(K),
gleam@result:'try'(
encode_key_material(Mat),
fun(Key_pairs) ->
gleam@result:'try'(
encode_metadata(K),
fun(Metadata_pairs) ->
{ok, lists:append(Key_pairs, Metadata_pairs)}
end
)
end
).
-file("src/gose/cose/key.gleam", 48).
?DOC(" Encode a Key to COSE_Key CBOR bytes.\n").
-spec to_cbor(gose@key:key(bitstring())) -> {ok, bitstring()} |
{error, gose:gose_error()}.
to_cbor(K) ->
gleam@result:'try'(
to_cbor_map(K),
fun(Pairs) -> {ok, gose@cbor:encode({map, Pairs})} end
).
-file("src/gose/cose/key.gleam", 561).
-spec key_op_from_cose(integer()) -> {ok, gose@key:key_op()} |
{error, gose:gose_error()}.
key_op_from_cose(Id) ->
case Id of
1 ->
{ok, sign};
2 ->
{ok, verify};
3 ->
{ok, encrypt};
4 ->
{ok, decrypt};
5 ->
{ok, wrap_key};
6 ->
{ok, unwrap_key};
7 ->
{ok, derive_key};
8 ->
{ok, derive_bits};
_ ->
{error,
{parse_error,
<<"unknown COSE key_op: "/utf8,
(erlang:integer_to_binary(Id))/binary>>}}
end.
-file("src/gose/cose/key.gleam", 490).
-spec decode_key_ops(list(gose@cbor:value())) -> {ok, list(gose@key:key_op())} |
{error, gose:gose_error()}.
decode_key_ops(Ops) ->
gleam@list:try_map(Ops, fun(V) -> case V of
{int, Id} ->
key_op_from_cose(Id);
_ ->
{error,
{parse_error, <<"key_ops must contain integers"/utf8>>}}
end end).
-file("src/gose/cose/key.gleam", 575).
-spec lookup_int(
list({gose@cbor:value(), gose@cbor:value()}),
integer(),
binary()
) -> {ok, integer()} | {error, gose:gose_error()}.
lookup_int(Map, Label, Error_msg) ->
case gleam@list:key_find(Map, {int, Label}) of
{ok, {int, Value}} ->
{ok, Value};
{ok, _} ->
{error, {parse_error, <<Error_msg/binary, " (wrong type)"/utf8>>}};
{error, _} ->
{error, {parse_error, Error_msg}}
end.
-file("src/gose/cose/key.gleam", 587).
-spec lookup_bytes(
list({gose@cbor:value(), gose@cbor:value()}),
integer(),
binary()
) -> {ok, bitstring()} | {error, gose:gose_error()}.
lookup_bytes(Map, Label, Error_msg) ->
case gleam@list:key_find(Map, {int, Label}) of
{ok, {bytes, Value}} ->
{ok, Value};
{ok, _} ->
{error, {parse_error, <<Error_msg/binary, " (wrong type)"/utf8>>}};
{error, _} ->
{error, {parse_error, Error_msg}}
end.
-file("src/gose/cose/key.gleam", 419).
-spec decode_symmetric(list({gose@cbor:value(), gose@cbor:value()})) -> {ok,
gose@key:key(bitstring())} |
{error, gose:gose_error()}.
decode_symmetric(Map) ->
gleam@result:'try'(
lookup_bytes(Map, -1, <<"missing symmetric key (label -1)"/utf8>>),
fun(K) -> gose@key:from_octet_bits(K) end
).
-file("src/gose/cose/key.gleam", 599).
-spec lookup_int_optional(
list({gose@cbor:value(), gose@cbor:value()}),
integer()
) -> {ok, gleam@option:option(integer())} | {error, gose:gose_error()}.
lookup_int_optional(Map, Label) ->
case gleam@list:key_find(Map, {int, Label}) of
{ok, {int, Value}} ->
{ok, {some, Value}};
{ok, _} ->
{error,
{parse_error,
<<<<"key parameter "/utf8,
(erlang:integer_to_binary(Label))/binary>>/binary,
" has wrong type"/utf8>>}};
{error, _} ->
{ok, none}
end.
-file("src/gose/cose/key.gleam", 446).
-spec apply_alg(
gose@key:key(bitstring()),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
apply_alg(K, Map) ->
gleam@result:'try'(
lookup_int_optional(Map, 3),
fun(Opt_alg_id) -> case Opt_alg_id of
{some, Alg_id} ->
gleam@result:map(
decode_alg(Alg_id),
fun(Alg) -> gose@key:with_alg(K, Alg) end
);
none ->
{ok, K}
end end
).
-file("src/gose/cose/key.gleam", 613).
-spec lookup_bytes_optional(
list({gose@cbor:value(), gose@cbor:value()}),
integer()
) -> {ok, gleam@option:option(bitstring())} | {error, gose:gose_error()}.
lookup_bytes_optional(Map, Label) ->
case gleam@list:key_find(Map, {int, Label}) of
{ok, {bytes, Value}} ->
{ok, {some, Value}};
{ok, _} ->
{error,
{parse_error,
<<<<"key parameter "/utf8,
(erlang:integer_to_binary(Label))/binary>>/binary,
" has wrong type"/utf8>>}};
{error, _} ->
{ok, none}
end.
-file("src/gose/cose/key.gleam", 435).
-spec apply_kid(
gose@key:key(bitstring()),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
apply_kid(K, Map) ->
gleam@result:'try'(
lookup_bytes_optional(Map, 2),
fun(Opt_kid) -> case Opt_kid of
{some, Kid_bytes} ->
{ok, gose@key:with_kid_bits(K, Kid_bytes)};
none ->
{ok, K}
end end
).
-file("src/gose/cose/key.gleam", 627).
-spec lookup_array_optional(
list({gose@cbor:value(), gose@cbor:value()}),
integer()
) -> {ok, gleam@option:option(list(gose@cbor:value()))} |
{error, gose:gose_error()}.
lookup_array_optional(Map, Label) ->
case gleam@list:key_find(Map, {int, Label}) of
{ok, {array, Items}} ->
{ok, {some, Items}};
{ok, _} ->
{error,
{parse_error,
<<<<"key parameter "/utf8,
(erlang:integer_to_binary(Label))/binary>>/binary,
" has wrong type"/utf8>>}};
{error, _} ->
{ok, none}
end.
-file("src/gose/cose/key.gleam", 460).
-spec apply_key_ops(
gose@key:key(bitstring()),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
apply_key_ops(K, Map) ->
gleam@result:'try'(
lookup_array_optional(Map, 4),
fun(Opt_ops) -> case Opt_ops of
{some, Ops_cbor} ->
gleam@result:'try'(
decode_key_ops(Ops_cbor),
fun(Ops) -> gose@key:with_key_ops(K, Ops) end
);
none ->
{ok, K}
end end
).
-file("src/gose/cose/key.gleam", 426).
-spec apply_metadata(
gose@key:key(bitstring()),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
apply_metadata(K, Map) ->
gleam@result:'try'(
apply_kid(K, Map),
fun(K@1) ->
gleam@result:'try'(
apply_alg(K@1, Map),
fun(K@2) -> apply_key_ops(K@2, Map) end
)
end
).
-file("src/gose/cose/key.gleam", 641).
-spec has_label(list({gose@cbor:value(), gose@cbor:value()}), integer()) -> boolean().
has_label(Map, Label) ->
_pipe = gleam@list:key_find(Map, {int, Label}),
gleam@result:is_ok(_pipe).
-file("src/gose/cose/key.gleam", 273).
-spec decode_ec2(list({gose@cbor:value(), gose@cbor:value()})) -> {ok,
gose@key:key(bitstring())} |
{error, gose:gose_error()}.
decode_ec2(Map) ->
gleam@result:'try'(
lookup_int(Map, -1, <<"missing EC curve (label -1)"/utf8>>),
fun(Crv_id) ->
gleam@result:'try'(
ec_curve_from_cose(Crv_id),
fun(Curve) ->
gleam@result:'try'(
lookup_bytes(
Map,
-2,
<<"missing EC x (label -2)"/utf8>>
),
fun(X) ->
gleam@result:'try'(
lookup_bytes(
Map,
-3,
<<"missing EC y (label -3)"/utf8>>
),
fun(Y) -> case has_label(Map, -4) of
true ->
gleam@result:'try'(
lookup_bytes(
Map,
-4,
<<"missing EC d (label -4)"/utf8>>
),
fun(D) ->
gleam@result:'try'(
begin
_pipe = kryptos_ffi:ec_private_key_from_bytes(
Curve,
D
),
gleam@result:replace_error(
_pipe,
{parse_error,
<<"invalid EC private key"/utf8>>}
)
end,
fun(_use0) ->
{Private, Public} = _use0,
Computed_point = kryptos_ffi:ec_public_key_to_raw_point(
Public
),
Raw_point = gleam_stdlib:bit_array_concat(
[<<16#04>>,
X,
Y]
),
gleam@bool:guard(
not fun kryptos_ffi:constant_time_equal/2(
Computed_point,
Raw_point
),
{error,
{parse_error,
<<"x/y do not match computed public key"/utf8>>}},
fun() ->
{ok,
gose@key:new_key(
{ec,
{ec_private,
Private,
Public,
Curve}}
)}
end
)
end
)
end
);
false ->
gose@key:ec_public_key_from_coordinates(
Curve,
X,
Y
)
end end
)
end
)
end
)
end
).
-file("src/gose/cose/key.gleam", 313).
-spec decode_eddsa_key(
kryptos@eddsa:curve(),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
decode_eddsa_key(Curve, Map) ->
case has_label(Map, -4) of
true ->
gleam@result:'try'(
lookup_bytes(Map, -2, <<"missing EdDSA x (label -2)"/utf8>>),
fun(X) ->
gleam@result:'try'(
lookup_bytes(
Map,
-4,
<<"missing EdDSA d (label -4)"/utf8>>
),
fun(D) ->
gleam@result:'try'(
begin
_pipe = kryptos_ffi:eddsa_private_key_from_bytes(
Curve,
D
),
gleam@result:replace_error(
_pipe,
{parse_error,
<<"invalid EdDSA private key"/utf8>>}
)
end,
fun(_use0) ->
{Private, Public} = _use0,
Computed_x = kryptos_ffi:eddsa_public_key_to_bytes(
Public
),
gleam@bool:guard(
not fun kryptos_ffi:constant_time_equal/2(
Computed_x,
X
),
{error,
{parse_error,
<<"x does not match computed public key"/utf8>>}},
fun() ->
{ok,
gose@key:new_key(
{eddsa,
{eddsa_private,
Private,
Public,
Curve}}
)}
end
)
end
)
end
)
end
);
false ->
gleam@result:'try'(
lookup_bytes(Map, -2, <<"missing EdDSA x (label -2)"/utf8>>),
fun(X@1) -> gose@key:from_eddsa_public_bits(Curve, X@1) end
)
end.
-file("src/gose/cose/key.gleam", 341).
-spec decode_xdh_key(
kryptos@xdh:curve(),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
decode_xdh_key(Curve, Map) ->
case has_label(Map, -4) of
true ->
gleam@result:'try'(
lookup_bytes(Map, -2, <<"missing XDH x (label -2)"/utf8>>),
fun(X) ->
gleam@result:'try'(
lookup_bytes(
Map,
-4,
<<"missing XDH d (label -4)"/utf8>>
),
fun(D) ->
gleam@result:'try'(
begin
_pipe = kryptos_ffi:xdh_private_key_from_bytes(
Curve,
D
),
gleam@result:replace_error(
_pipe,
{parse_error,
<<"invalid XDH private key"/utf8>>}
)
end,
fun(_use0) ->
{Private, Public} = _use0,
Computed_x = kryptos_ffi:xdh_public_key_to_bytes(
Public
),
gleam@bool:guard(
not fun kryptos_ffi:constant_time_equal/2(
Computed_x,
X
),
{error,
{parse_error,
<<"x does not match computed public key"/utf8>>}},
fun() ->
{ok,
gose@key:new_key(
{xdh,
{xdh_private,
Private,
Public,
Curve}}
)}
end
)
end
)
end
)
end
);
false ->
gleam@result:'try'(
lookup_bytes(Map, -2, <<"missing XDH x (label -2)"/utf8>>),
fun(X@1) -> gose@key:from_xdh_public_bits(Curve, X@1) end
)
end.
-file("src/gose/cose/key.gleam", 299).
-spec decode_okp(list({gose@cbor:value(), gose@cbor:value()})) -> {ok,
gose@key:key(bitstring())} |
{error, gose:gose_error()}.
decode_okp(Map) ->
gleam@result:'try'(
lookup_int(Map, -1, <<"missing OKP curve (label -1)"/utf8>>),
fun(Crv_id) -> case Crv_id of
6 ->
decode_eddsa_key(ed25519, Map);
7 ->
decode_eddsa_key(ed448, Map);
4 ->
decode_xdh_key(x25519, Map);
5 ->
decode_xdh_key(x448, Map);
_ ->
{error,
{parse_error,
<<"unsupported OKP curve: "/utf8,
(erlang:integer_to_binary(Crv_id))/binary>>}}
end end
).
-file("src/gose/cose/key.gleam", 385).
-spec decode_rsa_private(
list({gose@cbor:value(), gose@cbor:value()}),
bitstring(),
bitstring()
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
decode_rsa_private(Map, N, E) ->
gleam@result:'try'(
lookup_bytes(Map, -3, <<"missing RSA d (label -3)"/utf8>>),
fun(D) -> case has_label(Map, -4) of
true ->
gleam@result:'try'(
lookup_bytes(
Map,
-4,
<<"missing RSA p (label -4)"/utf8>>
),
fun(P) ->
gleam@result:'try'(
lookup_bytes(
Map,
-5,
<<"missing RSA q (label -5)"/utf8>>
),
fun(Q) ->
gleam@result:'try'(
lookup_bytes(
Map,
-6,
<<"missing RSA dp (label -6)"/utf8>>
),
fun(Dp) ->
gleam@result:'try'(
lookup_bytes(
Map,
-7,
<<"missing RSA dq (label -7)"/utf8>>
),
fun(Dq) ->
gleam@result:'try'(
lookup_bytes(
Map,
-8,
<<"missing RSA qi (label -8)"/utf8>>
),
fun(Qi) ->
_pipe = kryptos_ffi:rsa_private_key_from_full_components(
N,
E,
D,
P,
Q,
Dp,
Dq,
Qi
),
_pipe@1 = gleam@result:replace_error(
_pipe,
{parse_error,
<<"invalid RSA private key components"/utf8>>}
),
gleam@result:map(
_pipe@1,
fun(Pair) ->
{Private,
Public} = Pair,
gose@key:new_key(
{rsa,
{rsa_private,
Private,
Public}}
)
end
)
end
)
end
)
end
)
end
)
end
);
false ->
_pipe@2 = kryptos@rsa:from_components(N, E, D),
_pipe@3 = gleam@result:replace_error(
_pipe@2,
{parse_error,
<<"invalid RSA private key components"/utf8>>}
),
gleam@result:map(
_pipe@3,
fun(Pair@1) ->
{Private@1, Public@1} = Pair@1,
gose@key:new_key(
{rsa, {rsa_private, Private@1, Public@1}}
)
end
)
end end
).
-file("src/gose/cose/key.gleam", 367).
-spec decode_rsa(list({gose@cbor:value(), gose@cbor:value()})) -> {ok,
gose@key:key(bitstring())} |
{error, gose:gose_error()}.
decode_rsa(Map) ->
gleam@result:'try'(
lookup_bytes(Map, -1, <<"missing RSA n (label -1)"/utf8>>),
fun(N) ->
gleam@result:'try'(
lookup_bytes(Map, -2, <<"missing RSA e (label -2)"/utf8>>),
fun(E) -> case has_label(Map, -3) of
true ->
decode_rsa_private(Map, N, E);
false ->
_pipe = kryptos_ffi:rsa_public_key_from_components(
N,
E
),
_pipe@1 = gleam@result:replace_error(
_pipe,
{parse_error,
<<"invalid RSA public key components"/utf8>>}
),
gleam@result:map(
_pipe@1,
fun(Public_key) ->
gose@key:new_key(
{rsa, {rsa_public, Public_key}}
)
end
)
end end
)
end
).
-file("src/gose/cose/key.gleam", 259).
-spec decode_key_by_type(
integer(),
list({gose@cbor:value(), gose@cbor:value()})
) -> {ok, gose@key:key(bitstring())} | {error, gose:gose_error()}.
decode_key_by_type(Kty, Map) ->
case Kty of
1 ->
decode_okp(Map);
2 ->
decode_ec2(Map);
3 ->
decode_rsa(Map);
4 ->
decode_symmetric(Map);
_ ->
{error,
{parse_error,
<<"unsupported COSE key type: "/utf8,
(erlang:integer_to_binary(Kty))/binary>>}}
end.
-file("src/gose/cose/key.gleam", 73).
?DOC(" Decode from CBOR map entries.\n").
-spec from_cbor_map(list({gose@cbor:value(), gose@cbor:value()})) -> {ok,
gose@key:key(bitstring())} |
{error, gose:gose_error()}.
from_cbor_map(Map) ->
gleam@result:'try'(
lookup_int(Map, 1, <<"missing kty (label 1)"/utf8>>),
fun(Kty) ->
gleam@result:'try'(
decode_key_by_type(Kty, Map),
fun(Base_key) -> apply_metadata(Base_key, Map) end
)
end
).
-file("src/gose/cose/key.gleam", 54).
?DOC(" Decode COSE_Key from CBOR bytes to a Key.\n").
-spec from_cbor(bitstring()) -> {ok, gose@key:key(bitstring())} |
{error, gose:gose_error()}.
from_cbor(Data) ->
gleam@result:'try'(gose@cbor:decode(Data), fun(Value) -> case Value of
{map, Pairs} ->
from_cbor_map(Pairs);
_ ->
{error,
{parse_error, <<"COSE_Key must be a CBOR map"/utf8>>}}
end end).