-module(gose@key). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/gose/key.gleam"). -export([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_hmac_key/1, generate_enc_key/1, generate_aes_kw_key/1, generate_chacha20_kw_key/0, generate_rsa/1, generate_xdh/1, ec_public_key_from_coordinates/3, 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]). -export_type([key_use/0, key_op/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( " Format-neutral key type for JOSE and COSE operations.\n" "\n" " Use `gose/jose/jwk` for JSON (JWK) serialization and `gose/cose/key`\n" " for CBOR (COSE_Key) serialization.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " import gose/key\n" " import kryptos/ec\n" "\n" " // Generate an EC key and attach metadata\n" " let k =\n" " key.generate_ec(ec.P256)\n" " |> key.with_kid(\"my-signing-key\")\n" " ```\n" "\n" " ## Supported Key Types\n" "\n" " - Symmetric keys (octet sequences) for HMAC and direct encryption\n" " - RSA keys for signing and encryption\n" " - Elliptic Curve keys (P-256, P-384, P-521, secp256k1)\n" " - EdDSA keys (Ed25519, Ed448) for signing\n" " - XDH keys (X25519, X448) for key agreement\n" ). -type key_use() :: signing | encrypting. -type key_op() :: sign | verify | encrypt | decrypt | wrap_key | unwrap_key | derive_key | derive_bits. -type alg() :: {signing_alg, gose@algorithm:signing_alg()} | {key_encryption_alg, gose@algorithm:key_encryption_alg()} | {content_alg, gose@algorithm: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()} | {ec, ec_key_material()} | {eddsa, eddsa_key_material()} | {xdh, xdh_key_material()}. -type key_type() :: oct_key_type | rsa_key_type | ec_key_type | okp_key_type. -opaque key(KSR) :: {key, key_material(), gleam@option:option(KSR), gleam@option:option(key_use()), gleam@option:option(list(key_op())), gleam@option:option(alg())}. -file("src/gose/key.gleam", 147). ?DOC(false). -spec new_key(key_material()) -> key(any()). new_key(Material) -> {key, Material, none, none, none, none}. -file("src/gose/key.gleam", 158). ?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; {ec, {ec_private, _, _, _}} -> true; {ec, {ec_public, _, _}} -> false; {eddsa, {eddsa_private, _, _, _}} -> true; {eddsa, {eddsa_public, _, _}} -> false; {xdh, {xdh_private, _, _, _}} -> true; {xdh, {xdh_public, _, _}} -> false end. -file("src/gose/key.gleam", 174). ?DOC(false). -spec material(key(any())) -> key_material(). material(Key) -> erlang:element(2, Key). -file("src/gose/key.gleam", 179). ?DOC(false). -spec material_octet_secret(key_material()) -> {ok, bitstring()} | {error, gose:gose_error()}. material_octet_secret(Mat) -> case Mat of {octet_key, Secret} -> {ok, Secret}; {rsa, _} -> {error, {invalid_state, <<"expected octet key"/utf8>>}}; {ec, _} -> {error, {invalid_state, <<"expected octet key"/utf8>>}}; {eddsa, _} -> {error, {invalid_state, <<"expected octet key"/utf8>>}}; {xdh, _} -> {error, {invalid_state, <<"expected octet key"/utf8>>}} end. -file("src/gose/key.gleam", 190). ?DOC(false). -spec material_rsa(key_material()) -> {ok, rsa_key_material()} | {error, gose:gose_error()}. material_rsa(Mat) -> case Mat of {rsa, Rsa} -> {ok, Rsa}; {octet_key, _} -> {error, {invalid_state, <<"expected RSA key"/utf8>>}}; {ec, _} -> {error, {invalid_state, <<"expected RSA key"/utf8>>}}; {eddsa, _} -> {error, {invalid_state, <<"expected RSA key"/utf8>>}}; {xdh, _} -> {error, {invalid_state, <<"expected RSA key"/utf8>>}} end. -file("src/gose/key.gleam", 199). ?DOC(false). -spec material_ec(key_material()) -> {ok, ec_key_material()} | {error, gose:gose_error()}. material_ec(Mat) -> case Mat of {ec, Ec} -> {ok, Ec}; {octet_key, _} -> {error, {invalid_state, <<"expected EC key"/utf8>>}}; {rsa, _} -> {error, {invalid_state, <<"expected EC key"/utf8>>}}; {eddsa, _} -> {error, {invalid_state, <<"expected EC key"/utf8>>}}; {xdh, _} -> {error, {invalid_state, <<"expected EC key"/utf8>>}} end. -file("src/gose/key.gleam", 208). ?DOC(false). -spec material_eddsa(key_material()) -> {ok, eddsa_key_material()} | {error, gose:gose_error()}. material_eddsa(Mat) -> case Mat of {eddsa, Eddsa} -> {ok, Eddsa}; {octet_key, _} -> {error, {invalid_state, <<"expected EdDSA key"/utf8>>}}; {rsa, _} -> {error, {invalid_state, <<"expected EdDSA key"/utf8>>}}; {ec, _} -> {error, {invalid_state, <<"expected EdDSA key"/utf8>>}}; {xdh, _} -> {error, {invalid_state, <<"expected EdDSA key"/utf8>>}} end. -file("src/gose/key.gleam", 219). ?DOC(false). -spec material_xdh(key_material()) -> {ok, xdh_key_material()} | {error, gose: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>>}}; {ec, _} -> {error, {invalid_state, <<"expected XDH key"/utf8>>}}; {eddsa, _} -> {error, {invalid_state, <<"expected XDH key"/utf8>>}} end. -file("src/gose/key.gleam", 243). -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({ec, {ec_private, Private, Public, Curve}}). -file("src/gose/key.gleam", 249). -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({ec, {ec_public, Public, Curve}}). -file("src/gose/key.gleam", 254). -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({eddsa, {eddsa_private, Private, Public, Curve}}). -file("src/gose/key.gleam", 260). -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({eddsa, {eddsa_public, Public, Curve}}). -file("src/gose/key.gleam", 265). -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/key.gleam", 270). -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/key.gleam", 274). -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/key.gleam", 280). -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/key.gleam", 285). -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/key.gleam", 293). -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/key.gleam", 301). -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/key.gleam", 317). -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/key.gleam", 231). ?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: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/key.gleam", 329). ?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: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({eddsa, {eddsa_private, Private, Public, Curve}}) end ), gleam@result:replace_error( _pipe@1, {parse_error, <<"invalid EdDSA private key bits"/utf8>>} ). -file("src/gose/key.gleam", 344). ?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: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({eddsa, {eddsa_public, Public, Curve}}) end ), gleam@result:replace_error( _pipe@1, {parse_error, <<"invalid EdDSA public key bits"/utf8>>} ). -file("src/gose/key.gleam", 364). ?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) = key.from_octet_bits(secret)\n" " ```\n" ). -spec from_octet_bits(bitstring()) -> {ok, key(any())} | {error, gose: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/key.gleam", 387). -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/key.gleam", 395). -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/key.gleam", 403). -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/key.gleam", 419). -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/key.gleam", 375). ?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: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/key.gleam", 431). ?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: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/key.gleam", 446). ?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: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/key.gleam", 458). ?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({ec, {ec_private, Private, Public, Curve}}). -file("src/gose/key.gleam", 466). ?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({eddsa, {eddsa_private, Private, Public, Curve}}). -file("src/gose/key.gleam", 477). ?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(gose@algorithm:hmac_alg()) -> key(any()). generate_hmac_key(Alg) -> Size = gose@algorithm:hmac_alg_key_size(Alg), Secret = kryptos_ffi:random_bytes(Size), new_key({octet_key, Secret}). -file("src/gose/key.gleam", 494). ?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(gose@algorithm:content_alg()) -> key(any()). generate_enc_key(Enc) -> Size = gose@algorithm:content_alg_key_size(Enc), Secret = kryptos_ffi:random_bytes(Size), new_key({octet_key, Secret}). -file("src/gose/key.gleam", 506). ?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(gose@algorithm:aes_key_size()) -> key(any()). generate_aes_kw_key(Size) -> Byte_count = gose@algorithm:aes_key_size(Size), Secret = kryptos_ffi:random_bytes(Byte_count), new_key({octet_key, Secret}). -file("src/gose/key.gleam", 515). ?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/key.gleam", 523). ?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: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/key.gleam", 534). ?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/key.gleam", 540). ?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:gose_error()}. ec_public_key_from_coordinates(Curve, X, Y) -> _pipe = gose@internal@utils:ec_public_key_from_coordinates(Curve, X, Y), gleam@result:map( _pipe, fun(Public) -> new_key({ec, {ec_public, Public, Curve}}) end ). -file("src/gose/key.gleam", 550). ?DOC(" Set the algorithm (`alg`) metadata parameter on a key.\n"). -spec with_alg(key(KXC), alg()) -> key(KXC). 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/key.gleam", 597). ?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:gose_error()}. validate_rfc8037_key_use(Material, Use_) -> case {Material, Use_} of {{eddsa, _}, {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/key.gleam", 615). ?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/key.gleam", 623). ?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/key.gleam", 627). -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/key.gleam", 634). -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/key.gleam", 642). ?DOC(false). -spec validate_key_use_ops( gleam@option:option(key_use()), gleam@option:option(list(key_op())) ) -> {ok, nil} | {error, gose: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/key.gleam", 562). ?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(KXF), list(key_op())) -> {ok, key(KXF)} | {error, gose: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/key.gleam", 585). ?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(KXL), key_use()) -> {ok, key(KXL)} | {error, gose: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/key.gleam", 662). ?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/key.gleam", 669). ?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: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/key.gleam", 684). ?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: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/key.gleam", 700). ?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:gose_error()}. ec_public_key_coordinates(Key) -> gleam@result:'try'( ec_public_key(Key), fun(Public) -> gleam@result:'try'( ec_curve(Key), fun(Curve) -> gose@internal@utils:ec_public_key_coordinates(Public, Curve) end ) end ). -file("src/gose/key.gleam", 711). ?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: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/key.gleam", 726). ?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: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/key.gleam", 739). ?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/key.gleam", 744). ?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; {ec, _} -> ec_key_type; {eddsa, _} -> okp_key_type; {xdh, _} -> okp_key_type end. -file("src/gose/key.gleam", 754). ?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/key.gleam", 763). ?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(KZN)) -> {ok, KZN} | {error, nil}. kid(Key) -> gleam@option:to_result(erlang:element(3, Key), nil). -file("src/gose/key.gleam", 770). ?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: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/key.gleam", 783). ?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: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/key.gleam", 796). ?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: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/key.gleam", 811). ?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: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/key.gleam", 887). -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/key.gleam", 880). -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/key.gleam", 838). ?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 = key.generate_ec(ec.P256)\n" " let assert Ok(pub_key) = key.public_key(private_key)\n" " ```\n" ). -spec public_key(key(LAH)) -> {ok, key(LAH)} | {error, gose: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)}}; {ec, {ec_private, _, Public@1, Curve}} -> {ok, {key, {ec, {ec_public, Public@1, Curve}}, erlang:element(3, Key), erlang:element(4, Key), Filtered_ops, erlang:element(6, Key)}}; {ec, {ec_public, _, _}} -> {ok, {key, erlang:element(2, Key), erlang:element(3, Key), erlang:element(4, Key), Filtered_ops, erlang:element(6, Key)}}; {eddsa, {eddsa_private, _, Public@2, Curve@1}} -> {ok, {key, {eddsa, {eddsa_public, Public@2, Curve@1}}, erlang:element(3, Key), erlang:element(4, Key), Filtered_ops, erlang:element(6, Key)}}; {eddsa, {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/key.gleam", 899). ?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: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>>} ); {ec, {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>>} ); {ec, {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>>} ); {eddsa, {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>>} ); {eddsa, {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/key.gleam", 952). ?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:gose_error()}. to_octet_bits(Key) -> case erlang:element(2, Key) of {octet_key, Secret} -> {ok, Secret}; {eddsa, {eddsa_private, Private, _, _}} -> {ok, kryptos_ffi:eddsa_private_key_to_bytes(Private)}; {eddsa, {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>>}}; {ec, _} -> {error, {invalid_state, <<"key has no single-value byte representation"/utf8>>}} end. -file("src/gose/key.gleam", 968). ?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: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>>} ); {ec, {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>>} ); {ec, {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>>} ); {eddsa, {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>>} ); {eddsa, {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/key.gleam", 1018). ?DOC(false). -spec build( key_material(), gleam@option:option(LBE), gleam@option:option(key_use()), gleam@option:option(list(key_op())), gleam@option:option(alg()) ) -> key(LBE). build(Material, Kid, Key_use, Key_ops, Alg) -> {key, Material, Kid, Key_use, Key_ops, Alg}. -file("src/gose/key.gleam", 1031). ?DOC(false). -spec validate_rfc8037_key_use_public( key_material(), gleam@option:option(key_use()) ) -> {ok, nil} | {error, gose:gose_error()}. validate_rfc8037_key_use_public(Material, Use_) -> validate_rfc8037_key_use(Material, Use_).