1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
//! Sr25519 signatures.
use base64::prelude::*;
use schnorrkel;
use sha2::{Digest, Sha512_256};
use crate::crypto::signature::{Error, Signature};
/// A Sr25519 public key.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, cbor::Encode, cbor::Decode)]
#[cbor(transparent, no_default)]
pub struct PublicKey(Vec<u8>);
impl PublicKey {
/// Return a byte representation of this public key.
pub fn as_bytes(&self) -> &[u8] {
// schnorrkel::keys::PublicKey only has to_bytes, which
// returns a new array.
//
// Since we need to return a reference the easiest way to
// placate the borrow-checker involves just keeping the
// byte-serialized form of the public key instead of the
// decompressed one, and doing point-decompression each
// time we want to actually do something useful.
&self.0
}
/// Construct a public key from a slice of bytes.
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
// Ensure the bytes represents a valid public key.
PublicKey::decompress_public_key(bytes)?;
Ok(PublicKey(bytes.to_vec()))
}
/// Verify a signature.
pub fn verify(
&self,
context: &[u8],
message: &[u8],
signature: &Signature,
) -> Result<(), Error> {
let public_key = PublicKey::decompress_public_key(&self.0)?;
let signature = schnorrkel::Signature::from_bytes(signature.as_ref())
.map_err(|_| Error::MalformedSignature)?;
// 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 Sha512_256 instead of our hash,
// due to the need for FixedOutput.
let mut digest = Sha512_256::new();
digest.update(message);
let transcript = context.hash256(digest);
public_key
.verify(transcript, &signature)
.map_err(|_| Error::VerificationFailed)
}
fn decompress_public_key(bytes: &[u8]) -> Result<schnorrkel::PublicKey, Error> {
schnorrkel::PublicKey::from_bytes(bytes).map_err(|_| Error::MalformedPublicKey)
}
}
impl From<&'static str> for PublicKey {
fn from(s: &'static str) -> PublicKey {
PublicKey::from_bytes(&BASE64_STANDARD.decode(s).unwrap()).unwrap()
}
}