Skip to content

Commit

Permalink
crypto: Use pubkey as bytes (MystenLabs#11596)
Browse files Browse the repository at this point in the history
## Description 

Closes MystenLabs#10402
Closes MystenLabs#9994

Depends on MystenLabs/fastcrypto#577
## Test Plan 

Existing tests 

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
joyqvq authored May 8, 2023
1 parent 1999535 commit 21da013
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 98 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ move-prover-boogie-backend = { path = "external-crates/move/move-prover/boogie-b
move-stackless-bytecode = { path = "external-crates/move/move-prover/bytecode" }
move-symbol-pool = { path = "external-crates/move/move-symbol-pool" }

fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "c96c7422cf6a120d5bad921523bc6587c8160988" }
fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "c96c7422cf6a120d5bad921523bc6587c8160988", package = "fastcrypto-zkp" }
fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "c27e87fb539c88b6fe1eac7dd82561254bb1e07a" }
fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "c27e87fb539c88b6fe1eac7dd82561254bb1e07a", package = "fastcrypto-zkp" }

# anemo dependencies
anemo = { git = "https://github.com/mystenlabs/anemo.git", rev = "77f225832e361d068318073a6e06ec0310a16891" }
Expand Down
7 changes: 3 additions & 4 deletions crates/sui-rosetta/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::str::FromStr;
use axum::response::{IntoResponse, Response};
use axum::Json;
use fastcrypto::encoding::Hex;
use fastcrypto::traits::ToFromBytes;
use serde::de::Error as DeError;
use serde::{Deserialize, Serializer};
use serde::{Deserializer, Serialize};
Expand Down Expand Up @@ -329,15 +328,15 @@ impl From<SuiPublicKey> for PublicKey {
fn from(pk: SuiPublicKey) -> Self {
match pk {
SuiPublicKey::Ed25519(k) => PublicKey {
hex_bytes: Hex::from_bytes(k.as_bytes()),
hex_bytes: Hex::from_bytes(&k.0),
curve_type: CurveType::Edwards25519,
},
SuiPublicKey::Secp256k1(k) => PublicKey {
hex_bytes: Hex::from_bytes(k.as_bytes()),
hex_bytes: Hex::from_bytes(&k.0),
curve_type: CurveType::Secp256k1,
},
SuiPublicKey::Secp256r1(k) => PublicKey {
hex_bytes: Hex::from_bytes(k.as_bytes()),
hex_bytes: Hex::from_bytes(&k.0),
curve_type: CurveType::Secp256r1,
},
}
Expand Down
104 changes: 48 additions & 56 deletions crates/sui-types/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ use fastcrypto::bls12381::min_sig::{
BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature,
};
use fastcrypto::ed25519::{
Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, Ed25519SignatureAsBytes,
Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, Ed25519Signature,
Ed25519SignatureAsBytes,
};
use fastcrypto::secp256k1::{
Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1Signature, Secp256k1SignatureAsBytes,
Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature,
Secp256k1SignatureAsBytes,
};
use fastcrypto::secp256r1::{
Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1Signature, Secp256r1SignatureAsBytes,
Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature,
Secp256r1SignatureAsBytes,
};
pub use fastcrypto::traits::KeyPair as KeypairTraits;
pub use fastcrypto::traits::{
Expand All @@ -30,7 +33,7 @@ use serde::{Deserialize, Deserializer, Serialize};
use serde_with::{serde_as, Bytes};
use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
use std::collections::BTreeMap;
use std::fmt::{self, Display, Formatter};
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use strum::EnumString;
Expand Down Expand Up @@ -133,22 +136,12 @@ pub enum SuiKeyPair {
Secp256r1(Secp256r1KeyPair),
}

#[derive(Clone, PartialEq, Eq, From, JsonSchema)]
pub enum PublicKey {
#[schemars(with = "Base64")]
Ed25519(Ed25519PublicKey),
#[schemars(with = "Base64")]
Secp256k1(Secp256k1PublicKey),
#[schemars(with = "Base64")]
Secp256r1(Secp256r1PublicKey),
}

impl SuiKeyPair {
pub fn public(&self) -> PublicKey {
match self {
SuiKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().clone()),
SuiKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().clone()),
SuiKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().clone()),
SuiKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()),
SuiKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().into()),
SuiKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().into()),
}
}
}
Expand Down Expand Up @@ -176,17 +169,16 @@ impl EncodeDecodeBase64 for SuiKeyPair {
/// Encode a SuiKeyPair as `flag || privkey` in Base64. Note that the pubkey is not encoded.
fn encode_base64(&self) -> String {
let mut bytes: Vec<u8> = Vec::new();
bytes.push(self.public().flag());

match self {
SuiKeyPair::Ed25519(kp) => {
bytes.push(self.public().flag());
bytes.extend_from_slice(kp.as_bytes());
}
SuiKeyPair::Secp256k1(kp) => {
bytes.push(self.public().flag());
bytes.extend_from_slice(kp.as_bytes());
}
SuiKeyPair::Secp256r1(kp) => {
bytes.push(self.public().flag());
bytes.extend_from_slice(kp.as_bytes());
}
}
Expand Down Expand Up @@ -241,12 +233,19 @@ impl<'de> Deserialize<'de> for SuiKeyPair {
}
}

#[derive(Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum PublicKey {
Ed25519(Ed25519PublicKeyAsBytes),
Secp256k1(Secp256k1PublicKeyAsBytes),
Secp256r1(Secp256r1PublicKeyAsBytes),
}

impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
match self {
PublicKey::Ed25519(pk) => pk.as_ref(),
PublicKey::Secp256k1(pk) => pk.as_ref(),
PublicKey::Secp256r1(pk) => pk.as_ref(),
PublicKey::Ed25519(pk) => &pk.0,
PublicKey::Secp256k1(pk) => &pk.0,
PublicKey::Secp256r1(pk) => &pk.0,
}
}
}
Expand All @@ -263,21 +262,21 @@ impl EncodeDecodeBase64 for PublicKey {
let bytes = Base64::decode(value).map_err(|e| eyre!("{}", e.to_string()))?;
match bytes.first() {
Some(x) => {
if x == &<Ed25519PublicKey as SuiPublicKey>::SIGNATURE_SCHEME.flag() {
let pk = Ed25519PublicKey::from_bytes(
if x == &SignatureScheme::ED25519.flag() {
let pk: Ed25519PublicKey = Ed25519PublicKey::from_bytes(
bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
)?;
Ok(PublicKey::Ed25519(pk))
} else if x == &<Secp256k1PublicKey as SuiPublicKey>::SIGNATURE_SCHEME.flag() {
Ok(PublicKey::Ed25519((&pk).into()))
} else if x == &SignatureScheme::Secp256k1.flag() {
let pk = Secp256k1PublicKey::from_bytes(
bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
)?;
Ok(PublicKey::Secp256k1(pk))
} else if x == &<Secp256r1PublicKey as SuiPublicKey>::SIGNATURE_SCHEME.flag() {
Ok(PublicKey::Secp256k1((&pk).into()))
} else if x == &SignatureScheme::Secp256r1.flag() {
let pk = Secp256r1PublicKey::from_bytes(
bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?,
)?;
Ok(PublicKey::Secp256r1(pk))
Ok(PublicKey::Secp256r1((&pk).into()))
} else {
Err(eyre!("Invalid flag byte"))
}
Expand Down Expand Up @@ -309,35 +308,25 @@ impl<'de> Deserialize<'de> for PublicKey {
}
}

impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.encode_base64())
}
}

impl PublicKey {
pub fn flag(&self) -> u8 {
match self {
PublicKey::Ed25519(_) => Ed25519SuiSignature::SCHEME.flag(),
PublicKey::Secp256k1(_) => Secp256k1SuiSignature::SCHEME.flag(),
PublicKey::Secp256r1(_) => Secp256r1SuiSignature::SCHEME.flag(),
}
self.scheme().flag()
}

pub fn try_from_bytes(
curve: SignatureScheme,
key_bytes: &[u8],
) -> Result<PublicKey, eyre::Report> {
match curve {
SignatureScheme::ED25519 => {
Ok(PublicKey::Ed25519(Ed25519PublicKey::from_bytes(key_bytes)?))
}
SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1(Secp256k1PublicKey::from_bytes(
key_bytes,
)?)),
SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1(Secp256r1PublicKey::from_bytes(
key_bytes,
)?)),
SignatureScheme::ED25519 => Ok(PublicKey::Ed25519(
(&Ed25519PublicKey::from_bytes(key_bytes)?).into(),
)),
SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1(
(&Secp256k1PublicKey::from_bytes(key_bytes)?).into(),
)),
SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1(
(&Secp256r1PublicKey::from_bytes(key_bytes)?).into(),
)),
_ => Err(eyre!("Unsupported curve")),
}
}
Expand Down Expand Up @@ -728,16 +717,19 @@ impl Signature {
let bytes = self.public_key_bytes();
match self.scheme() {
SignatureScheme::ED25519 => Ok(PublicKey::Ed25519(
Ed25519PublicKey::from_bytes(bytes)
.map_err(|_| SuiError::KeyConversionError("Cannot parse pk".to_string()))?,
(&Ed25519PublicKey::from_bytes(bytes)
.map_err(|_| SuiError::KeyConversionError("Cannot parse pk".to_string()))?)
.into(),
)),
SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1(
Secp256k1PublicKey::from_bytes(bytes)
.map_err(|_| SuiError::KeyConversionError("Cannot parse pk".to_string()))?,
(&Secp256k1PublicKey::from_bytes(bytes)
.map_err(|_| SuiError::KeyConversionError("Cannot parse pk".to_string()))?)
.into(),
)),
SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1(
Secp256r1PublicKey::from_bytes(bytes)
.map_err(|_| SuiError::KeyConversionError("Cannot parse pk".to_string()))?,
(&Secp256r1PublicKey::from_bytes(bytes)
.map_err(|_| SuiError::KeyConversionError("Cannot parse pk".to_string()))?)
.into(),
)),
_ => Err(SuiError::UnsupportedFeatureError {
error: "Unsupported signature scheme in MultiSig".to_string(),
Expand Down
42 changes: 26 additions & 16 deletions crates/sui-types/src/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ impl AuthenticatorTrait for MultiSig {
});
}
}

if weight_sum >= self.multisig_pk.threshold {
Ok(())
} else {
Expand All @@ -193,23 +192,31 @@ impl MultiSig {
full_sigs: Vec<Signature>,
multisig_pk: MultiSigPublicKey,
) -> Result<Self, SuiError> {
if full_sigs.len() > multisig_pk.pk_map.len()
|| multisig_pk.pk_map.len() > MAX_SIGNER_IN_MULTISIG
|| full_sigs.is_empty()
|| multisig_pk.pk_map.is_empty()
{
multisig_pk
.validate()
.map_err(|_| SuiError::InvalidSignature {
error: "Invalid multisig public key".to_string(),
})?;

if full_sigs.len() > multisig_pk.pk_map.len() || full_sigs.is_empty() {
return Err(SuiError::InvalidSignature {
error: "Invalid number of signatures".to_string(),
});
}
let mut bitmap = RoaringBitmap::new();
let mut sigs = Vec::with_capacity(full_sigs.len());
for s in full_sigs {
bitmap.insert(multisig_pk.get_index(s.to_public_key()?).ok_or(
let pk = s.to_public_key()?;
let inserted = bitmap.insert(multisig_pk.get_index(&pk).ok_or(
SuiError::IncorrectSigner {
error: "pk does not exist".to_string(),
error: format!("pk does not exist: {:?}", pk),
},
)?);
if !inserted {
return Err(SuiError::InvalidSignature {
error: "Duplicate sigature".to_string(),
});
}
sigs.push(s.to_compressed()?);
}
Ok(MultiSig {
Expand Down Expand Up @@ -257,7 +264,7 @@ impl MultiSigPublicKey {
< threshold
{
return Err(SuiError::InvalidSignature {
error: "Invalid number of public keys".to_string(),
error: "Invalid multisig public key construction".to_string(),
});
}
Ok(MultiSigPublicKey {
Expand All @@ -266,8 +273,11 @@ impl MultiSigPublicKey {
})
}

pub fn get_index(&self, pk: PublicKey) -> Option<u32> {
self.pk_map.iter().position(|x| x.0 == pk).map(|x| x as u32)
pub fn get_index(&self, pk: &PublicKey) -> Option<u32> {
self.pk_map
.iter()
.position(|x| &x.0 == pk)
.map(|x| x as u32)
}

pub fn threshold(&self) -> &ThresholdUnit {
Expand All @@ -279,12 +289,12 @@ impl MultiSigPublicKey {
}

pub fn validate(&self) -> Result<(), FastCryptoError> {
let pk_map = self.pubkeys();
if self.threshold == 0
|| self.pubkeys().is_empty()
|| self.pubkeys().len() > MAX_SIGNER_IN_MULTISIG
|| self.pubkeys().iter().any(|(_pk, weight)| *weight == 0)
|| self
.pubkeys()
|| pk_map.is_empty()
|| pk_map.len() > MAX_SIGNER_IN_MULTISIG
|| pk_map.iter().any(|(_pk, weight)| *weight == 0)
|| pk_map
.iter()
.map(|(_pk, weight)| *weight as ThresholdUnit)
.sum::<ThresholdUnit>()
Expand Down
12 changes: 11 additions & 1 deletion crates/sui-types/src/unit_tests/crypto_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ use proptest::collection;
use proptest::prelude::*;

#[test]
fn temp_test() {
fn serde_pubkey() {
let skp = SuiKeyPair::Ed25519(get_key_pair().1);
let mut bytes = Vec::new();
bytes.extend_from_slice(&[skp.public().flag()]);
bytes.extend_from_slice(skp.public().as_ref());
let ser = serde_json::to_string(&skp.public()).unwrap();
assert_eq!(ser, format!("\"{}\"", Base64::encode(&bytes)));
}

#[test]
fn serde_round_trip_authority_quorum_sign_info() {
let info = AuthorityQuorumSignInfo::<true> {
epoch: 0,
signature: Default::default(),
Expand Down
Loading

0 comments on commit 21da013

Please sign in to comment.