Simple public-key cryptography for the modern age

DSA signature algorithm

type priv = private {
p : Z.t;(*


q : Z.t;(*

Subgroup order

gg : Z.t;(*

Group Generator

x : Z.t;(*

Private key proper

y : Z.t;(*

Public component


Private key. p, q and gg comprise domain parameters.

Sexplib convertible.

val priv : ?fips:bool -> p:Z.t -> q:Z.t -> gg:Z.t -> x:Z.t -> y:Z.t -> unit -> ( priv, [> `Msg of string ] ) result

priv ~fips ~p ~q ~gg ~x ~y () constructs a private DSA key from the given numbers. Will result in an error if parameters are ill-formed: same as pub, and additionally 0 < x < q and y = g ^ x mod p. Note that no time masking is done on the modular exponentiation.

type pub = private {
p : Z.t;
q : Z.t;
gg : Z.t;
y : Z.t;

Public key, a subset of private key.

Sexplib convertible.

val pub : ?fips:bool -> p:Z.t -> q:Z.t -> gg:Z.t -> y:Z.t -> unit -> ( pub, [> `Msg of string ] ) result

pub ~fips ~p ~q ~gg ~y () constructs a public DSA key from the given numbers. Will result in an error if the parameters are not well-formed: one < gg < p, q probabilistically a prime, p probabilistically prime and odd, 0 < y < p, q < p, and p - 1 mod q = 0. If fips is specified and true (defaults to false), only FIPS-specified bit length for p and q are accepted.

type keysize = [
| `Fips1024
| `Fips2048
| `Fips3072
| `Exactly of bits * bits

Key size request. Three Fips variants refer to FIPS-standardized L-values (p size) and imply the corresponding N (q size); The last variants specifies L and N directly.

type mask = [
| `No
| `Yes
| `Yes_with of Mirage_crypto_rng.g

Masking (cryptographic blinding) option.

val pub_of_priv : priv -> pub

Extract the public component from a private key.

val generate : ?g:Mirage_crypto_rng.g -> keysize -> priv

generate g size is a fresh private key. The domain parameters are derived using a modified FIPS.186-4 probabilistic process, but the derivation can not be validated. Note that no time masking is done for the modular exponentiations.

Note The process might diverge if it is impossible to find parameters with the given bit sizes. This happens when n gets too big for l, if the size was given as `Exactly (l, n).

  • raises Invalid_argument

    if size is (`Exactly (l, n)), and either l or n is ridiculously small.

val sign : ?mask:mask -> ?k:Z.t -> key:priv -> Cstruct.t -> Cstruct.t * Cstruct.t

sign ~mask ~k ~key digest is the signature, a pair of Cstruct.ts representing r and s in big-endian.

digest is the full digest of the actual message.

k, the random component, can either be provided, or is deterministically derived as per RFC6979, using SHA256.

  • raises Invalid_argument

    if k is unsuitable (leading to r or s being 0).

val verify : key:pub -> (Cstruct.t * Cstruct.t) -> Cstruct.t -> bool

verify ~key (r, s) digest verifies that the pair (r, s) is the signature of digest, the message digest, under the private counterpart to key.

val massage : key:pub -> Cstruct.t -> Cstruct.t

massage key digest is the numeric value of digest taken modulo q and represented in the leftmost bits(q) bits of the result.

Both FIPS.186-4 and RFC6979 specify that only the leftmost bits(q) bits of digest are to be taken into account, but some implementations consider the entire digest. In cases where sign and verify seem incompatible with a given implementation (esp. if sign produces signatures with the s component different from the other implementation's), it might help to pre-process digest using this function (e.g. sign ~key (massage ~key:(pub_of_priv key) digest)).

module K_gen (H : Mirage_crypto.Hash.S) : sig ... end

K_gen can be instantiated over a hashing module to obtain an RFC6979 compliant k-generator for that hash.