Skip to content

Commit

Permalink
runtime-sdk: Add in-memory signers for Ed25519 and Sr25519
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Sep 29, 2022
1 parent 0c10e5e commit d6b286b
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 13 deletions.
1 change: 1 addition & 0 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 contract-sdk/specs/token/oas20/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 runtime-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ oasis-runtime-sdk-macros = { path = "../runtime-sdk-macros", optional = true }
# Third party.
byteorder = "1.4.3"
curve25519-dalek = "3.2.0"
ed25519-dalek = "1.0.1"
digest = "0.10.3"
hmac = "0.11.0"
sha2 = "0.9.8"
Expand Down
61 changes: 61 additions & 0 deletions runtime-sdk/src/crypto/signature/ed25519.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Ed25519 signatures.
use curve25519_dalek::edwards::CompressedEdwardsY;
use sha2::{Digest, Sha512Trunc256};

use oasis_core_runtime::common::crypto::signature::{
PublicKey as CorePublicKey, Signature as CoreSignature,
Expand Down Expand Up @@ -94,3 +95,63 @@ impl From<PublicKey> for CorePublicKey {
pk.0
}
}

/// A memory-backed signer for Ed25519.
pub struct MemorySigner {
sk: ed25519_dalek::ExpandedSecretKey,
}

impl MemorySigner {
/// Creates a new signer from a RFC 8032 seed.
pub fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
let sk = ed25519_dalek::SecretKey::from_bytes(&seed).map_err(|_| Error::InvalidArgument)?;
let esk = ed25519_dalek::ExpandedSecretKey::from(&sk);
Ok(Self { sk: esk })
}

/// Generates a new signer deterministically from a test key name string.
pub fn new_test(name: &str) -> Self {
let mut digest = Sha512Trunc256::new();
digest.update(name.as_bytes());
let seed = digest.finalize();

Self::new_from_seed(&seed).unwrap()
}

/// Public key corresponding to the signer.
pub fn public(&self) -> PublicKey {
let pk = ed25519_dalek::PublicKey::from(&self.sk);
PublicKey::from_bytes(pk.as_bytes()).unwrap()
}

/// Generates a signature with the private key over the context and message.
pub fn context_sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
let mut digest = Sha512Trunc256::new();
for byte in &[context, message] {
digest.update(byte);
}
let message = digest.finalize();

let pk = ed25519_dalek::PublicKey::from(&self.sk);
let signature = self.sk.sign(&message, &pk);

Ok(signature.to_bytes().to_vec().into())
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_memory_signer() {
let signer = MemorySigner::new_test("memory signer test");
let ctx = b"oasis-core/test: context";
let message = b"this is a message";
let signature = signer.context_sign(ctx, message).unwrap();
let pk = signer.public();

pk.verify(ctx, message, &signature)
.expect("signature should verify");
}
}
65 changes: 65 additions & 0 deletions runtime-sdk/src/crypto/signature/sr25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,68 @@ impl From<&'static str> for PublicKey {
PublicKey::from_bytes(&base64::decode(s).unwrap()).unwrap()
}
}

/// A memory-backed signer for Sr25519.
pub struct MemorySigner {
kp: schnorrkel::Keypair,
}

impl MemorySigner {
/// Creates a new signer from a seed.
pub fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
let sk =
schnorrkel::MiniSecretKey::from_bytes(&seed).map_err(|_| Error::InvalidArgument)?;
let kp = sk.expand_to_keypair(schnorrkel::keys::ExpansionMode::Ed25519);
Ok(Self { kp })
}

/// Generates a new signer deterministically from a test key name string.
pub fn new_test(name: &str) -> Self {
let mut digest = Sha512Trunc256::new();
digest.update(name.as_bytes());
let seed = digest.finalize();

Self::new_from_seed(&seed).unwrap()
}

/// Public key corresponding to the signer.
pub fn public(&self) -> PublicKey {
PublicKey::from_bytes(&self.kp.public.to_bytes()).unwrap()
}

/// Generates a signature with the private key over the context and message.
pub fn context_sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
// Convert the context to a Sr25519 SigningContext.
let context = schnorrkel::context::SigningContext::new(context);

// Generate a SigningTranscript from the context, and a pre-hash
// of the message.
//
// Note: This requires using Sha512Trunc256 instead of our hash,
// due to the need for FixedOutput.
let mut digest = Sha512Trunc256::new();
digest.update(message);
let transcript = context.hash256(digest);

let signature = self.kp.sign(transcript);

Ok(signature.to_bytes().to_vec().into())
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_memory_signer() {
let signer = MemorySigner::new_test("memory signer test");
let ctx = b"oasis-core/test: context";
let message = b"this is a message";
let signature = signer.context_sign(ctx, message).unwrap();
let pk = signer.public();

pk.verify(ctx, message, &signature)
.expect("signature should verify");
}
}
39 changes: 26 additions & 13 deletions runtime-sdk/src/testing/keys.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! Module that contains known test keys.
// TODO: Should be derived from seeds once implemented in the Rust version.

/// Define an ed25519 test key.
macro_rules! test_key_ed25519 {
($doc:expr, $name:ident, $pk:expr) => {
($doc:expr, $name:ident, $seed:expr) => {
#[doc = " Test key "]
#[doc=$doc]
#[doc = "."]
Expand All @@ -25,7 +23,14 @@ macro_rules! test_key_ed25519 {
#[doc=$doc]
#[doc = "."]
pub fn pk_ed25519() -> ed25519::PublicKey {
$pk.into()
signer().public()
}

#[doc = " Test Ed25519 signer "]
#[doc=$doc]
#[doc = "."]
pub fn signer() -> ed25519::MemorySigner {
ed25519::MemorySigner::new_test($seed)
}

#[doc = " Test address derivation information "]
Expand Down Expand Up @@ -90,7 +95,7 @@ macro_rules! test_key_secp256k1 {

/// Define an sr25519 test key.
macro_rules! test_key_sr25519 {
($doc:expr, $name:ident, $pk:expr) => {
($doc:expr, $name:ident, $seed:expr) => {
#[doc = " Test key "]
#[doc=$doc]
#[doc = "."]
Expand All @@ -111,7 +116,14 @@ macro_rules! test_key_sr25519 {
#[doc=$doc]
#[doc = "."]
pub fn pk_sr25519() -> sr25519::PublicKey {
$pk.into()
signer().public()
}

#[doc = " Test Sr25519 signer "]
#[doc=$doc]
#[doc = "."]
pub fn signer() -> sr25519::MemorySigner {
sr25519::MemorySigner::new_test($seed)
}

#[doc = " Test address derivation information "]
Expand All @@ -131,10 +143,11 @@ macro_rules! test_key_sr25519 {
};
}

test_key_ed25519!("A", alice, "NcPzNW3YU2T+ugNUtUWtoQnRvbOL9dYSaBfbjHLP1pE=");
test_key_ed25519!("B", bob, "YgkEiVSR4SMQdfXw+ppuFYlqH0seutnCKk8KG8PyAx0=");
test_key_ed25519!("C", charlie, "8l1AQE+ETOPLckiNJ7NOD+AfZdaPw6wguir/vSF11YI=");
test_key_secp256k1!("D", dave, "AwF6GNjbybMzhi3XRj5R1oTiMMkO1nAwB7NZAlH1X4BE");
test_key_secp256k1!("E", erin, "A9i0oSK+5sLSONbMYGmaFUA+Fb8zzqYEMUMspacIgO09");
test_key_sr25519!("F", frank, "ljm9ZwdAldhlyWM2B4C+3gQZis+ceaxnt6QA4rOcP0k=");
test_key_sr25519!("G", grace, "0MHrNhjVTOFWmsOgpWcC3L8jIX3ZatKr0/yxMPtwckc=");
test_key_ed25519!("Alice", alice, "oasis-runtime-sdk/test-keys: alice");
test_key_ed25519!("Bob", bob, "oasis-runtime-sdk/test-keys: bob");
test_key_ed25519!("Charlie", charlie, "oasis-runtime-sdk/test-keys: charlie");
test_key_ed25519!("Cory", cory, "ekiden test entity key seed");
test_key_secp256k1!("Dave", dave, "AwF6GNjbybMzhi3XRj5R1oTiMMkO1nAwB7NZAlH1X4BE");
test_key_secp256k1!("Erin", erin, "A9i0oSK+5sLSONbMYGmaFUA+Fb8zzqYEMUMspacIgO09");
test_key_sr25519!("Frank", frank, "oasis-runtime-sdk/test-keys: frank");
test_key_sr25519!("Grace", grace, "oasis-runtime-sdk/test-keys: grace");
1 change: 1 addition & 0 deletions tests/contracts/hello/Cargo.lock

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

0 comments on commit d6b286b

Please sign in to comment.