Commit 1cd31972 authored by Sophia Gold's avatar Sophia Gold

merge ocaml-sapling

parent efe12e2a
Pipeline #38568142 failed with stages
in 3 minutes and 45 seconds
**/target
**/_build
**/.merlin
Cargo.lock
opam-version: "0.1"
name: "ocaml-sapling"
version: "0.1"
authors: "Sophia Gold <sophiagoldnyc@gmail.com>"
maintainer: "Sophia Gold <sophiagoldnyc@gmail.com>"
license: "MIT"
available: [
ocaml-version >= "4.06.0"
]
build: [
[ "dune" "build" "-p" name "@install" ]
[ "cargo" "build" "--release" ]
]
depends: [
"dune" {build & >= "1.0"}
"alcotest" {>= "0.8.3"}
"bigstring" {>= "0.2"}
"bitstring" {>= "3.1.0"}
"tezos-crypto" {= "0.2"}
"blake2" {= "1.2"}
]
[package]
name = "ocaml-sapling"
version = "0.1.0"
authors = ["sophia gold <sophiagoldnyc@gmail.com>"]
[dependencies]
derive-ocaml = { version = "0.1.1", features= ["stubs", "derive"] }
ocaml = "0.5.0"
sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto" }
pairing = { version = "0.14.2", features = ["expose-arith"] }
rand = "0.4"
bellman = "0.1"
zip32 = { git = "https://github.com/Sophia-Gold/zip32", rev = "e5a2dc55" }
lazy_static = "1.1.0"
byteorder = "1.0"
[lib]
crate-type = ["staticlib"]
\ No newline at end of file
The MIT License (MIT)
Copyright (c) Tezos Foundation <contact@tezos.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# NB: requires Rust nightly
# install using rustup: `curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly`
# if rustup is already installed: `rustup install nightly` or `rustup default nightly`
build:
cargo +nightly build --release
dune build
test: build
dune runtest src --force
clean:
rm -f src/*.a src/*.so
cargo clean
dune clean
This diff is collapsed.
(library
(name sapling)
(modules sapling)
(libraries bigstring bitstring tezos-crypto blake2)
(preprocess (pps bitstring.ppx))
(c_library_flags -L../../vendors/ocaml-sapling/target/release -locaml_sapling -lpthread -lc -lm))
(test
(name test)
(modules test)
(libraries sapling alcotest))
open Sapling
let main =
let (proof_gen_key, outgoing_viewing_key) = new_spending_key () in
print_string "proof generation key: ";
print_key proof_gen_key;
print_newline ();
print_string "outgoing viewing key: ";
print_key outgoing_viewing_key;
print_newline ();
let full_viewing_key = viewing_key_of_proof_generation_key proof_gen_key in
print_string "full viewing key: ";
print_key full_viewing_key;
print_newline ();
let group = new_address_group () in
let address = new_address group full_viewing_key in
print_string "address id: ";
print_key (get_address_id address);
print_newline ();
print_string "address group: ";
print_key (get_address_group address);
print_newline ();
Printf.printf "address matches?: %B\n\n" (match_address_group address group);
let merkle_root = new_merkle_root () in
let witness = Option.get (new_witness (Array.make 1065 (Char.chr 0))) in
let value = make_value (Int64.of_int 100) in
let (proof, verifying_key, signature, value_commitment, hold) = sign value address merkle_root witness proof_gen_key in
match (verify_sign signature value_commitment merkle_root hold verifying_key proof) with
| Ok x -> Printf.printf "verified: %B" x
| Error x -> Printf.printf "error: %s" x
This diff is collapsed.
type 'a key = char array
type proof_generation
type full_viewing
type outgoing_viewing
type incoming_viewing
type address_id
type address_group
type public
type pvt
type verifying
type address = { key: char array; base: char array }
let get_address_id (x: address) : address_id key = x.key
let get_address_group (x: address) : address_group key = x.base
type value = int64
type value_commitment = char array
type cheque_commitment = char array
type hold = char array
let make_value (x: int64) : value = x
type cheque =
{ value: value;
address: address;
cheque_commitment: cheque_commitment;
}
let get_cheque_value (x: cheque) : value = x.value
let get_cheque_address (x: cheque) : address = x.address
let get_cheque_commitment (x: cheque) : cheque_commitment = x.cheque_commitment
type merkle_root = char array
type witness = char array
let new_witness (x: char array) : witness option =
if Array.length x == 1065
then Some x
else None
type proof = char array
type signature = char array
let unsafe_key_to_ints (x: 'a key) : int array = Array.map Char.code x
let print_key (x: 'a key) : unit =
Array.iter (fun a -> Printf.printf "%i " (Char.code a)) x;
print_newline ()
external new_spending_key :
unit -> proof_generation key * outgoing_viewing key = "new_spending_key"
external new_blind_spending_key :
unit -> proof_generation key = "new_blind_spending_key"
external viewing_key_of_proof_generation_key :
proof_generation key -> full_viewing key = "viewing_key_of_proof_generation_key"
external incoming_of_viewing_key :
full_viewing key -> incoming_viewing key = "incoming_of_viewing_key"
external new_keypair :
address -> public key * pvt key = "new_keypair"
external make_shared_secret :
char array -> char array -> char array = "make_shared_secret"
external new_address_group :
unit -> address_group key = "new_address_base"
external new_address :
address_group key -> full_viewing key -> address = "new_address"
external match_address_group :
address -> address_group key -> bool = "check_address"
external new_merkle_root :
unit -> merkle_root = "new_anchor"
external make_cheque :
value -> address -> cheque = "make_note"
external sign :
value -> address -> merkle_root -> witness -> proof_generation key ->
proof * verifying key * signature * value_commitment * hold = "spend"
external endorse :
value -> address -> pvt key ->
proof * verifying key * value_commitment = "output"
(* The following two functions are necessary due to OCaml's five argument limit for externals. *)
external verify_sign_stub :
char array array -> verifying key -> proof ->
(bool, string) result = "verify_spend_stub"
external verify_sign_inputs :
signature -> value_commitment -> merkle_root -> hold ->
char array array = "verify_spend_inputs"
let verify_sign spend_sig vc an nf vk pr
= verify_sign_stub (verify_sign_inputs spend_sig vc an nf) vk pr
external verify_endorse :
value_commitment -> cheque_commitment -> public key -> verifying key -> proof ->
(bool, string) result = "verify_output"
type memo = char array
let make_memo (x: string) : memo option =
if (String.length x) <= 64
then let memo = Array.make 64 ' ' in
String.iteri (fun i x -> Array.set memo i x) x;
Some memo
else None
type ciphertext = char array
let bytes_of_int64 (x: int64) : char array =
let%bitstring bits = {| x : 64 |} in
let string = Bitstring.string_of_bitstring bits in
let bitlist = String.split_on_char '\\' string in
Array.of_list (List.map (fun x -> Char.chr (int_of_string x)) bitlist)
let int64_of_bytes (x: char array) : int64 =
let stringarray = Array.init 64 (fun i -> (String.make 1 (Array.get x i))) in
let bits = Bitstring.bitstring_of_string (String.concat "\\" (Array.to_list stringarray)) in
match%bitstring bits with
| {| x : 64 |} -> x
let serialize_plaintext (cheque: cheque) (memo: memo) : Bigstring.t =
(* 8 byte value + 11-byte address base + 32-byte address key + 32-byte cheque commitment + 512-byte memo field*)
let array = Array.append
(Array.append
(Array.append
(Array.append cheque.address.base cheque.address.key)
(bytes_of_int64 cheque.value))
cheque.cheque_commitment)
memo in
Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout array
let deserialize_plaintext (plaintext: Bigstring.t) : cheque * memo =
let plaintext = Bigstring.to_bytes plaintext in
let plaintext = Array.init 595 (fun i -> Bytes.get plaintext i) in
let cheque = { value = int64_of_bytes (Array.sub plaintext 0 8);
address = { base = Array.sub plaintext 8 11;
key = Array.sub plaintext 19 32 };
cheque_commitment = Array.sub plaintext 51 32;
} in
let memo = Array.sub plaintext 83 512 in
(cheque, memo)
let bigstring_to_ciphertext (x: Bigstring.t) : ciphertext =
let x = Bigstring.to_bytes x in
Array.init 595 (fun i -> Bytes.get x i)
let encrypt (public_key: public key) (private_key: pvt key) (cheque: cheque) (memo: memo) : (ciphertext, string) result =
if cheque.address.key == private_key
then let key_array = Array.append (make_shared_secret cheque.address.key private_key) public_key in
let key_bigarray = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout key_array in
let key = match Blake2.Blake2b.direct key_bigarray 64 with
| Hash (x) -> x in
let plaintext = serialize_plaintext cheque memo in
let cyphertext = Bigstring.empty in
Hacl.Secretbox.box ~key:(Hacl.Secretbox.unsafe_of_bytes key) ~nonce:(Hacl.Nonce.gen ()) ~msg:plaintext ~cmsg:cyphertext;
Ok (bigstring_to_ciphertext cyphertext)
else Error "Address identifying key does not match private key."
let encrypt_for_wallet (ovk: outgoing_viewing key) (cv: value_commitment) (cm: cheque_commitment) (public_key: public key)
(cheque: cheque) (memo: memo) : (ciphertext, string) result =
(* Never returns error, result type used for consistency. *)
let key_array = Array.append (Array.append (Array.append ovk cv) cm) public_key in
let key_bigarray = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout key_array in
let key = match Blake2.Blake2b.direct key_bigarray 128 with
| Hash (x) -> x in
let plaintext = serialize_plaintext cheque memo in
let cyphertext = Bigstring.empty in
Hacl.Secretbox.box ~key:(Hacl.Secretbox.unsafe_of_bytes key) ~nonce:(Hacl.Nonce.gen ()) ~msg:plaintext ~cmsg:cyphertext;
Ok (bigstring_to_ciphertext cyphertext)
let decrypt (ciphertext: ciphertext) (public_key: public key) (private_key: pvt key)
(address: address) : (cheque * memo, string) result =
if address.key == private_key
then let key_array = Array.append (make_shared_secret address.key private_key) public_key in
let key_bigarray = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout key_array in
let key = match Blake2.Blake2b.direct key_bigarray 64 with
| Hash (x) -> x in
let ciphertext = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout ciphertext in
let plaintext = Bigstring.empty in
let decrypted = Hacl.Secretbox.box_open ~key:(Hacl.Secretbox.unsafe_of_bytes key) ~nonce:(Hacl.Nonce.gen ()) ~cmsg:ciphertext ~msg:plaintext in
if decrypted
then Ok (deserialize_plaintext plaintext)
else Error "Wrong public key."
else Error "Address identifying key does not match private key."
let decrypt_incoming (ciphertext: ciphertext) (ivk: incoming_viewing key) (public_key: public key) : (cheque * memo, string) result =
if ivk == public_key
then let key_array = Array.append (make_shared_secret public_key ivk) public_key in
let key_bigarray = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout key_array in
let key = match Blake2.Blake2b.direct key_bigarray 64 with
| Hash (x) -> x in
let ciphertext = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout ciphertext in
let plaintext = Bigstring.empty in
let decrypted = Hacl.Secretbox.box_open ~key:(Hacl.Secretbox.unsafe_of_bytes key) ~nonce:(Hacl.Nonce.gen ()) ~cmsg:ciphertext ~msg:plaintext in
if decrypted
then Ok (deserialize_plaintext plaintext)
else Error "Wrong public key."
else Error "Address identifying key does not match private key."
let decrypt_outgoing (ciphertext: ciphertext) (ovk: outgoing_viewing key)
(cv: value_commitment) (cm: cheque_commitment) (public_key: public key) : (cheque * memo, string) result =
let key_array = Array.append (Array.append (Array.append ovk cv) cm) public_key in
let key_bigarray = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout key_array in
let key = match Blake2.Blake2b.direct key_bigarray 128 with
| Hash (x) -> x in
let ciphertext = Bigarray.Array1.of_array Bigarray.char Bigarray.c_layout ciphertext in
let plaintext = Bigstring.empty in
let decrypted = Hacl.Secretbox.box_open ~key:(Hacl.Secretbox.unsafe_of_bytes key) ~nonce:(Hacl.Nonce.gen ()) ~cmsg:ciphertext ~msg:plaintext in
if decrypted
then Ok (deserialize_plaintext plaintext)
else Error "Decryption failed."
(** Keys are exposed as phantom types for safety. **)
type 'a key
(** Convert key to array of ints from 0-255. **)
val unsafe_key_to_ints : 'a key -> int array
(** Print key as 8-bit numerals separated by spaces. **)
val print_key : 'a key -> unit
(** A key used to generate proofs. **)
type proof_generation
(** A key used for viewing encrypted cheques by sender, **)
type outgoing_viewing
(** Returns new proof generation key and outgoing viewing key. Not referentially transparent. **)
val new_spending_key : unit -> proof_generation key * outgoing_viewing key
(** Returns a new fresh proof generation key. Without an outgoing viewing key cheques cannot be decrypted by sender. Not referentially transparent. **)
val new_blind_spending_key : unit -> proof_generation key
(** A key used to verify proofs. **)
type verifying
(** A key used for deriving an incoming viewing key and new addresses. **)
type full_viewing
val viewing_key_of_proof_generation_key : proof_generation key -> full_viewing key
(** A key used for viewing encrypted cheques by recipient. **)
type incoming_viewing
val incoming_of_viewing_key : full_viewing key -> incoming_viewing key
(** Address for transactions. Consists of a group key and identifying key. **)
type address
(** A group key that can be shared by multiple distinct addresses. **)
type address_group
(** Returns the group key of an address. **)
val get_address_group : address -> address_group key
(** Returns a new address group. Not referentially transparent. **)
val new_address_group : unit -> address_group key
(** A unique key identifying an address. **)
type address_id
(** Returns a new address. Not referentially transparent. **)
val new_address : address_group key -> full_viewing key -> address
(** Returns the identifying key of an address. **)
val get_address_id : address -> address_id key
(** Returns true if address matches group. **)
val match_address_group: address -> address_group key -> bool
(** Number of satoshis in a cheque. **)
type value
(** Make a new value from an int64. **)
val make_value: int64 -> value
(** The basic unit of a shielded transaction. **)
type cheque
(** Make a new cheque transferring a value to an address. **)
val make_cheque : value -> address -> cheque
(** Returns the value of a cheque. **)
val get_cheque_value : cheque -> value
(** Pedersen commitment to a value **)
type value_commitment
(** Returns the address of a cheque. **)
val get_cheque_address : cheque -> address
(** Pedersen commitment to a cheque. **)
type cheque_commitment
(** Returns the Pedersen commitment of a cheque. **)
val get_cheque_commitment : cheque -> cheque_commitment
(** Unique identifier for cheques. Used to prevent sender from spending tokens that haven't been cashed by recipient. **)
type hold
(** A merkle root. **)
type merkle_root
(** Returns a new merkle root. Not referentially transparent. **)
val new_merkle_root : unit -> merkle_root
(** A witness used to generate a zero knowledge proof. **)
type witness
(** Make a witness from an array of chars. Returns None if argument is not 1065-bit. **)
val new_witness : char array -> witness option
(** A zero knowledge proof. **)
type proof
(** A signature used to sign a cheque. Can be used to delegate proof generation without revealing proof generation key. **)
type signature
(** A public key for encryption, decryption, and verifying proofs of endorsement. Need not be made public. **)
type public
(** A private key for encryption, decryption, and generating proofs of endorsement. **)
type pvt
(** Derives new public and private keys from an address. Not referentially transparent. **)
val new_keypair : address -> public key * pvt key
(** Generates a zero knowledge proof that a cheque has been signed by sender. **)
val sign :
value -> address -> merkle_root -> witness -> proof_generation key ->
proof * verifying key * signature * value_commitment * hold
(** Returns true if a proof of signature is valid. **)
val verify_sign :
signature -> value_commitment -> merkle_root -> hold -> verifying key -> proof ->
(bool, string) result
(** Generates a zero knowledge proof that a cheque has been cashed by receiver. **)
val endorse :
value -> address -> pvt key ->
proof * verifying key * value_commitment
(** Returns true if a proof of endorsement is valid. **)
val verify_endorse :
value_commitment -> cheque_commitment -> public key -> verifying key -> proof ->
(bool, string) result
(** A natural language message that accompanies a cheque. **)
type memo
(** Make a memo from a string. Pads with ' ' if argument is < 512-bits. Returns None if argument is > 512-bits **)
val make_memo : string -> memo option
(** An encrypted cheque and memo. **)
type ciphertext
(** Encrypt a cheque and memo for blockchain. **)
val encrypt : public key -> pvt key -> cheque -> memo -> (ciphertext, string) result
(** Encrypt a cheque and memo for sender's wallet. **)
val encrypt_for_wallet : outgoing_viewing key -> value_commitment -> cheque_commitment -> public key -> cheque -> memo -> (ciphertext, string) result
(** Decrypt a cheque and memo using a full keypair. **)
val decrypt : ciphertext -> public key -> pvt key -> address -> (cheque * memo, string) result
(** Decrypt a cheque and memo using an incoming viewing key. **)
val decrypt_incoming : ciphertext -> incoming_viewing key -> public key -> (cheque * memo, string) result
(** Decrypt a cheque and memo using an outgoing viewing key. **)
val decrypt_outgoing : ciphertext -> outgoing_viewing key -> value_commitment -> cheque_commitment -> public key -> (cheque * memo, string) result
module Test = struct
(* open Sapling *)
end
let tests = [
]
let () =
Alcotest.run "test-sapling" [
"tests", tests;
]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment