Skip to content

Commit

Permalink
Use well-defined signature scheme enum instead of arbitrary byte flag (
Browse files Browse the repository at this point in the history
…MystenLabs#3737)

Co-authored-by: Chris Li <[email protected]>
  • Loading branch information
patrickkuo and 666lcz authored Aug 5, 2022
1 parent bfb41f2 commit 0b6b198
Show file tree
Hide file tree
Showing 17 changed files with 100 additions and 60 deletions.
16 changes: 8 additions & 8 deletions crates/sui-gateway/src/unit_tests/rpc_server_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ async fn test_public_transfer_object() -> Result<(), anyhow::Error> {
let signature = keystore.sign(address, &tx_bytes)?;

let tx_bytes = Base64::from_bytes(&tx_bytes);
let flag_bytes = Base64::from_bytes(&[signature.flag_byte()]);
let sig_scheme = signature.scheme();
let signature_bytes = Base64::from_bytes(signature.signature_bytes());
let pub_key = Base64::from_bytes(signature.public_key_bytes());

let tx_response: TransactionResponse = http_client
.execute_transaction(tx_bytes, flag_bytes, signature_bytes, pub_key)
.execute_transaction(tx_bytes, sig_scheme, signature_bytes, pub_key)
.await?;

let effect = tx_response.to_effect_response()?.effects;
Expand Down Expand Up @@ -100,12 +100,12 @@ async fn test_publish() -> Result<(), anyhow::Error> {
let signature = keystore.sign(address, &tx_bytes)?;

let tx_bytes = Base64::from_bytes(&tx_bytes);
let flag_bytes = Base64::from_bytes(&[signature.flag_byte()]);
let sig_scheme = signature.scheme();
let signature_bytes = Base64::from_bytes(signature.signature_bytes());
let pub_key = Base64::from_bytes(signature.public_key_bytes());

let tx_response: TransactionResponse = http_client
.execute_transaction(tx_bytes, flag_bytes, signature_bytes, pub_key)
.execute_transaction(tx_bytes, sig_scheme, signature_bytes, pub_key)
.await?;

let response = tx_response.to_publish_response()?;
Expand Down Expand Up @@ -150,12 +150,12 @@ async fn test_move_call() -> Result<(), anyhow::Error> {
let signature = keystore.sign(address, &tx_bytes)?;

let tx_bytes = Base64::from_bytes(&tx_bytes);
let flag_bytes = Base64::from_bytes(&[signature.flag_byte()]);
let sig_scheme = signature.scheme();
let signature_bytes = Base64::from_bytes(signature.signature_bytes());
let pub_key = Base64::from_bytes(signature.public_key_bytes());

let tx_response: TransactionResponse = http_client
.execute_transaction(tx_bytes, flag_bytes, signature_bytes, pub_key)
.execute_transaction(tx_bytes, sig_scheme, signature_bytes, pub_key)
.await?;

let effect = tx_response.to_effect_response()?.effects;
Expand Down Expand Up @@ -204,12 +204,12 @@ async fn test_get_transaction() -> Result<(), anyhow::Error> {
let signature = keystore.sign(address, &tx_bytes)?;

let tx_bytes = Base64::from_bytes(&tx_bytes);
let flag_bytes = Base64::from_bytes(&[signature.flag_byte()]);
let sig_scheme = signature.scheme();
let signature_bytes = Base64::from_bytes(signature.signature_bytes());
let pub_key = Base64::from_bytes(signature.public_key_bytes());

let response: TransactionResponse = http_client
.execute_transaction(tx_bytes, flag_bytes, signature_bytes, pub_key)
.execute_transaction(tx_bytes, sig_scheme, signature_bytes, pub_key)
.await?;

if let TransactionResponse::EffectResponse(effects) = response {
Expand Down
3 changes: 2 additions & 1 deletion crates/sui-json-rpc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use sui_json_rpc_types::{
};
use sui_open_rpc_macros::open_rpc;
use sui_types::base_types::{ObjectID, SuiAddress, TransactionDigest};
use sui_types::crypto::SignatureScheme;
use sui_types::sui_serde::Base64;

#[open_rpc(namespace = "sui", tag = "Gateway Transaction Execution API")]
Expand All @@ -23,7 +24,7 @@ pub trait RpcGatewayApi {
/// transaction data bytes, as base-64 encoded string
tx_bytes: Base64,
/// Flag of the signature scheme that is used.
flag: Base64,
sig_scheme: SignatureScheme,
/// transaction signature, as base-64 encoded string
signature: Base64,
/// signer's public key, as base-64 encoded string
Expand Down
6 changes: 4 additions & 2 deletions crates/sui-json-rpc/src/gateway_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use sui_json_rpc_types::{
TransactionBytes, TransactionEffectsResponse, TransactionResponse,
};
use sui_open_rpc::Module;
use sui_types::crypto::SignatureScheme;
use sui_types::sui_serde::Base64;
use sui_types::{
base_types::{ObjectID, SuiAddress, TransactionDigest},
Expand Down Expand Up @@ -71,13 +72,14 @@ impl RpcGatewayApiServer for RpcGatewayImpl {
async fn execute_transaction(
&self,
tx_bytes: Base64,
flag: Base64,
sig_scheme: SignatureScheme,
signature: Base64,
pub_key: Base64,
) -> RpcResult<TransactionResponse> {
let data = TransactionData::from_signable_bytes(&tx_bytes.to_vec()?)?;
let flag = vec![sig_scheme.flag()];
let signature = crypto::Signature::from_bytes(
&[&*flag.to_vec()?, &*signature.to_vec()?, &pub_key.to_vec()?].concat(),
&[&*flag, &*signature.to_vec()?, &pub_key.to_vec()?].concat(),
)
.map_err(|e| anyhow!(e))?;
let result = self
Expand Down
15 changes: 11 additions & 4 deletions crates/sui-open-rpc/spec/openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@
}
},
{
"name": "flag",
"name": "sig_scheme",
"description": "Flag of the signature scheme that is used.",
"required": true,
"schema": {
"$ref": "#/components/schemas/Base64"
"$ref": "#/components/schemas/SignatureScheme"
}
},
{
Expand Down Expand Up @@ -200,8 +200,8 @@
"value": "VHJhbnNhY3Rpb25EYXRhOjoAABdY7bkmDSqGyDbvwF8X5cWVJeQExGwtZKelsdk7LYZGgF2NKhIvzNsCAAAAAAAAACA7x9yXWrdfyGV5NTb2bmQYkFA2D2I9yIq7gwAYDN0Kj9BnwzQmQaTOqrLB29rQESsuPkj4xqk9BRZR/i5O764oE0klaDiQqUICAAAAAAAAACD2POST9RLwss+3xCoHzpEwy20FmjiNiGFTbLnFuBqajQEAAAAAAAAA6AMAAAAAAAA="
},
{
"name": "flag",
"value": "AA=="
"name": "sig_scheme",
"value": "ED25519"
},
{
"name": "signature",
Expand Down Expand Up @@ -2924,6 +2924,13 @@
}
]
},
"SignatureScheme": {
"type": "string",
"enum": [
"ED25519",
"Secp256k1"
]
},
"SplitCoinResponse": {
"type": "object",
"required": [
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-open-rpc/src/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl RpcExampleProvider {
"Execute an object transfer transaction",
vec![
("tx_bytes", json!(tx_bytes.tx_bytes)),
("flag", json!(Base64::from_bytes(&[signature.flag_byte()]))),
("sig_scheme", json!(signature.scheme())),
(
"signature",
json!(Base64::from_bytes(signature.signature_bytes())),
Expand Down
20 changes: 10 additions & 10 deletions crates/sui-open-rpc/src/generate_json_rpc_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use sui_json_rpc_types::{
};
use sui_types::base_types::{ObjectID, SuiAddress};
use sui_types::crypto::SuiSignature;
use sui_types::sui_serde::{Base64, Encoding};
use sui_types::sui_serde::Base64;
use sui_types::SUI_FRAMEWORK_ADDRESS;
use test_utils::network::{start_rpc_test_network, TestNetwork};

Expand Down Expand Up @@ -340,22 +340,22 @@ async fn create_error_response(
let signature = context
.keystore
.sign(&address, &response.tx_bytes.to_vec()?)?;
let flag_bytes = Base64::encode(&[signature.flag_byte()]);
let signature_byte = Base64::encode(signature.signature_bytes());
let pub_key = Base64::encode(signature.public_key_bytes());
let tx_data = response.tx_bytes.encoded();
let sig_scheme = signature.scheme();
let signature_byte = Base64::from_bytes(signature.signature_bytes());
let pub_key = Base64::from_bytes(signature.public_key_bytes());
let tx_data = response.tx_bytes;

let client = Client::new();
let request = Request::builder()
.uri(network.rpc_url.clone())
.method(Method::POST)
.header("Content-Type", "application/json")
.body(Body::from(format!(
"{{ \"jsonrpc\": \"2.0\",\"method\": \"sui_executeTransaction\",\"params\": [\"{}\", \"{}\", \"{}\", \"{}\"],\"id\": 1 }}",
tx_data,
flag_bytes,
signature_byte,
pub_key
"{{ \"jsonrpc\": \"2.0\",\"method\": \"sui_executeTransaction\",\"params\": [{}, {}, {}, {}],\"id\": 1 }}",
json![tx_data],
json![sig_scheme],
json![signature_byte],
json![pub_key]
)))?;

let res = client.request(request).await?;
Expand Down
4 changes: 2 additions & 2 deletions crates/sui-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,15 @@ impl SuiClient {
Ok(match &self {
Self::Http(c) => {
let tx_bytes = Base64::from_bytes(&tx.data.to_bytes());
let flag = Base64::from_bytes(&[tx.tx_signature.flag_byte()]);
let flag = tx.tx_signature.scheme();
let signature = Base64::from_bytes(tx.tx_signature.signature_bytes());
let pub_key = Base64::from_bytes(tx.tx_signature.public_key_bytes());
c.execute_transaction(tx_bytes, flag, signature, pub_key)
.await?
}
Self::Ws(c) => {
let tx_bytes = Base64::from_bytes(&tx.data.to_bytes());
let flag = Base64::from_bytes(&[tx.tx_signature.flag_byte()]);
let flag = tx.tx_signature.scheme();
let signature = Base64::from_bytes(tx.tx_signature.signature_bytes());
let pub_key = Base64::from_bytes(tx.tx_signature.public_key_bytes());
c.execute_transaction(tx_bytes, flag, signature, pub_key)
Expand Down
4 changes: 2 additions & 2 deletions crates/sui-types/src/base_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl TryFrom<Vec<u8>> for SuiAddress {
impl From<&AuthorityPublicKeyBytes> for SuiAddress {
fn from(pkb: &AuthorityPublicKeyBytes) -> Self {
let mut hasher = Sha3_256::default();
hasher.update(&[AuthorityPublicKey::FLAG]);
hasher.update(&[AuthorityPublicKey::SIGNATURE_SCHEME.flag()]);
hasher.update(pkb);
let g_arr = hasher.finalize();

Expand All @@ -198,7 +198,7 @@ impl From<&AuthorityPublicKeyBytes> for SuiAddress {
impl<T: SuiPublicKey> From<&T> for SuiAddress {
fn from(pk: &T) -> Self {
let mut hasher = Sha3_256::default();
hasher.update(&[T::FLAG]);
hasher.update(&[T::SIGNATURE_SCHEME.flag()]);
hasher.update(pk);
let g_arr = hasher.finalize();

Expand Down
38 changes: 27 additions & 11 deletions crates/sui-types/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ impl AsRef<[u8]> for Signature {

impl signature::Signature for Signature {
fn from_bytes(bytes: &[u8]) -> Result<Self, signature::Error> {
match bytes.first().ok_or_else(signature::Error::new)? {
x if x == &Ed25519SuiSignature::FLAG => {
match bytes.first() {
Some(x) if x == &Ed25519SuiSignature::SCHEME.flag() => {
Ok(<Ed25519SuiSignature as ToFromBytes>::from_bytes(bytes)
.map_err(|_| signature::Error::new())?
.into())
Expand All @@ -314,7 +314,7 @@ impl signature::Signature for Signature {

impl std::fmt::Debug for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
let flag = base64ct::Base64::encode_string(&[self.flag_byte()]);
let flag = base64ct::Base64::encode_string(&[self.scheme().flag()]);
let s = base64ct::Base64::encode_string(self.signature_bytes());
let p = base64ct::Base64::encode_string(self.public_key_bytes());
write!(f, "{flag}@{s}@{p}")?;
Expand Down Expand Up @@ -342,7 +342,7 @@ impl SuiSignatureInner for Ed25519SuiSignature {
}

impl SuiPublicKey for Ed25519PublicKey {
const FLAG: u8 = 0x00;
const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519;
}

impl AsRef<[u8]> for Ed25519SuiSignature {
Expand Down Expand Up @@ -393,7 +393,7 @@ impl SuiSignatureInner for Secp256k1SuiSignature {
// }

impl SuiPublicKey for Secp256k1PublicKey {
const FLAG: u8 = 0xed;
const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1;
}

impl AsRef<[u8]> for Secp256k1SuiSignature {
Expand Down Expand Up @@ -430,7 +430,7 @@ pub trait SuiSignatureInner: Sized + signature::Signature {
type KeyPair: KeypairTraits<PubKey = Self::PubKey, Sig = Self::Sig>;

const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1;
const FLAG: u8 = Self::PubKey::FLAG;
const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME;

fn get_verification_inputs(&self, author: SuiAddress) -> SuiResult<(Self::Sig, Self::PubKey)> {
// Is this signature emitted by the expected author?
Expand Down Expand Up @@ -463,7 +463,8 @@ pub trait SuiSignatureInner: Sized + signature::Signature {
})?;

let mut signature_bytes: Vec<u8> = Vec::new();
signature_bytes.extend_from_slice(&[<Self::PubKey as SuiPublicKey>::FLAG]);
signature_bytes
.extend_from_slice(&[<Self::PubKey as SuiPublicKey>::SIGNATURE_SCHEME.flag()]);
signature_bytes.extend_from_slice(sig.as_ref());
signature_bytes.extend_from_slice(kp.public().as_ref());
Self::from_bytes(&signature_bytes[..]).map_err(|err| SuiError::InvalidSignature {
Expand All @@ -473,14 +474,14 @@ pub trait SuiSignatureInner: Sized + signature::Signature {
}

pub trait SuiPublicKey: VerifyingKey {
const FLAG: u8;
const SIGNATURE_SCHEME: SignatureScheme;
}

#[enum_dispatch(Signature)]
pub trait SuiSignature: Sized + signature::Signature {
fn signature_bytes(&self) -> &[u8];
fn public_key_bytes(&self) -> &[u8];
fn flag_byte(&self) -> u8;
fn scheme(&self) -> SignatureScheme;

fn verify<T>(&self, value: &T, author: SuiAddress) -> SuiResult<()>
where
Expand Down Expand Up @@ -536,8 +537,8 @@ impl<S: SuiSignatureInner + Sized> SuiSignature for S {
&self.as_ref()[S::Sig::LENGTH + 1..]
}

fn flag_byte(&self) -> u8 {
S::PubKey::FLAG
fn scheme(&self) -> SignatureScheme {
S::PubKey::SIGNATURE_SCHEME
}
}

Expand Down Expand Up @@ -1002,3 +1003,18 @@ pub mod bcs_signable_test {
(obligation, idx)
}
}

#[derive(Deserialize, Serialize, JsonSchema, Debug)]
pub enum SignatureScheme {
ED25519,
Secp256k1,
}

impl SignatureScheme {
pub fn flag(&self) -> u8 {
match self {
SignatureScheme::ED25519 => 0x00,
SignatureScheme::Secp256k1 => 0xed,
}
}
}
11 changes: 9 additions & 2 deletions sdk/typescript/src/cryptography/publickey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export type PublicKeyData = {
export const PUBLIC_KEY_SIZE = 32;
export const TYPE_BYTE = 0x00;

export type SignatureScheme = 'ED25519' | 'Secp256k1';

const SIGNATURE_SCHEME_TO_FLAG = {
ED25519: 0x00,
Secp256k1: 0x01,
};

function isPublicKeyData(value: PublicKeyInitData): value is PublicKeyData {
return (value as PublicKeyData)._bn !== undefined;
}
Expand Down Expand Up @@ -108,9 +115,9 @@ export class PublicKey {
/**
* Return the Sui address associated with this public key
*/
toSuiAddress(): string {
toSuiAddress(scheme: SignatureScheme = 'ED25519'): string {
let tmp = new Uint8Array(PUBLIC_KEY_SIZE + 1);
tmp.set([TYPE_BYTE]);
tmp.set([SIGNATURE_SCHEME_TO_FLAG[scheme]]);
tmp.set(this.toBytes(), 1);
const hexHash = sha3_256(tmp);
const publicKeyBytes = new BN(hexHash, 16).toArray(undefined, 32);
Expand Down
11 changes: 9 additions & 2 deletions sdk/typescript/src/index.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Generated type guards for "index.ts".
* WARNING: Do not manually change this file.
*/
import { Ed25519KeypairData, Keypair, PublicKeyInitData, PublicKeyData, TransferObjectTransaction, TransferSuiTransaction, MergeCoinTransaction, SplitCoinTransaction, MoveCallTransaction, PublishTransaction, TxnDataSerializer, SignaturePubkeyPair, Signer, TransactionDigest, SuiAddress, ObjectOwner, SuiObjectRef, SuiObjectInfo, ObjectContentFields, MovePackageContent, SuiData, SuiMoveObject, SuiMovePackage, SuiObject, ObjectStatus, ObjectType, GetOwnedObjectsResponse, GetObjectDataResponse, ObjectDigest, ObjectId, SequenceNumber, TransferObject, SuiTransferSui, SuiChangeEpoch, TransactionKindName, SuiTransactionKind, TransactionData, EpochId, AuthorityQuorumSignInfo, CertifiedTransaction, GasCostSummary, ExecutionStatusType, ExecutionStatus, OwnedObjectRef, TransactionEffects, TransactionEffectsResponse, GatewayTxSeqNumber, GetTxnDigestsResponse, MoveCall, SuiJsonValue, EmptySignInfo, AuthorityName, AuthoritySignature, TransactionBytes, MergeCoinResponse, SplitCoinResponse, PublishResponse, SuiPackage, TransactionResponse } from "./index";
import { Ed25519KeypairData, Keypair, PublicKeyInitData, PublicKeyData, SignatureScheme, TransferObjectTransaction, TransferSuiTransaction, MergeCoinTransaction, SplitCoinTransaction, MoveCallTransaction, PublishTransaction, TxnDataSerializer, SignaturePubkeyPair, Signer, TransactionDigest, SuiAddress, ObjectOwner, SuiObjectRef, SuiObjectInfo, ObjectContentFields, MovePackageContent, SuiData, SuiMoveObject, SuiMovePackage, SuiObject, ObjectStatus, ObjectType, GetOwnedObjectsResponse, GetObjectDataResponse, ObjectDigest, ObjectId, SequenceNumber, TransferObject, SuiTransferSui, SuiChangeEpoch, TransactionKindName, SuiTransactionKind, TransactionData, EpochId, AuthorityQuorumSignInfo, CertifiedTransaction, GasCostSummary, ExecutionStatusType, ExecutionStatus, OwnedObjectRef, TransactionEffects, TransactionEffectsResponse, GatewayTxSeqNumber, GetTxnDigestsResponse, MoveCall, SuiJsonValue, EmptySignInfo, AuthorityName, AuthoritySignature, TransactionBytes, MergeCoinResponse, SplitCoinResponse, PublishResponse, SuiPackage, TransactionResponse } from "./index";
import { BN } from "bn.js";
import { Base64DataBuffer } from "./serialization/base64";
import { PublicKey } from "./cryptography/publickey";
Expand Down Expand Up @@ -53,6 +53,13 @@ export function isPublicKeyData(obj: any, _argumentName?: string): obj is Public
)
}

export function isSignatureScheme(obj: any, _argumentName?: string): obj is SignatureScheme {
return (
(obj === "ED25519" ||
obj === "Secp256k1")
)
}

export function isTransferObjectTransaction(obj: any, _argumentName?: string): obj is TransferObjectTransaction {
return (
(obj !== null &&
Expand Down Expand Up @@ -164,7 +171,7 @@ export function isSignaturePubkeyPair(obj: any, _argumentName?: string): obj is
(obj !== null &&
typeof obj === "object" ||
typeof obj === "function") &&
obj.flag instanceof Base64DataBuffer &&
isSignatureScheme(obj.signatureScheme) as boolean &&
obj.signature instanceof Base64DataBuffer &&
obj.pubKey instanceof PublicKey
)
Expand Down
Loading

0 comments on commit 0b6b198

Please sign in to comment.