Skip to content
Open
Show file tree
Hide file tree
Changes from 41 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
27 changes: 27 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[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" }
mina-curves = { path = "./proof-systems/curves" }
commitment_dlog = { path = "./proof-systems/dlog/commitment" }

ark-ec = { version = "0.3.0", features = [ "parallel" ] }
ark-ff = { version = "0.3.0", features = [ "parallel", "asm" ] }
ark-serialize = { version = "0.3.0" }

rand = { version = "0.8.0" }
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 c632bb
183 changes: 183 additions & 0 deletions src/domain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//! Signer domain and helpers
//!
//! Shorthands and helpers for base and scalar field elements

use ark_ec::AffineCurve;
use ark_ff::PrimeField; // for into_repr()

use mina_curves::pasta::pallas as Pallas;

/// Affine curve point type
pub use Pallas::Affine as PallasPoint;
/// Base field element type
pub type PallasField = <PallasPoint as AffineCurve>::BaseField;
/// Scalar field element type
pub type PallasScalar = <PallasPoint as AffineCurve>::ScalarField;

use ark_serialize::{CanonicalDeserialize as _, CanonicalSerialize as _};

/// Base field element helpers
pub trait FieldHelpers {
/// Deserialize from bytes
fn from_bytes(bytes: &[u8]) -> PallasField;

/// Deserialize from hex
fn from_hex(hex: &str) -> Result<PallasField, &str>;

/// Serialize to bytes
fn to_bytes(self) -> Vec<u8>;

/// Serialize to hex
fn to_hex(self) -> String;
}

impl FieldHelpers for PallasField {
fn from_hex(hex: &str) -> Result<PallasField, &str> {
let bytes: Vec<u8> = hex::decode(hex).map_err(|_| "Failed to decode field hex")?;

PallasField::deserialize(&mut &bytes[..]).map_err(|_| "Failed to deserialize field bytes")
}

fn from_bytes(bytes: &[u8]) -> PallasField {
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");

bytes
}

fn to_hex(self) -> String {
let mut bytes = self.to_bytes();
bytes.reverse(); // mina order

hex::encode(bytes)
}
}

/// Scalar field element helpers
// TODO: Combine into single Helpers trait (why did rust require two?!)
pub trait ScalarHelpers {
/// Deserialize from hex
fn from_hex(hex: &str) -> Result<PallasScalar, &str>;

/// Serialize to bytes
fn to_bytes(self) -> Vec<u8>;

/// Serialize to hex
fn to_hex(self) -> String;
}

impl ScalarHelpers for PallasScalar {
fn from_hex(hex: &str) -> Result<PallasScalar, &str> {
let mut bytes: Vec<u8> = hex::decode(hex).map_err(|_| "Failed to decode scalar hex")?;
bytes.reverse(); // mina scalars hex format is in big-endian order

PallasScalar::deserialize(&mut &bytes[..]).map_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");

bytes
}

fn to_hex(self) -> String {
let mut bytes = self.to_bytes();
bytes.reverse(); // mina order

hex::encode(bytes)
}
}

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

#[test]
fn field_from_hex() {
assert_eq!(
PallasField::from_hex(""),
Err("Failed to deserialize field bytes")
);
assert_eq!(
PallasField::from_hex(
"1428fadcf0c02396e620f14f176fddb5d769b7de2027469d027a80142ef8f07"
),
Err("Failed to decode field hex")
);
assert_eq!(
PallasField::from_hex(
"0f5314f176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"
),
Err("Failed to decode field hex")
);
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("Failed to deserialize scalar bytes")
);
assert_eq!(
PallasScalar::from_hex(
"1428fadcf0c02396e620f14f176fddb5d769b7de2027469d027a80142ef8f07"
),
Err("Failed to decode scalar hex")
);
assert_eq!(
PallasScalar::from_hex(
"0f5314f176fddb5d769b7de2027469d027ad428fadcf0c02396e6280142efb7d8"
),
Err("Failed to decode scalar hex")
);
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
);
}
}
111 changes: 111 additions & 0 deletions src/keypair.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//! Keypair structures and algorithms
//!
//! Definition of secret key, keypairs and related helpers

use crate::{PallasPoint, PallasScalar, PubKey, PubKeyHelpers, ScalarHelpers};
use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ff::UniformRand;
use rand;

/// Secret key
pub type SecKey = PallasScalar;

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

impl Keypair {
/// Generate a random keypair
pub fn rand() -> Self {
let sec_key: PallasScalar = PallasScalar::rand(&mut rand::rngs::OsRng);
let pub_key: PallasPoint = PallasPoint::prime_subgroup_generator()
.mul(sec_key)
.into_affine();

Keypair { sec_key, pub_key }
}

/// Deserialize a keypair from secret key hex
pub fn from_hex(sec_key_hex: &str) -> Result<Self, &'static str> {
let sec_key = PallasScalar::from_hex(sec_key_hex).map_err(|_| "Invalid secret key hex")?;
let pub_key: PallasPoint = PallasPoint::prime_subgroup_generator()
.mul(sec_key)
.into_affine();

Ok(Keypair { sec_key, pub_key })
}

/// Obtain the Mina address corresponding to the keypair's public key
pub fn get_address(self) -> String {
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")
);

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

#[test]
fn get_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.get_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"
);
}
}
Loading