From 2d45e9ff123beeb96c50b21572838b6db18bbd16 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 23 Feb 2024 11:26:57 -0600 Subject: [PATCH] types: add various crypto types --- Cargo.toml | 1 + src/types/crypto/bls12381.rs | 87 ++++++++++++++++++++++++++++++++ src/types/crypto/ed25519.rs | 85 +++++++++++++++++++++++++++++++ src/types/crypto/mod.rs | 95 +++++++++++++++++++++++++++++++++++ src/types/crypto/secp256k1.rs | 87 ++++++++++++++++++++++++++++++++ src/types/crypto/secp256r1.rs | 87 ++++++++++++++++++++++++++++++++ src/types/crypto/signature.rs | 41 +++++++++++++++ src/types/mod.rs | 7 +++ 8 files changed, 490 insertions(+) create mode 100644 src/types/crypto/bls12381.rs create mode 100644 src/types/crypto/ed25519.rs create mode 100644 src/types/crypto/mod.rs create mode 100644 src/types/crypto/secp256k1.rs create mode 100644 src/types/crypto/secp256r1.rs create mode 100644 src/types/crypto/signature.rs diff --git a/Cargo.toml b/Cargo.toml index 0fbb295a9..740738dcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ rand = ["dep:rand_core"] hash = ["dep:blake2"] [dependencies] +base64ct = "1.6.0" bs58 = "0.5.0" hex = "0.4.3" diff --git a/src/types/crypto/bls12381.rs b/src/types/crypto/bls12381.rs new file mode 100644 index 000000000..8eeb94b4c --- /dev/null +++ b/src/types/crypto/bls12381.rs @@ -0,0 +1,87 @@ +//! Implementation of bls12381 min-sig public-key cryptogrophy. + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Bls12381PrivateKey( + #[cfg_attr( + feature = "serde", + serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") + )] + [u8; Self::LENGTH], +); + +impl Bls12381PrivateKey { + /// The length of an bls12381 private key in bytes. + pub const LENGTH: usize = 32; +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Bls12381PublicKey( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Bls12381PublicKey { + /// The length of an bls12381 public key in bytes. + pub const LENGTH: usize = 96; +} + +impl std::fmt::Display for Bls12381PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display96(&self.0), f) + } +} + +impl std::fmt::Debug for Bls12381PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Bls12381PublicKey") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Bls12381Signature( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Bls12381Signature { + /// The length of an bls12381 signature key in bytes. + pub const LENGTH: usize = 48; +} + +impl std::fmt::Display for Bls12381Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display48(&self.0), f) + } +} + +impl std::fmt::Debug for Bls12381Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Bls12381Signature") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} diff --git a/src/types/crypto/ed25519.rs b/src/types/crypto/ed25519.rs new file mode 100644 index 000000000..7f1e662c0 --- /dev/null +++ b/src/types/crypto/ed25519.rs @@ -0,0 +1,85 @@ +//! Implementation of ed25519 public-key cryptogrophy. + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Ed25519PrivateKey( + #[cfg_attr( + feature = "serde", + serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") + )] + [u8; Self::LENGTH], +); + +impl Ed25519PrivateKey { + /// The length of an ed25519 private key in bytes. + pub const LENGTH: usize = 32; +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Ed25519PublicKey( + #[cfg_attr( + feature = "serde", + serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") + )] + [u8; Self::LENGTH], +); + +impl Ed25519PublicKey { + /// The length of an ed25519 public key in bytes. + pub const LENGTH: usize = 32; +} + +impl std::fmt::Display for Ed25519PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display32(&self.0), f) + } +} + +impl std::fmt::Debug for Ed25519PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Ed25519PublicKey") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Ed25519Signature( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Ed25519Signature { + /// The length of an ed25519 signature key in bytes. + pub const LENGTH: usize = 64; +} + +impl std::fmt::Display for Ed25519Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display64(&self.0), f) + } +} + +impl std::fmt::Debug for Ed25519Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Ed25519Signature") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} diff --git a/src/types/crypto/mod.rs b/src/types/crypto/mod.rs new file mode 100644 index 000000000..8ad7781d3 --- /dev/null +++ b/src/types/crypto/mod.rs @@ -0,0 +1,95 @@ +mod bls12381; +mod ed25519; +mod secp256k1; +mod secp256r1; +mod signature; + +pub use bls12381::{Bls12381PrivateKey, Bls12381PublicKey, Bls12381Signature}; +pub use ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}; +pub use secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey, Secp256k1Signature}; +pub use secp256r1::{Secp256r1PrivateKey, Secp256r1PublicKey, Secp256r1Signature}; +pub use signature::{SignatureScheme, SimpleSignature, UserSignature}; + +// +// Implement various base64 fixed-size array helpers +// + +/// Utility for calculating base64 encoding lenghths. +/// +/// In the Base64 encoding each character is used to represent 6 bits (log2(64) = 6). This means +/// that 4 characters are used to represnet 4*6 = 24 bits = 3 bytes. So you need 4*(`n`/3) +/// characters in order to represent `n` bytes, and this needs to be rounded up to a multiple of 4. +/// The number of unused padding characters resulting from the rounding will be 0, 1, 2, or 3. +const fn base64_encoded_length(len: usize) -> usize { + ((4 * len / 3) + 3) & !3 +} + +macro_rules! impl_base64_helper { + ($base:ident, $display:ident, $fromstr:ident, $array_length:literal) => { + struct $base; + + impl $base { + const LENGTH: usize = $array_length; + const ENCODED_LENGTH: usize = base64_encoded_length(Self::LENGTH); + } + + struct $display<'a>(&'a [u8; $base::LENGTH]); + + impl<'a> std::fmt::Display for $display<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut buf = [0; $base::ENCODED_LENGTH]; + let encoded = + ::encode(self.0, &mut buf).unwrap(); + f.write_str(encoded) + } + } + + struct $fromstr([u8; $base::LENGTH]); + + impl std::str::FromStr for $fromstr { + type Err = base64ct::Error; + + fn from_str(s: &str) -> Result { + let mut buf = [0; $base::LENGTH]; + let decoded = ::decode(s, &mut buf)?; + assert_eq!(decoded.len(), $base::LENGTH); + Ok(Self(buf)) + } + } + + #[cfg(feature = "serde")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + impl serde_with::SerializeAs<[u8; Self::LENGTH]> for $base { + fn serialize_as( + source: &[u8; Self::LENGTH], + serializer: S, + ) -> Result + where + S: serde::Serializer, + { + let display = $display(source); + serde_with::DisplayFromStr::serialize_as(&display, serializer) + } + } + + #[cfg(feature = "serde")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] + impl<'de> serde_with::DeserializeAs<'de, [u8; Self::LENGTH]> for $base { + fn deserialize_as(deserializer: D) -> Result<[u8; Self::LENGTH], D::Error> + where + D: serde::Deserializer<'de>, + { + let array: $fromstr = serde_with::DisplayFromStr::deserialize_as(deserializer)?; + Ok(array.0) + } + } + + // TODO add tests + }; +} + +impl_base64_helper!(Base64Array32, Base64Display32, Base64FromStr32, 32); +impl_base64_helper!(Base64Array33, Base64Display33, Base64FromStr33, 33); +impl_base64_helper!(Base64Array48, Base64Display48, Base64FromStr48, 48); +impl_base64_helper!(Base64Array64, Base64Display64, Base64FromStr64, 64); +impl_base64_helper!(Base64Array96, Base64Display96, Base64FromStr96, 96); diff --git a/src/types/crypto/secp256k1.rs b/src/types/crypto/secp256k1.rs new file mode 100644 index 000000000..4d89133d7 --- /dev/null +++ b/src/types/crypto/secp256k1.rs @@ -0,0 +1,87 @@ +//! Implementation of secp256k1 public-key cryptogrophy. + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Secp256k1PrivateKey( + #[cfg_attr( + feature = "serde", + serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") + )] + [u8; Self::LENGTH], +); + +impl Secp256k1PrivateKey { + /// The length of an secp256k1 private key in bytes. + pub const LENGTH: usize = 32; +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Secp256k1PublicKey( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Secp256k1PublicKey { + /// The length of an secp256k1 public key in bytes. + pub const LENGTH: usize = 33; +} + +impl std::fmt::Display for Secp256k1PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display33(&self.0), f) + } +} + +impl std::fmt::Debug for Secp256k1PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secp256k1PublicKey") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Secp256k1Signature( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Secp256k1Signature { + /// The length of an secp256k1 signature key in bytes. + pub const LENGTH: usize = 64; +} + +impl std::fmt::Display for Secp256k1Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display64(&self.0), f) + } +} + +impl std::fmt::Debug for Secp256k1Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secp256k1Signature") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} diff --git a/src/types/crypto/secp256r1.rs b/src/types/crypto/secp256r1.rs new file mode 100644 index 000000000..b4922863b --- /dev/null +++ b/src/types/crypto/secp256r1.rs @@ -0,0 +1,87 @@ +//! Implementation of secp256r1 public-key cryptogrophy. + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Secp256r1PrivateKey( + #[cfg_attr( + feature = "serde", + serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") + )] + [u8; Self::LENGTH], +); + +impl Secp256r1PrivateKey { + /// The length of an secp256r1 private key in bytes. + pub const LENGTH: usize = 32; +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Secp256r1PublicKey( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Secp256r1PublicKey { + /// The length of an secp256r1 public key in bytes. + pub const LENGTH: usize = 33; +} + +impl std::fmt::Display for Secp256r1PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display33(&self.0), f) + } +} + +impl std::fmt::Debug for Secp256r1PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secp256r1PublicKey") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Secp256r1Signature( + #[cfg_attr( + feature = "serde", + serde( + with = "::serde_with::As::<::serde_with::IfIsHumanReadable>" + ) + )] + [u8; Self::LENGTH], +); + +impl Secp256r1Signature { + /// The length of an secp256r1 signature key in bytes. + pub const LENGTH: usize = 64; +} + +impl std::fmt::Display for Secp256r1Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&super::Base64Display64(&self.0), f) + } +} + +impl std::fmt::Debug for Secp256r1Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secp256r1Signature") + .field(&format_args!("\"{}\"", self)) + .finish() + } +} diff --git a/src/types/crypto/signature.rs b/src/types/crypto/signature.rs new file mode 100644 index 000000000..df8a25181 --- /dev/null +++ b/src/types/crypto/signature.rs @@ -0,0 +1,41 @@ +use super::Ed25519PublicKey; +use super::Ed25519Signature; +use super::Secp256k1PublicKey; +use super::Secp256k1Signature; +use super::Secp256r1PublicKey; +use super::Secp256r1Signature; + +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum SimpleSignature { + Ed25519 { + public_key: Ed25519PublicKey, + signature: Ed25519Signature, + }, + Secp256k1 { + public_key: Secp256k1PublicKey, + signature: Secp256k1Signature, + }, + Secp256r1 { + public_key: Secp256r1PublicKey, + signature: Secp256r1Signature, + }, +} + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +pub enum SignatureScheme { + ED25519 = 0x00, + Secp256k1 = 0x01, + Secp256r1 = 0x02, + MultiSig = 0x03, + BLS12381 = 0x04, // This is currently not supported for user addresses + ZkLoginAuthenticator = 0x05, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum UserSignature { + Simple(SimpleSignature), + // MultiSigLegacy, + // MultiSig, + // ZkLoginAuthenticator, +} diff --git a/src/types/mod.rs b/src/types/mod.rs index cfe426526..b9d3bddb7 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,9 +1,16 @@ mod address; +mod crypto; mod digest; mod gas; mod object_id; pub use address::Address; +pub use crypto::{ + Bls12381PrivateKey, Bls12381PublicKey, Bls12381Signature, Ed25519PrivateKey, Ed25519PublicKey, + Ed25519Signature, Secp256k1PrivateKey, Secp256k1PublicKey, Secp256k1Signature, + Secp256r1PrivateKey, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, + UserSignature, +}; pub use digest::{ CheckpointContentsDigest, CheckpointDigest, Digest, DigestParseError, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest,