Skip to content

Commit d6b286b

Browse files
committed
runtime-sdk: Add in-memory signers for Ed25519 and Sr25519
1 parent 0c10e5e commit d6b286b

7 files changed

Lines changed: 156 additions & 13 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contract-sdk/specs/token/oas20/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runtime-sdk/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ oasis-runtime-sdk-macros = { path = "../runtime-sdk-macros", optional = true }
1414
# Third party.
1515
byteorder = "1.4.3"
1616
curve25519-dalek = "3.2.0"
17+
ed25519-dalek = "1.0.1"
1718
digest = "0.10.3"
1819
hmac = "0.11.0"
1920
sha2 = "0.9.8"

runtime-sdk/src/crypto/signature/ed25519.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Ed25519 signatures.
22
use curve25519_dalek::edwards::CompressedEdwardsY;
3+
use sha2::{Digest, Sha512Trunc256};
34

45
use oasis_core_runtime::common::crypto::signature::{
56
PublicKey as CorePublicKey, Signature as CoreSignature,
@@ -94,3 +95,63 @@ impl From<PublicKey> for CorePublicKey {
9495
pk.0
9596
}
9697
}
98+
99+
/// A memory-backed signer for Ed25519.
100+
pub struct MemorySigner {
101+
sk: ed25519_dalek::ExpandedSecretKey,
102+
}
103+
104+
impl MemorySigner {
105+
/// Creates a new signer from a RFC 8032 seed.
106+
pub fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
107+
let sk = ed25519_dalek::SecretKey::from_bytes(&seed).map_err(|_| Error::InvalidArgument)?;
108+
let esk = ed25519_dalek::ExpandedSecretKey::from(&sk);
109+
Ok(Self { sk: esk })
110+
}
111+
112+
/// Generates a new signer deterministically from a test key name string.
113+
pub fn new_test(name: &str) -> Self {
114+
let mut digest = Sha512Trunc256::new();
115+
digest.update(name.as_bytes());
116+
let seed = digest.finalize();
117+
118+
Self::new_from_seed(&seed).unwrap()
119+
}
120+
121+
/// Public key corresponding to the signer.
122+
pub fn public(&self) -> PublicKey {
123+
let pk = ed25519_dalek::PublicKey::from(&self.sk);
124+
PublicKey::from_bytes(pk.as_bytes()).unwrap()
125+
}
126+
127+
/// Generates a signature with the private key over the context and message.
128+
pub fn context_sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
129+
let mut digest = Sha512Trunc256::new();
130+
for byte in &[context, message] {
131+
digest.update(byte);
132+
}
133+
let message = digest.finalize();
134+
135+
let pk = ed25519_dalek::PublicKey::from(&self.sk);
136+
let signature = self.sk.sign(&message, &pk);
137+
138+
Ok(signature.to_bytes().to_vec().into())
139+
}
140+
}
141+
142+
#[cfg(test)]
143+
mod test {
144+
use super::*;
145+
146+
#[test]
147+
fn test_memory_signer() {
148+
let signer = MemorySigner::new_test("memory signer test");
149+
let ctx = b"oasis-core/test: context";
150+
let message = b"this is a message";
151+
let signature = signer.context_sign(ctx, message).unwrap();
152+
let pk = signer.public();
153+
154+
pk.verify(ctx, message, &signature)
155+
.expect("signature should verify");
156+
}
157+
}

runtime-sdk/src/crypto/signature/sr25519.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,68 @@ impl From<&'static str> for PublicKey {
6969
PublicKey::from_bytes(&base64::decode(s).unwrap()).unwrap()
7070
}
7171
}
72+
73+
/// A memory-backed signer for Sr25519.
74+
pub struct MemorySigner {
75+
kp: schnorrkel::Keypair,
76+
}
77+
78+
impl MemorySigner {
79+
/// Creates a new signer from a seed.
80+
pub fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
81+
let sk =
82+
schnorrkel::MiniSecretKey::from_bytes(&seed).map_err(|_| Error::InvalidArgument)?;
83+
let kp = sk.expand_to_keypair(schnorrkel::keys::ExpansionMode::Ed25519);
84+
Ok(Self { kp })
85+
}
86+
87+
/// Generates a new signer deterministically from a test key name string.
88+
pub fn new_test(name: &str) -> Self {
89+
let mut digest = Sha512Trunc256::new();
90+
digest.update(name.as_bytes());
91+
let seed = digest.finalize();
92+
93+
Self::new_from_seed(&seed).unwrap()
94+
}
95+
96+
/// Public key corresponding to the signer.
97+
pub fn public(&self) -> PublicKey {
98+
PublicKey::from_bytes(&self.kp.public.to_bytes()).unwrap()
99+
}
100+
101+
/// Generates a signature with the private key over the context and message.
102+
pub fn context_sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
103+
// Convert the context to a Sr25519 SigningContext.
104+
let context = schnorrkel::context::SigningContext::new(context);
105+
106+
// Generate a SigningTranscript from the context, and a pre-hash
107+
// of the message.
108+
//
109+
// Note: This requires using Sha512Trunc256 instead of our hash,
110+
// due to the need for FixedOutput.
111+
let mut digest = Sha512Trunc256::new();
112+
digest.update(message);
113+
let transcript = context.hash256(digest);
114+
115+
let signature = self.kp.sign(transcript);
116+
117+
Ok(signature.to_bytes().to_vec().into())
118+
}
119+
}
120+
121+
#[cfg(test)]
122+
mod test {
123+
use super::*;
124+
125+
#[test]
126+
fn test_memory_signer() {
127+
let signer = MemorySigner::new_test("memory signer test");
128+
let ctx = b"oasis-core/test: context";
129+
let message = b"this is a message";
130+
let signature = signer.context_sign(ctx, message).unwrap();
131+
let pk = signer.public();
132+
133+
pk.verify(ctx, message, &signature)
134+
.expect("signature should verify");
135+
}
136+
}

runtime-sdk/src/testing/keys.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
//! Module that contains known test keys.
22
3-
// TODO: Should be derived from seeds once implemented in the Rust version.
4-
53
/// Define an ed25519 test key.
64
macro_rules! test_key_ed25519 {
7-
($doc:expr, $name:ident, $pk:expr) => {
5+
($doc:expr, $name:ident, $seed:expr) => {
86
#[doc = " Test key "]
97
#[doc=$doc]
108
#[doc = "."]
@@ -25,7 +23,14 @@ macro_rules! test_key_ed25519 {
2523
#[doc=$doc]
2624
#[doc = "."]
2725
pub fn pk_ed25519() -> ed25519::PublicKey {
28-
$pk.into()
26+
signer().public()
27+
}
28+
29+
#[doc = " Test Ed25519 signer "]
30+
#[doc=$doc]
31+
#[doc = "."]
32+
pub fn signer() -> ed25519::MemorySigner {
33+
ed25519::MemorySigner::new_test($seed)
2934
}
3035

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

9196
/// Define an sr25519 test key.
9297
macro_rules! test_key_sr25519 {
93-
($doc:expr, $name:ident, $pk:expr) => {
98+
($doc:expr, $name:ident, $seed:expr) => {
9499
#[doc = " Test key "]
95100
#[doc=$doc]
96101
#[doc = "."]
@@ -111,7 +116,14 @@ macro_rules! test_key_sr25519 {
111116
#[doc=$doc]
112117
#[doc = "."]
113118
pub fn pk_sr25519() -> sr25519::PublicKey {
114-
$pk.into()
119+
signer().public()
120+
}
121+
122+
#[doc = " Test Sr25519 signer "]
123+
#[doc=$doc]
124+
#[doc = "."]
125+
pub fn signer() -> sr25519::MemorySigner {
126+
sr25519::MemorySigner::new_test($seed)
115127
}
116128

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

134-
test_key_ed25519!("A", alice, "NcPzNW3YU2T+ugNUtUWtoQnRvbOL9dYSaBfbjHLP1pE=");
135-
test_key_ed25519!("B", bob, "YgkEiVSR4SMQdfXw+ppuFYlqH0seutnCKk8KG8PyAx0=");
136-
test_key_ed25519!("C", charlie, "8l1AQE+ETOPLckiNJ7NOD+AfZdaPw6wguir/vSF11YI=");
137-
test_key_secp256k1!("D", dave, "AwF6GNjbybMzhi3XRj5R1oTiMMkO1nAwB7NZAlH1X4BE");
138-
test_key_secp256k1!("E", erin, "A9i0oSK+5sLSONbMYGmaFUA+Fb8zzqYEMUMspacIgO09");
139-
test_key_sr25519!("F", frank, "ljm9ZwdAldhlyWM2B4C+3gQZis+ceaxnt6QA4rOcP0k=");
140-
test_key_sr25519!("G", grace, "0MHrNhjVTOFWmsOgpWcC3L8jIX3ZatKr0/yxMPtwckc=");
146+
test_key_ed25519!("Alice", alice, "oasis-runtime-sdk/test-keys: alice");
147+
test_key_ed25519!("Bob", bob, "oasis-runtime-sdk/test-keys: bob");
148+
test_key_ed25519!("Charlie", charlie, "oasis-runtime-sdk/test-keys: charlie");
149+
test_key_ed25519!("Cory", cory, "ekiden test entity key seed");
150+
test_key_secp256k1!("Dave", dave, "AwF6GNjbybMzhi3XRj5R1oTiMMkO1nAwB7NZAlH1X4BE");
151+
test_key_secp256k1!("Erin", erin, "A9i0oSK+5sLSONbMYGmaFUA+Fb8zzqYEMUMspacIgO09");
152+
test_key_sr25519!("Frank", frank, "oasis-runtime-sdk/test-keys: frank");
153+
test_key_sr25519!("Grace", grace, "oasis-runtime-sdk/test-keys: grace");

tests/contracts/hello/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)