Skip to content

Commit

Permalink
String (#167)
Browse files Browse the repository at this point in the history
* Remove P224 - see mirage/mirage-crypto#209

* Remove cstruct from x509 package

* fixes, tests are now passing

* x509 requires ohex

* Update pin-depends (pbkdf) to fix the CI

* x509 requires OCaml 4.13

* ecdsa signature encoding: potentially remove superfluous leading 0s

* adapt to recent mirage-crypto API change

* use updated mirage-crypto

* lower asn1 bound is 0.3.0 now

* lower ohex bound

* use mirage-crypto at main (since the merge was done)

* algorithm: use unsigned integer, as provided by asn1-combinators 0.3.1

* serial is an unsigned integer now, with a limit of 20 for the octets

* minor changes for mirage-crypto at HEAD

* Remove pin-depends

* Add a Changes entry about #166 and #167

---------

Co-authored-by: Hannes Mehnert <[email protected]>
  • Loading branch information
dinosaure and hannesm authored Jul 18, 2024
1 parent f3cdac6 commit f453f70
Show file tree
Hide file tree
Showing 29 changed files with 559 additions and 536 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v1.0.0 (2024-07-17)

* **breaking change** Replace `Cstruct.t` by `string` (@dinosaure, @hannesm, #167)
* Remove P224 (@dinosaure, @hannes, #166)
* The serial number of certificates is a `string` and enforced to be a positive
integer of at most 20 bytes in length (@hannesm, #167)

## v0.16.5 (2023-07-03)

* Always embed local_key_id in PKCS12 bags (reported #163 by @NightBlues,
Expand Down
46 changes: 15 additions & 31 deletions lib/algorithm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ open Asn_grammars
*)

type ec_curve =
[ `SECP224R1 | `SECP256R1 | `SECP384R1 | `SECP521R1 ]
[ `SECP256R1 | `SECP384R1 | `SECP521R1 ]

let ec_curve_to_string = function
| `SECP224R1 -> "SECP224R1"
| `SECP256R1 -> "SECP256R1"
| `SECP384R1 -> "SECP384R1"
| `SECP521R1 -> "SECP521R1"
Expand Down Expand Up @@ -65,19 +64,19 @@ type t =
| HMAC_SHA512

(* symmetric block ciphers *)
| AES128_CBC of Cstruct.t
| AES192_CBC of Cstruct.t
| AES256_CBC of Cstruct.t
| AES128_CBC of string
| AES192_CBC of string
| AES256_CBC of string

(* PBE encryption algorithms *)
| SHA_RC4_128 of Cstruct.t * int
| SHA_RC4_40 of Cstruct.t * int
| SHA_3DES_CBC of Cstruct.t * int
| SHA_2DES_CBC of Cstruct.t * int
| SHA_RC2_128_CBC of Cstruct.t * int
| SHA_RC2_40_CBC of Cstruct.t * int

| PBKDF2 of Cstruct.t * int * int option * t
| SHA_RC4_128 of string * int
| SHA_RC4_40 of string * int
| SHA_3DES_CBC of string * int
| SHA_2DES_CBC of string * int
| SHA_RC2_128_CBC of string * int
| SHA_RC2_40_CBC of string * int

| PBKDF2 of string * int * int option * t
| PBES2 of t * t

let to_string = function
Expand Down Expand Up @@ -218,13 +217,11 @@ let curve_of_oid, curve_to_oid =
let open Registry.ANSI_X9_62 in
(let default oid = Asn.(S.parse_error "Unknown algorithm %a" OID.pp oid) in
case_of_oid ~default [
(secp224r1, `SECP224R1) ;
(secp256r1, `SECP256R1) ;
(secp384r1, `SECP384R1) ;
(secp521r1, `SECP521R1) ;
]),
(function
| `SECP224R1 -> secp224r1
| `SECP256R1 -> secp256r1
| `SECP384R1 -> secp384r1
| `SECP521R1 -> secp521r1)
Expand Down Expand Up @@ -406,24 +403,11 @@ let identifier =
(choice4 null oid pbkdf2_or_pbe_or_pbes2_params octet_string)))

let ecdsa_sig =
let f (r, s) =
if Z.sign r < 0 then
Asn.S.parse_error "ECDSA signature: r < 0"
else if Z.sign s < 0 then
Asn.S.parse_error "ECDSA signature: s < 0"
else
Mirage_crypto_pk.Z_extra.to_cstruct_be r,
Mirage_crypto_pk.Z_extra.to_cstruct_be s
and g (r, s) =
Mirage_crypto_pk.Z_extra.of_cstruct_be r,
Mirage_crypto_pk.Z_extra.of_cstruct_be s
in
map f g @@
sequence2
(required ~label:"r" integer)
(required ~label:"s" integer)
(required ~label:"r" unsigned_integer)
(required ~label:"s" unsigned_integer)

let ecdsa_sig_of_cstruct, ecdsa_sig_to_cstruct =
let ecdsa_sig_of_octets, ecdsa_sig_to_octets =
projections_of Asn.der ecdsa_sig

let pp fmt x = Fmt.string fmt (to_string x)
14 changes: 13 additions & 1 deletion lib/asn_grammars.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ let ( let* ) = Result.bind

let decode codec cs =
let* a, cs = Asn.decode codec cs in
if Cstruct.length cs = 0 then Ok a else Error (`Parse "Leftover")
if String.length cs = 0 then Ok a else Error (`Parse "Leftover")

let projections_of encoding asn =
let c = Asn.codec encoding asn in (decode c, Asn.encode c)
Expand Down Expand Up @@ -53,3 +53,15 @@ let generalized_time_no_frac_s =
parse_error "generalized time has fractional seconds")
(fun y -> Ptime.truncate ~frac_s:0 y)
generalized_time)

(* serial number, as defined in RFC 5280 4.1.2.2: must be > 0 and not be longer than 20 octets
we accept 0. *)
let serial =
Asn.S.(map
(fun x ->
if String.length x > 20 then parse_error "serial exceeds 20 octets";
x)
(fun y ->
if String.length y > 20 then failwith "serial exceeds 20 octets";
y)
unsigned_integer)
4 changes: 2 additions & 2 deletions lib/authenticator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ let fingerprint_of_string s =
`Msg (Fmt.str "Invalid base64 encoding in fingerprint (%s): %S" m s))
(Base64.decode ~pad:false s)
in
Ok (Cstruct.of_string d)
Ok d

let format =
{|
Expand Down Expand Up @@ -73,7 +73,7 @@ let of_string str =
List.fold_left (fun acc s ->
let* acc = acc in
let* der = Base64.decode ~pad:false s in
let* cert = Certificate.decode_der (Cstruct.of_string der) in
let* cert = Certificate.decode_der der in
Ok (cert :: acc))
(Ok []) certs
in
Expand Down
48 changes: 23 additions & 25 deletions lib/certificate.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
*)
type tBSCertificate = {
version : [ `V1 | `V2 | `V3 ] ;
serial : Z.t ;
serial : string ;
signature : Algorithm.t ;
issuer : Distinguished_name.t ;
validity : Ptime.t * Ptime.t ;
subject : Distinguished_name.t ;
pk_info : Public_key.t ;
issuer_id : Cstruct.t option ;
subject_id : Cstruct.t option ;
issuer_id : string option ;
subject_id : string option ;
extensions : Extension.t
}

type certificate = {
tbs_cert : tBSCertificate ;
signature_algo : Algorithm.t ;
signature_val : Cstruct.t
signature_val : string
}

(*
* There are two reasons to carry Cstruct.t around:
* - we still need to hack on the cstruct to get bytes to hash
* There are two reasons to carry octets around:
* - we still need to hack on the octets to get bytes to hash
* ( this needs to go )
* - we need a cs to send to the peer
* It's a bit ugly to have two levels, and both are better solved by extending
Expand All @@ -31,7 +31,7 @@ type certificate = {
*)
type t = {
asn : certificate ;
raw : Cstruct.t
raw : string
}

module Asn = struct
Expand All @@ -43,8 +43,6 @@ module Asn = struct
(function `V3 -> 2 | `V2 -> 1 | `V1 -> 0)
int

let certificate_sn = integer

let time =
let f = function `C1 t -> t | `C2 t -> t
and g t =
Expand All @@ -57,12 +55,11 @@ module Asn = struct
(required ~label:"not before" time)
(required ~label:"not after" time)

let unique_identifier = bit_string_cs
let unique_identifier = bit_string_octets

let tBSCertificate =
let f = fun (a, (b, (c, (d, (e, (f, (g, (h, (i, j))))))))) ->
let extn = match j with None -> Extension.empty | Some xs -> xs
in
let extn = match j with None -> Extension.empty | Some xs -> xs in
{ version = def `V1 a ; serial = b ;
signature = c ; issuer = d ;
validity = e ; subject = f ;
Expand All @@ -74,14 +71,13 @@ module Asn = struct
validity = e ; subject = f ;
pk_info = g ; issuer_id = h ;
subject_id = i ; extensions = j } ->
let extn = if Extension.is_empty j then None else Some j
in
let extn = if Extension.is_empty j then None else Some j in
(def' `V1 a, (b, (c, (d, (e, (f, (g, (h, (i, extn)))))))))
in
map f g @@
sequence @@
(optional ~label:"version" @@ explicit 0 version) (* default v1 *)
@ (required ~label:"serialNumber" @@ certificate_sn)
@ (required ~label:"serialNumber" @@ serial)
@ (required ~label:"signature" @@ Algorithm.identifier)
@ (required ~label:"issuer" @@ Distinguished_name.Asn.name)
@ (required ~label:"validity" @@ validity)
Expand All @@ -94,7 +90,7 @@ module Asn = struct
(* v3 if present *)
-@ (optional ~label:"extensions" @@ explicit 3 Extension.Asn.extensions_der)

let (tbs_certificate_of_cstruct, tbs_certificate_to_cstruct) =
let (tbs_certificate_of_octets, tbs_certificate_to_octets) =
projections_of Asn.der tBSCertificate

let certificate =
Expand All @@ -108,9 +104,9 @@ module Asn = struct
sequence3
(required ~label:"tbsCertificate" tBSCertificate)
(required ~label:"signatureAlgorithm" Algorithm.identifier)
(required ~label:"signatureValue" bit_string_cs)
(required ~label:"signatureValue" bit_string_octets)

let (certificate_of_cstruct, certificate_to_cstruct) =
let (certificate_of_octets, certificate_to_octets) =
projections_of Asn.der certificate

let pkcs1_digest_info =
Expand All @@ -126,19 +122,19 @@ module Asn = struct
(required ~label:"digestAlgorithm" Algorithm.identifier)
(required ~label:"digest" octet_string)

let (pkcs1_digest_info_of_cstruct, pkcs1_digest_info_to_cstruct) =
let (pkcs1_digest_info_of_octets, pkcs1_digest_info_to_octets) =
projections_of Asn.der pkcs1_digest_info
end

let decode_pkcs1_digest_info cs =
Asn_grammars.err_to_msg (Asn.pkcs1_digest_info_of_cstruct cs)
Asn_grammars.err_to_msg (Asn.pkcs1_digest_info_of_octets cs)

let encode_pkcs1_digest_info = Asn.pkcs1_digest_info_to_cstruct
let encode_pkcs1_digest_info = Asn.pkcs1_digest_info_to_octets

let ( let* ) = Result.bind

let decode_der cs =
let* asn = Asn_grammars.err_to_msg (Asn.certificate_of_cstruct cs) in
let* asn = Asn_grammars.err_to_msg (Asn.certificate_of_octets cs) in
Ok { asn ; raw = cs }

let encode_der { raw ; _ } = raw
Expand All @@ -158,7 +154,7 @@ let encode_pem v =
Pem.unparse ~tag:"CERTIFICATE" (encode_der v)

let encode_pem_multiple cs =
Cstruct.concat (List.map encode_pem cs)
String.concat "" (List.map encode_pem cs)

let pp_version ppf v =
Fmt.string ppf (match v with `V1 -> "1" | `V2 -> "2" | `V3 -> "3")
Expand All @@ -175,15 +171,17 @@ let pp ppf { asn ; _ } =
let tbs = asn.tbs_cert in
let sigalg = Algorithm.to_signature_algorithm tbs.signature in
Fmt.pf ppf "X.509 [email protected] %[email protected] %[email protected] %[email protected] %[email protected] from %a until %[email protected] %[email protected] %a"
pp_version tbs.version Z.pp_print tbs.serial
pp_version tbs.version Ohex.pp tbs.serial
Fmt.(option ~none:(any "NONE") pp_sigalg) sigalg
Distinguished_name.pp tbs.issuer
(Ptime.pp_human ~tz_offset_s:0 ()) (fst tbs.validity)
(Ptime.pp_human ~tz_offset_s:0 ()) (snd tbs.validity)
Distinguished_name.pp tbs.subject
Extension.pp tbs.extensions

let fingerprint hash cert = Mirage_crypto.Hash.digest hash cert.raw
let fingerprint hash cert =
let module Hash = (val (Digestif.module_of_hash' hash)) in
Hash.(to_raw_string (digest_string cert.raw))

let issuer { asn ; _ } = asn.tbs_cert.issuer

Expand Down
22 changes: 11 additions & 11 deletions lib/crl.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type revoked_cert = {
serial : Z.t ;
serial : string ;
date : Ptime.t ;
extensions : Extension.t
}
Expand All @@ -17,7 +17,7 @@ type tBS_CRL = {
type crl = {
tbs_crl : tBS_CRL ;
signature_algo : Algorithm.t ;
signature_val : Cstruct.t
signature_val : string
}

module Asn = struct
Expand All @@ -34,7 +34,7 @@ module Asn = struct
in
map f g @@
sequence3
(required ~label:"userCertificate" @@ Certificate.Asn.certificate_sn)
(required ~label:"userCertificate" @@ serial)
(required ~label:"revocationDate" @@ Certificate.Asn.time)
(optional ~label:"crlEntryExtensions" @@ Extension.Asn.extensions_der)

Expand Down Expand Up @@ -81,17 +81,17 @@ module Asn = struct
sequence3
(required ~label:"tbsCertList" @@ tBSCertList)
(required ~label:"signatureAlgorithm" @@ Algorithm.identifier)
(required ~label:"signatureValue" @@ bit_string_cs)
(required ~label:"signatureValue" @@ bit_string_octets)

let (crl_of_cstruct, crl_to_cstruct) =
let (crl_of_octets, crl_to_octets) =
projections_of Asn.der certificateList

let (tbs_CRL_of_cstruct, tbs_CRL_to_cstruct) =
let (tbs_CRL_of_octets, tbs_CRL_to_octets) =
projections_of Asn.der tBSCertList
end

type t = {
raw : Cstruct.t ;
raw : string ;
asn : crl ;
}

Expand All @@ -100,7 +100,7 @@ let guard p e = if p then Ok () else Error e
let ( let* ) = Result.bind

let decode_der raw =
let* asn = Asn_grammars.err_to_msg (Asn.crl_of_cstruct raw) in
let* asn = Asn_grammars.err_to_msg (Asn.crl_of_octets raw) in
Ok { raw ; asn }

let encode_der { raw ; _ } = raw
Expand Down Expand Up @@ -187,7 +187,7 @@ let is_revoked ?allowed_hashes ~issuer:super ~cert (crls : t list) =
| Ok () ->
begin try
let entry = List.find
(fun r -> Z.equal (Certificate.serial cert) r.serial)
(fun r -> String.equal (Certificate.serial cert) r.serial)
(revoked_certificates crl)
in
match reason entry with
Expand All @@ -202,14 +202,14 @@ let is_revoked ?allowed_hashes ~issuer:super ~cert (crls : t list) =
crls

let sign_tbs (tbs : tBS_CRL) key =
let tbs_raw = Asn.tbs_CRL_to_cstruct tbs in
let tbs_raw = Asn.tbs_CRL_to_octets tbs in
match Algorithm.to_signature_algorithm tbs.signature with
| None -> Error (`Msg "couldn't parse signature algorithm")
| Some (_, hash) ->
let scheme = Key_type.x509_default_scheme (Private_key.key_type key) in
let* signature_val = Private_key.sign hash ~scheme key (`Message tbs_raw) in
let asn = { tbs_crl = tbs ; signature_algo = tbs.signature ; signature_val } in
let raw = Asn.crl_to_cstruct asn in
let raw = Asn.crl_to_octets asn in
Ok { asn ; raw }

let revoke
Expand Down
6 changes: 3 additions & 3 deletions lib/distinguished_name.ml
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,10 @@ module Asn = struct
in
sequence_of rd_name (* A vacuous choice, in the standard. *)

let (name_of_cstruct, name_to_cstruct) =
let (name_of_octets, name_to_octets) =
projections_of Asn.der name
end

let decode_der cs = Asn_grammars.err_to_msg (Asn.name_of_cstruct cs)
let decode_der cs = Asn_grammars.err_to_msg (Asn.name_of_octets cs)

let encode_der = Asn.name_to_cstruct
let encode_der = Asn.name_to_octets
Loading

0 comments on commit f453f70

Please sign in to comment.