-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add forc-crypto plugin exposing a CLI for common-use cryptographic op…
…erations Fixes #4318 Supported algorithms: * [x] keccak256 * [x] sha256 * [x] bech32-to-hex * [x] hex-to-bech32 * [x] new-key * [x] new-parse secret
- Loading branch information
Showing
13 changed files
with
518 additions
and
29 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# forc crypto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
[package] | ||
name = "forc-crypto" | ||
version = "0.46.0" | ||
description = "A `forc` plugin for handling various cryptographic operations and conversions." | ||
authors.workspace = true | ||
edition.workspace = true | ||
homepage.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
|
||
|
||
[dependencies] | ||
anyhow = "1.0.75" | ||
async-trait = "0.1.58" | ||
clap = { version = "3", features = ["derive", "env"] } | ||
forc-tracing = { version = "0.46.0", path = "../../forc-tracing" } | ||
fuel-core-types = { version = "0.20.5" } | ||
fuel-crypto = { workspace = true } | ||
fuels-core = { workspace = true } | ||
futures = "0.3" | ||
hex = "0.4.3" | ||
libp2p-identity = { version = "0.2.4", features = ["secp256k1", "peerid"] } | ||
rand = "0.8" | ||
serde = "1.0" | ||
serde_json = "1" | ||
sha3 = "0.10.8" | ||
tokio = { version = "1.8", features = ["macros", "rt-multi-thread", "process"] } | ||
tracing = "0.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use anyhow::anyhow; | ||
use fuel_crypto::fuel_types::Address; | ||
use fuels_core::types::bech32::Bech32Address; | ||
use std::str::{from_utf8, FromStr}; | ||
|
||
/// Takes a valid address in any supported format and returns them in all | ||
/// supported format. This is meant to be a tool that can be used to convert any | ||
/// address format to all other formats | ||
pub fn dump_address<T: AsRef<[u8]>>(data: T) -> anyhow::Result<String> { | ||
let bytes_32: Result<[u8; 32], _> = data.as_ref().try_into(); | ||
let (bech32, addr) = match bytes_32 { | ||
Ok(bytes) => ( | ||
Bech32Address::from(Address::from(bytes)), | ||
Address::from(bytes), | ||
), | ||
Err(_) => handle_string_conversion(data)?, | ||
}; | ||
|
||
Ok(format!("Bench32: {}\nAddress: 0x{}\n", bech32, addr)) | ||
} | ||
|
||
fn handle_string_conversion<T: AsRef<[u8]>>(data: T) -> anyhow::Result<(Bech32Address, Address)> { | ||
let addr = from_utf8(data.as_ref())?; | ||
if let Ok(bech32) = Bech32Address::from_str(addr) { | ||
Ok((bech32.clone(), Address::from(bech32))) | ||
} else if let Ok(addr) = Address::from_str(addr) { | ||
Ok((Bech32Address::from(addr), addr)) | ||
} else { | ||
Err(anyhow!("{} cannot be parsed to a valid address", addr)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
use anyhow::{anyhow, Result}; | ||
use std::{ | ||
fs::read, | ||
path::PathBuf, | ||
str::{from_utf8, FromStr}, | ||
}; | ||
|
||
#[derive(Clone, Debug, PartialEq)] | ||
pub enum Content { | ||
Path(PathBuf, Vec<u8>), | ||
Binary(Vec<u8>), | ||
} | ||
|
||
impl Content { | ||
pub fn from_hex_or_utf8(input: Vec<u8>) -> Result<Vec<u8>> { | ||
if let Ok(text) = from_utf8(&input) { | ||
let text = text.trim(); | ||
if let Some(text) = text.strip_prefix("0x") { | ||
if let Ok(bin) = hex::decode(text) { | ||
return Ok(bin); | ||
} | ||
} | ||
Ok(text.as_bytes().to_vec()) | ||
} else { | ||
Err(anyhow!( | ||
"{:?} is not a valid UTF-8 string nor a valid hex string", | ||
input | ||
)) | ||
} | ||
} | ||
} | ||
|
||
impl FromStr for Content { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { | ||
let path = PathBuf::from(s); | ||
match read(&path) { | ||
Ok(content) => Ok(Content::Path(path, Self::from_hex_or_utf8(content)?)), | ||
Err(_) => Ok(Content::Binary(Self::from_hex_or_utf8( | ||
s.as_bytes().to_vec(), | ||
)?)), | ||
} | ||
} | ||
} | ||
|
||
impl AsRef<[u8]> for Content { | ||
fn as_ref(&self) -> &[u8] { | ||
match self { | ||
Content::Path(_, content) => content.as_ref(), | ||
Content::Binary(raw) => raw.as_ref(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
use sha3::{Digest, Keccak256}; | ||
|
||
/// Hashes a given data using Keccak256 | ||
pub fn hash<T: AsRef<[u8]>>(data: T) -> anyhow::Result<Vec<u8>> { | ||
let mut hasher = Keccak256::new(); | ||
hasher.update(data); | ||
Ok(hasher.finalize().to_vec()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use clap::ValueEnum; | ||
|
||
pub mod new_key; | ||
pub mod parse_secret; | ||
|
||
pub const BLOCK_PRODUCTION: &str = "block_production"; | ||
pub const P2P: &str = "p2p"; | ||
|
||
#[derive(Clone, Debug, Default, ValueEnum)] | ||
#[clap(rename_all = "snake_case")] | ||
pub enum KeyType { | ||
#[default] | ||
BlockProduction, | ||
Peering, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
//! This file is migrated from https://github.com/FuelLabs/fuel-core/blob/master/bin/keygen/src/keygen.rs | ||
use super::{KeyType, BLOCK_PRODUCTION, P2P}; | ||
use anyhow::Result; | ||
use fuel_core_types::{ | ||
fuel_crypto::{ | ||
rand::{prelude::StdRng, SeedableRng}, | ||
SecretKey, | ||
}, | ||
fuel_tx::Input, | ||
}; | ||
use libp2p_identity::{secp256k1, Keypair, PeerId}; | ||
use serde_json::json; | ||
use std::ops::Deref; | ||
|
||
/// Generate a random new secret & public key in the format expected by fuel-core | ||
#[derive(Debug, clap::Args)] | ||
#[clap(author, version, about = "Creates a new key for use with fuel-core")] | ||
pub struct Arg { | ||
#[clap(long = "pretty", short = 'p')] | ||
pretty: bool, | ||
#[clap( | ||
long = "key-type", | ||
short = 'k', | ||
value_enum, | ||
default_value = BLOCK_PRODUCTION | ||
)] | ||
key_type: KeyType, | ||
} | ||
|
||
pub fn handler(arg: Arg) -> Result<String> { | ||
let mut rng = StdRng::from_entropy(); | ||
let secret = SecretKey::random(&mut rng); | ||
let public_key = secret.public_key(); | ||
let secret_str = secret.to_string(); | ||
|
||
let output = match arg.key_type { | ||
KeyType::BlockProduction => { | ||
let address = Input::owner(&public_key); | ||
json!({ | ||
"secret": secret_str, | ||
"address": address, | ||
"type": BLOCK_PRODUCTION, | ||
}) | ||
} | ||
KeyType::Peering => { | ||
let mut bytes = *secret.deref(); | ||
let p2p_secret = secp256k1::SecretKey::try_from_bytes(&mut bytes) | ||
.expect("Should be a valid private key"); | ||
let p2p_keypair = secp256k1::Keypair::from(p2p_secret); | ||
let libp2p_keypair = Keypair::from(p2p_keypair); | ||
let peer_id = PeerId::from_public_key(&libp2p_keypair.public()); | ||
json!({ | ||
"secret": secret_str, | ||
"peer_id": peer_id.to_string(), | ||
"type": P2P | ||
}) | ||
} | ||
}; | ||
Ok(if arg.pretty { | ||
serde_json::to_string_pretty(&output) | ||
} else { | ||
serde_json::to_string(&output) | ||
}?) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
//! This file is migrated from https://github.com/FuelLabs/fuel-core/blob/master/bin/keygen/src/keygen.rs | ||
use super::{KeyType, BLOCK_PRODUCTION, P2P}; | ||
use anyhow::Result; | ||
use fuel_core_types::{fuel_crypto::SecretKey, fuel_tx::Input}; | ||
use libp2p_identity::{secp256k1, Keypair, PeerId}; | ||
use serde_json::json; | ||
use std::{ops::Deref, str::FromStr}; | ||
|
||
/// Parse a secret key to view the associated public key | ||
#[derive(Debug, clap::Args)] | ||
#[clap( | ||
author, | ||
version, | ||
about = "Parses a private key to view the associated public key" | ||
)] | ||
pub struct Arg { | ||
secret: String, | ||
#[clap(long = "pretty", short = 'p')] | ||
pretty: bool, | ||
#[clap( | ||
long = "key-type", | ||
short = 'k', | ||
value_enum, | ||
default_value = BLOCK_PRODUCTION | ||
)] | ||
key_type: KeyType, | ||
} | ||
|
||
pub fn handler(arg: Arg) -> Result<String> { | ||
let secret = SecretKey::from_str(&arg.secret)?; | ||
let output = match arg.key_type { | ||
KeyType::BlockProduction => { | ||
let address = Input::owner(&secret.public_key()); | ||
let output = json!({ | ||
"address": address.to_string(), | ||
"type": BLOCK_PRODUCTION | ||
}); | ||
output | ||
} | ||
KeyType::Peering => { | ||
let mut bytes = *secret.deref(); | ||
let p2p_secret = secp256k1::SecretKey::try_from_bytes(&mut bytes) | ||
.expect("Should be a valid private key"); | ||
let p2p_keypair = secp256k1::Keypair::from(p2p_secret); | ||
let libp2p_keypair = Keypair::from(p2p_keypair); | ||
let peer_id = PeerId::from_public_key(&libp2p_keypair.public()); | ||
let output = json!({ | ||
"peer_id": peer_id.to_string(), | ||
"type": P2P | ||
}); | ||
output | ||
} | ||
}; | ||
Ok(if arg.pretty { | ||
serde_json::to_string_pretty(&output) | ||
} else { | ||
serde_json::to_string(&output) | ||
}?) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//! A `forc` plugin for converting a given string or path to their hash. | ||
|
||
use anyhow::Result; | ||
use clap::Parser; | ||
use forc_tracing::{init_tracing_subscriber, println_error}; | ||
use std::default::Default; | ||
use tracing::info; | ||
|
||
mod address; | ||
mod content; | ||
mod keccak256; | ||
mod keygen; | ||
mod sha256; | ||
|
||
#[derive(Debug, clap::Args)] | ||
#[clap(author, version, about = "Hashes the argument or file with this hash")] | ||
pub struct HashArgs { | ||
pub content: content::Content, | ||
} | ||
|
||
#[derive(Debug, clap::Args)] | ||
#[clap( | ||
author, | ||
version, | ||
about = "Converts any valid address to all supported formats" | ||
)] | ||
pub struct AddressArgs { | ||
pub content: String, | ||
} | ||
|
||
#[derive(Debug, Parser)] | ||
#[clap( | ||
name = "forc-crypto", | ||
about = "Forc plugin for hashing arbitrary data.", | ||
version | ||
)] | ||
pub enum Command { | ||
Keccak256(HashArgs), | ||
Sha256(HashArgs), | ||
Address(AddressArgs), | ||
NewKey(keygen::new_key::Arg), | ||
ParseSecret(keygen::parse_secret::Arg), | ||
} | ||
|
||
fn main() { | ||
init_tracing_subscriber(Default::default()); | ||
if let Err(err) = run() { | ||
println_error(&format!("{}", err)); | ||
std::process::exit(1); | ||
} | ||
} | ||
|
||
fn run() -> Result<()> { | ||
let app = Command::parse(); | ||
let content = match app { | ||
Command::Keccak256(arg) => hex::encode(keccak256::hash(arg.content)?), | ||
Command::Sha256(arg) => hex::encode(sha256::hash(arg.content)?), | ||
Command::Address(arg) => address::dump_address(arg.content)?, | ||
Command::NewKey(arg) => keygen::new_key::handler(arg)?, | ||
Command::ParseSecret(arg) => keygen::parse_secret::handler(arg)?, | ||
}; | ||
|
||
info!("{}", content); | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
use fuel_crypto::Hasher; | ||
|
||
/// Hashes a given data to Sha256 | ||
pub fn hash<T: AsRef<[u8]>>(data: T) -> anyhow::Result<Vec<u8>> { | ||
let mut hasher = Hasher::default(); | ||
hasher.input(data); | ||
Ok(hasher.finalize().to_vec()) | ||
} |