Skip to content
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
ef326e9
Initial work on mina signer library
jspada Aug 27, 2021
a4bde88
Removed blake2 hasher from signer context
jspada Aug 31, 2021
891fa52
Refactored Context into Schnorr
jspada Aug 31, 2021
bcb57c1
Some cleanup
jspada Aug 31, 2021
0f2e302
Work in progress on random oracle input
jspada Sep 3, 2021
62fac46
Implemented functionality in order to unit test
jspada Sep 6, 2021
c469f56
Added get_address unit tests
jspada Sep 9, 2021
7de6229
Clean up some things
jspada Sep 10, 2021
ab5f933
Work on random oracle input
jspada Sep 13, 2021
2bd0a70
Progess with random oracle input
jspada Sep 13, 2021
b2b2f41
Completed random oracle to_bytes
jspada Sep 14, 2021
6561369
Completed random oracle input implementation
jspada Sep 21, 2021
73d81e1
Finished up Schnorr signing algorithm
jspada Sep 27, 2021
1868f66
Added some positive test cases
jspada Nov 8, 2021
bb8ceba
Started on mina sign and verify unit testing
jspada Nov 8, 2021
02584d2
Implemented PubKey::from_address()/b58 and memo_str()
jspada Nov 9, 2021
ff48083
Work on first transaction signing unit test
jspada Nov 10, 2021
8042c3e
Work in progress on signing unit tests
jspada Nov 11, 2021
e79812f
Work in progress on transaction signing
jspada Nov 16, 2021
498f144
Completed work on signing and basic signing unit tests
jspada Nov 17, 2021
5cde7e0
Signer improvements
jspada Nov 18, 2021
2cba726
Implemented Mina signature verification
jspada Nov 18, 2021
9c84cab
Updated gitignore
jspada Nov 18, 2021
68b1d93
Ran cargo fmt
jspada Nov 18, 2021
64b3b91
Improvements suggested by clippy
jspada Nov 19, 2021
8057454
Remove spaces between use
jspada Nov 19, 2021
405b74f
Removed unnecessary test gating
jspada Nov 19, 2021
bec13a1
Minimize imports, don't use domain::*
jspada Nov 19, 2021
9f1c5c8
Be more verbose about test name
jspada Nov 19, 2021
341cd72
Updated to latest proof-systems
jspada Nov 19, 2021
96c8dee
Fixed formatting
jspada Nov 19, 2021
78a2142
Code review items
jspada Nov 22, 2021
951bcba
Code review update
jspada Nov 22, 2021
8247678
Created documentation
jspada Nov 22, 2021
269aadf
Documentation updates
jspada Nov 22, 2021
f7b2990
Finished documentation
jspada Nov 22, 2021
5bb8888
Added an example to documentation
jspada Nov 22, 2021
f3f4e62
Added zero signature unit test
jspada Nov 22, 2021
456cf3e
Add a comment about converting field
jspada Nov 22, 2021
638743d
Switch away from bitvec's as_raw_slice()
jspada Nov 22, 2021
6be248d
Switched some function args from Vec to slice
jspada Nov 22, 2021
9f1c6e9
Improved names on Keypair members and updated mutability on a couple …
jspada Nov 23, 2021
ab987ec
Minor updates
jspada Nov 23, 2021
89a7b56
Renamed some things for clarity and to remove dependence on specific …
jspada Nov 23, 2021
c16fad5
Documented some security decisions and internal functions
jspada Nov 23, 2021
f60c6da
Added scalar from bytes helper interface
jspada Nov 23, 2021
f56606b
Switch some of verification to projective form
jspada Nov 23, 2021
1e2db04
Move Mina-specific hex serialization to Keypair/SecKey/Signature layer
jspada Nov 23, 2021
e6f2f80
Refactored FieldHelpers into single generic trait for all field eleme…
jspada Nov 24, 2021
83d55f2
Split Input trait into Hashable and Signable
jspada Nov 24, 2021
c4a4dd7
Added wrapper structs for SecKey and PubKey
jspada Nov 24, 2021
8574c72
Moved SecKey to separate module
jspada Nov 24, 2021
085e740
Updated Keypair::rand to take Rng argument
jspada Nov 24, 2021
e10c662
Refactor unit test macros into single macro
jspada Nov 24, 2021
3a2913e
Updated documentation
jspada Dec 2, 2021
f9e8c9f
Removed submodule proof-systems
jspada Dec 2, 2021
729a879
Updated dependencies
jspada Dec 2, 2021
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

.vscode
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "proof-systems"]
path = proof-systems
url = [email protected]:o1-labs/proof-systems.git
23 changes: 23 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "signer"
version = "0.1.0"
authors = ["Joseph Spadavecchia <[email protected]>"]
edition = "2018"

[lib]
path = "src/lib.rs"

[dependencies]
oracle = { path = "./proof-systems/oracle" }
algebra = { path = "./proof-systems/zexe/algebra", features = [ "pasta" ] }
mina-curves = { path = "./proof-systems/curves" }
commitment_dlog = { path = "./proof-systems/dlog/commitment" }
rand_core = { version = "0.5" }
array-init = { version = "0.1.1" }
blake2 = { version = "0.9.1" }
hex = { version = "0.4" }
bitvec = { version = "0.22.3" }
sha2 = { version = "0.9.6" }
bs58 = { version = "0.4.0" }
byteordered = { version = "0.6.0" }
byteorder = { version = "1.4.3" }
1 change: 1 addition & 0 deletions proof-systems
Submodule proof-systems added at 6b93db
119 changes: 119 additions & 0 deletions src/domain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
pub use algebra::AffineCurve;
pub use algebra::Field;

use mina_curves::pasta::pallas as Pallas;
pub use Pallas::Affine as PallasPoint;
pub type PallasField = <PallasPoint as AffineCurve>::BaseField;
pub type PallasScalar = <PallasPoint as AffineCurve>::ScalarField;

use algebra::{
CanonicalSerialize as _,
CanonicalDeserialize as _,
PrimeField, // for into_repr()
};

pub trait FieldHelpers {
fn from_bytes(bytes: Vec<u8>) -> PallasField;
fn from_hex(hex: &str) -> Result<PallasField, &str>;
fn to_bytes(self) -> Vec<u8>;
fn to_string(self) -> String;
}

impl FieldHelpers for PallasField {
fn from_hex(hex: &str) -> Result<PallasField, &str> {
if hex.len() != 64 {
return Err("Invalid field hex length");
}

let bytes: Vec<u8> = hex::decode(hex).or_else(
|_| Err("Failed to decode field hex")
)?;

return PallasField::deserialize(&mut &bytes[..]).or_else(
|_| Err("Failed to deserialize field bytes")
);
}

fn from_bytes(bytes: Vec<u8>) -> PallasField {
return PallasField::deserialize(&mut &bytes[..]).expect("failed to deserialize field");
}

fn to_bytes(self) -> Vec<u8> {
let mut bytes: Vec<u8> = vec![];
self.into_repr()
.serialize(&mut bytes)
.expect("Failed to serialize field"); // TODO: OK error handling?
return bytes;
}

fn to_string(self) -> String {
let mut bytes = self.to_bytes();
bytes.reverse();
return hex::encode(bytes);
}
}

// TODO: Combine into single Helpers trait (why did rust require two?!)
pub trait ScalarHelpers {
fn from_hex(hex: &str) -> Result<PallasScalar, &str>;
fn to_bytes(self) -> Vec<u8>;
fn to_string(self) -> String;
}

impl ScalarHelpers for PallasScalar {
fn from_hex(hex: &str) -> Result<PallasScalar, &str> {
if hex.len() != 64 {
return Err("Invalid scalar hex length");
}

let mut bytes: Vec<u8> = hex::decode(hex).or_else(
|_| Err("Failed to decode scalar hex")
)?;
bytes.reverse(); // mina scalars hex format is in big-endian order

return PallasScalar::deserialize(&mut &bytes[..]).or_else(
|_| Err("Failed to deserialize scalar bytes")
);
}

fn to_bytes(self) -> Vec<u8> {
let mut bytes: Vec<u8> = vec![];
self.into_repr()
.serialize(&mut bytes)
.expect("failed to serialize scalar"); // TODO: OK error handling?
return bytes;
}

fn to_string(self) -> String {
let mut bytes = self.to_bytes();
bytes.reverse();
return hex::encode(bytes);
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn field_from_hex() {
assert_eq!(PallasField::from_hex(""), Err("Invalid field hex length"));
assert_eq!(PallasField::from_hex("1428fadcf0c02396e620f14f176fddb5d769b7de2027469d027a80142ef8f07"), Err("Invalid field hex length"));
assert_eq!(PallasField::from_hex("0f5314f176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"), Err("Invalid field hex length"));
assert_eq!(PallasField::from_hex("g64244176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"), Err("Failed to decode field hex"));
assert_eq!(PallasField::from_hex("0cdaf334e9632268a5aa959c2781fb32bf45565fe244ae42c849d3fdc7c644fd"), Err("Failed to deserialize field bytes"));

assert_eq!(PallasField::from_hex("2eaedae42a7461d5952d27b97ecad068b698ebb94e8a0e4c45388bb613de7e08").is_ok(), true);
}

#[test]
fn scalar_from_hex() {
assert_eq!(PallasScalar::from_hex(""), Err("Invalid scalar hex length"));
assert_eq!(PallasScalar::from_hex("1428fadcf0c02396e620f14f176fddb5d769b7de2027469d027a80142ef8f07"), Err("Invalid scalar hex length"));
assert_eq!(PallasScalar::from_hex("0f5314f176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"), Err("Invalid scalar hex length"));
assert_eq!(PallasScalar::from_hex("g64244176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"), Err("Failed to decode scalar hex"));
assert_eq!(PallasScalar::from_hex("dd4244176fddb5d769b7de2027469d027ad428fadcc0c02396e6280142efb718"), Err("Failed to deserialize scalar bytes"));

assert_eq!(PallasScalar::from_hex("238344cc01fd5d8cfc7c69cc4a7497bcdb3cb9810d0f8b571615dc3da2433cc2").is_ok(), true);
}
}
69 changes: 69 additions & 0 deletions src/keypair.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use algebra::{ProjectiveCurve, UniformRand};

use crate::domain::*;

use crate::pubkey::*;

pub type SecKey = PallasScalar;

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Keypair {
pub sec_key: SecKey,
pub pub_key: PubKey,
}

impl Keypair {
pub fn rand() -> Self {
let sec_key: PallasScalar = PallasScalar::rand(&mut rand_core::OsRng);
let pub_key: PallasPoint = PallasPoint::prime_subgroup_generator().mul(sec_key).into_affine();
return Keypair { sec_key: sec_key, pub_key: pub_key};
}

pub fn from_hex(sec_key_hex: &str) -> Result<Self, &'static str> {
let sec_key = PallasScalar::from_hex(sec_key_hex).or_else(
|_| Err("Invalid secret key hex")
)?;
let pub_key: PallasPoint = PallasPoint::prime_subgroup_generator().mul(sec_key).into_affine();
return Ok(Keypair { sec_key: sec_key, pub_key: pub_key});
}

pub fn address(self) -> String {
return self.pub_key.to_address();
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn from_hex() {
assert_eq!(Keypair::from_hex(""), Err("Invalid secret key hex"));
assert_eq!(Keypair::from_hex("1428fadcf0c02396e620f14f176fddb5d769b7de2027469d027a80142ef8f07"), Err("Invalid secret key hex"));
assert_eq!(Keypair::from_hex("0f5314f176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"), Err("Invalid secret key hex"));
assert_eq!(Keypair::from_hex("g64244176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"), Err("Invalid secret key hex"));
assert_eq!(Keypair::from_hex("dd4244176fddb5d769b7de2027469d027ad428fadcc0c02396e6280142efb718"), Err("Invalid secret key hex"));

let kp = Keypair::from_hex("164244176fddb5d769b7de2027469d027ad428fadcc0c02396e6280142efb718").expect(
"failed to decode keypair secret key");

println!("address = {}", kp.pub_key.to_address());
}

#[test]
fn address() {
macro_rules! assert_get_address_eq {
($sec_key_hex:expr, $target_address:expr) => {
let kp = Keypair::from_hex($sec_key_hex).expect("failed to create keypair");
assert_eq!(kp.address(), $target_address);
};
}

assert_get_address_eq!("164244176fddb5d769b7de2027469d027ad428fadcc0c02396e6280142efb718", "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV");
assert_get_address_eq!("3ca187a58f09da346844964310c7e0dd948a9105702b716f4d732e042e0c172e", "B62qicipYxyEHu7QjUqS7QvBipTs5CzgkYZZZkPoKVYBu6tnDUcE9Zt");
assert_get_address_eq!("336eb4a19b3d8905824b0f2254fb495573be302c17582748bf7e101965aa4774", "B62qrKG4Z8hnzZqp1AL8WsQhQYah3quN1qUj3SyfJA8Lw135qWWg1mi");
assert_get_address_eq!("1dee867358d4000f1dafa5978341fb515f89eeddbe450bd57df091f1e63d4444", "B62qoqiAgERjCjXhofXiD7cMLJSKD8hE8ZtMh4jX5MPNgKB4CFxxm1N");
assert_get_address_eq!("20f84123a26e58dd32b0ea3c80381f35cd01bc22a20346cc65b0a67ae48532ba", "B62qkiT4kgCawkSEF84ga5kP9QnhmTJEYzcfgGuk6okAJtSBfVcjm1M");
assert_get_address_eq!("3414fc16e86e6ac272fda03cf8dcb4d7d47af91b4b726494dab43bf773ce1779", "B62qoG5Yk4iVxpyczUrBNpwtx2xunhL48dydN53A2VjoRwF8NUTbVr4");
}
}
62 changes: 62 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
pub mod domain;
pub mod pubkey;
pub mod keypair;
pub mod roinput;
pub mod signature;
pub mod schnorr;

pub use domain::*;

pub use pubkey::PubKey;
pub use pubkey::PubKeyHelpers;
pub use pubkey::CompressedPubKey;
pub use keypair::Keypair;
pub use roinput::{
Input,
ROInput,
};
pub use signature::Signature;
pub use schnorr::Schnorr;

use oracle::{
pasta,
poseidon::{
Sponge,
ArithmeticSponge,
SpongeConstants,
ArithmeticSpongeParams,
PlonkSpongeConstants,
},
};

#[derive(Copy, Clone)]
#[repr(u8)]
pub enum NetworkId {
TESTNET = 0x00,
MAINNET = 0x01,
}

impl Into<u8> for NetworkId {
fn into(self) -> u8 {
self as u8
}
}

pub trait Signer {
fn sign<I: Input>(&mut self, kp: Keypair, input: I) -> Signature;
fn verify<I: Input>(&mut self, sig: Signature, pub_key: PubKey, input: I) -> bool;
}

pub fn create(network_id: NetworkId) -> impl Signer {
return Schnorr::<PlonkSpongeConstants>::new(
ArithmeticSponge::<PallasField, PlonkSpongeConstants>::new(pasta::fp::params()),
network_id,
);
}

pub fn custom<SC: SpongeConstants>(params: ArithmeticSpongeParams<PallasField>, network_id: NetworkId) -> impl Signer {
return Schnorr::<SC>::new(
ArithmeticSponge::<PallasField, SC>::new(params),
network_id,
);
}
Loading