Skip to content

Commit

Permalink
runtime-sdk: add bw compat to ed25519 signer
Browse files Browse the repository at this point in the history
  • Loading branch information
nhynes committed Oct 3, 2023
1 parent a76a420 commit 0625f0a
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 20 deletions.
2 changes: 1 addition & 1 deletion runtime-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +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 = { version = "2.0.0", features = ["digest"] }
ed25519-dalek = { version = "2.0.0", features = ["digest", "hazmat"] }
digest = "0.10.3"
hmac = "0.12.1"
sha2 = "0.10.8"
Expand Down
153 changes: 134 additions & 19 deletions runtime-sdk/src/crypto/signature/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use std::convert::TryInto;

use curve25519_dalek::{digest::consts::U64, edwards::CompressedEdwardsY};
use ed25519_dalek::Signer as _;
use sha2::{Digest as _, Sha512_256};
use sha2::{Digest as _, Sha512, Sha512_256};

use oasis_core_runtime::common::crypto::signature::{
PublicKey as CorePublicKey, Signature as CoreSignature,
};

use crate::crypto::signature::{Error, Signature};
use crate::crypto::signature::{Error, Signature, Signer};

/// An Ed25519 public key.
#[derive(Clone, Debug, PartialEq, Eq, cbor::Encode, cbor::Decode)]
Expand Down Expand Up @@ -118,54 +118,169 @@ impl From<PublicKey> for CorePublicKey {

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

/// The original version of this signer returned the "expanded" signing key from `to_bytes`.
/// If the contract passes in one of these, it's gets the same thing out. New invocations get the "proper" version.
enum Key {
Expanded {
esk: ed25519_dalek::hazmat::ExpandedSecretKey,
/// The hash output that is used to create the "expanded" secret key. It is stored to return from `from_bytes` because `esk` is a function of the hash, not the hash directly.
hash: [u8; 64],
},
Regular(ed25519_dalek::SigningKey),
}

impl Key {
fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() == 64 {
let hash: [u8; 64] = bytes.try_into().unwrap();
Ok(Self::Expanded {
esk: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&hash),
hash,
})
} else if bytes.len() == 32 {
bytes
.try_into()
.map(ed25519_dalek::SigningKey::from_bytes)
.map(Self::Regular)
.map_err(|_| Error::MalformedPrivateKey)
} else {
Err(Error::MalformedPrivateKey)
}
}

fn to_bytes(&self) -> Vec<u8> {
match self {
Self::Expanded { hash, .. } => hash.to_vec(),
Self::Regular(sk) => sk.to_bytes().to_vec(),
}
}

fn sign(&self, message: &[u8]) -> Signature {
match self {
Self::Expanded { esk, .. } => {
let verifying_key = ed25519_dalek::VerifyingKey::from(esk);
ed25519_dalek::hazmat::raw_sign::<Sha512>(&esk, message, &verifying_key)
}
Self::Regular(sk) => sk.sign(message),
}
.to_bytes()
.to_vec()
.into()
}

fn sign_digest<D>(&self, digest: D) -> Result<Signature, Error>
where
D: ed25519_dalek::Digest<OutputSize = U64>,
{
match self {
Key::Expanded { esk, .. } => {
let verifying_key = ed25519_dalek::VerifyingKey::from(esk);
ed25519_dalek::hazmat::raw_sign_prehashed::<Sha512, _>(
&esk,
digest,
&verifying_key,
None,
)
}
Key::Regular(sk) => sk.sign_prehashed(digest, None),
}
.map_err(|_| Error::SigningError)
.map(|sig| sig.to_bytes().to_vec().into())
}

fn public_key(&self) -> super::PublicKey {
let pk = match self {
Self::Expanded { esk, .. } => ed25519_dalek::VerifyingKey::from(esk),
Self::Regular(sk) => sk.verifying_key(),
};
super::PublicKey::Ed25519(PublicKey::from_bytes(pk.as_bytes()).unwrap())
}
}

impl MemorySigner {
pub fn sign_digest<D>(&self, digest: D) -> Result<Signature, Error>
where
D: ed25519_dalek::Digest<OutputSize = U64>,
{
self.sk
.sign_prehashed(digest, None)
.map_err(|_| Error::SigningError)
.map(|sig| sig.to_bytes().to_vec().into())
self.key.sign_digest(digest)
}
}

impl super::Signer for MemorySigner {
impl Signer for MemorySigner {
fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
if seed.len() != 32 {
return Err(Error::MalformedPublicKey);
}
Self::from_bytes(seed)
}

fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
Ok(Self {
sk: bytes.try_into().map_err(|_| Error::MalformedPrivateKey)?,
key: Key::from_bytes(bytes)?,
})
}

fn to_bytes(&self) -> Vec<u8> {
self.sk.to_bytes().to_vec()
self.key.to_bytes()
}

fn public_key(&self) -> super::PublicKey {
let pk = ed25519_dalek::VerifyingKey::from(&self.sk);
super::PublicKey::Ed25519(PublicKey::from_bytes(pk.as_bytes()).unwrap())
self.key.public_key()
}

fn sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
let mut digest = Sha512_256::new();
digest.update(context);
digest.update(message);
let message = digest.finalize();

let signature = self.sk.sign(&message);

Ok(signature.to_bytes().to_vec().into())
Ok(self.key.sign(&digest.finalize()))
}

fn sign_raw(&self, message: &[u8]) -> Result<Signature, Error> {
let signature = self.sk.sign(message);
Ok(signature.to_bytes().to_vec().into())
Ok(self.key.sign(message))
}
}

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

#[test]
fn legacy_bytes_conversion() {
let seed = [42u8; 32];
let signer = MemorySigner::new_from_seed(&seed).unwrap();

let esk = ed25519_dalek::hazmat::ExpandedSecretKey::from(&seed);
let esk_hash = Sha512::digest(seed);
let esk_signer = MemorySigner::from_bytes(&esk_hash).unwrap();

let esk_public_key = super::super::PublicKey::Ed25519(
PublicKey::from_bytes(&ed25519_dalek::VerifyingKey::from(&esk).to_bytes()).unwrap(),
);

assert_eq!(
esk_signer.to_bytes().as_slice(),
esk_hash.as_slice(),
"esk roundtrip"
);
assert_eq!(signer.to_bytes(), seed, "sk roundtrip");

let message = b"hello, world!";
let sig = signer.sign_raw(message).unwrap();
let esk_sig = esk_signer.sign_raw(message).unwrap();
assert_eq!(sig, esk_sig, "sig != esk_sig");

assert_eq!(
signer.public_key(),
esk_public_key,
"signer pk != esk_public_key"
);
assert_eq!(
esk_signer.public_key(),
esk_public_key,
"esk_signer pk != esk_pubic_key"
);
}
}

0 comments on commit 0625f0a

Please sign in to comment.