Current section
Files
Jump to
Current section
Files
src/gose.erl
-module(gose).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gose.gleam").
-export([error_message/1, new_key/1, is_private_key/1, material/1, material_octet_secret/1, material_rsa/1, material_ec/1, material_eddsa/1, material_xdh/1, from_der/1, from_eddsa_bits/2, from_eddsa_public_bits/2, from_octet_bits/1, from_pem/1, from_xdh_bits/2, from_xdh_public_bits/2, generate_ec/1, generate_eddsa/1, generate_chacha20_kw_key/0, generate_rsa/1, generate_xdh/1, ec_public_key_from_raw_coordinates/3, ec_public_key_from_coordinates/3, ec_raw_coordinates/2, with_alg/2, with_kid/2, with_kid_bits/2, validate_key_use_ops/2, with_key_ops/2, with_key_use/2, alg/1, ec_curve/1, ec_public_key/1, ec_public_key_coordinates/1, eddsa_curve/1, eddsa_public_key/1, key_ops/1, key_type/1, key_use/1, kid/1, octet_key_size/1, rsa_public_key/1, xdh_curve/1, xdh_public_key/1, public_key/1, to_der/1, to_octet_bits/1, to_pem/1, build/5, validate_rfc8037_key_use_public/2, aes_key_size/1, generate_aes_kw_key/1, hmac_alg_key_size/1, generate_hmac_key/1, content_alg_key_size/1, generate_enc_key/1, chacha20_kw_nonce_size/1]).
-export_type([gose_error/0, key_use/0, key_op/0, aes_key_size/0, aes_kw_mode/0, cha_cha20_kw/0, hmac_alg/0, rsa_pkcs1_alg/0, rsa_pss_alg/0, ecdsa_alg/0, digital_signature_alg/0, mac_alg/0, signing_alg/0, rsa_encryption_alg/0, ecdh_es_alg/0, pbes2_alg/0, key_encryption_alg/0, content_alg/0, alg/0, rsa_key_material/0, ec_key_material/0, eddsa_key_material/0, xdh_key_material/0, key_material/0, key_type/0, key/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(
" A Gleam library for JOSE (JSON Object Signing and Encryption) and\n"
" COSE (CBOR Object Signing and Encryption).\n"
"\n"
" Core:\n"
" - `gose`: error type, algorithm identifiers, and key management (types,\n"
" generators, builders, accessors, serializers)\n"
" - `gose/cbor`: CBOR encoding for COSE\n"
"\n"
" `gose/key`, `gose/algorithm`, `gose/jose/algorithm`, `gose/cose/key`, and\n"
" `gose/cose/algorithm` are deprecated shims retained for the v2.x migration\n"
" window. They will be removed in v3.0. New code should import `gose`,\n"
" `gose/jose`, and `gose/cose` directly.\n"
"\n"
" JOSE:\n"
" - `gose/jose`: JOSE algorithm string conversion ([RFC 7518](https://www.rfc-editor.org/rfc/rfc7518.html))\n"
" - `gose/jose/jws`: JSON Web Signature ([RFC 7515](https://www.rfc-editor.org/rfc/rfc7515.html))\n"
" - `gose/jose/jws_multi`: JWS JSON Serialization for multi-signer workflows\n"
" - `gose/jose/jwe`: JSON Web Encryption ([RFC 7516](https://www.rfc-editor.org/rfc/rfc7516.html))\n"
" - `gose/jose/jwe_multi`: JWE JSON Serialization for multi-recipient workflows\n"
" - `gose/jose/jwk`: JSON Web Key serialization ([RFC 7517](https://www.rfc-editor.org/rfc/rfc7517.html))\n"
" - `gose/jose/key_set`: JWK Set ([RFC 7517 Section 5](https://www.rfc-editor.org/rfc/rfc7517.html#section-5))\n"
" - `gose/jose/encrypted_key`: encrypted JWK export/import\n"
" - `gose/jose/jwt`: JSON Web Token ([RFC 7519](https://www.rfc-editor.org/rfc/rfc7519.html))\n"
" - `gose/jose/encrypted_jwt`: encrypted JWT (JWE-based)\n"
"\n"
" COSE:\n"
" - `gose/cose`: header parameters, the `Key` alias, COSE_Key CBOR serialization, and COSE algorithm ID mapping ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html), [RFC 9053](https://www.rfc-editor.org/rfc/rfc9053.html))\n"
" - `gose/cose/sign1`: COSE_Sign1 ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html))\n"
" - `gose/cose/sign`: COSE_Sign multi-signer ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html))\n"
" - `gose/cose/encrypt0`: COSE_Encrypt0 ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html))\n"
" - `gose/cose/encrypt`: COSE_Encrypt multi-recipient ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html))\n"
" - `gose/cose/mac0`: COSE_Mac0 ([RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html))\n"
" - `gose/cose/cwt`: CBOR Web Token ([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html))\n"
" - `gose/cose/encrypted_cwt`: encrypted CWT (Encrypt0-wrapped Sign1)\n"
).
-type gose_error() :: {parse_error, binary()} |
{crypto_error, binary()} |
{invalid_state, binary()} |
verification_failed.
-type key_use() :: signing | encrypting.
-type key_op() :: sign |
verify |
encrypt |
decrypt |
wrap_key |
unwrap_key |
derive_key |
derive_bits.
-type aes_key_size() :: aes128 | aes192 | aes256.
-type aes_kw_mode() :: aes_kw | aes_gcm_kw.
-type cha_cha20_kw() :: c20_p_kw | x_c20_p_kw.
-type hmac_alg() :: hmac_sha256 | hmac_sha384 | hmac_sha512.
-type rsa_pkcs1_alg() :: rsa_pkcs1_sha256 | rsa_pkcs1_sha384 | rsa_pkcs1_sha512.
-type rsa_pss_alg() :: rsa_pss_sha256 | rsa_pss_sha384 | rsa_pss_sha512.
-type ecdsa_alg() :: ecdsa_p256 | ecdsa_p384 | ecdsa_p521 | ecdsa_secp256k1.
-type digital_signature_alg() :: {rsa_pkcs1, rsa_pkcs1_alg()} |
{rsa_pss, rsa_pss_alg()} |
{ecdsa, ecdsa_alg()} |
eddsa.
-type mac_alg() :: {hmac, hmac_alg()}.
-type signing_alg() :: {digital_signature, digital_signature_alg()} |
{mac, mac_alg()}.
-type rsa_encryption_alg() :: rsa_pkcs1v15 | rsa_oaep_sha1 | rsa_oaep_sha256.
-type ecdh_es_alg() :: ecdh_es_direct |
{ecdh_es_aes_kw, aes_key_size()} |
{ecdh_es_cha_cha20_kw, cha_cha20_kw()}.
-type pbes2_alg() :: pbes2_sha256_aes128_kw |
pbes2_sha384_aes192_kw |
pbes2_sha512_aes256_kw.
-type key_encryption_alg() :: direct |
{aes_key_wrap, aes_kw_mode(), aes_key_size()} |
{cha_cha20_key_wrap, cha_cha20_kw()} |
{rsa_encryption, rsa_encryption_alg()} |
{ecdh_es, ecdh_es_alg()} |
{pbes2, pbes2_alg()}.
-type content_alg() :: {aes_gcm, aes_key_size()} |
{aes_cbc_hmac, aes_key_size()} |
cha_cha20_poly1305 |
x_cha_cha20_poly1305.
-type alg() :: {signing_alg, signing_alg()} |
{key_encryption_alg, key_encryption_alg()} |
{content_alg, content_alg()}.
-type rsa_key_material() :: {rsa_private,
kryptos@rsa:private_key(),
kryptos@rsa:public_key()} |
{rsa_public, kryptos@rsa:public_key()}.
-type ec_key_material() :: {ec_private,
kryptos@ec:private_key(),
kryptos@ec:public_key(),
kryptos@ec:curve()} |
{ec_public, kryptos@ec:public_key(), kryptos@ec:curve()}.
-type eddsa_key_material() :: {eddsa_private,
kryptos@eddsa:private_key(),
kryptos@eddsa:public_key(),
kryptos@eddsa:curve()} |
{eddsa_public, kryptos@eddsa:public_key(), kryptos@eddsa:curve()}.
-type xdh_key_material() :: {xdh_private,
kryptos@xdh:private_key(),
kryptos@xdh:public_key(),
kryptos@xdh:curve()} |
{xdh_public, kryptos@xdh:public_key(), kryptos@xdh:curve()}.
-type key_material() :: {octet_key, bitstring()} |
{rsa, rsa_key_material()} |
{elliptic, ec_key_material()} |
{edwards, eddsa_key_material()} |
{xdh, xdh_key_material()}.
-type key_type() :: oct_key_type | rsa_key_type | ec_key_type | okp_key_type.
-opaque key(JSA) :: {key,
key_material(),
gleam@option:option(JSA),
gleam@option:option(key_use()),
gleam@option:option(list(key_op())),
gleam@option:option(alg())}.
-file("src/gose.gleam", 68).
?DOC(" Extract the message string from a GoseError, regardless of variant.\n").
-spec error_message(gose_error()) -> binary().
error_message(Error) ->
case Error of
{parse_error, Msg} ->
Msg;
{crypto_error, Msg@1} ->
Msg@1;
{invalid_state, Msg@2} ->
Msg@2;
verification_failed ->
<<"verification failed"/utf8>>
end.
-file("src/gose.gleam", 342).
?DOC(false).
-spec new_key(key_material()) -> key(any()).
new_key(Material) ->
{key, Material, none, none, none, none}.
-file("src/gose.gleam", 353).
?DOC(false).
-spec is_private_key(key(any())) -> boolean().
is_private_key(Key) ->
case erlang:element(2, Key) of
{octet_key, _} ->
true;
{rsa, {rsa_private, _, _}} ->
true;
{rsa, {rsa_public, _}} ->
false;
{elliptic, {ec_private, _, _, _}} ->
true;
{elliptic, {ec_public, _, _}} ->
false;
{edwards, {eddsa_private, _, _, _}} ->
true;
{edwards, {eddsa_public, _, _}} ->
false;
{xdh, {xdh_private, _, _, _}} ->
true;
{xdh, {xdh_public, _, _}} ->
false
end.
-file("src/gose.gleam", 368).
?DOC(false).
-spec material(key(any())) -> key_material().
material(Key) ->
erlang:element(2, Key).
-file("src/gose.gleam", 373).
?DOC(false).
-spec material_octet_secret(key_material()) -> {ok, bitstring()} |
{error, gose_error()}.
material_octet_secret(Mat) ->
case Mat of
{octet_key, Secret} ->
{ok, Secret};
{rsa, _} ->
{error, {invalid_state, <<"expected octet key"/utf8>>}};
{elliptic, _} ->
{error, {invalid_state, <<"expected octet key"/utf8>>}};
{edwards, _} ->
{error, {invalid_state, <<"expected octet key"/utf8>>}};
{xdh, _} ->
{error, {invalid_state, <<"expected octet key"/utf8>>}}
end.
-file("src/gose.gleam", 382).
?DOC(false).
-spec material_rsa(key_material()) -> {ok, rsa_key_material()} |
{error, gose_error()}.
material_rsa(Mat) ->
case Mat of
{rsa, Rsa} ->
{ok, Rsa};
{octet_key, _} ->
{error, {invalid_state, <<"expected RSA key"/utf8>>}};
{elliptic, _} ->
{error, {invalid_state, <<"expected RSA key"/utf8>>}};
{edwards, _} ->
{error, {invalid_state, <<"expected RSA key"/utf8>>}};
{xdh, _} ->
{error, {invalid_state, <<"expected RSA key"/utf8>>}}
end.
-file("src/gose.gleam", 391).
?DOC(false).
-spec material_ec(key_material()) -> {ok, ec_key_material()} |
{error, gose_error()}.
material_ec(Mat) ->
case Mat of
{elliptic, Ec} ->
{ok, Ec};
{octet_key, _} ->
{error, {invalid_state, <<"expected EC key"/utf8>>}};
{rsa, _} ->
{error, {invalid_state, <<"expected EC key"/utf8>>}};
{edwards, _} ->
{error, {invalid_state, <<"expected EC key"/utf8>>}};
{xdh, _} ->
{error, {invalid_state, <<"expected EC key"/utf8>>}}
end.
-file("src/gose.gleam", 400).
?DOC(false).
-spec material_eddsa(key_material()) -> {ok, eddsa_key_material()} |
{error, gose_error()}.
material_eddsa(Mat) ->
case Mat of
{edwards, Eddsa} ->
{ok, Eddsa};
{octet_key, _} ->
{error, {invalid_state, <<"expected EdDSA key"/utf8>>}};
{rsa, _} ->
{error, {invalid_state, <<"expected EdDSA key"/utf8>>}};
{elliptic, _} ->
{error, {invalid_state, <<"expected EdDSA key"/utf8>>}};
{xdh, _} ->
{error, {invalid_state, <<"expected EdDSA key"/utf8>>}}
end.
-file("src/gose.gleam", 409).
?DOC(false).
-spec material_xdh(key_material()) -> {ok, xdh_key_material()} |
{error, gose_error()}.
material_xdh(Mat) ->
case Mat of
{xdh, Xdh} ->
{ok, Xdh};
{octet_key, _} ->
{error, {invalid_state, <<"expected XDH key"/utf8>>}};
{rsa, _} ->
{error, {invalid_state, <<"expected XDH key"/utf8>>}};
{elliptic, _} ->
{error, {invalid_state, <<"expected XDH key"/utf8>>}};
{edwards, _} ->
{error, {invalid_state, <<"expected XDH key"/utf8>>}}
end.
-file("src/gose.gleam", 433).
-spec ec_private_key({kryptos@ec:private_key(), kryptos@ec:public_key()}) -> key(any()).
ec_private_key(Pair) ->
{Private, Public} = Pair,
Curve = kryptos_ffi:ec_private_key_curve(Private),
new_key({elliptic, {ec_private, Private, Public, Curve}}).
-file("src/gose.gleam", 439).
-spec ec_public_key_internal(kryptos@ec:public_key()) -> key(any()).
ec_public_key_internal(Public) ->
Curve = kryptos_ffi:ec_public_key_curve(Public),
new_key({elliptic, {ec_public, Public, Curve}}).
-file("src/gose.gleam", 444).
-spec eddsa_private_key(
{kryptos@eddsa:private_key(), kryptos@eddsa:public_key()}
) -> key(any()).
eddsa_private_key(Pair) ->
{Private, Public} = Pair,
Curve = kryptos_ffi:eddsa_private_key_curve(Private),
new_key({edwards, {eddsa_private, Private, Public, Curve}}).
-file("src/gose.gleam", 450).
-spec eddsa_public_key_internal(kryptos@eddsa:public_key()) -> key(any()).
eddsa_public_key_internal(Public) ->
Curve = kryptos_ffi:eddsa_public_key_curve(Public),
new_key({edwards, {eddsa_public, Public, Curve}}).
-file("src/gose.gleam", 455).
-spec rsa_private_key_internal(
{kryptos@rsa:private_key(), kryptos@rsa:public_key()}
) -> key(any()).
rsa_private_key_internal(Pair) ->
{Private, Public} = Pair,
new_key({rsa, {rsa_private, Private, Public}}).
-file("src/gose.gleam", 460).
-spec rsa_public_key_internal(kryptos@rsa:public_key()) -> key(any()).
rsa_public_key_internal(Public) ->
new_key({rsa, {rsa_public, Public}}).
-file("src/gose.gleam", 464).
-spec xdh_private_key({kryptos@xdh:private_key(), kryptos@xdh:public_key()}) -> key(any()).
xdh_private_key(Pair) ->
{Private, Public} = Pair,
Curve = kryptos_ffi:xdh_private_key_curve(Private),
new_key({xdh, {xdh_private, Private, Public, Curve}}).
-file("src/gose.gleam", 470).
-spec xdh_public_key_internal(kryptos@xdh:public_key()) -> key(any()).
xdh_public_key_internal(Public) ->
Curve = kryptos_ffi:xdh_public_key_curve(Public),
new_key({xdh, {xdh_public, Public, Curve}}).
-file("src/gose.gleam", 475).
-spec parse_ec_der(bitstring()) -> {ok, key(any())} | {error, nil}.
parse_ec_der(Der) ->
_pipe = kryptos_ffi:ec_import_private_key_der(Der),
_pipe@1 = gleam@result:map(_pipe, fun ec_private_key/1),
gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:ec_import_public_key_der(Der),
gleam@result:map(_pipe@2, fun ec_public_key_internal/1) end
).
-file("src/gose.gleam", 483).
-spec parse_eddsa_der(bitstring()) -> {ok, key(any())} | {error, nil}.
parse_eddsa_der(Der) ->
_pipe = kryptos_ffi:eddsa_import_private_key_der(Der),
_pipe@1 = gleam@result:map(_pipe, fun eddsa_private_key/1),
gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:eddsa_import_public_key_der(Der),
gleam@result:map(_pipe@2, fun eddsa_public_key_internal/1) end
).
-file("src/gose.gleam", 491).
-spec parse_rsa_der(bitstring()) -> {ok, key(any())} | {error, nil}.
parse_rsa_der(Der) ->
_pipe = kryptos_ffi:rsa_import_private_key_der(Der, pkcs8),
_pipe@1 = gleam@result:map(_pipe, fun rsa_private_key_internal/1),
_pipe@3 = gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:rsa_import_private_key_der(Der, pkcs1),
gleam@result:map(_pipe@2, fun rsa_private_key_internal/1) end
),
_pipe@5 = gleam@result:lazy_or(
_pipe@3,
fun() -> _pipe@4 = kryptos_ffi:rsa_import_public_key_der(Der, spki),
gleam@result:map(_pipe@4, fun rsa_public_key_internal/1) end
),
gleam@result:lazy_or(
_pipe@5,
fun() ->
_pipe@6 = kryptos_ffi:rsa_import_public_key_der(Der, rsa_public_key),
gleam@result:map(_pipe@6, fun rsa_public_key_internal/1)
end
).
-file("src/gose.gleam", 507).
-spec parse_xdh_der(bitstring()) -> {ok, key(any())} | {error, nil}.
parse_xdh_der(Der) ->
_pipe = kryptos_ffi:xdh_import_private_key_der(Der),
_pipe@1 = gleam@result:map(_pipe, fun xdh_private_key/1),
gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:xdh_import_public_key_der(Der),
gleam@result:map(_pipe@2, fun xdh_public_key_internal/1) end
).
-file("src/gose.gleam", 421).
?DOC(
" Create a key from DER-encoded data.\n"
"\n"
" Auto-detects key type (RSA, EC, EdDSA, XDH) and format (PKCS#1, PKCS#8, SPKI).\n"
" Supports both private and public keys.\n"
).
-spec from_der(bitstring()) -> {ok, key(any())} | {error, gose_error()}.
from_der(Der) ->
_pipe = parse_rsa_der(Der),
_pipe@1 = gleam@result:lazy_or(_pipe, fun() -> parse_eddsa_der(Der) end),
_pipe@2 = gleam@result:lazy_or(_pipe@1, fun() -> parse_xdh_der(Der) end),
_pipe@3 = gleam@result:lazy_or(_pipe@2, fun() -> parse_ec_der(Der) end),
gleam@result:map_error(
_pipe@3,
fun(_) ->
{parse_error,
<<"invalid DER: not a recognized RSA, EC, EdDSA, or XDH key format"/utf8>>}
end
).
-file("src/gose.gleam", 519).
?DOC(
" Create an EdDSA key pair from raw private key bytes.\n"
"\n"
" The public key is derived from the private key.\n"
" This is the inverse of `to_octet_bits` for EdDSA private keys.\n"
).
-spec from_eddsa_bits(kryptos@eddsa:curve(), bitstring()) -> {ok, key(any())} |
{error, gose_error()}.
from_eddsa_bits(Curve, Private_bits) ->
_pipe = kryptos_ffi:eddsa_private_key_from_bytes(Curve, Private_bits),
_pipe@1 = gleam@result:map(
_pipe,
fun(Pair) ->
{Private, Public} = Pair,
new_key({edwards, {eddsa_private, Private, Public, Curve}})
end
),
gleam@result:replace_error(
_pipe@1,
{parse_error, <<"invalid EdDSA private key bits"/utf8>>}
).
-file("src/gose.gleam", 534).
?DOC(
" Create an EdDSA public key from raw bytes.\n"
"\n"
" This is the inverse of `to_octet_bits` for EdDSA public keys.\n"
).
-spec from_eddsa_public_bits(kryptos@eddsa:curve(), bitstring()) -> {ok,
key(any())} |
{error, gose_error()}.
from_eddsa_public_bits(Curve, Public_bits) ->
_pipe = kryptos_ffi:eddsa_public_key_from_bytes(Curve, Public_bits),
_pipe@1 = gleam@result:map(
_pipe,
fun(Public) -> new_key({edwards, {eddsa_public, Public, Curve}}) end
),
gleam@result:replace_error(
_pipe@1,
{parse_error, <<"invalid EdDSA public key bits"/utf8>>}
).
-file("src/gose.gleam", 556).
?DOC(
" Create a symmetric key from raw bytes.\n"
"\n"
" Used for HMAC signing (HS256/384/512) and direct encryption.\n"
" Returns an error if the secret is empty.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let secret = crypto.random_bytes(32)\n"
" let assert Ok(key) = gose.from_octet_bits(secret)\n"
" ```\n"
).
-spec from_octet_bits(bitstring()) -> {ok, key(any())} | {error, gose_error()}.
from_octet_bits(Secret) ->
case erlang:byte_size(Secret) of
0 ->
{error, {invalid_state, <<"oct key must not be empty"/utf8>>}};
_ ->
{ok, new_key({octet_key, Secret})}
end.
-file("src/gose.gleam", 579).
-spec parse_ec_pem(binary()) -> {ok, key(any())} | {error, nil}.
parse_ec_pem(Pem) ->
_pipe = kryptos_ffi:ec_import_private_key_pem(Pem),
_pipe@1 = gleam@result:map(_pipe, fun ec_private_key/1),
gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:ec_import_public_key_pem(Pem),
gleam@result:map(_pipe@2, fun ec_public_key_internal/1) end
).
-file("src/gose.gleam", 587).
-spec parse_eddsa_pem(binary()) -> {ok, key(any())} | {error, nil}.
parse_eddsa_pem(Pem) ->
_pipe = kryptos_ffi:eddsa_import_private_key_pem(Pem),
_pipe@1 = gleam@result:map(_pipe, fun eddsa_private_key/1),
gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:eddsa_import_public_key_pem(Pem),
gleam@result:map(_pipe@2, fun eddsa_public_key_internal/1) end
).
-file("src/gose.gleam", 595).
-spec parse_rsa_pem(binary()) -> {ok, key(any())} | {error, nil}.
parse_rsa_pem(Pem) ->
_pipe = kryptos_ffi:rsa_import_private_key_pem(Pem, pkcs8),
_pipe@1 = gleam@result:map(_pipe, fun rsa_private_key_internal/1),
_pipe@3 = gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:rsa_import_private_key_pem(Pem, pkcs1),
gleam@result:map(_pipe@2, fun rsa_private_key_internal/1) end
),
_pipe@5 = gleam@result:lazy_or(
_pipe@3,
fun() -> _pipe@4 = kryptos_ffi:rsa_import_public_key_pem(Pem, spki),
gleam@result:map(_pipe@4, fun rsa_public_key_internal/1) end
),
gleam@result:lazy_or(
_pipe@5,
fun() ->
_pipe@6 = kryptos_ffi:rsa_import_public_key_pem(Pem, rsa_public_key),
gleam@result:map(_pipe@6, fun rsa_public_key_internal/1)
end
).
-file("src/gose.gleam", 611).
-spec parse_xdh_pem(binary()) -> {ok, key(any())} | {error, nil}.
parse_xdh_pem(Pem) ->
_pipe = kryptos_ffi:xdh_import_private_key_pem(Pem),
_pipe@1 = gleam@result:map(_pipe, fun xdh_private_key/1),
gleam@result:lazy_or(
_pipe@1,
fun() -> _pipe@2 = kryptos_ffi:xdh_import_public_key_pem(Pem),
gleam@result:map(_pipe@2, fun xdh_public_key_internal/1) end
).
-file("src/gose.gleam", 567).
?DOC(
" Create a key from PEM-encoded data.\n"
"\n"
" Auto-detects key type (RSA, EC, EdDSA, XDH) and format (PKCS#1, PKCS#8, SPKI).\n"
" Supports both private and public keys.\n"
).
-spec from_pem(binary()) -> {ok, key(any())} | {error, gose_error()}.
from_pem(Pem) ->
_pipe = parse_rsa_pem(Pem),
_pipe@1 = gleam@result:lazy_or(_pipe, fun() -> parse_eddsa_pem(Pem) end),
_pipe@2 = gleam@result:lazy_or(_pipe@1, fun() -> parse_xdh_pem(Pem) end),
_pipe@3 = gleam@result:lazy_or(_pipe@2, fun() -> parse_ec_pem(Pem) end),
gleam@result:map_error(
_pipe@3,
fun(_) ->
{parse_error,
<<"invalid PEM: not a recognized RSA, EC, EdDSA, or XDH key format"/utf8>>}
end
).
-file("src/gose.gleam", 623).
?DOC(
" Create an XDH key pair from raw private key bytes.\n"
"\n"
" The public key is derived from the private key.\n"
" This is the inverse of `to_octet_bits` for XDH private keys.\n"
).
-spec from_xdh_bits(kryptos@xdh:curve(), bitstring()) -> {ok, key(any())} |
{error, gose_error()}.
from_xdh_bits(Curve, Private_bits) ->
_pipe = kryptos_ffi:xdh_private_key_from_bytes(Curve, Private_bits),
_pipe@1 = gleam@result:map(
_pipe,
fun(Pair) ->
{Private, Public} = Pair,
new_key({xdh, {xdh_private, Private, Public, Curve}})
end
),
gleam@result:replace_error(
_pipe@1,
{parse_error, <<"invalid XDH private key bits"/utf8>>}
).
-file("src/gose.gleam", 638).
?DOC(
" Create an XDH public key from raw bytes.\n"
"\n"
" This is the inverse of `to_octet_bits` for XDH public keys.\n"
).
-spec from_xdh_public_bits(kryptos@xdh:curve(), bitstring()) -> {ok, key(any())} |
{error, gose_error()}.
from_xdh_public_bits(Curve, Public_bits) ->
_pipe = kryptos_ffi:xdh_public_key_from_bytes(Curve, Public_bits),
_pipe@1 = gleam@result:map(
_pipe,
fun(Public) -> new_key({xdh, {xdh_public, Public, Curve}}) end
),
gleam@result:replace_error(
_pipe@1,
{parse_error, <<"invalid XDH public key bits"/utf8>>}
).
-file("src/gose.gleam", 650).
?DOC(
" Generate a new EC key pair for the given curve.\n"
"\n"
" Supported curves: P256, P384, P521, Secp256k1.\n"
).
-spec generate_ec(kryptos@ec:curve()) -> key(any()).
generate_ec(Curve) ->
{Private, Public} = kryptos_ffi:ec_generate_key_pair(Curve),
new_key({elliptic, {ec_private, Private, Public, Curve}}).
-file("src/gose.gleam", 658).
?DOC(
" Generate a new EdDSA key pair for the given curve.\n"
"\n"
" Supported curves: Ed25519, Ed448.\n"
).
-spec generate_eddsa(kryptos@eddsa:curve()) -> key(any()).
generate_eddsa(Curve) ->
{Private, Public} = kryptos_ffi:eddsa_generate_key_pair(Curve),
new_key({edwards, {eddsa_private, Private, Public, Curve}}).
-file("src/gose.gleam", 707).
?DOC(
" Generate a symmetric key for ChaCha20-Poly1305 Key Wrap (C20PKW / XC20PKW).\n"
"\n"
" Always generates a 32-byte key, as both ChaCha20 and XChaCha20 use 256-bit keys.\n"
).
-spec generate_chacha20_kw_key() -> key(any()).
generate_chacha20_kw_key() ->
Secret = kryptos_ffi:random_bytes(32),
new_key({octet_key, Secret}).
-file("src/gose.gleam", 715).
?DOC(
" Generate a new RSA key pair with the given key size in bits.\n"
" Common sizes are 2048, 3072, and 4096. Keys smaller than 2048\n"
" bits are not recommended for security.\n"
).
-spec generate_rsa(integer()) -> {ok, key(any())} | {error, gose_error()}.
generate_rsa(Bits) ->
case kryptos@rsa:generate_key_pair(Bits) of
{ok, {Private, Public}} ->
{ok, new_key({rsa, {rsa_private, Private, Public}})};
{error, _} ->
{error, {crypto_error, <<"RSA key generation failed"/utf8>>}}
end.
-file("src/gose.gleam", 726).
?DOC(
" Generate a new XDH key pair for key agreement.\n"
"\n"
" Supported curves: X25519, X448.\n"
).
-spec generate_xdh(kryptos@xdh:curve()) -> key(any()).
generate_xdh(Curve) ->
{Private, Public} = kryptos_ffi:xdh_generate_key_pair(Curve),
new_key({xdh, {xdh_private, Private, Public, Curve}}).
-file("src/gose.gleam", 742).
?DOC(false).
-spec ec_public_key_from_raw_coordinates(
kryptos@ec:curve(),
bitstring(),
bitstring()
) -> {ok, kryptos@ec:public_key()} | {error, gose_error()}.
ec_public_key_from_raw_coordinates(Curve, X, Y) ->
Coord_size = kryptos@ec:coordinate_size(Curve),
gleam@bool:guard(
erlang:byte_size(X) /= Coord_size,
{error, {parse_error, <<"EC x coordinate wrong length"/utf8>>}},
fun() ->
gleam@bool:guard(
erlang:byte_size(Y) /= Coord_size,
{error, {parse_error, <<"EC y coordinate wrong length"/utf8>>}},
fun() ->
Raw_point = gleam_stdlib:bit_array_concat([<<16#04>>, X, Y]),
_pipe = kryptos_ffi:ec_public_key_from_raw_point(
Curve,
Raw_point
),
gleam@result:replace_error(
_pipe,
{parse_error, <<"invalid EC coordinates"/utf8>>}
)
end
)
end
).
-file("src/gose.gleam", 732).
?DOC(" Create an EC public key from curve and x,y coordinates (big-endian bytes).\n").
-spec ec_public_key_from_coordinates(
kryptos@ec:curve(),
bitstring(),
bitstring()
) -> {ok, key(any())} | {error, gose_error()}.
ec_public_key_from_coordinates(Curve, X, Y) ->
_pipe = ec_public_key_from_raw_coordinates(Curve, X, Y),
gleam@result:map(
_pipe,
fun(Public) -> new_key({elliptic, {ec_public, Public, Curve}}) end
).
-file("src/gose.gleam", 762).
?DOC(false).
-spec ec_raw_coordinates(kryptos@ec:public_key(), kryptos@ec:curve()) -> {ok,
{bitstring(), bitstring()}} |
{error, gose_error()}.
ec_raw_coordinates(Public, Curve) ->
Coord_size = kryptos@ec:coordinate_size(Curve),
Raw_point = kryptos_ffi:ec_public_key_to_raw_point(Public),
Expected_size = 1 + (Coord_size * 2),
case {erlang:byte_size(Raw_point) =:= Expected_size, Raw_point} of
{true, <<16#04, Rest/bitstring>>} ->
Error = {invalid_state, <<"invalid raw point format"/utf8>>},
gleam@result:'try'(
begin
_pipe = gleam_stdlib:bit_array_slice(Rest, 0, Coord_size),
gleam@result:replace_error(_pipe, Error)
end,
fun(X) ->
gleam@result:'try'(
begin
_pipe@1 = gleam_stdlib:bit_array_slice(
Rest,
Coord_size,
Coord_size
),
gleam@result:replace_error(_pipe@1, Error)
end,
fun(Y) -> {ok, {X, Y}} end
)
end
);
{_, _} ->
{error, {invalid_state, <<"invalid raw point format"/utf8>>}}
end.
-file("src/gose.gleam", 787).
?DOC(" Set the algorithm (`alg`) metadata parameter on a key.\n").
-spec with_alg(key(JWP), alg()) -> key(JWP).
with_alg(Key, Alg) ->
{key,
erlang:element(2, Key),
erlang:element(3, Key),
erlang:element(4, Key),
erlang:element(5, Key),
{some, Alg}}.
-file("src/gose.gleam", 831).
?DOC(
" Validate key use against RFC 8037 curve restrictions.\n"
" - EdDSA keys (Ed25519/Ed448): only `sig` allowed\n"
" - XDH keys (X25519/X448): only `enc` allowed\n"
).
-spec validate_rfc8037_key_use(key_material(), gleam@option:option(key_use())) -> {ok,
nil} |
{error, gose_error()}.
validate_rfc8037_key_use(Material, Use_) ->
case {Material, Use_} of
{{edwards, _}, {some, encrypting}} ->
{error,
{invalid_state,
<<"EdDSA keys (Ed25519/Ed448) cannot be used for encryption"/utf8>>}};
{{xdh, _}, {some, signing}} ->
{error,
{invalid_state,
<<"XDH keys (X25519/X448) cannot be used for signing"/utf8>>}};
{_, _} ->
{ok, nil}
end.
-file("src/gose.gleam", 847).
?DOC(" Set the key ID (`kid`) metadata parameter on a key.\n").
-spec with_kid(key(any()), binary()) -> key(binary()).
with_kid(Key, Kid) ->
{key,
erlang:element(2, Key),
{some, Kid},
erlang:element(4, Key),
erlang:element(5, Key),
erlang:element(6, Key)}.
-file("src/gose.gleam", 855).
?DOC(
" Set the key ID (`kid`) metadata parameter on a key using raw bytes.\n"
"\n"
" In COSE (RFC 9052), kid is a bstr that may contain arbitrary bytes.\n"
" For JWK interoperability where kid is a JSON string, use `with_kid`.\n"
).
-spec with_kid_bits(key(any()), bitstring()) -> key(bitstring()).
with_kid_bits(Key, Kid) ->
{key,
erlang:element(2, Key),
{some, Kid},
erlang:element(4, Key),
erlang:element(5, Key),
erlang:element(6, Key)}.
-file("src/gose.gleam", 859).
-spec is_signing_op(key_op()) -> boolean().
is_signing_op(Op) ->
case Op of
sign ->
true;
verify ->
true;
encrypt ->
false;
decrypt ->
false;
wrap_key ->
false;
unwrap_key ->
false;
derive_key ->
false;
derive_bits ->
false
end.
-file("src/gose.gleam", 866).
-spec is_encrypting_op(key_op()) -> boolean().
is_encrypting_op(Op) ->
case Op of
encrypt ->
true;
decrypt ->
true;
wrap_key ->
true;
unwrap_key ->
true;
derive_key ->
true;
derive_bits ->
true;
sign ->
false;
verify ->
false
end.
-file("src/gose.gleam", 874).
?DOC(false).
-spec validate_key_use_ops(
gleam@option:option(key_use()),
gleam@option:option(list(key_op()))
) -> {ok, nil} | {error, gose_error()}.
validate_key_use_ops(Key_use, Key_ops) ->
case {Key_use, Key_ops} of
{none, _} ->
{ok, nil};
{_, none} ->
{ok, nil};
{{some, signing}, {some, Ops}} ->
case gleam@list:all(Ops, fun is_signing_op/1) of
true ->
{ok, nil};
false ->
{error,
{invalid_state,
<<"key_ops incompatible with use=sig"/utf8>>}}
end;
{{some, encrypting}, {some, Ops@1}} ->
case gleam@list:all(Ops@1, fun is_encrypting_op/1) of
true ->
{ok, nil};
false ->
{error,
{invalid_state,
<<"key_ops incompatible with use=enc"/utf8>>}}
end
end.
-file("src/gose.gleam", 799).
?DOC(
" Set the key operations parameter.\n"
"\n"
" Per RFC 7517, the values should be consistent with `key_use` if both are present:\n"
" - `Signing` use implies `Sign` and/or `Verify` operations\n"
" - `Encrypting` use implies `Encrypt`, `Decrypt`, `WrapKey`, `UnwrapKey`, `DeriveKey`, `DeriveBits`\n"
"\n"
" Returns an error if the list is empty, contains duplicates, or is\n"
" incompatible with the key's existing `key_use`.\n"
).
-spec with_key_ops(key(JWS), list(key_op())) -> {ok, key(JWS)} |
{error, gose_error()}.
with_key_ops(Key, Ops) ->
case Ops of
[] ->
{error, {invalid_state, <<"key_ops must not be empty"/utf8>>}};
_ ->
gleam@bool:guard(
gleam@list:unique(Ops) /= Ops,
{error,
{invalid_state,
<<"key_ops must not contain duplicates"/utf8>>}},
fun() ->
_pipe = validate_key_use_ops(
erlang:element(4, Key),
{some, Ops}
),
gleam@result:replace(
_pipe,
{key,
erlang:element(2, Key),
erlang:element(3, Key),
erlang:element(4, Key),
{some, Ops},
erlang:element(6, Key)}
)
end
)
end.
-file("src/gose.gleam", 822).
?DOC(
" Set the public key use parameter.\n"
"\n"
" Returns an error if the key already has `key_ops` that are incompatible with\n"
" the specified use, or if the use is incompatible with the key type per RFC\n"
" 8037 (EdDSA keys can only be used for signing, XDH keys can only be used for\n"
" encryption).\n"
).
-spec with_key_use(key(JWY), key_use()) -> {ok, key(JWY)} |
{error, gose_error()}.
with_key_use(Key, Use_) ->
gleam@result:'try'(
validate_key_use_ops({some, Use_}, erlang:element(5, Key)),
fun(_) ->
gleam@result:'try'(
validate_rfc8037_key_use(erlang:element(2, Key), {some, Use_}),
fun(_) ->
{ok,
{key,
erlang:element(2, Key),
erlang:element(3, Key),
{some, Use_},
erlang:element(5, Key),
erlang:element(6, Key)}}
end
)
end
).
-file("src/gose.gleam", 894).
?DOC(" Get the algorithm (`alg`) parameter.\n").
-spec alg(key(any())) -> {ok, alg()} | {error, nil}.
alg(Key) ->
gleam@option:to_result(erlang:element(6, Key), nil).
-file("src/gose.gleam", 901).
?DOC(
" Get the curve used by an EC key.\n"
"\n"
" Returns an error if the key is not an EC key.\n"
).
-spec ec_curve(key(any())) -> {ok, kryptos@ec:curve()} | {error, gose_error()}.
ec_curve(Key) ->
_pipe = material_ec(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Ec) -> case Ec of
{ec_private, _, _, Curve} ->
Curve;
{ec_public, _, Curve} ->
Curve
end end).
-file("src/gose.gleam", 916).
?DOC(
" Extract the EC public key.\n"
"\n"
" Works with both EC private keys (extracts the public component)\n"
" and EC public keys.\n"
"\n"
" Returns an error if the key is not an EC key.\n"
).
-spec ec_public_key(key(any())) -> {ok, kryptos@ec:public_key()} |
{error, gose_error()}.
ec_public_key(Key) ->
_pipe = material_ec(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Ec) -> case Ec of
{ec_private, _, Public, _} ->
Public;
{ec_public, K, _} ->
K
end end).
-file("src/gose.gleam", 932).
?DOC(
" Get the x and y coordinates from an EC public key.\n"
"\n"
" The coordinates are returned as raw big-endian bytes, padded to\n"
" the coordinate size for the curve.\n"
"\n"
" Returns an error if the key is not an EC key.\n"
).
-spec ec_public_key_coordinates(key(any())) -> {ok, {bitstring(), bitstring()}} |
{error, gose_error()}.
ec_public_key_coordinates(Key) ->
gleam@result:'try'(
ec_public_key(Key),
fun(Public) ->
gleam@result:'try'(
ec_curve(Key),
fun(Curve) -> ec_raw_coordinates(Public, Curve) end
)
end
).
-file("src/gose.gleam", 943).
?DOC(
" Get the curve used by an EdDSA key.\n"
"\n"
" Returns an error if the key is not an EdDSA key.\n"
).
-spec eddsa_curve(key(any())) -> {ok, kryptos@eddsa:curve()} |
{error, gose_error()}.
eddsa_curve(Key) ->
_pipe = material_eddsa(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Eddsa) -> case Eddsa of
{eddsa_private, _, _, Curve} ->
Curve;
{eddsa_public, _, Curve} ->
Curve
end end).
-file("src/gose.gleam", 958).
?DOC(
" Extract the EdDSA public key.\n"
"\n"
" Works with both EdDSA private keys (extracts the public component)\n"
" and EdDSA public keys.\n"
"\n"
" Returns an error if the key is not an EdDSA key.\n"
).
-spec eddsa_public_key(key(any())) -> {ok, kryptos@eddsa:public_key()} |
{error, gose_error()}.
eddsa_public_key(Key) ->
_pipe = material_eddsa(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Eddsa) -> case Eddsa of
{eddsa_private, _, Public, _} ->
Public;
{eddsa_public, K, _} ->
K
end end).
-file("src/gose.gleam", 969).
?DOC(" Get the key operations parameter.\n").
-spec key_ops(key(any())) -> {ok, list(key_op())} | {error, nil}.
key_ops(Key) ->
gleam@option:to_result(erlang:element(5, Key), nil).
-file("src/gose.gleam", 974).
?DOC(" Get the key type (kty) for this key.\n").
-spec key_type(key(any())) -> key_type().
key_type(Key) ->
case erlang:element(2, Key) of
{octet_key, _} ->
oct_key_type;
{rsa, _} ->
rsa_key_type;
{elliptic, _} ->
ec_key_type;
{edwards, _} ->
okp_key_type;
{xdh, _} ->
okp_key_type
end.
-file("src/gose.gleam", 984).
?DOC(" Get the public key use parameter.\n").
-spec key_use(key(any())) -> {ok, key_use()} | {error, nil}.
key_use(Key) ->
gleam@option:to_result(erlang:element(4, Key), nil).
-file("src/gose.gleam", 993).
?DOC(
" Get the key ID (kid) parameter.\n"
"\n"
" The return type depends on the key's kid type parameter:\n"
" - `Key(String)` (from JWK) → `Result(String, Nil)`\n"
" - `Key(BitArray)` (from COSE) → `Result(BitArray, Nil)`\n"
).
-spec kid(key(JZA)) -> {ok, JZA} | {error, nil}.
kid(Key) ->
gleam@option:to_result(erlang:element(3, Key), nil).
-file("src/gose.gleam", 1000).
?DOC(
" Get the size of an octet (symmetric) key in bytes.\n"
"\n"
" Returns an error if the key is not an octet key.\n"
).
-spec octet_key_size(key(any())) -> {ok, integer()} | {error, gose_error()}.
octet_key_size(Key) ->
case material_octet_secret(erlang:element(2, Key)) of
{ok, Secret} ->
{ok, erlang:byte_size(Secret)};
{error, _} ->
{error, {invalid_state, <<"key is not an octet key"/utf8>>}}
end.
-file("src/gose.gleam", 1013).
?DOC(
" Extract the RSA public key.\n"
"\n"
" Works with both RSA private keys (extracts the public component)\n"
" and RSA public keys.\n"
"\n"
" Returns an error if the key is not an RSA key.\n"
).
-spec rsa_public_key(key(any())) -> {ok, kryptos@rsa:public_key()} |
{error, gose_error()}.
rsa_public_key(Key) ->
_pipe = material_rsa(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Rsa) -> case Rsa of
{rsa_private, _, Public} ->
Public;
{rsa_public, K} ->
K
end end).
-file("src/gose.gleam", 1026).
?DOC(
" Get the curve used by an XDH key.\n"
"\n"
" Returns an error if the key is not an XDH key.\n"
).
-spec xdh_curve(key(any())) -> {ok, kryptos@xdh:curve()} | {error, gose_error()}.
xdh_curve(Key) ->
_pipe = material_xdh(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Xdh) -> case Xdh of
{xdh_private, _, _, Curve} ->
Curve;
{xdh_public, _, Curve} ->
Curve
end end).
-file("src/gose.gleam", 1041).
?DOC(
" Extract the XDH public key (X25519/X448).\n"
"\n"
" Works with both XDH private keys (extracts the public component)\n"
" and XDH public keys.\n"
"\n"
" Returns an error if the key is not an XDH key.\n"
).
-spec xdh_public_key(key(any())) -> {ok, kryptos@xdh:public_key()} |
{error, gose_error()}.
xdh_public_key(Key) ->
_pipe = material_xdh(erlang:element(2, Key)),
gleam@result:map(_pipe, fun(Xdh) -> case Xdh of
{xdh_private, _, Public, _} ->
Public;
{xdh_public, K, _} ->
K
end end).
-file("src/gose.gleam", 1117).
-spec map_public_key_op(key_op()) -> {ok, key_op()} | {error, nil}.
map_public_key_op(Op) ->
case Op of
sign ->
{ok, verify};
decrypt ->
{error, nil};
unwrap_key ->
{error, nil};
verify ->
{ok, Op};
encrypt ->
{ok, Op};
wrap_key ->
{ok, Op};
derive_key ->
{ok, Op};
derive_bits ->
{ok, Op}
end.
-file("src/gose.gleam", 1110).
-spec filter_public_key_ops(list(key_op())) -> {ok, list(key_op())} |
{error, nil}.
filter_public_key_ops(Ops) ->
case gleam@list:unique(gleam@list:filter_map(Ops, fun map_public_key_op/1)) of
[] ->
{error, nil};
Filtered ->
{ok, Filtered}
end.
-file("src/gose.gleam", 1068).
?DOC(
" Extract the public key from an asymmetric key.\n"
"\n"
" For private keys, extracts the corresponding public key.\n"
" For public keys, returns the key unchanged.\n"
" Returns an error for symmetric octet keys.\n"
"\n"
" When extracting a public key, `key_ops` are filtered to public-safe operations:\n"
" - `Sign` is mapped to `Verify`\n"
" - `Decrypt` and `UnwrapKey` are removed (private-only)\n"
" - Other operations are preserved\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let private_key = gose.generate_ec(ec.P256)\n"
" let assert Ok(pub_key) = gose.public_key(private_key)\n"
" ```\n"
).
-spec public_key(key(JZU)) -> {ok, key(JZU)} | {error, gose_error()}.
public_key(Key) ->
Filtered_ops = begin
_pipe = erlang:element(5, Key),
_pipe@1 = gleam@option:map(_pipe, fun filter_public_key_ops/1),
gleam@option:then(_pipe@1, fun gleam@option:from_result/1)
end,
case erlang:element(2, Key) of
{rsa, {rsa_private, _, Public}} ->
{ok,
{key,
{rsa, {rsa_public, Public}},
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{rsa, {rsa_public, _}} ->
{ok,
{key,
erlang:element(2, Key),
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{elliptic, {ec_private, _, Public@1, Curve}} ->
{ok,
{key,
{elliptic, {ec_public, Public@1, Curve}},
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{elliptic, {ec_public, _, _}} ->
{ok,
{key,
erlang:element(2, Key),
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{edwards, {eddsa_private, _, Public@2, Curve@1}} ->
{ok,
{key,
{edwards, {eddsa_public, Public@2, Curve@1}},
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{edwards, {eddsa_public, _, _}} ->
{ok,
{key,
erlang:element(2, Key),
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{xdh, {xdh_private, _, Public@3, Curve@2}} ->
{ok,
{key,
{xdh, {xdh_public, Public@3, Curve@2}},
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{xdh, {xdh_public, _, _}} ->
{ok,
{key,
erlang:element(2, Key),
erlang:element(3, Key),
erlang:element(4, Key),
Filtered_ops,
erlang:element(6, Key)}};
{octet_key, _} ->
{error, {invalid_state, <<"octet keys are not asymmetric"/utf8>>}}
end.
-file("src/gose.gleam", 1129).
?DOC(
" Serialize a key to DER format.\n"
"\n"
" Supports RSA, EC, EdDSA, and XDH keys (both private and public).\n"
" Uses PKCS#8 for private keys and SPKI for public keys.\n"
).
-spec to_der(key(any())) -> {ok, bitstring()} | {error, gose_error()}.
to_der(Key) ->
case erlang:element(2, Key) of
{rsa, {rsa_private, Private, _}} ->
_pipe = kryptos_ffi:rsa_export_private_key_der(Private, pkcs8),
gleam@result:replace_error(
_pipe,
{invalid_state, <<"failed to serialize RSA private key"/utf8>>}
);
{rsa, {rsa_public, Public}} ->
_pipe@1 = kryptos_ffi:rsa_export_public_key_der(Public, spki),
gleam@result:replace_error(
_pipe@1,
{invalid_state, <<"failed to serialize RSA public key"/utf8>>}
);
{elliptic, {ec_private, Private@1, _, _}} ->
_pipe@2 = kryptos_ffi:ec_export_private_key_der(Private@1),
gleam@result:replace_error(
_pipe@2,
{invalid_state, <<"failed to serialize EC private key"/utf8>>}
);
{elliptic, {ec_public, Public@1, _}} ->
_pipe@3 = kryptos_ffi:ec_export_public_key_der(Public@1),
gleam@result:replace_error(
_pipe@3,
{invalid_state, <<"failed to serialize EC public key"/utf8>>}
);
{edwards, {eddsa_private, Private@2, _, _}} ->
_pipe@4 = kryptos_ffi:eddsa_export_private_key_der(Private@2),
gleam@result:replace_error(
_pipe@4,
{invalid_state,
<<"failed to serialize EdDSA private key"/utf8>>}
);
{edwards, {eddsa_public, Public@2, _}} ->
_pipe@5 = kryptos_ffi:eddsa_export_public_key_der(Public@2),
gleam@result:replace_error(
_pipe@5,
{invalid_state, <<"failed to serialize EdDSA public key"/utf8>>}
);
{xdh, {xdh_private, Private@3, _, _}} ->
_pipe@6 = kryptos_ffi:xdh_export_private_key_der(Private@3),
gleam@result:replace_error(
_pipe@6,
{invalid_state, <<"failed to serialize XDH private key"/utf8>>}
);
{xdh, {xdh_public, Public@3, _}} ->
_pipe@7 = kryptos_ffi:xdh_export_public_key_der(Public@3),
gleam@result:replace_error(
_pipe@7,
{invalid_state, <<"failed to serialize XDH public key"/utf8>>}
);
{octet_key, _} ->
{error,
{invalid_state,
<<"octet keys cannot be serialized to DER"/utf8>>}}
end.
-file("src/gose.gleam", 1174).
?DOC(
" Export the raw bytes of a key.\n"
"\n"
" Supported key types:\n"
" - Octet keys: returns the secret bytes\n"
" - EdDSA/XDH private keys: returns the private key bytes (d)\n"
" - EdDSA/XDH public keys: returns the public key bytes (x)\n"
).
-spec to_octet_bits(key(any())) -> {ok, bitstring()} | {error, gose_error()}.
to_octet_bits(Key) ->
case erlang:element(2, Key) of
{octet_key, Secret} ->
{ok, Secret};
{edwards, {eddsa_private, Private, _, _}} ->
{ok, kryptos_ffi:eddsa_private_key_to_bytes(Private)};
{edwards, {eddsa_public, Public, _}} ->
{ok, kryptos_ffi:eddsa_public_key_to_bytes(Public)};
{xdh, {xdh_private, Private@1, _, _}} ->
{ok, kryptos_ffi:xdh_private_key_to_bytes(Private@1)};
{xdh, {xdh_public, Public@1, _}} ->
{ok, kryptos_ffi:xdh_public_key_to_bytes(Public@1)};
{rsa, _} ->
{error,
{invalid_state,
<<"key has no single-value byte representation"/utf8>>}};
{elliptic, _} ->
{error,
{invalid_state,
<<"key has no single-value byte representation"/utf8>>}}
end.
-file("src/gose.gleam", 1191).
?DOC(
" Serialize a key to PEM format.\n"
"\n"
" Supports RSA, EC, EdDSA, and XDH keys (both private and public).\n"
" Uses PKCS#8 for private keys and SPKI for public keys.\n"
).
-spec to_pem(key(any())) -> {ok, binary()} | {error, gose_error()}.
to_pem(Key) ->
case erlang:element(2, Key) of
{rsa, {rsa_private, Private, _}} ->
_pipe = kryptos@rsa:to_pem(Private, pkcs8),
gleam@result:replace_error(
_pipe,
{invalid_state, <<"failed to serialize RSA private key"/utf8>>}
);
{rsa, {rsa_public, Public}} ->
_pipe@1 = kryptos@rsa:public_key_to_pem(Public, spki),
gleam@result:replace_error(
_pipe@1,
{invalid_state, <<"failed to serialize RSA public key"/utf8>>}
);
{elliptic, {ec_private, Private@1, _, _}} ->
_pipe@2 = kryptos@ec:to_pem(Private@1),
gleam@result:replace_error(
_pipe@2,
{invalid_state, <<"failed to serialize EC private key"/utf8>>}
);
{elliptic, {ec_public, Public@1, _}} ->
_pipe@3 = kryptos@ec:public_key_to_pem(Public@1),
gleam@result:replace_error(
_pipe@3,
{invalid_state, <<"failed to serialize EC public key"/utf8>>}
);
{edwards, {eddsa_private, Private@2, _, _}} ->
_pipe@4 = kryptos@eddsa:to_pem(Private@2),
gleam@result:replace_error(
_pipe@4,
{invalid_state,
<<"failed to serialize EdDSA private key"/utf8>>}
);
{edwards, {eddsa_public, Public@2, _}} ->
_pipe@5 = kryptos@eddsa:public_key_to_pem(Public@2),
gleam@result:replace_error(
_pipe@5,
{invalid_state, <<"failed to serialize EdDSA public key"/utf8>>}
);
{xdh, {xdh_private, Private@3, _, _}} ->
_pipe@6 = kryptos@xdh:to_pem(Private@3),
gleam@result:replace_error(
_pipe@6,
{invalid_state, <<"failed to serialize XDH private key"/utf8>>}
);
{xdh, {xdh_public, Public@3, _}} ->
_pipe@7 = kryptos@xdh:public_key_to_pem(Public@3),
gleam@result:replace_error(
_pipe@7,
{invalid_state, <<"failed to serialize XDH public key"/utf8>>}
);
{octet_key, _} ->
{error,
{invalid_state,
<<"octet keys cannot be serialized to PEM"/utf8>>}}
end.
-file("src/gose.gleam", 1231).
?DOC(false).
-spec build(
key_material(),
gleam@option:option(KAR),
gleam@option:option(key_use()),
gleam@option:option(list(key_op())),
gleam@option:option(alg())
) -> key(KAR).
build(Material, Kid, Key_use, Key_ops, Alg) ->
{key, Material, Kid, Key_use, Key_ops, Alg}.
-file("src/gose.gleam", 1242).
?DOC(false).
-spec validate_rfc8037_key_use_public(
key_material(),
gleam@option:option(key_use())
) -> {ok, nil} | {error, gose_error()}.
validate_rfc8037_key_use_public(Material, Use_) ->
validate_rfc8037_key_use(Material, Use_).
-file("src/gose.gleam", 1250).
?DOC(false).
-spec aes_key_size(aes_key_size()) -> integer().
aes_key_size(Size) ->
case Size of
aes128 ->
16;
aes192 ->
24;
aes256 ->
32
end.
-file("src/gose.gleam", 698).
?DOC(
" Generate a symmetric key for AES Key Wrap.\n"
"\n"
" The key size is derived from the AES variant:\n"
" - `Aes128` → 16 bytes\n"
" - `Aes192` → 24 bytes\n"
" - `Aes256` → 32 bytes\n"
).
-spec generate_aes_kw_key(aes_key_size()) -> key(any()).
generate_aes_kw_key(Size) ->
Byte_count = aes_key_size(Size),
Secret = kryptos_ffi:random_bytes(Byte_count),
new_key({octet_key, Secret}).
-file("src/gose.gleam", 1259).
?DOC(false).
-spec hmac_alg_key_size(hmac_alg()) -> integer().
hmac_alg_key_size(Alg) ->
case Alg of
hmac_sha256 ->
32;
hmac_sha384 ->
48;
hmac_sha512 ->
64
end.
-file("src/gose.gleam", 669).
?DOC(
" Generate a symmetric key for HMAC signing.\n"
"\n"
" The key size is derived from the algorithm:\n"
" - `HmacSha256` → 32 bytes\n"
" - `HmacSha384` → 48 bytes\n"
" - `HmacSha512` → 64 bytes\n"
).
-spec generate_hmac_key(hmac_alg()) -> key(any()).
generate_hmac_key(Alg) ->
Size = hmac_alg_key_size(Alg),
Secret = kryptos_ffi:random_bytes(Size),
new_key({octet_key, Secret}).
-file("src/gose.gleam", 1268).
?DOC(false).
-spec content_alg_key_size(content_alg()) -> integer().
content_alg_key_size(Enc) ->
case Enc of
{aes_gcm, Size} ->
aes_key_size(Size);
{aes_cbc_hmac, Size@1} ->
aes_key_size(Size@1) * 2;
cha_cha20_poly1305 ->
32;
x_cha_cha20_poly1305 ->
32
end.
-file("src/gose.gleam", 686).
?DOC(
" Generate a symmetric key for JWE content encryption.\n"
"\n"
" The key size is derived from the encryption algorithm:\n"
" - `AesGcm(Aes128)` → 16 bytes\n"
" - `AesGcm(Aes192)` → 24 bytes\n"
" - `AesGcm(Aes256)` → 32 bytes\n"
" - `AesCbcHmac(Aes128)` → 32 bytes (16 + 16 for MAC)\n"
" - `AesCbcHmac(Aes192)` → 48 bytes (24 + 24 for MAC)\n"
" - `AesCbcHmac(Aes256)` → 64 bytes (32 + 32 for MAC)\n"
" - `ChaCha20Poly1305` → 32 bytes\n"
" - `XChaCha20Poly1305` → 32 bytes\n"
).
-spec generate_enc_key(content_alg()) -> key(any()).
generate_enc_key(Enc) ->
Size = content_alg_key_size(Enc),
Secret = kryptos_ffi:random_bytes(Size),
new_key({octet_key, Secret}).
-file("src/gose.gleam", 1278).
?DOC(false).
-spec chacha20_kw_nonce_size(cha_cha20_kw()) -> integer().
chacha20_kw_nonce_size(Variant) ->
case Variant of
c20_p_kw ->
12;
x_c20_p_kw ->
24
end.