Skip to content

Commit

Permalink
Switch to RustCrypto and implement EcPoint on top of ProjectivePoint;
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat committed Jun 3, 2020
1 parent 39f3ec7 commit b7d36d7
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 20 deletions.
3 changes: 2 additions & 1 deletion sigma-tree/Cargo.toml
Expand Up @@ -11,7 +11,8 @@ crate-type = ["cdylib", "rlib"]
sigma-ser = { path = "../sigma-ser" }
indexmap = "1.3.2"
base16 = "0.2.1"
libsecp256k1 = "0.3.5"
k256 = "0.2.0"
elliptic-curve = {version = "0.3.0", features = ["getrandom"]}

[dev-dependencies]
wasm-bindgen-test = "0.3.10"
Expand Down
84 changes: 65 additions & 19 deletions sigma-tree/src/ecpoint.rs
@@ -1,52 +1,98 @@
use secp256k1::PublicKey;
use k256::{
arithmetic::{AffinePoint, ProjectivePoint, Scalar},
PublicKey,
};
use sigma_ser::{
serializer::{SerializationError, SigmaSerializable},
vlq_encode,
};
use std::convert::TryInto;
use std::io;

#[derive(PartialEq, Eq, Debug)]
pub struct EcPoint(pub PublicKey);
#[derive(PartialEq, Debug)]
pub struct EcPoint(ProjectivePoint);

impl EcPoint {
pub const PUBLIC_KEY_SIZE: usize = secp256k1::util::COMPRESSED_PUBLIC_KEY_SIZE;
pub const GROUP_SIZE: usize = 33;

pub fn random() -> EcPoint {
let scalar = loop {
// Generate a new secret key using the operating system's
// cryptographically secure random number generator
let sk = k256::SecretKey::generate();
let bytes: [u8; 32] = sk
.secret_scalar()
.as_ref()
.as_slice()
.try_into()
.expect("expected 32 bytes");
// Returns None if the byte array does not contain
// a big-endian integer in the range [0, n), where n is group order.
let maybe_scalar = Scalar::from_bytes(bytes);
if bool::from(maybe_scalar.is_some()) {
break maybe_scalar.unwrap();
}
};
// we treat EC as a multiplicative group, therefore, exponentiate point is multiply.
let pkp = ProjectivePoint::generator() * &scalar;
EcPoint(pkp)
}
}

impl Eq for EcPoint {}

impl SigmaSerializable for EcPoint {
fn sigma_serialize<W: vlq_encode::WriteSigmaVlqExt>(&self, w: &mut W) -> Result<(), io::Error> {
w.write_all(&self.0.serialize_compressed())?;
let caff = self.0.to_affine();
if bool::from(caff.is_some()) {
let pubkey = caff.unwrap().to_compressed_pubkey();
w.write_all(pubkey.as_bytes())?;
} else {
// infinity point
let zeroes = [0u8; EcPoint::GROUP_SIZE];
w.write_all(&zeroes)?;
}
Ok(())
}

fn sigma_parse<R: vlq_encode::ReadSigmaVlqExt>(r: &mut R) -> Result<Self, SerializationError> {
let mut bytes = [0; EcPoint::PUBLIC_KEY_SIZE];
r.read_exact(&mut bytes[..])?;
let pk = PublicKey::parse_compressed(&bytes)
.map_err(|_| SerializationError::Misc("invalid secp256k1 compressed public key"))?;
Ok(EcPoint(pk))
let mut buf = [0; EcPoint::GROUP_SIZE];
r.read_exact(&mut buf[..])?;
if buf[0] != 0 {
let pubkey = PublicKey::from_bytes(&buf[..])
.ok_or(SerializationError::Misc("failed to parse PK from bytes"))?;
let cp = AffinePoint::from_pubkey(&pubkey);
if bool::from(cp.is_none()) {
Err(SerializationError::Misc(
"failed to get affine point from PK",
))
} else {
Ok(EcPoint(ProjectivePoint::from(cp.unwrap())))
}
} else {
// infinity point
Ok(EcPoint(ProjectivePoint::identity()))
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
use rand::thread_rng;
use secp256k1::SecretKey;
use sigma_ser::test_helpers::*;

impl Arbitrary for EcPoint {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(any::<i32>())
.prop_map(|_| {
let sk = SecretKey::random(&mut thread_rng());
let pk = PublicKey::from_secret_key(&sk);
EcPoint(pk)
})
.boxed()
prop_oneof![
prop::num::u8::ANY.prop_map(|_| EcPoint(ProjectivePoint::generator())),
prop::num::u8::ANY.prop_map(|_| EcPoint(ProjectivePoint::identity())),
prop::num::u8::ANY.prop_map(|_| EcPoint::random()),
]
.boxed()
}
}

Expand Down

0 comments on commit b7d36d7

Please sign in to comment.