oasis_runtime_sdk/crypto/signature/
ed25519.rs

1//! Ed25519 signatures.
2use std::convert::TryInto;
3
4use base64::prelude::*;
5use curve25519_dalek::{digest::consts::U64, edwards::CompressedEdwardsY};
6use ed25519_dalek::Signer as _;
7use rand_core::{CryptoRng, RngCore};
8use sha2::{Digest as _, Sha512, Sha512_256};
9
10use oasis_core_runtime::common::crypto::signature::{
11    PublicKey as CorePublicKey, Signature as CoreSignature,
12};
13
14use crate::crypto::signature::{Error, Signature, Signer};
15
16/// An Ed25519 public key.
17#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, cbor::Encode, cbor::Decode)]
18#[cbor(transparent, no_default)]
19pub struct PublicKey(CorePublicKey);
20
21impl PublicKey {
22    /// Return a byte representation of this public key.
23    pub fn as_bytes(&self) -> &[u8] {
24        self.0.as_ref()
25    }
26
27    /// Construct a public key from a slice of bytes.
28    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
29        // CorePublicKey::from doesn't support error checking.
30        if bytes.len() != CorePublicKey::len() {
31            return Err(Error::MalformedPublicKey);
32        }
33
34        // Ensure that the public key is a valid compressed point.
35        //
36        // Note: This could do the small order public key check,
37        // but just assume that signature verification will impose
38        // whatever semantics it desires.
39        let a = CompressedEdwardsY::from_slice(bytes).unwrap(); // Length is checked above.
40        let _a = match a.decompress() {
41            Some(point) => point,
42            None => return Err(Error::MalformedPublicKey),
43        };
44
45        Ok(PublicKey(CorePublicKey::from(bytes)))
46    }
47
48    /// Verify a signature.
49    pub fn verify(
50        &self,
51        context: &[u8],
52        message: &[u8],
53        signature: &Signature,
54    ) -> Result<(), Error> {
55        // CoreSignature::from doesn't support error checking either.
56        if signature.0.len() != CoreSignature::len() {
57            return Err(Error::MalformedSignature);
58        }
59        let sig: &[u8] = signature.0.as_ref();
60        let sig = CoreSignature::from(sig);
61
62        sig.verify(&self.0, context, message)
63            .map_err(|_| Error::VerificationFailed)
64    }
65
66    /// Verify signature without applying domain separation.
67    pub fn verify_raw(&self, message: &[u8], signature: &Signature) -> Result<(), Error> {
68        // CoreSignature::from doesn't support error checking either.
69        if signature.0.len() != CoreSignature::len() {
70            return Err(Error::MalformedSignature);
71        }
72        let sig: &[u8] = signature.0.as_ref();
73        let sig = CoreSignature::from(sig);
74
75        sig.verify_raw(&self.0, message)
76            .map_err(|_| Error::VerificationFailed)
77    }
78
79    /// Verify signature of a pre-hashed message.
80    pub fn verify_digest<D>(&self, digest: D, signature: &Signature) -> Result<(), Error>
81    where
82        D: ed25519_dalek::Digest<OutputSize = U64>,
83    {
84        let sig: ed25519_dalek::Signature = signature
85            .as_ref()
86            .try_into()
87            .map_err(|_| Error::MalformedSignature)?;
88        let pk: ed25519_dalek::VerifyingKey = self
89            .as_bytes()
90            .try_into()
91            .map_err(|_| Error::MalformedPublicKey)?;
92        pk.verify_prehashed(digest, None, &sig)
93            .map_err(|_| Error::VerificationFailed)
94    }
95}
96
97impl From<&'static str> for PublicKey {
98    fn from(s: &'static str) -> PublicKey {
99        PublicKey::from_bytes(&BASE64_STANDARD.decode(s).unwrap()).unwrap()
100    }
101}
102
103impl From<CorePublicKey> for PublicKey {
104    fn from(pk: CorePublicKey) -> PublicKey {
105        PublicKey(pk)
106    }
107}
108
109impl From<&CorePublicKey> for PublicKey {
110    fn from(pk: &CorePublicKey) -> PublicKey {
111        PublicKey(*pk)
112    }
113}
114
115impl From<PublicKey> for CorePublicKey {
116    fn from(pk: PublicKey) -> CorePublicKey {
117        pk.0
118    }
119}
120
121/// A memory-backed signer for Ed25519.
122pub struct MemorySigner {
123    key: Key,
124}
125
126/// The original version of the Ed25519 signer returned the "expanded" secret key from `to_bytes`.
127/// A contract may have stored the "expanded" key and expects its use to continue to succeed.
128/// For backwards compatibility, the signer works with both "expanded" and regular keys.
129/// New invocations receive a regular/proper key, and from-"expanded" ones get the old behavior.
130enum Key {
131    Expanded {
132        esk: ed25519_dalek::hazmat::ExpandedSecretKey,
133        /// The hash output that is used to create the "expanded" secret key.
134        /// It is stored to return from `from_bytes` because it is not recoverable from `esk`.
135        hash: zeroize::Zeroizing<[u8; 64]>,
136    },
137    Regular(ed25519_dalek::SigningKey),
138}
139
140impl Key {
141    fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
142        match bytes.len() {
143            // It's a new/correct-style secret key.
144            32 => bytes
145                .try_into()
146                .map(ed25519_dalek::SigningKey::from_bytes)
147                .map(Self::Regular)
148                .map_err(|_| Error::MalformedPrivateKey),
149            // It's an "expanded" secret key, which is treated as the output of a 64-byte hash function.
150            64 => bytes
151                .try_into()
152                .map(|hash| Self::Expanded {
153                    esk: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&hash),
154                    hash: hash.into(),
155                })
156                .map_err(|_| Error::MalformedPrivateKey),
157            _ => Err(Error::MalformedPrivateKey),
158        }
159    }
160
161    fn to_bytes(&self) -> Vec<u8> {
162        match self {
163            Self::Expanded { hash, .. } => hash.to_vec(),
164            Self::Regular(sk) => sk.to_bytes().to_vec(),
165        }
166    }
167
168    fn sign(&self, message: &[u8]) -> Signature {
169        match self {
170            Self::Expanded { esk, .. } => {
171                let verifying_key = ed25519_dalek::VerifyingKey::from(esk);
172                ed25519_dalek::hazmat::raw_sign::<Sha512>(esk, message, &verifying_key)
173            }
174            Self::Regular(sk) => sk.sign(message),
175        }
176        .to_bytes()
177        .to_vec()
178        .into()
179    }
180
181    fn sign_digest<D>(&self, digest: D) -> Result<Signature, Error>
182    where
183        D: ed25519_dalek::Digest<OutputSize = U64>,
184    {
185        match self {
186            Key::Expanded { esk, .. } => {
187                let verifying_key = ed25519_dalek::VerifyingKey::from(esk);
188                ed25519_dalek::hazmat::raw_sign_prehashed::<Sha512, _>(
189                    esk,
190                    digest,
191                    &verifying_key,
192                    None,
193                )
194            }
195            Key::Regular(sk) => sk.sign_prehashed(digest, None),
196        }
197        .map_err(|_| Error::SigningError)
198        .map(|sig| sig.to_bytes().to_vec().into())
199    }
200
201    fn public_key(&self) -> super::PublicKey {
202        let pk = match self {
203            Self::Expanded { esk, .. } => ed25519_dalek::VerifyingKey::from(esk),
204            Self::Regular(sk) => sk.verifying_key(),
205        };
206        super::PublicKey::Ed25519(PublicKey::from_bytes(pk.as_bytes()).unwrap())
207    }
208}
209
210impl MemorySigner {
211    pub fn sign_digest<D>(&self, digest: D) -> Result<Signature, Error>
212    where
213        D: ed25519_dalek::Digest<OutputSize = U64>,
214    {
215        self.key.sign_digest(digest)
216    }
217}
218
219impl Signer for MemorySigner {
220    fn random(rng: &mut (impl RngCore + CryptoRng)) -> Result<Self, Error> {
221        let mut seed = [0u8; 32];
222        rng.fill_bytes(&mut seed);
223        Self::new_from_seed(&seed)
224    }
225
226    fn new_from_seed(seed: &[u8]) -> Result<Self, Error> {
227        if seed.len() != 32 {
228            return Err(Error::MalformedPublicKey);
229        }
230        Self::from_bytes(seed)
231    }
232
233    fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
234        Ok(Self {
235            key: Key::from_bytes(bytes)?,
236        })
237    }
238
239    fn to_bytes(&self) -> Vec<u8> {
240        self.key.to_bytes()
241    }
242
243    fn public_key(&self) -> super::PublicKey {
244        self.key.public_key()
245    }
246
247    fn sign(&self, context: &[u8], message: &[u8]) -> Result<Signature, Error> {
248        let mut digest = Sha512_256::new();
249        digest.update(context);
250        digest.update(message);
251        Ok(self.key.sign(&digest.finalize()))
252    }
253
254    fn sign_raw(&self, message: &[u8]) -> Result<Signature, Error> {
255        Ok(self.key.sign(message))
256    }
257}
258
259#[cfg(test)]
260mod test {
261    use super::*;
262
263    #[test]
264    fn legacy_esk_equivalence() {
265        let seed = [42u8; 32];
266        let signer = MemorySigner::new_from_seed(&seed).unwrap();
267
268        let esk = ed25519_dalek::hazmat::ExpandedSecretKey::from(&seed);
269        let esk_hash = Sha512::digest(seed);
270        let esk_signer = MemorySigner::from_bytes(&esk_hash).unwrap();
271
272        let esk_public_key = super::super::PublicKey::Ed25519(
273            PublicKey::from_bytes(&ed25519_dalek::VerifyingKey::from(&esk).to_bytes()).unwrap(),
274        );
275
276        assert_eq!(
277            esk_signer.to_bytes().as_slice(),
278            esk_hash.as_slice(),
279            "esk roundtrip"
280        );
281        assert_eq!(signer.to_bytes(), seed, "sk roundtrip");
282
283        let context = b"tests";
284        let message = b"hello, world!";
285        let digest = Sha512::new().chain_update(context).chain_update(message);
286
287        let sig = signer.sign(context, message).unwrap();
288        let esk_sig = esk_signer.sign(context, message).unwrap();
289        assert_eq!(sig, esk_sig, "sig != esk_sig");
290
291        let raw_sig = signer.sign_raw(message).unwrap();
292        let esk_raw_sig = esk_signer.sign_raw(message).unwrap();
293        assert_eq!(raw_sig, esk_raw_sig, "raw_sig != esk_raw_sig");
294
295        let digest_sig = signer.sign_digest(digest.clone()).unwrap();
296        let esk_digest_sig = esk_signer.sign_digest(digest).unwrap();
297        assert_eq!(digest_sig, esk_digest_sig, "digest_sig != esk_digest_sig");
298
299        assert_eq!(
300            signer.public_key(),
301            esk_public_key,
302            "signer pk != esk_public_key"
303        );
304        assert_eq!(
305            esk_signer.public_key(),
306            esk_public_key,
307            "esk_signer pk != esk_pubic_key"
308        );
309    }
310}