forked from mirage/mirage-crypto
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmirage_crypto_pk.mli
520 lines (390 loc) · 21 KB
/
mirage_crypto_pk.mli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
(** {1 Public-key cryptography} *)
(** Public and private key types are private, the constructors validate their
well-formedness as much as possible, esp. so that [powm_sec] will not raise
an exception (exponent > 1, or odd modulus). All modular exponentiations
(unless otherwise noted) use the {!Z.powm_sec} function, which uses a static
access pattern and operates in constant time (of the bit size of the input),
independent of which bits are set and not set. The performance is up to 20%
worse than [powm]. Additionally, blinding is applied to RSA and DSA by
default. *)
(** {b RSA} public-key cryptography algorithm. *)
module Rsa : sig
(** {1 Keys}
Messages are checked not to exceed the key size, and this is signalled via
the {!Insufficient_key} exception.
Private-key operations are optionally protected through RSA blinding. *)
exception Insufficient_key
(** Raised if the key is too small to transform the given message, i.e. if the
numerical interpretation of the (potentially padded) message is not
smaller than the modulus. *)
type pub = private {
e : Z.t ; (** Public exponent *)
n : Z.t ; (** Modulus *)
}
(** The public portion of the key. *)
val pub : e:Z.t -> n:Z.t -> (pub, [> `Msg of string ]) result
(** [pub ~e ~n] validates the public key: [1 < e < n], [n > 0],
[is_odd n], and [numbits n >= 89] (a requirement for PKCS1 operations). *)
type priv = private {
e : Z.t ; (** Public exponent *)
d : Z.t ; (** Private exponent *)
n : Z.t ; (** Modulus ([p q])*)
p : Z.t ; (** Prime factor [p] *)
q : Z.t ; (** Prime factor [q] *)
dp : Z.t ; (** [d mod (p-1)] *)
dq : Z.t ; (** [d mod (q-1)] *)
q' : Z.t ; (** [q^(-1) mod p] *)
}
(** Full private key (two-factor version).
{b Note} The key layout assumes that [p > q], which affects the quantity
[q'] (sometimes called [u]), and the computation of the private transform.
Some systems assume otherwise. When using keys produced by a system that
computes [u = p^(-1) mod q], either exchange [p] with [q] and [dp] with
[dq], or re-generate the full private key using
{{!priv_of_primes}[priv_of_primes]}. *)
val priv : e:Z.t -> d:Z.t -> n:Z.t -> p:Z.t -> q:Z.t -> dp:Z.t -> dq:Z.t ->
q':Z.t -> (priv, [> `Msg of string ]) result
(** [priv ~e ~d ~n ~p ~q ~dp ~dq ~q'] validates the private key: [e, n] must
be a valid {!type-pub}, [p] and [q] valid prime numbers [> 0], [odd],
probabilistically prime, [p <> q], [n = p * q], [e] probabilistically
prime and coprime to both [p] and [q], [q' = q ^ -1 mod p], [1 < d < n],
[dp = d mod (p - 1)], [dq = d mod (q - 1)],
and [d = e ^ -1 mod (p - 1) (q - 1)]. *)
val pub_bits : pub -> int
(** Bit-size of a public key. *)
val priv_bits : priv -> int
(** Bit-size of a private key. *)
val priv_of_primes : e:Z.t -> p:Z.t -> q:Z.t ->
(priv, [> `Msg of string ]) result
(** [priv_of_primes ~e ~p ~q] is the {{!type-priv}private key} derived from the
minimal description [(e, p, q)]. *)
val priv_of_exp : ?g:Mirage_crypto_rng.g -> ?attempts:int -> e:Z.t -> d:Z.t ->
n:Z.t -> unit -> (priv, [> `Msg of string ]) result
(** [priv_of_exp ?g ?attempts ~e ~d n] is the unique {{!type-priv}private key}
characterized by the public ([e]) and private ([d]) exponents, and modulus
[n]. This operation uses a probabilistic process that can fail to recover
the key.
[~attempts] is the number of trials. For triplets that form an RSA key,
the probability of failure is at most [2^(-attempts)]. [attempts] defaults
to an unspecified number that yields a very high probability of recovering
valid keys.
Note that no time masking is done for the computations in this function. *)
val pub_of_priv : priv -> pub
(** Extract the public component from a private key. *)
(** {1 The RSA transformation} *)
type 'a or_digest = [ `Message of 'a | `Digest of string ]
(** Either an ['a] or its digest, according to some hash algorithm. *)
type mask = [ `No | `Yes | `Yes_with of Mirage_crypto_rng.g ]
(** Masking (cryptographic blinding) mode for the RSA transform with the
private key. Masking does not change the result, but it does change the
timing profile of the operation.
{ul
{- [`No] disables masking. It is slightly faster but it {b exposes the
private key to timing-based attacks}.}
{- [`Yes] uses random masking with the global RNG instance. This is
the sane option.}
{- [`Yes_with g] uses random masking with the generator [g].}} *)
val encrypt : key:pub -> string -> string
(** [encrypt key message] is the encrypted [message].
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key})
@raise Invalid_argument if [message] is [0x00] or [0x01]. *)
val decrypt : ?crt_hardening:bool -> ?mask:mask -> key:priv ->
string -> string
(** [decrypt ~crt_hardening ~mask key ciphertext] is the decrypted
[ciphertext], left-padded with [0x00] up to [key] size.
[~crt_hardening] defaults to [false]. If [true] verifies that the
result is correct. This is to counter Chinese remainder theorem attacks to
factorize primes. If the computed signature is incorrect, it is again
computed in the classical way (c ^ d mod n) without the Chinese remainder
theorem optimization. The deterministic {{!PKCS1.sign}PKCS1 signing},
which is at danger, uses [true] as default.
[~mask] defaults to [`Yes].
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key}) *)
(** {1 Key generation} *)
val generate : ?g:Mirage_crypto_rng.g -> ?e:Z.t -> bits:int -> unit -> priv
(** [generate ~g ~e ~bits ()] is a new {{!type-priv}private key}. The new key is
guaranteed to be well formed, see {!val-priv}.
[e] defaults to [2^16+1].
{b Note} This process might diverge if there are no keys for the given
bit size. This can happen when [bits] is extremely small.
@raise Invalid_argument if [e] is not a prime number (checked
probabilistically) or not in the range [1 < e < 2^bits], or if
[bits < 89] (as above, required for PKCS1 operations). *)
(** {1 PKCS#1 padded modes} *)
(** {b PKCS v1.5} operations, as defined by {b PKCS #1 v1.5}.
For the operations that only add the raw padding, the key size must be at
least 11 bytes larger than the message. For full {{!PKCS1.sign}signing}, the
minimal key size varies according to the hash algorithm. In this case, the
key size is [priv_bits key / 8], rounded up. *)
module PKCS1 : sig
val encrypt : ?g:Mirage_crypto_rng.g -> key:pub -> string -> string
(** [encrypt g key message] is a PKCS1-padded (type 2) and encrypted
[message].
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key}) *)
val decrypt : ?crt_hardening:bool -> ?mask:mask -> key:priv ->
string -> string option
(** [decrypt ~crt_hardening ~mask ~key ciphertext] is [Some message] if
the [ciphertext] was produced by the corresponding {{!encrypt}encrypt}
operation, or [None] otherwise. [crt_hardening] defaults to
[false]. *)
val sig_encode : ?crt_hardening:bool -> ?mask:mask -> key:priv ->
string -> string
(** [sig_encode ~crt_hardening ~mask ~key message] is the PKCS1-padded
(type 1) [message] signed by the [key]. [crt_hardening] defaults to
[true] and verifies that the computed signature is correct.
{b Note} This operation performs only the padding and RSA transformation
steps of the PKCS 1.5 signature. The full signature is implemented by
{{!sign}[sign]}.
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key}) *)
val sig_decode : key:pub -> string -> string option
(** [sig_decode key signature] is [Some message] when the [signature]
was produced with the given [key] as per {{!sig_encode}sig_encode}, or
[None] *)
val min_key : [< Digestif.hash' > `MD5 `SHA1 `SHA224 `SHA256 `SHA384 `SHA512 ] -> int
(** [min_key hash] is the minimum key size required by {{!sign}[sign]}. *)
val sign : ?crt_hardening:bool -> ?mask:mask ->
hash:[< Digestif.hash' > `MD5 `SHA1 `SHA224 `SHA256 `SHA384 `SHA512 ] ->
key:priv -> string or_digest -> string
(** [sign ~crt_hardening ~mask ~hash ~key message] is the PKCS 1.5
signature of [message], signed by the [key], using the hash function
[hash]. This is the full signature, with the ASN-encoded message digest
as the payload. [crt_hardening] defaults to [true] and verifies that
the computed signature is correct.
[message] is either the actual message, or its digest.
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key})
@raise Invalid_argument if message is a [`Digest] of the wrong size. *)
val verify : hashp:([< Digestif.hash' > `MD5 `SHA1 `SHA224 `SHA256 `SHA384 `SHA512 ] -> bool) ->
key:pub -> signature:string -> string or_digest -> bool
(** [verify ~hashp ~key ~signature message] checks that [signature] is the
PKCS 1.5 signature of the [message] under the given [key].
[message] is either the actual message, or its digest.
[hashp] determines the allowed hash algorithms. Whenever [hashp] is
[false], [verify] is also [false].
@raise Invalid_argument if message is a [`Digest] of the wrong size. *)
end
(** {1 OAEP padded modes} *)
(** {b OAEP}-padded encryption, as defined by {b PKCS #1 v2.1}.
The same hash function is used for padding and MGF. MGF is {b MGF1} as
defined in {b PKCS #1 2.1}.
Keys must have a minimum of [2 + 2 * hlen + len(message)] bytes, where
[hlen] is the hash length. *)
module OAEP (H : Digestif.S) : sig
val encrypt : ?g:Mirage_crypto_rng.g -> ?label:string -> key:pub ->
string -> string
(** [encrypt ~g ~label ~key message] is {b OAEP}-padded and encrypted
[message], using the optional [label].
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key}) *)
val decrypt : ?crt_hardening:bool -> ?mask:mask -> ?label:string ->
key:priv -> string -> string option
(** [decrypt ~crt_hardening ~mask ~label ~key ciphertext] is
[Some message] if the [ciphertext] was produced by the corresponding
{{!encrypt}encrypt} operation, or [None] otherwise. [crt_hardening]
defaults to [false]. *)
end
(** {1 PSS signing} *)
(** {b PSS}-based signing, as defined by {b PKCS #1 v2.1}.
The same hash function is used for padding, MGF and computing message
digest. MGF is {b MGF1} as defined in {b PKCS #1 2.1}.
Keys must have a minimum of [2 + hlen + slen] bytes, where [hlen] is the
hash length and [slen] is the seed length. *)
module PSS (H: Digestif.S) : sig
val sign : ?g:Mirage_crypto_rng.g -> ?crt_hardening:bool ->
?mask:mask -> ?slen:int -> key:priv -> string or_digest -> string
(** [sign ~g ~crt_hardening ~mask ~slen ~key message] the [PSS]-padded
digest of [message], signed with the [key]. [crt_hardening] defaults
to [false].
[slen] is the optional seed length and defaults to the size of the
underlying hash function.
[message] is either the actual message, or its digest.
@raise Insufficient_key (see {{!Insufficient_key}Insufficient_key})
@raise Invalid_argument if message is a [`Digest] of the wrong size. *)
val verify : ?slen:int -> key:pub -> signature:string -> string or_digest -> bool
(** [verify ~slen ~key ~signature message] checks whether [signature] is a
valid {b PSS} signature of the [message] under the given [key].
[message] is either the actual message, or its digest.
@raise Invalid_argument if message is a [`Digest] of the wrong size. *)
end
end
(** {b DSA} digital signature algorithm. *)
module Dsa : sig
(** {1 DSA signature algorithm} *)
type priv = private {
p : Z.t ; (** Modulus *)
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 {i domain parameters}. *)
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
{!val-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 {{!type-priv}private key}. *)
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 int * int ]
(** Key size request. Three {e 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 {{!type-priv}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.
{b 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)].
@raise 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 -> string -> string * string
(** [sign ~mask ~k ~key digest] is the signature, a pair of strings
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.
@raise Invalid_argument if [k] is unsuitable (leading to r or s being 0).
*)
val verify : key:pub -> string * string -> string -> 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 -> string -> string
(** [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}sign} and {{!verify}verify} seem
incompatible with a given implementation (esp. if {{!sign}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)]). *)
(** [K_gen] can be instantiated over a hashing module to obtain an RFC6979
compliant [k]-generator for that hash. *)
module K_gen (H : Digestif.S) : sig
val generate : key:priv -> string -> Z.t
(** [generate key digest] deterministically takes the given private key and
message digest to a [k] suitable for seeding the signing process. *)
end
end
(** Diffie-Hellman, MODP version. *)
module Dh : sig
(** {1 Diffie-Hellman key exchange} *)
exception Invalid_key
(** Raised if the private key material is degenerate.
The following invariants are checked:
Secret key: [1 < secret < p]
Public key: [1 < public < p-1] && [public <> gg]
*)
type group = private {
p : Z.t ; (** modulus *)
gg : Z.t ; (** generator *)
q : Z.t option ; (** subgroup order; potentially unknown *)
}
(** A DH group. *)
val group : p:Z.t -> gg:Z.t -> ?q:Z.t -> unit ->
(group, [> `Msg of string ]) result
(** [group ~p ~gg ~q ()] constructs a group if [p] is odd, a prime number,
and greater than [zero]. [gg] must be in the range [1 < gg < p]. *)
type secret = private { group : group ; x : Z.t }
(** A private key. *)
val modulus_size : group -> int
(** Bit size of the modulus. *)
val key_of_secret : group -> s:string -> secret * string
(** [key_of_secret group s] is the {!secret} and the corresponding public
key which use [s] as the secret exponent.
@raise Invalid_key if [s] is degenerate. *)
val gen_key : ?g:Mirage_crypto_rng.g -> ?bits:int -> group -> secret * string
(** Generate a random {!secret} and the corresponding public key.
[bits] is the exact bit-size of {!secret} and defaults to a value
dependent on the {!type-group}'s [p].
{b Note} The process might diverge when [bits] is extremely small. *)
val shared : secret -> string -> string option
(** [shared secret public] is [Some shared_key] given a
a previously generated {!secret} (which specifies the [group])
and the other party's public key.
[shared_key] is the unpadded big-endian representation of the shared key.
It is [None] if these invariants do not hold for [public]:
[1 < public < p-1] && [public <> gg]. *)
val gen_group : ?g:Mirage_crypto_rng.g -> bits:int -> unit -> group
(** [gen_group ~g ~bits ()] generates a random {!type-group} with modulus size
[bits]. Uses a safe prime [p = 2q + 1] (with [q] prime) for the modulus
and [2] for the generator, such that [2^q = 1 mod p].
Runtime is on the order of a minute for 1024 bits.
Note that no time masking is done for the modular exponentiation.
{b Note} The process might diverge if there are no suitable groups. This
happens with extremely small [bits] values. *)
(** A small catalog of standardized {!type-group}s. *)
module Group : sig
(** From RFC 2409: *)
val oakley_1 : group
val oakley_2 : group
(** From RFC 3526: *)
val oakley_5 : group
val oakley_14 : group
val oakley_15 : group
val oakley_16 : group
val oakley_17 : group
val oakley_18 : group
(** From RFC 5114: *)
val rfc_5114_1 : group
val rfc_5114_2 : group
val rfc_5114_3 : group
(** From draft-ietf-tls-negotiated-ff-dhe-08 *)
val ffdhe2048 : group
val ffdhe3072 : group
val ffdhe4096 : group
val ffdhe6144 : group
val ffdhe8192 : group
end
end
(** {b Z} Convert Z to big endian string and generate random Z values. *)
module Z_extra : sig
(** {1 Conversion to and from string} *)
val of_octets_be : ?bits:int -> string -> Z.t
(** [of_octets_be ~bits buf] interprets the bit pattern of [buf] as a
{{!Z.t}[t]} in big-endian.
If [~bits] is not given, the operation considers the entire [buf],
otherwise the initial [min ~bits (bit-length buf)] bits of [buf].
Assuming [n] is the number of bits to extract, the [n]-bit in [buf] is
always the least significant bit of the result. Therefore:
{ul
{- if the bit size [k] of [t] is larger than [n], [k - n] most
significant bits in the result are [0]; and}
{- if [k] is smaller than [n], the result contains [k] last of the [n]
first bits of [buf].}} *)
val to_octets_be : ?size:int -> Z.t -> string
(** [to_octets_be ~size t] is the big-endian representation of [t].
If [~size] is not given, it defaults to the minimal number of bytes
needed to represent [t], which is [bits t / 8] rounded up.
The least-significant bit of [t] is always the last bit in the result.
If the size is larger than needed, the output is padded with zero bits.
If it is smaller, the high bits in [t] are dropped. *)
val into_octets_be : Z.t -> bytes -> unit
(** [into_octets_be t buf] writes the big-endian representation of [t] into
[buf]. It behaves like {{!to_octets_be}[to_octets_be]}, with [~size]
spanning the entire [buf]. *)
(** {1 Random generation} *)
val gen : ?g:Mirage_crypto_rng.g -> Z.t -> Z.t
(** [gen ~g n] picks a value in the interval [\[0, n - 1\]] uniformly at random. *)
val gen_r : ?g:Mirage_crypto_rng.g -> Z.t -> Z.t -> Z.t
(** [gen_r ~g low high] picks a value from the interval [\[low, high - 1\]]
uniformly at random. *)
end