Index
WebAuthn.WebAuthnWebAuthn.EC2PublicKeyWebAuthn.OKPPublicKeyWebAuthn.RSAPublicKeyWebAuthn.WebAuthnPublicKeyWebAuthn.assetWebAuthn.asset_pathWebAuthn.authentication_optionsWebAuthn.base64urldecodeWebAuthn.base64urlencodeWebAuthn.cose_key_parseWebAuthn.cose_key_to_pemWebAuthn.der_to_pemWebAuthn.enforce_signcountWebAuthn.enforce_up_uvWebAuthn.extract_credential_public_keyWebAuthn.generate_challengeWebAuthn.parse_assertionWebAuthn.parse_attestation_objectWebAuthn.parse_clientdata_jsonWebAuthn.parse_credential_public_keyWebAuthn.parse_ec_pem_xyWebAuthn.parse_ed25519_pem_xWebAuthn.parse_rsa_pem_neWebAuthn.pem_to_derWebAuthn.registration_optionsWebAuthn.verify_attestation_objectWebAuthn.verify_attestation_packedWebAuthn.verify_authentication_responseWebAuthn.verify_challengeWebAuthn.verify_originWebAuthn.verify_p256_signature_raw_xyWebAuthn.verify_registration_responseWebAuthn.verify_rsa_signature_raw_neWebAuthn.verify_webauthn_signature
API
WebAuthn.WebAuthn — Modulemodule WebAuthnA 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.
WebAuthn.EC2PublicKey — Typestruct EC2PublicKeyRepresent 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.
WebAuthn.OKPPublicKey — Typestruct OKPPublicKeyRepresent a COSE kty=1 OKP public key (Ed25519).
Fields:
- x::Vector{UInt8} # public key bytes
- alg::Int # COSE alg, usually -8
- crv::Int # COSE curve ID, usually 6
See also: EC2PublicKey and RSAPublicKey.
WebAuthn.RSAPublicKey — Typestruct RSAPublicKeyRepresent a COSE kty=3 RSA public key.
Fields:
- n::Vector{UInt8} # modulus
- e::Vector{UInt8} # exponent
- alg::Int # COSE alg, usually -257
See also: EC2PublicKey and OKPPublicKey.
WebAuthn.WebAuthnPublicKey — Typeabstract type WebAuthnPublicKeyAbstract supertype for all WebAuthn public key structs.
WebAuthn.asset — Methodasset(name::AbstractString)::StringRead and return the contents of an asset script (e.g. JS file) shipped with WebAuthn.jl. Example: WebAuthn.asset("webauthn_register.js")
WebAuthn.asset_path — Methodasset_path(name::AbstractString)::StringGet absolute path to an asset shipped with WebAuthn.jl.
WebAuthn.authentication_options — Methodauthentication_options(rpid; allow_credential_ids=[],
challenge=nothing, timeout=60000, kwargs...)::DictBuild 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"]
trueSee also: registration_options.
WebAuthn.base64urldecode — Methodbase64urldecode(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
0x03See also: base64urlencode.
WebAuthn.base64urlencode — Methodbase64urlencode(bytes::Vector{UInt8})::StringEncode 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.
WebAuthn.cose_key_parse — Methodcose_key_parse(cose::Dict)::WebAuthnPublicKeyParse 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
trueSee also: EC2PublicKey, RSAPublicKey and OKPPublicKey.
WebAuthn.cose_key_to_pem — Methodcose_key_to_pem(key::WebAuthnPublicKey)::StringConvert 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.
WebAuthn.der_to_pem — Functionder_to_pem(derbytes::Vector{UInt8}, label::String = "CERTIFICATE")::StringConvert 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.
WebAuthn.enforce_signcount — Methodenforce_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:
[...]WebAuthn.enforce_up_uv — Methodenforce_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
WebAuthn.extract_credential_public_key — Methodextract_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
WebAuthn.generate_challenge — Functiongenerate_challenge([n=32])::StringGenerate a random base64url-encoded challenge of n bytes.
Examples
julia> using WebAuthn
julia> challenge = generate_challenge();
julia> length(base64urldecode(challenge))
32WebAuthn.parse_assertion — Methodparse_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.
WebAuthn.parse_attestation_object — Methodparse_attestation_object(b64::String)::DictParse 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.
WebAuthn.parse_clientdata_json — Methodparse_clientdata_json(b64::String)::DictParse 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.
WebAuthn.parse_credential_public_key — Methodparse_credential_public_key(authData::Vector{UInt8})::WebAuthnPublicKeyExtract 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
trueSee also: extract_credential_public_key and cose_key_parse.
WebAuthn.parse_ec_pem_xy — Methodparse_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.
WebAuthn.parse_ed25519_pem_x — Methodparse_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)
32See also: pem_to_der and parse_ec_pem_xy.
WebAuthn.parse_rsa_pem_ne — Methodparse_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]
trueSee also: pem_to_der and parse_ec_pem_xy.
WebAuthn.pem_to_der — Methodpem_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);WebAuthn.registration_options — Methodregistration_options(rpid, rpname, user_id, user_name, user_display;
exclude_ids=[], challenge=nothing,
attestation="none", timeout=60000, kwargs...)::DictConstruct 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.
WebAuthn.verify_attestation_object — Methodverify_attestation_object(attObj_b64, clientDataJSON)::BoolVerify 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)
trueSee also: verify_webauthn_signature.
WebAuthn.verify_attestation_packed — Methodverify_attestation_packed(attStmt::Dict, msg::Vector{UInt8},
authData::Vector{UInt8}[, clientDataJSON::Vector{UInt8}])::BoolVerify 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)
falseWebAuthn.verify_authentication_response — Methodverify_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 fromnavigator.credentials.get(), with top-level"id"and a"response"subdict.public_key: Public key structure, e.g., parsed usingcose_key_parsefrom 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 (defaulttruefor 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
trueSee also: verify_registration_response, authentication_options and verify_webauthn_signature](@ref).
WebAuthn.verify_challenge — Methodverify_challenge(clientDataJSON_b64, expected_challenge_b64)::BoolVerify 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)
trueSee also: generate_challenge
WebAuthn.verify_origin — Methodverify_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:
[...]WebAuthn.verify_p256_signature_raw_xy — Methodverify_p256_signature_raw_xy(x, y, digest, sig)::BoolVerify 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)
falseWebAuthn.verify_registration_response — Methodverify_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
trueSee also: cose_key_parse, registration_options, verify_attestation_object and verify_authentication_response.
WebAuthn.verify_rsa_signature_raw_ne — Methodverify_rsa_signature_raw_ne(n, e, digest, sig)::BoolVerify 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)
falseWebAuthn.verify_webauthn_signature — Functionverify_webauthn_signature(key_or_pem, authenticatorData,
clientDataJSON)::BoolVerify 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)
trueSee also: cose_key_to_pem and parse_assertion.