Skip to content

Commit

Permalink
feat(*): Adds support for wasm32 targets
Browse files Browse the repository at this point in the history
This will allow anyone who wants to use the nkeys library in a `wasm32-unknown-unknown`
target to do so (such as in wasmcloud actors). It also enables users
to generate their keys using different sources of random data

Signed-off-by: Taylor Thomas <[email protected]>
  • Loading branch information
thomastaylor312 committed Dec 16, 2021
1 parent 173aee4 commit 3710daf
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/target
**/*.rs.bk
Cargo.lock
.idea
.idea
.vscode/
15 changes: 10 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "nkeys"
version = "0.1.0"
version = "0.2.0"
authors = ["Kevin Hoffman <[email protected]>"]
edition = "2018"
edition = "2021"
description = "Rust implementation of the NATS nkeys library"
license = "Apache-2.0"
homepage = "https://github.com/encabulators/nkeys"
Expand All @@ -23,9 +23,9 @@ name = "nk"
required-features = ["cli"]

[dependencies]
signatory = "0.21"
signatory = "0.23"
ed25519-dalek = { version = "1.0.1", default-features = false, features = ["u64_backend"] }
rand = "0.7.3"
rand = "0.8"
byteorder = "1.3.4"
data-encoding = "2.3.0"
log = "0.4.11"
Expand All @@ -35,5 +35,10 @@ quicli = { version = "0.4", optional = true }
structopt = { version = "0.3.17", optional = true }
term-table = { version = "1.3.0", optional = true }
exitfailure = { version = "0.5.1", optional =true }
env_logger = { version = "0.7.1", optional = true }
env_logger = { version = "0.9", optional = true }
serde_json = { version = "1.0", optional = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
# DIRTY HACK WARNING: We don't actually use this, but if we don't specify custom, things die because
# wasm32-unknown-unknown isn't supported
getrandom = { version = "0.2", default-features = false, features = ["custom"] }
7 changes: 3 additions & 4 deletions src/bin/nk/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ enum Command {
#[derive(StructOpt, Debug, Clone)]
enum Output {
Text,
JSON,
Json,
}

impl FromStr for Output {
type Err = OutputParseErr;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(Output::JSON),
"json" => Ok(Output::Json),
"text" => Ok(Output::Text),
_ => Err(OutputParseErr),
}
Expand All @@ -63,7 +63,6 @@ impl fmt::Display for OutputParseErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
"error parsing output type, see help for the list of accepted outputs"
)
}
Expand Down Expand Up @@ -91,7 +90,7 @@ fn generate(kt: &KeyPairType, output_type: &Output) {
kp.seed().unwrap()
);
}
Output::JSON => {
Output::Json => {
let output = json!({
"public_key": kp.public_key(),
"seed": kp.seed().unwrap(),
Expand Down
70 changes: 55 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@
#![allow(dead_code)]

use std::fmt::{self, Debug};

use crc::{extract_crc, push_crc, valid_checksum};
use ed25519_dalek::{ExpandedSecretKey, PublicKey, SecretKey, Signature, Verifier};
use rand::prelude::*;
use std::fmt;
use std::fmt::Debug;

const ENCODED_SEED_LENGTH: usize = 58;

Expand Down Expand Up @@ -145,57 +145,95 @@ impl From<u8> for KeyPairType {
}

impl KeyPair {
/// Creates a new key pair of the given type
/// Creates a new key pair of the given type.
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new(kp_type: KeyPairType) -> KeyPair {
// If this unwrap fails, then the library is invalid, so the unwrap is OK here
let s = create_seed().unwrap();
KeyPair {
Self::new_from_raw(kp_type, generate_seed_rand()).unwrap()
}

/// Create a new keypair using a pre-existing set of random bytes.
///
/// Returns an error if there is an issue using the bytes to generate the key
/// NOTE: These bytes should be generated from a cryptographically secure random source.
pub fn new_from_raw(kp_type: KeyPairType, random_bytes: [u8; 32]) -> Result<KeyPair> {
let s = create_seed(random_bytes)?;
Ok(KeyPair {
kp_type,
pk: pk_from_seed(&s),
sk: Some(s),
}
})
}

/// Creates a new user key pair with a seed that has a **U** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_user() -> KeyPair {
Self::new(KeyPairType::User)
}

/// Creates a new account key pair with a seed that has an **A** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_account() -> KeyPair {
Self::new(KeyPairType::Account)
}

/// Creates a new operator key pair with a seed that has an **O** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_operator() -> KeyPair {
Self::new(KeyPairType::Operator)
}

/// Creates a new cluster key pair with a seed that has the **C** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_cluster() -> KeyPair {
Self::new(KeyPairType::Cluster)
}

/// Creates a new server key pair with a seed that has the **N** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_server() -> KeyPair {
Self::new(KeyPairType::Server)
}

/// Creates a new module (e.g. WebAssembly) key pair with a seed that has the **M** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_module() -> KeyPair {
Self::new(KeyPairType::Module)
}

/// Creates a new service / service provider key pair with a seed that has the **V** prefix
///
/// NOTE: This is not available if using on a wasm32-unknown-unknown target due to the lack of
/// rand support. Use [`new_from_raw`](KeyPair::new_from_raw) instead
#[cfg(not(target_arch = "wasm32"))]
pub fn new_service() -> KeyPair {
Self::new(KeyPairType::Service)
}

/// Returns the encoded, human-readable public key of this key pair
pub fn public_key(&self) -> String {
let mut raw = vec![];

raw.push(get_prefix_byte(&self.kp_type));
let mut raw = vec![get_prefix_byte(&self.kp_type)];

raw.extend(self.pk.as_bytes());

Expand All @@ -216,9 +254,9 @@ impl KeyPair {

/// Attempts to verify that the given signature is valid for the given input
pub fn verify(&self, input: &[u8], sig: &[u8]) -> Result<()> {
let mut fixedsig = [0; ed25519_dalek::ed25519::SIGNATURE_LENGTH];
let mut fixedsig = [0; ed25519_dalek::Signature::BYTE_SIZE];
fixedsig.copy_from_slice(sig);
let insig = ed25519_dalek::Signature::new(fixedsig);
let insig = ed25519_dalek::Signature::from_bytes(&fixedsig)?;

match self.pk.verify(input, &insig) {
Ok(()) => Ok(()),
Expand Down Expand Up @@ -288,7 +326,7 @@ impl KeyPair {
Err(err!(
InvalidPrefix,
"Incorrect byte prefix: {}",
source.chars().nth(0).unwrap()
source.chars().next().unwrap()
))
} else {
let b2 = (raw[0] & 7) << 5 | ((raw[1] & 248) >> 3);
Expand Down Expand Up @@ -323,11 +361,13 @@ fn decode_raw(raw: &[u8]) -> Result<Vec<u8>> {
}
}

fn create_seed() -> Result<SecretKey> {
fn generate_seed_rand() -> [u8; 32] {
let mut rng = rand::thread_rng();
let rnd_bytes = rng.gen::<[u8; 32]>();
rng.gen::<[u8; 32]>()
}

SecretKey::from_bytes(&rnd_bytes[..]).map_err(|e| e.into())
fn create_seed(rand_bytes: [u8; 32]) -> Result<SecretKey> {
SecretKey::from_bytes(&rand_bytes[..]).map_err(|e| e.into())
}

fn get_prefix_byte(kp_type: &KeyPairType) -> u8 {
Expand Down

0 comments on commit 3710daf

Please sign in to comment.