Skip to content

Commit

Permalink
Merge pull request #18 from hannesm/ccm-gcm
Browse files Browse the repository at this point in the history
ccm gcm adjustments
  • Loading branch information
hannesm authored Feb 27, 2020
2 parents 287cd42 + af300cd commit 40dc0ff
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 20 deletions.
30 changes: 13 additions & 17 deletions src/ccm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ let (<+>) = Cs.(<+>)
let block_size = 16

let flags bit6 len1 len2 =
assert (len1 < 8 && len2 < 8);
let byte = Cstruct.create 1
and data = bit6 lsl 6 + len1 lsl 3 + len2 in
Cstruct.set_uint8 byte 0 data ;
Expand All @@ -32,10 +31,7 @@ let format nonce adata q t (* mac len *) =
let small_q = 15 - n in
(* first byte (flags): *)
(* reserved | adata | (t - 2) / 2 | q - 1 *)
let b6 = match adata with
| Some _ -> 1
| None -> 0
in
let b6 = if Cstruct.len adata = 0 then 0 else 1 in
let flag = flags b6 ((t - 2) / 2) (small_q - 1) in
(* first octet block:
0 : flags
Expand Down Expand Up @@ -76,18 +72,15 @@ let gen_ctr nonce i =
let pre, _, q = gen_ctr_prefix nonce in
pre <+> encode_len q i

let prepare_header nonce adata tlen plen =
let ada = match adata with
| Some x -> gen_adata x
| None -> Cstruct.empty
in
let prepare_header nonce adata plen tlen =
let ada = if Cstruct.len adata = 0 then Cstruct.empty else gen_adata adata in
format nonce adata plen tlen <+> ada

type mode = Encrypt | Decrypt

let crypto_core ~cipher ~mode ~key ~nonce ~maclen ?adata data =
let crypto_core ~cipher ~mode ~key ~nonce ~maclen ?(adata = Cstruct.empty) data =
let datalen = Cstruct.len data in
let cbcheader = prepare_header nonce adata maclen datalen in
let cbcheader = prepare_header nonce adata datalen maclen in
let target = Cstruct.create datalen in

let blkprefix, blkpreflen, preflen = gen_ctr_prefix nonce in
Expand Down Expand Up @@ -148,17 +141,20 @@ let crypto_t t nonce cipher key =
cipher ~key ctr ctr ;
Cs.xor_into ctr t (Cstruct.len t)

let valid_nonce nonce =
let nsize = Cstruct.len nonce in
if nsize < 7 || nsize > 13 then
invalid_arg "CCM: nonce length not between 7 and 13: %d" nsize

let generation_encryption ~cipher ~key ~nonce ~maclen ?adata data =
valid_nonce nonce;
let cdata, t = crypto_core ~cipher ~mode:Encrypt ~key ~nonce ~maclen ?adata data in
crypto_t t nonce cipher key ;
cdata <+> t

let decryption_verification ~cipher ~key ~nonce ~maclen ?adata data =
let () =
let nsize = Cstruct.len nonce in
if nsize < 7 || nsize > 13 then
invalid_arg "CCM: nonce length %d" nsize in
if Cstruct.len data <= maclen then
valid_nonce nonce;
if Cstruct.len data < maclen then
None
else
let pclen = Cstruct.len data - maclen in
Expand Down
3 changes: 2 additions & 1 deletion src/cipher_block.ml
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,8 @@ module Modes2 = struct
BE.set_uint64 _cs 0 a; BE.set_uint64 _cs 8 b; _cs

let counter ~hkey iv = match len iv with
| 12 -> let (w1, w2) = BE.(get_uint64 iv 0, BE.get_uint32 iv 8) in
| 0 -> invalid_arg "GCM: invalid IV of length 0"
| 12 -> let (w1, w2) = BE.get_uint64 iv 0, BE.get_uint32 iv 8 in
(w1, Int64.(shift_left (of_int32 w2) 32 |> add 1L))
| _ -> CTR.ctr_of_cstruct @@
GHASH.digesti ~key:hkey @@ iter2 iv (pack64s 0L (bits64 iv))
Expand Down
10 changes: 8 additions & 2 deletions src/mirage_crypto.mli
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,17 @@ module Cipher_block : sig
val encrypt : key:key -> iv:Cstruct.t -> ?adata:Cstruct.t -> Cstruct.t -> result
(** [encrypt ~key ~iv ?adata msg] is the {{!result}[result]} containing
[msg] encrypted under [key], with [iv] as the initialization vector,
and the authentication tag computed over both [adata] and [msg]. *)
and the authentication tag computed over both [adata] and [msg].
@raise Invalid_argument if the length [iv] is 0.
*)

val decrypt : key:key -> iv:Cstruct.t -> ?adata:Cstruct.t -> Cstruct.t -> result
(** [decrypt ~key ~iv ?adata msg] is the result containing the inversion
of [encrypt] and the same authentication tag. *)
of [encrypt] and the same authentication tag.
@raise Invalid_argument if the length [iv] is 0.
*)
end

(** {e Counter with CBC-MAC} mode. *)
Expand Down
92 changes: 92 additions & 0 deletions tests/test_cipher.ml
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,102 @@ let ccm_cases =
~maclen: 8
]

let ccm_regressions =
let open Cipher_block.AES.CCM in
let no_vs_empty_ad _ =
(* as reported in https://github.com/mirleft/ocaml-nocrypto/issues/166 *)
(* see RFC 3610 Section 2.1, AD of length 0 should be same as no AD *)
let key = of_secret ~maclen:16 (vx "000102030405060708090a0b0c0d0e0f")
and nonce = vx "0001020304050607"
and plaintext = Cstruct.of_string "hello"
in
assert_cs_equal ~msg:"CCM no vs empty ad"
(encrypt ~key ~nonce plaintext)
(encrypt ~adata:Cstruct.empty ~key ~nonce plaintext)
and short_nonce_enc _ =
(* as reported in https://github.com/mirleft/ocaml-nocrypto/issues/167 *)
(* valid nonce sizes for CCM are 7..13 (L can be 2..8, nonce is 15 - L)*)
(* see RFC3610 Section 2.1 *)
let key = of_secret ~maclen:16 (vx "000102030405060708090a0b0c0d0e0f")
and nonce = Cstruct.empty
and plaintext = Cstruct.of_string "hello"
in
assert_raises ~msg:"CCM with short nonce raises"
(Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 0")
(fun () -> encrypt ~key ~nonce plaintext)
and short_nonce_enc2 _ =
let key = of_secret ~maclen:16 (vx "000102030405060708090a0b0c0d0e0f")
and nonce = vx "00"
and plaintext = Cstruct.of_string "hello"
in
assert_raises ~msg:"CCM with short nonce raises"
(Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 1")
(fun () -> encrypt ~key ~nonce plaintext)
and short_nonce_enc3 _ =
let key = of_secret ~maclen:16 (vx "000102030405060708090a0b0c0d0e0f")
and nonce = vx "000102030405"
and plaintext = Cstruct.of_string "hello"
in
assert_raises ~msg:"CCM with short nonce raises"
(Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 6")
(fun () -> encrypt ~key ~nonce plaintext)
and long_nonce_enc _ =
let key = of_secret ~maclen:16 (vx "000102030405060708090a0b0c0d0e0f")
and nonce = vx "000102030405060708090a0b0c0d"
and plaintext = Cstruct.of_string "hello"
in
assert_raises ~msg:"CCM with short nonce raises"
(Invalid_argument "Mirage_crypto: CCM: nonce length not between 7 and 13: 14")
(fun () -> encrypt ~key ~nonce plaintext)
and enc_dec_empty_message _ =
(* as reported in https://github.com/mirleft/ocaml-nocrypto/issues/168 *)
let key = of_secret ~maclen:16 (vx "000102030405060708090a0b0c0d0e0f")
and nonce = vx "0001020304050607"
and adata = Cstruct.of_string "hello"
and p = Cstruct.empty
in
let cipher = encrypt ~adata ~key ~nonce p in
match decrypt ~key ~nonce ~adata cipher with
| Some x -> assert_cs_equal ~msg:"CCM decrypt of empty message" p x
| None -> assert_failure "decryption broken"
in
[
test_case no_vs_empty_ad ;
test_case short_nonce_enc ;
test_case short_nonce_enc2 ;
test_case short_nonce_enc3 ;
test_case long_nonce_enc ;
test_case enc_dec_empty_message ;
]

let gcm_regressions =
let open Cipher_block.AES.GCM in
let msg = vx "000102030405060708090a0b0c0d0e0f" in
let key = of_secret msg
and iv = Cstruct.empty
in
let iv_zero_length_enc _ =
(* reported in https://github.com/mirleft/ocaml-nocrypto/issues/169 *)
assert_raises ~msg:"GCM with iv of length 0"
(Invalid_argument "Mirage_crypto: GCM: invalid IV of length 0")
(fun () -> encrypt ~key ~iv msg)
and iv_zero_length_dec _ =
assert_raises ~msg:"GCM with iv of 0"
(Invalid_argument "Mirage_crypto: GCM: invalid IV of length 0")
(fun () -> decrypt ~key ~iv msg)
in
[
test_case iv_zero_length_enc ;
test_case iv_zero_length_dec ;
]


let suite = [
"AES-ECB" >::: [ "SP 300-38A" >::: aes_ecb_cases ] ;
"AES-CBC" >::: [ "SP 300-38A" >::: aes_cbc_cases ] ;
"AES-CTR" >::: [ "SP 300-38A" >::: aes_ctr_cases; ] ;
"AES-GCM" >::: gcm_cases ;
"AES-CCM" >::: ccm_cases ;
"AES-CCM-REGRESSION" >::: ccm_regressions ;
"AES-GCM-REGRESSION" >::: gcm_regressions ;
]

0 comments on commit 40dc0ff

Please sign in to comment.