Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

String #167

Merged
merged 17 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

dinosaure marked this conversation as resolved.
Show resolved Hide resolved
* **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)

dinosaure marked this conversation as resolved.
Show resolved Hide resolved
## 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