Skip to content

Commit

Permalink
crypto: sui-keys crate for keystore and key derivation (MystenLabs#5363)
Browse files Browse the repository at this point in the history
  • Loading branch information
joyqvq authored Oct 21, 2022
1 parent 5229858 commit e76d745
Show file tree
Hide file tree
Showing 39 changed files with 287 additions and 160 deletions.
35 changes: 27 additions & 8 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"crates/sui-json",
"crates/sui-json-rpc",
"crates/sui-json-rpc-types",
"crates/sui-keys",
"crates/sui-macros",
"crates/sui-network",
"crates/sui-node",
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-benchmark/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ sui-core = { path = "../sui-core" }
sui-config = { path = "../sui-config" }
sui-network = { path = "../sui-network" }
sui-types = { path = "../sui-types" }
sui-sdk = { path = "../sui-sdk" }
sui-keys = { path = "../sui-keys" }
sui-node = { path = "../sui-node" }
sui-json-rpc-types = { path = "../sui-json-rpc-types" }

Expand Down
2 changes: 1 addition & 1 deletion crates/sui-benchmark/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use sui_sdk::crypto::{AccountKeystore, FileBasedKeystore};
use sui_keys::keystore::{AccountKeystore, FileBasedKeystore};
use sui_types::{
base_types::SuiAddress,
crypto::{AccountKeyPair, KeypairTraits, SuiKeyPair},
Expand Down
1 change: 1 addition & 0 deletions crates/sui-cluster-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ sui-swarm = { path = "../sui-swarm" }
sui = { path = "../sui" }
sui-json-rpc-types= { path = "../sui-json-rpc-types" }
sui-sdk = { path = "../sui-sdk" }
sui-keys = { path = "../sui-keys" }
sui-types = { path = "../sui-types" }
sui-core = { path = "../sui-core" }
sui-json = { path = "../sui-json" }
Expand Down
4 changes: 1 addition & 3 deletions crates/sui-cluster-test/src/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use sui::config::SuiClientConfig;
use sui_config::genesis_config::GenesisConfig;
use sui_config::Config;
use sui_config::SUI_KEYSTORE_FILENAME;
use sui_sdk::crypto::AccountKeystore;
use sui_sdk::crypto::FileBasedKeystore;
use sui_sdk::crypto::Keystore;
use sui_keys::keystore::{AccountKeystore, FileBasedKeystore, Keystore};
use sui_sdk::ClientType;
use sui_swarm::memory::Swarm;
use sui_types::base_types::SuiAddress;
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-cluster-test/src/wallet_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::cluster::new_wallet_context_from_cluster;

use super::Cluster;
use sui::client_commands::WalletContext;
use sui_sdk::crypto::AccountKeystore;
use sui_keys::keystore::AccountKeystore;
use sui_sdk::SuiClient;
use sui_types::base_types::SuiAddress;
use sui_types::crypto::{KeypairTraits, Signature};
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-faucet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sui-node = { path = "../sui-node" }
sui-json-rpc-types= { path = "../sui-json-rpc-types" }
sui-types = { path = "../sui-types" }
sui-config = { path = "../sui-config" }
sui-sdk = { path = "../sui-sdk" }
sui-keys = { path = "../sui-keys" }
telemetry-subscribers.workspace = true
workspace-hack.workspace = true

Expand Down
2 changes: 1 addition & 1 deletion crates/sui-faucet/src/faucet/simple_faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use sui::client_commands::{SuiClientCommands, WalletContext};
use sui_json_rpc_types::{
SuiExecutionStatus, SuiObjectRead, SuiTransactionKind, SuiTransactionResponse, SuiTransferSui,
};
use sui_sdk::crypto::AccountKeystore;
use sui_keys::keystore::AccountKeystore;
use sui_types::object::Owner;
use sui_types::{
base_types::{ObjectID, SuiAddress, TransactionDigest},
Expand Down
1 change: 1 addition & 0 deletions crates/sui-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ sui-json = { path = "../sui-json" }
sui-json-rpc = { path = "../sui-json-rpc" }
sui-json-rpc-types= { path = "../sui-json-rpc-types" }
sui-node = { path = "../sui-node" }
sui-keys = { path = "../sui-keys" }


mysten-network.workspace = true
Expand Down
4 changes: 1 addition & 3 deletions crates/sui-gateway/src/unit_tests/rpc_server_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ use sui_json_rpc::api::{
RpcGatewayApiClient, RpcReadApiClient, RpcTransactionBuilderClient, WalletSyncApiClient,
};
use sui_json_rpc_types::{GetObjectDataResponse, SuiTransactionResponse, TransactionBytes};
use sui_sdk::crypto::AccountKeystore;
use sui_sdk::crypto::FileBasedKeystore;
use sui_sdk::crypto::Keystore;
use sui_keys::keystore::{AccountKeystore, FileBasedKeystore, Keystore};
use sui_sdk::SuiClient;
use sui_types::base_types::ObjectID;
use sui_types::base_types::TransactionDigest;
Expand Down
26 changes: 26 additions & 0 deletions crates/sui-keys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "sui-keys"
version = "0.0.0"
authors = ["Mysten Labs <build@mystenlabs.com>"]
license = "Apache-2.0"
publish = false
edition = "2021"

[dependencies]
anyhow = "1.0.64"
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.83"
signature = "1.6.0"
rand = "0.8.5"
tiny-bip39 = "1.0.0"
bip32 = "0.4.0"
slip10_ed25519 = "0.1.3"
fastcrypto = { workspace = true, features = ["copy_key"] }

sui-types = { path = "../sui-types" }

workspace-hack.workspace = true

[dev-dependencies]
tempfile = "3.3.0"
sha3 = "0.10.4"
120 changes: 120 additions & 0 deletions crates/sui-keys/src/key_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use bip32::{ChildNumber, DerivationPath, XPrv};
use fastcrypto::ed25519::Ed25519KeyPair;
use fastcrypto::{
ed25519::Ed25519PrivateKey,
secp256k1::{Secp256k1KeyPair, Secp256k1PrivateKey},
traits::{KeyPair, ToFromBytes},
};
use slip10_ed25519::derive_ed25519_private_key;
use sui_types::{
base_types::SuiAddress,
crypto::{SignatureScheme, SuiKeyPair},
error::SuiError,
};

pub const DERIVATION_PATH_COIN_TYPE: u32 = 784;
pub const DERVIATION_PATH_PURPOSE_ED25519: u32 = 44;
pub const DERVIATION_PATH_PURPOSE_SECP256K1: u32 = 54;

/// Ed25519 follows SLIP-0010 using hardened path: m/44'/784'/0'/0'/{index}'
/// Secp256k1 follows BIP-32 using path where the first 3 levels are hardened: m/54'/784'/0'/0/{index}
/// Note that the purpose for Secp256k1 is registered as 54, to differentiate from Ed25519 with purpose 44.
pub fn derive_key_pair_from_path(
seed: &[u8],
derivation_path: Option<DerivationPath>,
key_scheme: &SignatureScheme,
) -> Result<(SuiAddress, SuiKeyPair), SuiError> {
let path = validate_path(key_scheme, derivation_path)?;
match key_scheme {
SignatureScheme::ED25519 => {
let indexes = path.into_iter().map(|i| i.into()).collect::<Vec<_>>();
let derived = derive_ed25519_private_key(seed, &indexes);
let sk = Ed25519PrivateKey::from_bytes(&derived)
.map_err(|e| SuiError::SignatureKeyGenError(e.to_string()))?;
let kp = Ed25519KeyPair::from(sk);
Ok((kp.public().into(), SuiKeyPair::Ed25519SuiKeyPair(kp)))
}
SignatureScheme::Secp256k1 => {
let child_xprv = XPrv::derive_from_path(seed, &path)
.map_err(|e| SuiError::SignatureKeyGenError(e.to_string()))?;
let kp = Secp256k1KeyPair::from(
Secp256k1PrivateKey::from_bytes(child_xprv.private_key().to_bytes().as_slice())
.unwrap(),
);
Ok((kp.public().into(), SuiKeyPair::Secp256k1SuiKeyPair(kp)))
}
SignatureScheme::BLS12381 => Err(SuiError::UnsupportedFeatureError {
error: "BLS is not supported for user key derivation".to_string(),
}),
}
}

pub fn validate_path(
key_scheme: &SignatureScheme,
path: Option<DerivationPath>,
) -> Result<DerivationPath, SuiError> {
match key_scheme {
SignatureScheme::ED25519 => {
match path {
Some(p) => {
// The derivation path must be hardened at all levels with purpose = 44, coin_type = 784
if let &[purpose, coin_type, account, change, address] = p.as_ref() {
if purpose
== ChildNumber::new(DERVIATION_PATH_PURPOSE_ED25519, true).unwrap()
&& coin_type
== ChildNumber::new(DERIVATION_PATH_COIN_TYPE, true).unwrap()
&& account.is_hardened()
&& change.is_hardened()
&& address.is_hardened()
{
Ok(p)
} else {
Err(SuiError::SignatureKeyGenError("Invalid path".to_string()))
}
} else {
Err(SuiError::SignatureKeyGenError("Invalid path".to_string()))
}
}
None => Ok(format!(
"m/{DERVIATION_PATH_PURPOSE_ED25519}'/{DERIVATION_PATH_COIN_TYPE}'/0'/0'/0'"
)
.parse()
.unwrap()),
}
}
SignatureScheme::Secp256k1 => {
match path {
Some(p) => {
// The derivation path must be hardened at first 3 levels with purpose = 54, coin_type = 784
if let &[purpose, coin_type, account, change, address] = p.as_ref() {
if purpose
== ChildNumber::new(DERVIATION_PATH_PURPOSE_SECP256K1, true).unwrap()
&& coin_type
== ChildNumber::new(DERIVATION_PATH_COIN_TYPE, true).unwrap()
&& account.is_hardened()
&& !change.is_hardened()
&& !address.is_hardened()
{
Ok(p)
} else {
Err(SuiError::SignatureKeyGenError("Invalid path".to_string()))
}
} else {
Err(SuiError::SignatureKeyGenError("Invalid path".to_string()))
}
}
None => Ok(format!(
"m/{DERVIATION_PATH_PURPOSE_SECP256K1}'/{DERIVATION_PATH_COIN_TYPE}'/0'/0/0"
)
.parse()
.unwrap()),
}
}
SignatureScheme::BLS12381 => Err(SuiError::UnsupportedFeatureError {
error: "BLS is not supported for user key derivation".to_string(),
}),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ use std::path::{Path, PathBuf};

use sui_types::base_types::SuiAddress;
use sui_types::crypto::{
derive_key_pair_from_path, enum_dispatch, get_key_pair_from_rng, EncodeDecodeBase64, PublicKey,
Signature, SignatureScheme, SuiKeyPair,
enum_dispatch, get_key_pair_from_rng, EncodeDecodeBase64, PublicKey, Signature,
SignatureScheme, SuiKeyPair,
};

use crate::key_derive::derive_key_pair_from_path;

#[derive(Serialize, Deserialize)]
#[enum_dispatch(AccountKeystore)]
pub enum Keystore {
Expand Down
5 changes: 5 additions & 0 deletions crates/sui-keys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

pub mod key_derive;
pub mod keystore;
Loading

0 comments on commit e76d745

Please sign in to comment.