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

Paillier enc #245

Closed
wants to merge 3 commits into from
Closed
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
6 changes: 6 additions & 0 deletions pk/common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ let rec until p f = let r = f () in if p r then r else until p f

let guard p err = if p then Ok () else Error err

let valid_prime name p =
guard Z.(p > zero && is_odd p && Z_extra.pseudoprime p)
(`Msg ("invalid prime " ^ name))

let rprime a b = Z.(gcd a b = one)

let ( let* ) = Result.bind
1 change: 1 addition & 0 deletions pk/mirage_crypto_pk.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ module Dh = Dh
module Dsa = Dsa
module Rsa = Rsa
module Z_extra = Z_extra
module Paillier = Paillier
52 changes: 52 additions & 0 deletions pk/mirage_crypto_pk.mli
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,55 @@ module Z_extra : sig
(** [gen_r ~g low high] picks a value from the interval [\[low, high - 1\]]
uniformly at random. *)
end

(** {b Paillier Encryption algorith} Paillier Encryption algorith. *)
module Paillier : sig

(** {1 Paillier Encryption algorithm} *)

type priv = private {
g : Z.t ; (** Generator *)
n : Z.t ; (** Modulus *)
n2 : Z.t ; (** Modulus squared *)
p : Z.t ; (** First prime *)
q : Z.t ; (** Second prime *)
lambda : Z.t ; (** Carmichael's totient function *)
mu : Z.t ; (** Inverse of totient function *)
}

val priv : p:Z.t -> q:Z.t -> (priv, [> `Msg of string ]) result
(** [priv ~p ~q] constructs a private Paillier keys from the given
numbers. Will result in an error if parameters are ill-formed: same as
{!val-pub}. *)

type pub = private {
g : Z.t ; (** Generator *)
n : Z.t ; (** Modulus *)
n2 : Z.t ; (** Modulus squared *)
}
(** Public key, a subset of {{!type-priv}private key}. *)

val pub : g:Z.t -> n:Z.t -> (pub, [> `Msg of string ]) result
(** [pub ~g ~n] constructs a public Paillier key from the given
numbers. Will result in an error if the parameters are not well-formed. *)

val pub_of_priv : priv -> pub
(** Extract the public component from a private key. *)

val pub_bits : pub -> int
(** [pub_bits ~pub] returns the number of bits in the public key *)

val priv_bits : priv -> int
(** [priv_bits ~priv] returns the number of bits in the private key *)

val generate : ?g:Mirage_crypto_rng.g -> bits:int -> unit -> pub*priv
(** [generate g bits] is a fresh {{!type-priv}private} key. *)

val encrypt : pub_key:pub -> msg:Z.t -> ?r:Z.t -> unit -> Z.t
(** [encrypt msg pub ~r ()] encrypts the message [msg] using the public key
[r] is a random value used to ensure that the encryption is unpredictable.
@raise Invalid_argument if [msg] is negative. *)

val decrypt : priv_key:priv -> c:Z.t -> Z.t
(** [decrypt ciphertext priv] decrypts the ciphertext using the private key. *)
end
60 changes: 60 additions & 0 deletions pk/paillier.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
open Mirage_crypto.Uncommon
open Common

type pub = { g : Z.t ; n : Z.t ; n2: Z.t}

type priv = {g:Z.t; n:Z.t ; n2:Z.t ; p: Z.t ; q: Z.t ; lambda: Z.t ; mu: Z.t }

let two = Z.(~$2)

let minimum_octets = 12
let minimum_bits = 8 * minimum_octets - 7

let pub ~g ~n =
let n2 = Z.(mul n n) in
let* () = guard ( g < n2 ) ( `Msg "g is greater than n^2" ) in
let* () = guard Z.( n > zero && numbits n >= minimum_bits) ( `Msg "invalid nZ" ) in
Ok {g; n; n2}

let priv ~p ~q =
let* () = valid_prime "p" p in
let* () = valid_prime "q" q in
let* () = guard (p <> q) (`Msg "p and q are the same number") in
let* () = guard Z.((lcm (mul p q) (mul (pred p) (pred q)))=one) (`Msg "gcd(p.q, (p−1).(q−1)) = 1") in
let n = Z.(mul p q) in
let g = Z.(succ n) in
let n2 = Z.(mul n n) in
let lambda = Z.(lcm (pred p) (pred q)) in
let mu = Z.(invert lambda n) in
let _ = pub ~g ~n in
Ok {g; n; n2; p; q; lambda; mu}

let pub_of_priv ({g; n; n2; _}: priv) = {g; n; n2}

let pub_bits ({ n; _ } : pub) = Z.numbits n
and priv_bits ({ n; _ } : priv) = Z.numbits n

let random_bigint_range ~lower ~upper =
let diff = Z.sub upper lower in
Z.(add lower (Random.State.bits (Random.get_state ()) |> of_int) |> rem diff)

let rec generate ?g ~bits ()=
if bits < minimum_bits then
invalid_arg "Paillier.generate: bits: %d < minimum_bits: %d" bits minimum_bits;
let (pb, qb) = (bits / 2, bits - bits / 2) in
let (p, q) = Z_extra.(prime ?g ~msb:2 pb, prime ?g ~msb:2 qb) in
match priv ~p ~q with
| Error _ -> generate ?g ~bits ()
| Ok priv_key -> pub_of_priv priv_key, priv_key

let encrypt ~(pub_key : pub) ~msg ?(r=random_bigint_range ~lower:Z.one ~upper:Z.(pred pub_key.n)) () =
if msg < Z.zero then
invalid_arg "Paillier.encrypt: msg must be non-negative";
let gm = Z.powm pub_key.g msg pub_key.n2 in
let rn = Z.powm r pub_key.n pub_key.n2 in
Z.(rem (mul gm rn) pub_key.n2)

let decrypt ~(priv_key : priv) ~c =
let cn = Z.powm c priv_key.lambda priv_key.n2 in
let lx = Z.(div (sub cn one) priv_key.n) in
Z.(rem (mul priv_key.mu lx) priv_key.n)
6 changes: 0 additions & 6 deletions pk/rsa.ml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ type priv = {
p : Z.t ; q : Z.t ; dp : Z.t ; dq : Z.t ; q' : Z.t
}

let valid_prime name p =
guard Z.(p > zero && is_odd p && Z_extra.pseudoprime p)
(`Msg ("invalid prime " ^ name))

let rprime a b = Z.(gcd a b = one)

let valid_e ~e ~p ~q =
let* () =
guard (rprime e (Z.pred p) && rprime e (Z.pred q))
Expand Down
2 changes: 1 addition & 1 deletion tests/dune
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
(libraries test_common mirage-crypto-pk mirage-crypto-rng.unix randomconv
ounit2)
(package mirage-crypto-pk)
(modules test_numeric test_dh test_dsa test_rsa test_pk_runner))
(modules test_numeric test_dh test_dsa test_rsa test_paillier test_pk_runner))

(test
(name test_entropy_collection)
Expand Down
62 changes: 62 additions & 0 deletions tests/test_paillier.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
open OUnit2

(* open Mirage_crypto.Uncommon *)
open Mirage_crypto_pk

open Test_common

let vz = Z.of_string_base 16

module Null = struct

type g = string ref

let block = 1

let create ?time:_ () = ref ""

let generate_into ~g buf ~off n =
try
Bytes.blit_string !g 0 buf off n;
g := String.sub !g n (String.length !g - n)
with Invalid_argument _ -> raise Mirage_crypto_rng.Unseeded_generator

let reseed ~g buf = g := !g ^ buf

let seeded ~g = String.length !g > 0

let accumulate ~g _source = `Acc (reseed ~g)

let pools = 0
end

let random_is seed =
Mirage_crypto_rng.create ~seed:seed (module Null)

let gen_paillier ~bits =
let key = Paillier.(generate ~bits ()) in
assert_equal
~msg:Printf.(sprintf "key size not %d bits" bits)
bits Paillier.(priv_bits (snd key)) ;
key

let paillier_selftest ~bits n =
"selftest" >:: times ~n @@ fun _ ->
let msg = Z.(~$100) in
let key = gen_paillier ~bits in
let enc = Paillier.(encrypt ~pub_key:(fst key) ~msg ()) in
let dec = Paillier.(decrypt ~priv_key:(snd key) ~c:enc) in

assert_equal
~msg:Printf.(sprintf "failed decryption with")
msg dec


let suite = [
"Paillier" >::: [
paillier_selftest ~bits:89 100 ;
paillier_selftest ~bits:131 100 ;
paillier_selftest ~bits:1024 10 ;
paillier_selftest ~bits:2048 10 ;
] ;
]
1 change: 1 addition & 0 deletions tests/test_pk_runner.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ let suite =
"DHE" >::: Test_dh.suite;
"DSA" >::: Test_dsa.suite;
"RSA" >::: Test_rsa.suite;
"Paillier" >::: Test_paillier.suite;
]

let () =
Expand Down
Loading