Skip to content

Commit

Permalink
Add crypto/encoding, move logic and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sugyan committed May 16, 2024
1 parent d236db0 commit a435510
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 124 deletions.
105 changes: 0 additions & 105 deletions atrium-crypto/src/algorithm.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use crate::error::Result;
use ecdsa::VerifyingKey;
use k256::Secp256k1;
use multibase::Base;
use p256::NistP256;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Algorithm {
Expand All @@ -27,111 +23,10 @@ impl Algorithm {
_ => None,
}
}
pub fn format_multikey(&self, key: &[u8]) -> Result<String> {
Ok(self.format_mulikey_compressed(&self.compress_pubkey(key)?))
}
pub(crate) fn format_mulikey_compressed(&self, key: &[u8]) -> String {
let mut v = Vec::with_capacity(2 + key.len());
v.extend_from_slice(&self.prefix());
v.extend_from_slice(key);
multibase::encode(Base::Base58Btc, v)
}
pub(crate) fn compress_pubkey(&self, key: &[u8]) -> Result<Vec<u8>> {
self.pubkey_bytes(key, true)
}
pub(crate) fn decompress_pubkey(&self, key: &[u8]) -> Result<Vec<u8>> {
self.pubkey_bytes(key, false)
}
fn pubkey_bytes(&self, key: &[u8], compress: bool) -> Result<Vec<u8>> {
Ok(match self {
Algorithm::P256 => VerifyingKey::<NistP256>::from_sec1_bytes(key)?
.to_encoded_point(compress)
.as_bytes()
.to_vec(),
Algorithm::Secp256k1 => VerifyingKey::<Secp256k1>::from_sec1_bytes(key)?
.to_encoded_point(compress)
.as_bytes()
.to_vec(),
})
}
}

#[cfg(test)]
mod tests {
use super::Algorithm;
use crate::did::parse_did_key;
use crate::keypair::{Did, P256Keypair, Secp256k1Keypair};
use rand::rngs::ThreadRng;

#[test]
fn p256_compress_decompress() {
let did = P256Keypair::create(&mut ThreadRng::default()).did();
let (alg, key) = parse_did_key(&did).expect("parsing did key should succeed");
assert_eq!(alg, Algorithm::P256);
// compress a key to the correct length
let compressed = alg
.pubkey_bytes(&key, true)
.expect("compressing public key should succeed");
assert_eq!(compressed.len(), 33);
// decompress a key to the original
let decompressed = alg
.pubkey_bytes(&compressed, false)
.expect("decompressing public key should succeed");
assert_eq!(decompressed.len(), 65);
assert_eq!(key, decompressed);

// works consitesntly
let keys = (0..100)
.map(|_| {
let did = P256Keypair::create(&mut ThreadRng::default()).did();
let (_, key) = parse_did_key(&did).expect("parsing did key should succeed");
key
})
.collect::<Vec<_>>();
let compressed = keys
.iter()
.filter_map(|key| alg.pubkey_bytes(key, true).ok())
.collect::<Vec<_>>();
let decompressed = compressed
.iter()
.filter_map(|key| alg.pubkey_bytes(key, false).ok())
.collect::<Vec<_>>();
assert_eq!(keys, decompressed);
}

#[test]
fn secp256k1_compress_decompress() {
let did = Secp256k1Keypair::create(&mut ThreadRng::default()).did();
let (alg, key) = parse_did_key(&did).expect("parsing did key should succeed");
assert_eq!(alg, Algorithm::Secp256k1);
// compress a key to the correct length
let compressed = alg
.pubkey_bytes(&key, true)
.expect("compressing public key should succeed");
assert_eq!(compressed.len(), 33);
// decompress a key to the original
let decompressed = alg
.pubkey_bytes(&compressed, false)
.expect("decompressing public key should succeed");
assert_eq!(decompressed.len(), 65);
assert_eq!(key, decompressed);

// works consitesntly
let keys = (0..100)
.map(|_| {
let did = Secp256k1Keypair::create(&mut ThreadRng::default()).did();
let (_, key) = parse_did_key(&did).expect("parsing did key should succeed");
key
})
.collect::<Vec<_>>();
let compressed = keys
.iter()
.filter_map(|key| alg.pubkey_bytes(key, true).ok())
.collect::<Vec<_>>();
let decompressed = compressed
.iter()
.filter_map(|key| alg.pubkey_bytes(key, false).ok())
.collect::<Vec<_>>();
assert_eq!(keys, decompressed);
}
}
16 changes: 7 additions & 9 deletions atrium-crypto/src/did.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use super::error::{Error, Result};
use super::{Algorithm, DID_KEY_PREFIX};
use crate::encoding::{compress_pubkey, decompress_pubkey};
use crate::error::{Error, Result};
use crate::{Algorithm, DID_KEY_PREFIX};

pub fn parse_multikey(multikey: &str) -> Result<(Algorithm, Vec<u8>)> {
let (_, decoded) = multibase::decode(multikey)?;
if let Ok(prefix) = decoded[..2].try_into() {
if let Some(alg) = Algorithm::from_prefix(prefix) {
return Ok((alg, alg.decompress_pubkey(&decoded[2..])?));
return Ok((alg, decompress_pubkey(alg, &decoded[2..])?));
}
}
Err(Error::UnsupportedMultikeyType)
Expand All @@ -19,13 +20,10 @@ pub fn parse_did_key(did: &str) -> Result<(Algorithm, Vec<u8>)> {
}
}

pub fn format_did_key_str(alg: Algorithm, s: &str) -> Result<String> {
let (_, key) = multibase::decode(s)?;
format_did_key(alg, &key)
}

pub fn format_did_key(alg: Algorithm, key: &[u8]) -> Result<String> {
Ok(prefix_did_key(&alg.format_multikey(key)?))
Ok(prefix_did_key(
&alg.format_mulikey_compressed(&compress_pubkey(alg, key)?),
))
}

pub(crate) fn prefix_did_key(multikey: &str) -> String {
Expand Down
100 changes: 100 additions & 0 deletions atrium-crypto/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::{error::Result, Algorithm};
use ecdsa::VerifyingKey;
use k256::Secp256k1;
use p256::NistP256;

pub(crate) fn compress_pubkey(alg: Algorithm, key: &[u8]) -> Result<Vec<u8>> {
pubkey_bytes(alg, key, true)
}

pub(crate) fn decompress_pubkey(alg: Algorithm, key: &[u8]) -> Result<Vec<u8>> {
pubkey_bytes(alg, key, false)
}

fn pubkey_bytes(alg: Algorithm, key: &[u8], compress: bool) -> Result<Vec<u8>> {
Ok(match alg {
Algorithm::P256 => VerifyingKey::<NistP256>::from_sec1_bytes(key)?
.to_encoded_point(compress)
.as_bytes()
.to_vec(),
Algorithm::Secp256k1 => VerifyingKey::<Secp256k1>::from_sec1_bytes(key)?
.to_encoded_point(compress)
.as_bytes()
.to_vec(),
})
}

#[cfg(test)]
mod tests {
use super::{compress_pubkey, decompress_pubkey};
use crate::did::parse_did_key;
use crate::keypair::{Did, P256Keypair, Secp256k1Keypair};
use crate::Algorithm;
use rand::rngs::ThreadRng;

#[test]
fn p256_compress_decompress() {
let did = P256Keypair::create(&mut ThreadRng::default()).did();
let (alg, key) = parse_did_key(&did).expect("parsing did key should succeed");
assert_eq!(alg, Algorithm::P256);
// compress a key to the correct length
let compressed = compress_pubkey(alg, &key).expect("compressing public key should succeed");
assert_eq!(compressed.len(), 33);
// decompress a key to the original
let decompressed =
decompress_pubkey(alg, &compressed).expect("decompressing public key should succeed");
assert_eq!(decompressed.len(), 65);
assert_eq!(key, decompressed);

// works consitesntly
let keys = (0..100)
.map(|_| {
let did = P256Keypair::create(&mut ThreadRng::default()).did();
let (_, key) = parse_did_key(&did).expect("parsing did key should succeed");
key
})
.collect::<Vec<_>>();
let compressed = keys
.iter()
.filter_map(|key| compress_pubkey(alg, &key).ok())
.collect::<Vec<_>>();
let decompressed = compressed
.iter()
.filter_map(|key| decompress_pubkey(alg, &key).ok())
.collect::<Vec<_>>();
assert_eq!(keys, decompressed);
}

#[test]
fn secp256k1_compress_decompress() {
let did = Secp256k1Keypair::create(&mut ThreadRng::default()).did();
let (alg, key) = parse_did_key(&did).expect("parsing did key should succeed");
assert_eq!(alg, Algorithm::Secp256k1);
// compress a key to the correct length
let compressed = compress_pubkey(alg, &key).expect("compressing public key should succeed");
assert_eq!(compressed.len(), 33);
// decompress a key to the original
let decompressed =
decompress_pubkey(alg, &compressed).expect("decompressing public key should succeed");
assert_eq!(decompressed.len(), 65);
assert_eq!(key, decompressed);

// works consitesntly
let keys = (0..100)
.map(|_| {
let did = Secp256k1Keypair::create(&mut ThreadRng::default()).did();
let (_, key) = parse_did_key(&did).expect("parsing did key should succeed");
key
})
.collect::<Vec<_>>();
let compressed = keys
.iter()
.filter_map(|key| compress_pubkey(alg, key).ok())
.collect::<Vec<_>>();
let decompressed = compressed
.iter()
.filter_map(|key| decompress_pubkey(alg, key).ok())
.collect::<Vec<_>>();
assert_eq!(keys, decompressed);
}
}
4 changes: 3 additions & 1 deletion atrium-crypto/src/keypair.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{did::prefix_did_key, error::Result, Algorithm};
use crate::did::prefix_did_key;
use crate::error::Result;
use crate::Algorithm;
use ecdsa::elliptic_curve::{
generic_array::ArrayLength,
ops::Invert,
Expand Down
2 changes: 2 additions & 0 deletions atrium-crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![doc = include_str!("../README.md")]
mod algorithm;
pub mod did;
mod encoding;
pub mod error;
pub mod keypair;
pub mod verify;

pub use algorithm::Algorithm;
pub use multibase;

const DID_KEY_PREFIX: &str = "did:key:";
15 changes: 6 additions & 9 deletions atrium-crypto/src/verify.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::{
did::parse_did_key,
error::{Error, Result},
Algorithm,
};
use crate::did::parse_did_key;
use crate::error::{Error, Result};
use crate::Algorithm;
use ecdsa::der::{MaxOverhead, MaxSize};
use ecdsa::elliptic_curve::{
generic_array::ArrayLength,
sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
Expand All @@ -12,6 +11,7 @@ use ecdsa::hazmat::{DigestPrimitive, VerifyPrimitive};
use ecdsa::{SignatureSize, VerifyingKey};
use k256::Secp256k1;
use p256::NistP256;
use std::ops::Add;

pub fn verify_signature(did_key: &str, msg: &[u8], signature: &[u8]) -> Result<()> {
let (alg, public_key) = parse_did_key(did_key)?;
Expand All @@ -23,8 +23,6 @@ pub struct Verifier {
allow_malleable: bool,
}

use ecdsa::der::{MaxOverhead, MaxSize};
use std::ops::Add;
impl Verifier {
pub fn new(allow_malleable: bool) -> Self {
Self { allow_malleable }
Expand All @@ -47,7 +45,6 @@ impl Verifier {
AffinePoint<C>: VerifyPrimitive<C> + FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: ModulusSize,
SignatureSize<C>: ArrayLength<u8>,

MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
Expand Down Expand Up @@ -160,7 +157,7 @@ mod tests {
}

#[test]
fn verify_high_s_signatures() {
fn verify_high_s() {
let vectors = test_vectors(Some("high-s"));
assert!(vectors.len() >= 2);
let verifier = Verifier::new(true);
Expand Down

0 comments on commit a435510

Please sign in to comment.