Skip to content
This repository was archived by the owner on Feb 26, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = { version = "0.2" }
wasm-bindgen-test = { version = "0.2" }
schnorrkel = { version = "0.1", features = ["nightly"]}
schnorrkel = { version = "0.6.1", features = ["nightly"]}

[dev-dependencies]
hex-literal = "0.1.4"
Expand Down
63 changes: 62 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,28 @@ pub fn sign(public: &[u8], private: &[u8], message: &[u8]) -> Vec<u8> {
__sign(public, private, message).to_vec()
}

/// Verify a message and its corresponding against a public key;
/// Sign a message with a given signing context
///
/// The combination of both public and private key must be provided.
/// This is effectively equivalent to a keypair.
///
/// * public: UIntArray with 32 element
/// * private: UIntArray with 64 element
/// * message: Arbitrary length UIntArray
/// * signing_ctx: Arbitrary length UIntArray
///
/// * returned vector is the signature consisting of 64 bytes.
#[wasm_bindgen]
pub fn sign_with_ctx(
public: &[u8],
private: &[u8],
message: &[u8],
signing_ctx: &[u8],
) -> Vec<u8> {
__sign_with_ctx(public, private, message, signing_ctx).to_vec()
}

/// Verify a message and its corresponding signature against a public key;
///
/// * signature: UIntArray with 64 element
/// * message: Arbitrary length UIntArray
Expand All @@ -64,6 +85,22 @@ pub fn verify(signature: &[u8], message: &[u8], pubkey: &[u8]) -> bool {
__verify(signature, message, pubkey)
}

/// Verify a message with a given signing context;
///
/// * signature: UIntArray with 64 element
/// * message: Arbitrary length UIntArray
/// * pubkey: UIntArray with 32 element
/// * signing_ctx: UIntArray with 32 element
#[wasm_bindgen]
pub fn verify_with_ctx(
signature: &[u8],
message: &[u8],
pubkey: &[u8],
signing_ctx: &[u8]
) -> bool {
__verify_with_ctx(signature, message, pubkey, signing_ctx)
}

/// Generate a secret key (aka. private key) from a seed phrase.
///
/// * seed: UIntArray with 32 element
Expand Down Expand Up @@ -129,6 +166,18 @@ pub mod tests {
assert!(signature.len() == SIGNATURE_LENGTH);
}

#[wasm_bindgen_test]
fn can_sign_message_with_ctx() {
let seed = generate_random_seed();
let keypair = keypair_from_seed(seed.as_slice());
let private = &keypair[0..SECRET_KEY_LENGTH];
let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
let message = b"this is a message";
let ctx = b"my context";
let signature = sign_with_ctx(public, private, message, ctx);
assert!(signature.len() == SIGNATURE_LENGTH);
}

#[wasm_bindgen_test]
fn can_verify_message() {
let seed = generate_random_seed();
Expand All @@ -140,6 +189,18 @@ pub mod tests {
assert!(verify(&signature[..], message, public));
}

#[wasm_bindgen_test]
fn can_verify_message_with_ctx() {
let seed = generate_random_seed();
let keypair = keypair_from_seed(seed.as_slice());
let private = &keypair[0..SECRET_KEY_LENGTH];
let public = &keypair[SECRET_KEY_LENGTH..KEYPAIR_LENGTH];
let message = b"this is a message";
let signing_ctx = b"a signing context";
let signature = sign_with_ctx(public, private, message, signing_ctx);
assert!(verify_with_ctx(&signature[..], message, public, signing_ctx));
}

#[wasm_bindgen_test]
fn hard_derives_pair() {
let cc = hex!("14416c6963650000000000000000000000000000000000000000000000000000"); // Alice
Expand Down
46 changes: 41 additions & 5 deletions src/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use schnorrkel::derive::{Derivation, ChainCode, CHAIN_CODE_LENGTH};
use schnorrkel::sign::{Signature,SIGNATURE_LENGTH};

// We must make sure that this is the same as declared in the substrate source code.
const SIGNING_CTX: &'static [u8] = b"substrate";
const DEFAULT_SIGNING_CTX: &'static[u8] = b"substrate";

/// Private helper function.
fn keypair_from_seed(seed: &[u8]) -> Keypair {
Expand Down Expand Up @@ -67,7 +67,12 @@ pub fn __secret_from_seed(seed: &[u8]) -> [u8; SECRET_KEY_LENGTH] {
s
}

pub fn __verify(signature: &[u8], message: &[u8], pubkey: &[u8]) -> bool {
pub fn __verify_with_ctx(
signature: &[u8],
message: &[u8],
pubkey: &[u8],
signing_ctx: &[u8]
) -> bool {
let sig = match Signature::from_bytes(signature) {
Ok(some_sig) => some_sig,
Err(_) => return false
Expand All @@ -76,10 +81,26 @@ pub fn __verify(signature: &[u8], message: &[u8], pubkey: &[u8]) -> bool {
Ok(some_pk) => some_pk,
Err(_) => return false
};
pk.verify_simple(SIGNING_CTX, message, &sig)

pk.verify_simple(signing_ctx, message, &sig).is_ok()
}

pub fn __verify(signature: &[u8], message: &[u8], pubkey: &[u8]) -> bool {
__verify_with_ctx(
signature,
message,
pubkey,
DEFAULT_SIGNING_CTX
)
}

pub fn __sign(public: &[u8], private: &[u8], message: &[u8]) -> [u8; SIGNATURE_LENGTH] {
pub fn __sign_with_ctx(
public: &[u8],
private: &[u8],
message: &[u8],
signing_ctx: &[u8]
) -> [u8; SIGNATURE_LENGTH] {

// despite being a method of KeyPair, only the secret is used for signing.
let secret = match SecretKey::from_bytes(private) {
Ok(some_secret) => some_secret,
Expand All @@ -91,6 +112,21 @@ pub fn __sign(public: &[u8], private: &[u8], message: &[u8]) -> [u8; SIGNATURE_L
Err(_) => panic!("Provided public key is invalid.")
};

let context = signing_context(SIGNING_CTX);
let context = signing_context(signing_ctx);

secret.sign(context.bytes(message), &public).to_bytes()
}

pub fn __sign(
public: &[u8],
private: &[u8],
message: &[u8],
) -> [u8; SIGNATURE_LENGTH] {
__sign_with_ctx(
public,
private,
message,
DEFAULT_SIGNING_CTX
)
}