Index

API

WebAuthn.WebAuthnModule
module WebAuthn

A Julia package for WebAuthn/FIDO2 credential registration and authentication in web applications.

Supports all required cryptographic formats: CBOR/COSE, Ed25519 (OKP), ECDSA (P-256), RSA public keys, base64url, and PEM export. Utility functions simplify browser-challenge generation, and parsing and verification of registration and authentication responses.

source
WebAuthn.EC2PublicKeyType
struct EC2PublicKey

Represent a COSE kty=2 EC2 public key (P-256/ES256).

Fields:

  • x::Vector{UInt8} # x coordinate
  • y::Vector{UInt8} # y coordinate
  • alg::Int # COSE alg, usually -7
  • crv::Int # COSE curve, usually 1 (P-256)

See also: RSAPublicKey and OKPPublicKey.

source
WebAuthn.assetMethod
asset(name::AbstractString)::String

Read and return the contents of an asset script (e.g. JS file) shipped with WebAuthn.jl. Example: WebAuthn.asset("webauthn_register.js")

source
WebAuthn.asset_pathMethod
asset_path(name::AbstractString)::String

Get absolute path to an asset shipped with WebAuthn.jl.

source
WebAuthn.authentication_optionsMethod
authentication_options(rpid; allow_credential_ids=[], 
    challenge=nothing, timeout=60000, kwargs...)::Dict

Build a browser authentication options dictionary for navigator.credentials.get.

Keywords

  • allow_credential_ids: List of credential IDs (strings) that are allowed (default: empty).
  • challenge: Challenge string to use (default: random).
  • timeout: Timeout in milliseconds (default: 60000).
  • kwargs: Any additional field for WebAuthn/PublicKeyCredentialRequestOptions; future extensions or browser-specific options can be passed directly as keywords.

Examples

julia> using WebAuthn

julia> opts = authentication_options("example.com";
           allow_credential_ids=["id1", "id2"], userVerification="required", 
           extensions=Dict("appid"=>true));

julia> opts["rpId"]
"example.com"

julia> opts["allowCredentials"][1]["id"]
"id1"

julia> opts["userVerification"]
"required"

julia> opts["extensions"]["appid"]
true

See also: registration_options.

source
WebAuthn.base64urldecodeMethod
base64urldecode(s::AbstractString)::Vector{UInt8}

Decode a base64url string (URL-safe, no padding) to a byte vector.

Restores standard base64 alphabet and adds padding if necessary before decoding.

Examples

julia> using WebAuthn

julia> base64urldecode("AQID")
3-element Vector{UInt8}:
 0x01
 0x02
 0x03

See also: base64urlencode.

source
WebAuthn.base64urlencodeMethod
base64urlencode(bytes::Vector{UInt8})::String

Encode a byte vector to a base64url string without padding.

Replaces + with - and / with _ for URL safety, then removes padding characters.

Examples

julia> using WebAuthn

julia> base64urlencode(UInt8[1, 2, 3])
"AQID"

See also: base64urldecode.

source
WebAuthn.cose_key_parseMethod
cose_key_parse(cose::Dict)::WebAuthnPublicKey

Parse a COSE_Key dictionary to the appropriate public key struct.

Examples

julia> using WebAuthn

julia> x = UInt8[0x2b, 0x80, 0x96, 0x04, 0x72, 0x26, 0x46, 0x00, 0x15, 0x3d, 
                 0x7a, 0xe6, 0x50, 0x3e, 0xd5, 0x84, 0xd1, 0x56, 0xd6, 0xc5, 
                 0x2c, 0x18, 0x8d, 0x7b, 0xb6, 0xf6, 0x8d, 0xe8, 0xa1, 0x24, 
                 0x06, 0xe9];

julia> y = UInt8[0x1d, 0x3a, 0x3f, 0x1c, 0x5e, 0x76, 0x52, 0x9e, 0x11, 0x6e, 
                 0x3e, 0xb7, 0xfc, 0x6a, 0x33, 0xfc, 0x2a, 0xb4, 0x68, 0x62, 
                 0x26, 0x6a, 0x1d, 0x51, 0x72, 0xf3, 0x3c, 0xab, 0x3c, 0x9c, 
                 0xc5, 0x9f];

julia> cose = Dict(1=>2, 3=>-7, -1=>1, -2=>x, -3=>y);

julia> key = cose_key_parse(cose);

julia> key isa WebAuthn.EC2PublicKey
true

See also: EC2PublicKey, RSAPublicKey and OKPPublicKey.

source
WebAuthn.cose_key_to_pemMethod
cose_key_to_pem(key::WebAuthnPublicKey)::String

Convert a WebAuthn public key struct to PEM SubjectPublicKeyInfo string.

Creates a standard PEM format public key from a parsed WebAuthn/COSE public key (e.g., EC2PublicKey, RSAPublicKey, or OKPPublicKey) for use with external cryptographic libraries.

Examples

julia> using WebAuthn

julia> key = EC2PublicKey(rand(UInt8,32), rand(UInt8,32), -7, 1);

julia> pem = cose_key_to_pem(key);

See also: cose_key_parse.

source
WebAuthn.der_to_pemFunction
der_to_pem(derbytes::Vector{UInt8}, label::String = "CERTIFICATE")::String

Convert DER-encoded bytes to a PEM string with the specified label.

Formats binary DER data as a PEM-encoded string, splitting base64 lines to 64 characters and adding header and footer lines for the given type.

Examples

julia> using WebAuthn

julia> der = [
           0x30,0x59,0x30,0x13,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,
           0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07,0x03,0x42,0x00,
           0x04,0xac,0x21,0x45,0xb3,0x77,0x1d,0xdd,0xab,0xbd,0x1c,0x34,0x5e,
           0xed,0x9d,0x6a,0x81,0xd9,0x32,0x42,0xe0,0xaf,0xf4,0x77,0x07,0xf0,
           0x65,0x64,0x69,0xae,0x95,0x50,0xfe,0x86,0x21,0xba,0xcf,0xb5,0x43,
           0xaf,0x90,0xe2,0xda,0xf4,0x2f,0x9f,0x9b,0x71,0x09,0x55,0x7a,0x53,
           0xc3,0x63,0x1e,0xb9,0xa9,0x3f,0xfb,0x66,0x32,0xfa,0x3a,0x49,0x01,
           0x01 ];

julia> pem = WebAuthn.der_to_pem(der);

See also: cose_key_to_pem.

source
WebAuthn.enforce_signcountMethod
enforce_signcount(old::Integer, new::Integer)

Reject cloned/replayed credentials per WebAuthn spec.

Examples

julia> enforce_signcount(0, 20)   # Initial use, old is 0: always OK
true

julia> enforce_signcount(10, 11)  # Normal monotonic increase
true

julia> enforce_signcount(5, 5)    # No increase (should fail)
ERROR: ArgumentError: 
[...]

julia> enforce_signcount(10, 3)   # Replay/clone (should fail)
ERROR: ArgumentError: 
[...]
source
WebAuthn.enforce_up_uvMethod
enforce_up_uv(authData::Vector{UInt8}; require_uv=false)

Check user presence (UP) and user verification (UV) flags in authenticatorData.

Examples

```jldoctest julia> # UP (bit 0) set, UV (bit 2) not set

julia> enforceupuv([zeros(UInt8,33); 0x01]) true

julia> # UP and UV set

julia> enforceupuv([zeros(UInt8,33); 0x05]) true

julia> # UP not set

julia> enforceupuv([zeros(UInt8,33); 0x00]) ERROR: [...]

julia> # UP set, require UV (but UV not set)

julia> enforceupuv([zeros(UInt8,33); 0x01]; require_uv=true) ERROR: [...]

julia> # UP and UV set, require UV (pass)

julia> enforceupuv([zeros(UInt8,33); 0x05]; require_uv=true) true

source
WebAuthn.extract_credential_public_keyMethod
extract_credential_public_key(authData::Vector{UInt8})::Vector{UInt8}

Extract the credential public key bytes from authenticator data.

Returns the raw CBOR-encoded public key (COSE_Key) after parsing authData as specified by the WebAuthn/FIDO2 standard.

Examples

julia> using WebAuthn

julia> fake_pk = UInt8[0xa5, 0x01, 0x02, 0x03, 0x38, 0x20, 0x01, 0x33, 0x58, 
       0x20];

julia> authData = vcat(zeros(UInt8, 37+16), 
       0x00, 0x10, rand(UInt8, 16), fake_pk);

julia> pkbytes = extract_credential_public_key(authData);

See also: parse_credential_public_key

source
WebAuthn.generate_challengeFunction
generate_challenge([n=32])::String

Generate a random base64url-encoded challenge of n bytes.

Examples

julia> using WebAuthn

julia> challenge = generate_challenge();

julia> length(base64urldecode(challenge))
32
source
WebAuthn.parse_assertionMethod
parse_assertion(authdata_b64, sig_b64) -> (authData, signature)

Base64url-decode authenticatorData and signature as a tuple of byte vectors.

Returns the authenticator data and signature, each as a vector of bytes.

Examples

julia> using WebAuthn

julia> ad_b64 = base64urlencode(rand(UInt8, 37));

julia> sig_b64 = base64urlencode(rand(UInt8, 64));

julia> ad, sig = parse_assertion(ad_b64, sig_b64);

See also: parse_attestation_object and parse_clientdata_json.

source
WebAuthn.parse_attestation_objectMethod
parse_attestation_object(b64::String)::Dict

Parse a base64url-encoded attestationObject to a dictionary.

Decodes and parses the CBOR-encoded attestation object returned during WebAuthn registration.

Examples

julia> using WebAuthn, CBOR

julia> attObj_b64 =
           "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVikgokmRebwXda2X9CScZAt1" *
           "FS1U7jvFi6aDLY8eyICQjhBAAAAAcDiKS6EGVQBSZ3UWJ8FVioAIJYypOb6RxF" *
           "Q4ocIvwK78qeEYp67CAWrj59jW99h8_tNpQECAyYgASFYIOyFijz315Yd_Aexkt" *
           "5l6YaFK4MK2CQgRZPXQqGGD06MIlgg54dhMxLg9u84Y3urvWdHrViXea0aMYq7v" *
           "7QI-DP1Duk";

julia> obj = parse_attestation_object(attObj_b64);

See also: parse_clientdata_json.

source
WebAuthn.parse_clientdata_jsonMethod
parse_clientdata_json(b64::String)::Dict

Parse a base64url-encoded clientDataJSON to a dictionary.

Decodes the clientDataJSON, converting from base64url and UTF-8 JSON to a Julia dictionary.

Examples

julia> using WebAuthn

julia> json = """{"type": "webauthn.get"}""";

julia> cdj_b64 = base64urlencode(Vector{UInt8}(json));

julia> cdj = parse_clientdata_json(cdj_b64)
Dict{String, Any} with 1 entry:
  "type" => "webauthn.get"

See also: parse_attestation_object.

source
WebAuthn.parse_credential_public_keyMethod
parse_credential_public_key(authData::Vector{UInt8})::WebAuthnPublicKey

Extract and parse credential public key from authenticator data.

Reads the CBOR-encoded credentialPublicKey field from authData and returns the corresponding parsed struct (e.g., EC2PublicKey, RSAPublicKey, or OKPPublicKey).

Examples

julia> using WebAuthn, CBOR

julia> x = UInt8[0x2b, 0x80, 0x96, 0x04, 0x72, 0x26, 0x46, 0x00, 0x15, 0x3d, 
                 0x7a, 0xe6, 0x50, 0x3e, 0xd5, 0x84, 0xd1, 0x56, 0xd6, 0xc5, 
                 0x2c, 0x18, 0x8d, 0x7b, 0xb6, 0xf6, 0x8d, 0xe8, 0xa1, 0x24, 
                 0x06, 0xe9];

julia> y = UInt8[0x1d, 0x3a, 0x3f, 0x1c, 0x5e, 0x76, 0x52, 0x9e, 0x11, 0x6e, 
                 0x3e, 0xb7, 0xfc, 0x6a, 0x33, 0xfc, 0x2a, 0xb4, 0x68, 0x62, 
                 0x26, 0x6a, 0x1d, 0x51, 0x72, 0xf3, 0x3c, 0xab, 0x3c, 0x9c, 
                 0xc5, 0x9f];

julia> fake_pk = CBOR.encode(Dict(1 => 2, 3 => -7, -1 => 1, -2 => x, -3 => y));

julia> authData = vcat(zeros(UInt8, 37+16), 0x00, 0x20, rand(UInt8, 32), fake_pk);


julia> key = parse_credential_public_key(authData);

julia> key isa WebAuthn.EC2PublicKey
true

See also: extract_credential_public_key and cose_key_parse.

source
WebAuthn.parse_ec_pem_xyMethod
parse_ec_pem_xy(pem::AbstractString)

Extract XY EC coordinates from a PEM/SPKI P-256 public key.

Parses a PEM-encoded (or plain DER) SubjectPublicKeyInfo containing a P-256 (secp256r1) uncompressed EC public key, as produced for WebAuthn and most browsers or OpenSSL tools. Returns X and Y coordinates of the EC point, each as a 32-byte vector.

Handles both short- and long-form DER length encodings, and scans all BIT STRING tags for an uncompressed point (0x04 prefix, 65 bytes total). Raises an error if not found.

Example

julia> using WebAuthn

julia> pem = """
       -----BEGIN PUBLIC KEY-----
       MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEK4CWBHImRgAVPXrmUD7VhNFW1sUs
       GI17tvaN6KEkBukdOj8cXnZSnhFuPrf8ajP8KrRoYiZqHVFy8zyrPJzFnw==
       -----END PUBLIC KEY-----
       """;

julia> x, y = parse_ec_pem_xy(pem);

julia> length(x), length(y)
(32, 32)

See also: pem_to_der, parse_rsa_pem_ne and parse_ed25519_pem_x.

source
WebAuthn.parse_ed25519_pem_xMethod
parse_ed25519_pem_x(pem::AbstractString)::x::Vector{UInt8}

Extract the x-coordinate (32 bytes) from an Ed25519 public key (PEM/SPKI).

Parses a PEM-encoded Ed25519 SubjectPublicKeyInfo as generated by OpenSSL or Python and returns the 32-byte raw public key (x). This is useful for verifying Ed25519 signatures.

Example

julia> using WebAuthn

julia> pem = """
       -----BEGIN PUBLIC KEY-----
       MCowBQYDK2VwAyEAoi3rnmJUD+qXNlp2pBkQWpXCUUjccW+6Ue5r0QDPF94=
       -----END PUBLIC KEY-----
       """;

julia> x = parse_ed25519_pem_x(pem);

julia> length(x)
32

See also: pem_to_der and parse_ec_pem_xy.

source
WebAuthn.parse_rsa_pem_neMethod
parse_rsa_pem_ne(pem::AbstractString)

Extract the modulus (n) and exponent (e) from an RSA public key (PEM/SPKI).

Reads a PEM-encoded RSA SubjectPublicKeyInfo (as produced by OpenSSL, browser tools, etc.) and returns the modulus and exponent as byte vectors suitable for cryptographic use.

Raises an error if the ASN.1 structure is unexpected or unsupported.

Example

julia> using WebAuthn

julia> pem = """
       -----BEGIN PUBLIC KEY-----
       MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2auHrQvvpQv/DI1wQAmd
       YCUYzOr8hWU2rMtDEae/p5Cu5pyNJRdv7DjOdOeWeWq1rA/Od/GY/HYazst1lWMC
       XjiW3nf0yV1jHoXl9Dc1wFTxl7EKrykK2EsijB8f93f4scxEalIyfjauXC6hIPi4
       /yWGS8cJOuj35ac/7N6GrUMIdzCj0qumTrUhRkmsJo2HtGm3dKkEZxHnL7fs/hln
       wwVOl2rqH0EcaVBSZwvjuEt6EfVMhqINp9FhRaIq5gx0ZpBR6OpFPM5oXrtMgsKO
       vIdZK7yZPcKw0JLmymZMi5gjgGVTX48YHoM8Mi6KtB4k2Rbp3Ouqc56odpLx2K2S
       cwIDAQAB
       -----END PUBLIC KEY-----
       """;

julia> n, e = parse_rsa_pem_ne(pem);

julia> length(n) > 200 && e == UInt8[0x01, 0x00, 0x01]
true

See also: pem_to_der and parse_ec_pem_xy.

source
WebAuthn.pem_to_derMethod
pem_to_der(pem::AbstractString)::Vector{UInt8}

Decode a PEM-formatted string (e.g., a PEM public key or certificate) to a DER-encoded byte vector.

Strips header/footer and whitespace, decodes the enclosed base64.

Example

julia> using WebAuthn

julia> pem = """
       -----BEGIN PUBLIC KEY-----
       MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEK4CWBHImRgAVPXrmUD7VhNFW1sUs
       GI17tvaN6KEkBukdOj8cXnZSnhFuPrf8ajP8KrRoYiZqHVFy8zyrPJzFnw==
       -----END PUBLIC KEY-----
       """;

julia> der = WebAuthn.pem_to_der(pem);
source
WebAuthn.registration_optionsMethod
registration_options(rpid, rpname, user_id, user_name, user_display;
    exclude_ids=[], challenge=nothing,
    attestation="none", timeout=60000, kwargs...)::Dict

Construct WebAuthn registration options for navigator.credentials.create.

Arguments

  • rpid: relying party identifier (e.g., "example.com").
  • rpname: display name of the RP.
  • user_id: user handle (string or byte vector).
  • user_name: username (string).
  • user_display: display name (string).

Keywords

  • exclude_ids: vector of credential IDs to exclude (default empty).
  • challenge: challenge string to use (default: random).
  • attestation: "none" (default), "packed", "direct", "enterprise", etc.
  • timeout: timeout in ms (default 60000).
  • kwargs: any additional WebAuthn/PublicKeyCredentialCreationOptions fields; future extensions can be set directly as keywords.

Examples

julia> using WebAuthn

julia> opts = registration_options("foo.com", "Demo", 123, "bob", 
           "Bob", attestation="packed", extensions=Dict("my_custom"=>"hi"));

julia> opts["attestation"]
"packed"

julia> opts["extensions"]["my_custom"]
"hi"

See also: authentication_options.

source
WebAuthn.verify_attestation_objectMethod
verify_attestation_object(attObj_b64, clientDataJSON)::Bool

Verify a WebAuthn attestation object of format "none" or "packed".

Examples

julia> using WebAuthn

julia> attObj_b64 =
       "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVikgokmRebwXda2X9CScZAt1" *
       "FS1U7jvFi6aDLY8eyICQjhBAAAAAcDiKS6EGVQBSZ3UWJ8FVioAIJYypOb6RxF" *
       "Q4ocIvwK78qeEYp67CAWrj59jW99h8_tNpQECAyYgASFYIOyFijz315Yd_Aexkt" *
       "5l6YaFK4MK2CQgRZPXQqGGD06MIlgg54dhMxLg9u84Y3urvWdHrViXea0aMYq7v" *
       "7QI-DP1Duk";

julia> cdj_b64 =
       "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiazF6eV9hU0N" *
       "PTDdOMFBWVmFVVElpVm5BU1ZGVDE4MDB1TWJVa1R2dUdKSSIsIm9yaWdpbiI6Im" *
       "h0dHBzOi8vd2ViYXV0aG4tdGVzdC5sb2NhbCIsImNyb3NzT3JpZ2luIjpmYWxze" *
       "X0";

julia> clientDataJSON = WebAuthn.base64urldecode(cdj_b64);

julia> ok = verify_attestation_object(attObj_b64, clientDataJSON)
true

See also: verify_webauthn_signature.

source
WebAuthn.verify_attestation_packedMethod
verify_attestation_packed(attStmt::Dict, msg::Vector{UInt8},
    authData::Vector{UInt8}[, clientDataJSON::Vector{UInt8}])::Bool

Verify a WebAuthn "packed" attestation statement (e.g., self-attested Ed25519 / ECDSA / RSA, as used in device authentication).

On success, returns true; otherwise, returns false. For Ed25519 (OKP, alg -8), the signature must be over SHA256(clientDataJSON) (not the full authData || SHA256(clientDataJSON)).

Examples

julia> using WebAuthn, CBOR, Sodium, SHA

julia> pk = Vector{UInt8}(undef, Sodium.crypto_sign_PUBLICKEYBYTES);

julia> sk = Vector{UInt8}(undef, Sodium.crypto_sign_SECRETKEYBYTES);

julia> Sodium.crypto_sign_keypair(pk, sk);

julia> cose = Dict(1=>1, 3=>-8, -1=>6, -2=>pk);

julia> cbor_pk = CBOR.encode(cose);

julia> rpId = "example.com";

julia> rpIdHash = SHA.sha256(Vector{UInt8}(rpId));

julia> flags = 0x41; signCount = UInt8[0,0,0,1]; aaguid = zeros(UInt8,16);

julia> credId = rand(UInt8,16); credIdLen = [UInt8(length(credId) >> 8), 
           UInt8(length(credId)&0xff)];

julia> authData = vcat(rpIdHash, flags, signCount, aaguid, credIdLen, 
           credId, cbor_pk);

julia> clientDataJSON = b"""{"type":"webauthn.create","challenge":"abc",
            "origin":"https://example.com"}""";

julia> clientDataHash = SHA.sha256(clientDataJSON);

julia> sig = Vector{UInt8}(undef, Sodium.crypto_sign_BYTES);

julia> sl = Ref{UInt64}();

julia> Sodium.crypto_sign_detached(sig, sl, clientDataHash, 
            length(clientDataHash), sk);

julia> msg = vcat(authData, clientDataHash);

julia> attStmt = Dict("sig"=>sig, "alg"=>-8);

julia> verify_attestation_packed(attStmt, msg, authData)
true

julia> attStmt_bad = deepcopy(attStmt); attStmt_bad["sig"][1] ⊻= 0xFF;

julia> verify_attestation_packed(attStmt_bad, msg, authData)
false
source
WebAuthn.verify_authentication_responseMethod
verify_authentication_response(response::Dict{String,Any};
    public_key,
    expected_challenge::AbstractString,
    expected_origin::AbstractString,
    previous_signcount::Integer=0,
    require_uv::Bool=true)

Verify a WebAuthn authentication (assertion) response using all protocol checks.

Performs challenge, origin, and signature checks, enforces user presence and verification flags, and ensures that the authenticator's signCount is monotonically increasing (prevents credential cloning/reuse). By default, user verification (biometric/PIN) is required (require_uv=true).

Arguments

  • response: Assertion response dict as received from navigator.credentials.get(), with top-level "id" and a "response" subdict.
  • public_key: Public key structure, e.g., parsed using cose_key_parse from stored registration outcome.
  • expected_challenge: Server-issued challenge for this authentication.
  • expected_origin: Allowed browser origin.
  • previous_signcount: Last recorded signCount for this credential (0 if first use).
  • require_uv: Require user verification (default true for secure passkey flows).

Examples

julia> using WebAuthn, Sodium, CBOR, SHA, JSON3

julia> pk = Vector{UInt8}(undef, Sodium.crypto_sign_PUBLICKEYBYTES);

julia> sk = Vector{UInt8}(undef, Sodium.crypto_sign_SECRETKEYBYTES);

julia> Sodium.crypto_sign_keypair(pk, sk);

julia> challenge = b"fixedchallenge";

julia> credid = b"credtestidfixed!";

julia> credlen = UInt8[0x00, length(credid)];

julia> flags = 0x45;

julia> signcount = UInt8[0,0,0,1];

julia> aaguid = zeros(UInt8,16);

julia> pk_cbor = CBOR.encode(Dict(1=>1, 3=>-8, -1=>6, -2=>pk));

julia> authData = vcat(SHA.sha256(b"example.com"), flags, signcount, 
           aaguid, credlen, credid, pk_cbor);

julia> clientData = JSON3.write(Dict("type"=>"webauthn.get", 
               "challenge"=>String(challenge), "origin"=>"https://ex"));

julia> cdj_b64 = WebAuthn.base64urlencode(Vector{UInt8}(clientData));

julia> msg = vcat(authData, SHA.sha256(Vector{UInt8}(clientData)));

julia> sig = Vector{UInt8}(undef, Sodium.crypto_sign_BYTES); sl = Ref{Culonglong}();

julia> Sodium.crypto_sign_detached(sig, sl, msg, length(msg), sk);

julia> assert_resp = Dict(
         "response"=>Dict(
           "authenticatorData"=>WebAuthn.base64urlencode(authData),
           "clientDataJSON"=>cdj_b64,
           "signature"=>WebAuthn.base64urlencode(sig)
         ),
         "id"=>String(credid)
       );

julia> public_key = WebAuthn.cose_key_parse(CBOR.decode(pk_cbor));

julia> result = verify_authentication_response(assert_resp;
           public_key=public_key, expected_challenge=String(challenge), 
           expected_origin="https://ex");

julia> result.ok   # should now be true
true

julia> result.new_signcount >= 1
true

See also: verify_registration_response, authentication_options and verify_webauthn_signature](@ref).

source
WebAuthn.verify_challengeMethod
verify_challenge(clientDataJSON_b64, expected_challenge_b64)::Bool

Verify the clientDataJSON challenge matches the expected challenge.

Examples

julia> using WebAuthn, JSON3

julia> challenge = generate_challenge(16);

julia> cdj = JSON3.write(Dict("type"=>"webauthn.create",
            "challenge"=>challenge, "origin"=>"https://site"));

julia> cdj_b64 = base64urlencode(Vector{UInt8}(cdj));

julia> verify_challenge(cdj_b64, challenge)
true

See also: generate_challenge

source
WebAuthn.verify_originMethod
verify_origin(cdj::Dict, expected::String)

Check that the 'origin' field in the parsed clientDataJSON matches what you expect.

Examples

julia> verify_origin(Dict("origin" => "https://demo.test"), "https://demo.test")
true

julia> verify_origin(Dict("origin" => "https://evil.site"), "https://demo.test")
ERROR: ArgumentError: 
[...]

julia> # If 'origin' key is missing in dict

julia> verify_origin(Dict("foo" => 42), "https://demo.test")
ERROR: ArgumentError: 
[...]
source
WebAuthn.verify_p256_signature_raw_xyMethod
verify_p256_signature_raw_xy(x, y, digest, sig)::Bool

Verify a P-256 ECDSA sig with OpenSSL using raw pubkey XY, SHA256 digest, and DER-encoded signature.

Examples

julia> using WebAuthn, SHA

julia> x = UInt8[
           0x7d, 0xf1, 0xb2, 0x85, 0x1d, 0xdc, 0x04, 0x29,
           0xdf, 0x71, 0xbd, 0x49, 0x9f, 0xea, 0x1d, 0xef,
           0xca, 0x63, 0x4c, 0x5c, 0xbe, 0x8e, 0xaa, 0xa5,
           0xcd, 0x3b, 0xa6, 0x65, 0xb7, 0xba, 0x3e, 0x32
       ];

julia> y = UInt8[
           0x6e, 0x33, 0x03, 0x54, 0xd9, 0x5f, 0x0d, 0xb7,
           0x28, 0x69, 0xec, 0x9a, 0x47, 0x82, 0x99, 0xe4,
           0xe9, 0x34, 0x44, 0xe4, 0x8d, 0x40, 0x8f, 0xbe,
           0xa1, 0x61, 0xf9, 0x3a, 0x4f, 0xd2, 0x14, 0xb8
       ];

julia> msg    = b"test message";

julia> digest = SHA.sha256(msg);

julia> sig = UInt8[
           0x30, 0x45, 0x02, 0x21, 0x00, 0xa9, 0xe7, 0x2a,
           0x18, 0x6a, 0xe2, 0xa3, 0x5d, 0x7a, 0x34, 0xa2,
           0x60, 0xef, 0xff, 0x36, 0x27, 0xbb, 0x59, 0x31,
           0xf9, 0xe3, 0xdb, 0xe5, 0xf8, 0x0a, 0xa2, 0xff,
           0x3f, 0xad, 0x2e, 0x3c, 0x79, 0x02, 0x20, 0x74,
           0x24, 0x4a, 0xb1, 0x19, 0xf2, 0x70, 0x4f, 0xa2,
           0x62, 0xdc, 0x87, 0x6c, 0x6b, 0xeb, 0xda, 0x77,
           0xe1, 0x5a, 0x5a, 0x13, 0xb9, 0xec, 0x87, 0x4e,
           0x29, 0x4b, 0x25, 0x95, 0x83, 0xc9, 0xdf];

julia> WebAuthn.verify_p256_signature_raw_xy(x, y, digest, sig)
true

julia> sig2 = copy(sig); sig2[1] ⊻= 0xFF;

julia> WebAuthn.verify_p256_signature_raw_xy(x, y, digest, sig2)
false
source
WebAuthn.verify_registration_responseMethod
verify_registration_response(response::Dict{String,Any};
    expected_challenge::AbstractString,
    expected_origin::AbstractString,
    attestation_policy::String="packed"
)

Verify a WebAuthn registration (credential creation) response in a single call.

This function validates the client-provided registration response using all required WebAuthn/FIDO2 steps: base64 and CBOR decoding, challenge and origin check, full attestation verification (with secure "packed" default), and then extracts the new credential's public key and ID. Errors are reported in the result's :ok and:reason fields.

Arguments

  • response: Registration response dict as received from the browser (with "response" and "id" fields).
  • expected_challenge: Server-issued challenge, must match the client's signed field.
  • expected_origin: Allowed browser origin for the client.
  • attestation_policy: "packed" (default; require strong attestation), "none", or "skip" (testing only).

Examples

julia> using WebAuthn, Sodium, CBOR, SHA, JSON3

julia> pk = Vector{UInt8}(undef, Sodium.crypto_sign_PUBLICKEYBYTES);

julia> sk = Vector{UInt8}(undef, Sodium.crypto_sign_SECRETKEYBYTES);

julia> Sodium.crypto_sign_keypair(pk, sk);

julia> challenge = b"fixedchallenge";

julia> cose_pk = Dict(1=>1, 3=>-8, -1=>6, -2=>pk);

julia> pk_cbor = CBOR.encode(cose_pk);

julia> credid = b"credtestidfixed!";

julia> credlen = [0x00, length(credid)];

julia> flags = 0x45; signcount = [0,0,0,1]; aaguid = zeros(UInt8, 16);

julia> authData = vcat(SHA.sha256(b"example.com"), flags, signcount, aaguid,
                       credlen, credid, pk_cbor);

julia> attobj = Dict("fmt"=>"none", "authData"=>authData, "attStmt"=>Dict());

julia> att_b64 = WebAuthn.base64urlencode(CBOR.encode(attobj));

julia> clientData = JSON3.write(Dict("type"=>"webauthn.create",
                  "challenge"=>String(challenge), "origin"=>"https://ex"));

julia> cdj_b64 = WebAuthn.base64urlencode(Vector{UInt8}(clientData));

julia> reg_resp = Dict("response"=>Dict("attestationObject"=>att_b64,
                  "clientDataJSON"=>cdj_b64), "id"=>String(credid));

julia> result = verify_registration_response(reg_resp;
                  expected_challenge=String(challenge),
                  expected_origin="https://ex");

julia> result.ok
true

See also: cose_key_parse, registration_options, verify_attestation_object and verify_authentication_response.

source
WebAuthn.verify_rsa_signature_raw_neMethod
verify_rsa_signature_raw_ne(n, e, digest, sig)::Bool

Verify an RSA PKCS1v1.5/SHA256 signature using raw modulus and exponent buffers, with everything in memory.

Examples

julia> using WebAuthn, SHA, Test

julia> n = UInt8[0xc8,0xac,0xb4,0xd0,0x1c,0x87,0x78,0x45,0x1d,0xcc,0xfa,
                 0xee,0x0a,0xe1,0x38,0xb4,0x12,0x71,0xf2,0x85,0x9d,0x00,
                 0xb1,0xf8,0x01,0x74,0x01,0xb9,0xcc,0x8f,0x02,0xda,0x6b,
                 0xa3,0x7a,0xe3,0xd8,0x35,0x00,0x63,0x52,0x16,0x4e,0xfc,
                 0xe3,0x8b,0xd0,0x88,0x8e,0xa8,0x03,0x6c,0x56,0x38,0x1f,
                 0x85,0xfd,0xf4,0xc0,0xa4,0x5b,0x6c,0x3f,0x0d];

julia> e = UInt8[0x01, 0x00, 0x01];

julia> msg = b"hello RSA";

julia> digest = SHA.sha256(msg);

julia> sig = UInt8[0x15,0xe4,0xed,0xd0,0x0a,0xe1,0x62,0xda,0xe5,0x84,0xb6,
                   0x9c,0x91,0x96,0x5e,0xba,0xa8,0x3a,0x01,0x55,0xc5,0x6e,
                   0x06,0xce,0xd5,0xd1,0x02,0x49,0x17,0x4c,0xb4,0xbf,0x36,
                   0x77,0x88,0x48,0x46,0x59,0x2e,0xd9,0xac,0x3e,0xcd,0x83,
                   0x69,0xab,0x46,0x8a,0xfe,0xa8,0xd0,0xee,0x1f,0x9c,0xfd,
                   0xe1,0xd9,0x03,0x57,0x70,0x34,0xe5,0x80,0xeb];

julia> WebAuthn.verify_rsa_signature_raw_ne(n, e, digest, sig)
true

julia> sig_bad = copy(sig);

julia> sig_bad[1] ⊻= 0xFF;

julia> WebAuthn.verify_rsa_signature_raw_ne(n, e, digest, sig_bad)
false
source
WebAuthn.verify_webauthn_signatureFunction
verify_webauthn_signature(key_or_pem, authenticatorData, 
    clientDataJSON)::Bool

Verify a WebAuthn assertion signature for the given public key or PEM.

Examples

julia> using WebAuthn, Sodium, SHA

julia> pk = Vector{UInt8}(undef, Sodium.crypto_sign_PUBLICKEYBYTES);

julia> sk = Vector{UInt8}(undef, Sodium.crypto_sign_SECRETKEYBYTES);

julia> Sodium.crypto_sign_keypair(pk, sk);

julia> key = OKPPublicKey(pk, -8, 6);

julia> ad = rand(UInt8, 37);

julia> cdj = rand(UInt8, 32);

julia> msg = vcat(ad, SHA.sha256(cdj));

julia> sig = Vector{UInt8}(undef, Sodium.crypto_sign_BYTES);

julia> sl = Ref{Culonglong}();

julia> Sodium.crypto_sign_detached(sig, sl, msg, length(msg), sk);

julia> valid = verify_webauthn_signature(key, ad, cdj, sig)
true

See also: cose_key_to_pem and parse_assertion.

source