diff --git a/runtime-sdk/src/crypto/signature/sr25519.rs b/runtime-sdk/src/crypto/signature/sr25519.rs index bab1905eac..e845e497a6 100644 --- a/runtime-sdk/src/crypto/signature/sr25519.rs +++ b/runtime-sdk/src/crypto/signature/sr25519.rs @@ -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 { + 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 { + // 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"); + } +} diff --git a/runtime-sdk/src/testing/keys.rs b/runtime-sdk/src/testing/keys.rs index 3d0723b306..33c17faf94 100644 --- a/runtime-sdk/src/testing/keys.rs +++ b/runtime-sdk/src/testing/keys.rs @@ -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 = "."] @@ -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 "] @@ -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 = "."] @@ -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 "] @@ -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");