documentation
This commit is contained in:
parent
d926cf90b2
commit
78be1d42df
3 changed files with 165 additions and 3 deletions
29
README.md
29
README.md
|
@ -0,0 +1,29 @@
|
||||||
|
## WebAuthn - authenticating users to services using public key cryptography
|
||||||
|
|
||||||
|
WebAuthn is a web standard published by the W3C. Its goal is to
|
||||||
|
standardize an interfacefor authenticating users to web-based
|
||||||
|
applications and services using public key cryptography. Modern web
|
||||||
|
browsers support WebAuthn functionality.
|
||||||
|
|
||||||
|
WebAuthn provides two funcitons: register and authenticate. Usually the
|
||||||
|
public and private keypair is stored on an external token (Yuikey etc.)
|
||||||
|
or part of the platform (TPM). After the public key is registered, it can
|
||||||
|
be used to authenticate to the same service.
|
||||||
|
|
||||||
|
This module does not preserve a database of registered public keys, their
|
||||||
|
credential ID, usernames and pending challenges - instead this data must
|
||||||
|
be stored by a client of this API in a database or other persistent
|
||||||
|
storage.
|
||||||
|
|
||||||
|
[WebAuthn specification at W3C](https://w3c.github.io/webauthn/)
|
||||||
|
|
||||||
|
A basic demonstration server is provided (`bin/webauthn_demo`),
|
||||||
|
running at [webauthn-demo.robur.coop](https://webauthn-demo.robur.coop).
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[API documentation](https://roburio.github.io/webauthn/doc) is available online.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`opam install webauthn` will install this library.
|
|
@ -1,11 +1,48 @@
|
||||||
|
(** WebAuthn - authenticating users to services using public key cryptography
|
||||||
|
|
||||||
|
WebAuthn is a web standard published by the W3C. Its goal is to
|
||||||
|
standardize an interfacefor authenticating users to web-based
|
||||||
|
applications and services using public key cryptography. Modern web
|
||||||
|
browsers support WebAuthn functionality.
|
||||||
|
|
||||||
|
WebAuthn provides two funcitons: register and authenticate. Usually the
|
||||||
|
public and private keypair is stored on an external token (Yuikey etc.)
|
||||||
|
or part of the platform (TPM). After the public key is registered, it can
|
||||||
|
be used to authenticate to the same service.
|
||||||
|
|
||||||
|
This module implements at the moment only "fido-u2f" and "none"
|
||||||
|
attestations with P256 keys.
|
||||||
|
|
||||||
|
A common use of this module is that on startup a {!t} is created (using
|
||||||
|
{!create}). A public key can then be registered ({!register}) with a server
|
||||||
|
generated {!challenge}. When this is successfull, the client can be
|
||||||
|
authenticated {!authenticate}.
|
||||||
|
|
||||||
|
This module does not preserve a database of registered public keys, their
|
||||||
|
credential ID, usernames and pending challenges - instead this data must
|
||||||
|
be stored by a client of this API in a database or other persistent
|
||||||
|
storage.
|
||||||
|
|
||||||
|
{{:https://w3c.github.io/webauthn/}WebAuthn specification at W3C.}
|
||||||
|
*)
|
||||||
|
|
||||||
|
(** The type of a webauthn state, containing the [origin]. *)
|
||||||
type t
|
type t
|
||||||
|
|
||||||
|
(** [create origin] is a webauthn state, or an error if the origin does not
|
||||||
|
meet the specification (schema must be https, the host must be a valid
|
||||||
|
hostname. An optional port is supported: https://example.com:4444 *)
|
||||||
val create : string -> (t, string) result
|
val create : string -> (t, string) result
|
||||||
|
|
||||||
|
(** [rpid t] is the relying party ID. Specifically, it is the effective domain
|
||||||
|
of the origin. Using registrable domain suffix as the relying party ID is
|
||||||
|
currently unsupported. *)
|
||||||
val rpid : t -> string
|
val rpid : t -> string
|
||||||
|
|
||||||
|
(** The type os json decoding errors: context, message, and data. *)
|
||||||
type json_decoding_error = [ `Json_decoding of string * string * string ]
|
type json_decoding_error = [ `Json_decoding of string * string * string ]
|
||||||
|
|
||||||
|
(** The variant of decoding errors with the various encoding formats. *)
|
||||||
type decoding_error = [
|
type decoding_error = [
|
||||||
json_decoding_error
|
json_decoding_error
|
||||||
| `Base64_decoding of string * string * string
|
| `Base64_decoding of string * string * string
|
||||||
|
@ -15,6 +52,7 @@ type decoding_error = [
|
||||||
| `Attestation_object_decoding of string * string * string
|
| `Attestation_object_decoding of string * string * string
|
||||||
]
|
]
|
||||||
|
|
||||||
|
(** The variant of errors. *)
|
||||||
type error = [
|
type error = [
|
||||||
decoding_error
|
decoding_error
|
||||||
| `Unsupported_key_type of int
|
| `Unsupported_key_type of int
|
||||||
|
@ -29,24 +67,38 @@ type error = [
|
||||||
| `Signature_verification of string
|
| `Signature_verification of string
|
||||||
]
|
]
|
||||||
|
|
||||||
|
(** [pp_error ppf e] pretty-prints the error [e] on [ppf]. *)
|
||||||
val pp_error : Format.formatter -> [< error ] -> unit
|
val pp_error : Format.formatter -> [< error ] -> unit
|
||||||
|
|
||||||
|
(** The abstract type of challenges. *)
|
||||||
type challenge
|
type challenge
|
||||||
|
|
||||||
|
(** [generate_challenge ~size ()] generates a new challenge, and returns a pair
|
||||||
|
of the challenge and its Base64 URI safe encoding.
|
||||||
|
|
||||||
|
@raise Invalid_argument if size is smaller than 16. *)
|
||||||
val generate_challenge : ?size:int -> unit -> challenge * string
|
val generate_challenge : ?size:int -> unit -> challenge * string
|
||||||
|
|
||||||
|
(** [challenge_to_string c] is a string representing this challenge. *)
|
||||||
val challenge_to_string : challenge -> string
|
val challenge_to_string : challenge -> string
|
||||||
|
|
||||||
|
(** [challenge_of_string s] decodes [s] as a challenge. *)
|
||||||
val challenge_of_string : string -> challenge option
|
val challenge_of_string : string -> challenge option
|
||||||
|
|
||||||
|
(** [challenge_equal a b] is [true] if [a] and [b] are the same challenge. *)
|
||||||
val challenge_equal : challenge -> challenge -> bool
|
val challenge_equal : challenge -> challenge -> bool
|
||||||
|
|
||||||
|
(** The type of credential identifiers. *)
|
||||||
type credential_id = string
|
type credential_id = string
|
||||||
|
|
||||||
|
(** The type for credential data. *)
|
||||||
type credential_data = {
|
type credential_data = {
|
||||||
aaguid : string ;
|
aaguid : string ;
|
||||||
credential_id : credential_id ;
|
credential_id : credential_id ;
|
||||||
public_key : Mirage_crypto_ec.P256.Dsa.pub ;
|
public_key : Mirage_crypto_ec.P256.Dsa.pub ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(** The type for a registration. *)
|
||||||
type registration = {
|
type registration = {
|
||||||
user_present : bool ;
|
user_present : bool ;
|
||||||
user_verified : bool ;
|
user_verified : bool ;
|
||||||
|
@ -57,11 +109,26 @@ type registration = {
|
||||||
certificate : X509.Certificate.t option ;
|
certificate : X509.Certificate.t option ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(** The type for a register_response. *)
|
||||||
type register_response
|
type register_response
|
||||||
val register_response_of_string : string -> (register_response, json_decoding_error) result
|
|
||||||
|
|
||||||
val register : t -> register_response -> (challenge * registration, error) result
|
(** [register_response_of_string s] decodes the json encoded response
|
||||||
|
(consisting of a JSON dictionary with an attestationObject and
|
||||||
|
clientDataJSON - both Base64 URI safe encoded). The result is a
|
||||||
|
register_response or a decoding error. *)
|
||||||
|
val register_response_of_string : string ->
|
||||||
|
(register_response, json_decoding_error) result
|
||||||
|
|
||||||
|
(** [register t response] registers the response, and returns the used
|
||||||
|
challenge and a registration. The challenge needs to be verified to be
|
||||||
|
valid by the caller. If a direct attestation is used, the certificate
|
||||||
|
is returned -- and the signature is validated to establish the trust
|
||||||
|
chain between certificate and public key. The certificate should be
|
||||||
|
validated by the caller. *)
|
||||||
|
val register : t -> register_response ->
|
||||||
|
(challenge * registration, error) result
|
||||||
|
|
||||||
|
(** The type for an authentication. *)
|
||||||
type authentication = {
|
type authentication = {
|
||||||
user_present : bool ;
|
user_present : bool ;
|
||||||
user_verified : bool ;
|
user_verified : bool ;
|
||||||
|
@ -70,8 +137,21 @@ type authentication = {
|
||||||
client_extensions : (string * Yojson.Safe.t) list ;
|
client_extensions : (string * Yojson.Safe.t) list ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(** The type for an authentication response. *)
|
||||||
type authenticate_response
|
type authenticate_response
|
||||||
val authenticate_response_of_string : string -> (authenticate_response, json_decoding_error) result
|
|
||||||
|
|
||||||
|
(** [authentication_response_of_string s] decodes the response (a JSON
|
||||||
|
dictionary of Base64 URI-safe encoded values: authenticatorData,
|
||||||
|
clientDataJSON, signature, userHandle). If decoding fails, an
|
||||||
|
error is reported. *)
|
||||||
|
val authenticate_response_of_string : string ->
|
||||||
|
(authenticate_response, json_decoding_error) result
|
||||||
|
|
||||||
|
(** [authenticate t public_key response] authenticates [response], by checking
|
||||||
|
the signature with the [public_key]. If it is valid, the used [challenge]
|
||||||
|
is returned together with the authentication. The challenge needs to be
|
||||||
|
validated by the caller, and then caller is responsible for looking up the
|
||||||
|
public key corresponding to the credential id returned by the client web
|
||||||
|
browser. *)
|
||||||
val authenticate : t -> Mirage_crypto_ec.P256.Dsa.pub -> authenticate_response ->
|
val authenticate : t -> Mirage_crypto_ec.P256.Dsa.pub -> authenticate_response ->
|
||||||
(challenge * authentication, error) result
|
(challenge * authentication, error) result
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
opam-version: "2.0"
|
||||||
|
homepage: "https://github.com/roburio/webauthn"
|
||||||
|
dev-repo: "git+https://github.com/roburio/webauthn.git"
|
||||||
|
bug-reports: "https://github.com/roburio/webauthn/issues"
|
||||||
|
doc: "https://roburio.github.io/webauthn/doc"
|
||||||
|
maintainer: [ "team@robur.coop" ]
|
||||||
|
authors: [ "Reynir Björnsson <reynir@reynir.dk>" "Hannes Mehnert <hannes@mehnert.org>" ]
|
||||||
|
license: "BSD-2-Clause"
|
||||||
|
|
||||||
|
build: [
|
||||||
|
["dune" "subst"] {dev}
|
||||||
|
["dune" "build" "-p" name "-j" jobs]
|
||||||
|
["dune" "runtest" "-p" name "-j" jobs] {with-test}
|
||||||
|
]
|
||||||
|
|
||||||
|
depends: [
|
||||||
|
"ocaml" {>= "4.08.0"}
|
||||||
|
"dune" {>= "2.7"}
|
||||||
|
"dream" {dev}
|
||||||
|
"ppx_blob" {dev}
|
||||||
|
"cmdliner" {dev}
|
||||||
|
"logs" {dev}
|
||||||
|
"lwt" {dev}
|
||||||
|
"yojson"
|
||||||
|
"ppx_deriving_yojson"
|
||||||
|
"mirage-crypto-ec"
|
||||||
|
"mirage-crypto-rng"
|
||||||
|
"ocplib-endian"
|
||||||
|
"x509" {>= "0.13.0"}
|
||||||
|
"base64" {>= "3.1.0"}
|
||||||
|
"cstruct" {>= "6.0.0"}
|
||||||
|
]
|
||||||
|
|
||||||
|
synopsis: "WebAuthn - authenticating users to services using public key cryptography"
|
||||||
|
description: """
|
||||||
|
WebAuthn is a web standard published by the W3C. Its goal is to
|
||||||
|
standardize an interfacefor authenticating users to web-based
|
||||||
|
applications and services using public key cryptography. Modern web
|
||||||
|
browsers support WebAuthn functionality.
|
||||||
|
|
||||||
|
WebAuthn provides two funcitons: register and authenticate. Usually the
|
||||||
|
public and private keypair is stored on an external token (Yuikey etc.)
|
||||||
|
or part of the platform (TPM). After the public key is registered, it can
|
||||||
|
be used to authenticate to the same service.
|
||||||
|
|
||||||
|
This module does not preserve a database of registered public keys, their
|
||||||
|
credential ID, usernames and pending challenges - instead this data must
|
||||||
|
be stored by a client of this API in a database or other persistent
|
||||||
|
storage.
|
||||||
|
|
||||||
|
[Demo server](https://webauthn-demo.robur.coop)
|
||||||
|
[WebAuthn specification at W3C](https://w3c.github.io/webauthn/)
|
||||||
|
"""
|
Loading…
Reference in a new issue