pub mod secp256k1;
#[cfg(feature = "transact-compat")]
pub mod transact;
use std;
use std::borrow::Borrow;
use std::error::Error as StdError;
#[derive(Debug)]
pub enum Error {
NoSuchAlgorithm(String),
ParseError(String),
SigningError(Box<dyn StdError>),
KeyGenError(String),
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::NoSuchAlgorithm(ref msg) => msg,
Error::ParseError(ref msg) => msg,
Error::SigningError(ref err) => err.description(),
Error::KeyGenError(ref msg) => msg,
}
}
fn cause(&self) -> Option<&dyn StdError> {
match *self {
Error::NoSuchAlgorithm(_) => None,
Error::ParseError(_) => None,
Error::SigningError(ref err) => Some(err.borrow()),
Error::KeyGenError(_) => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Error::NoSuchAlgorithm(ref s) => write!(f, "NoSuchAlgorithm: {}", s),
Error::ParseError(ref s) => write!(f, "ParseError: {}", s),
Error::SigningError(ref err) => write!(f, "SigningError: {}", err.description()),
Error::KeyGenError(ref s) => write!(f, "KeyGenError: {}", s),
}
}
}
pub trait PrivateKey {
fn get_algorithm_name(&self) -> &str;
fn as_hex(&self) -> String;
fn as_slice(&self) -> &[u8];
}
pub trait PublicKey {
fn get_algorithm_name(&self) -> &str;
fn as_hex(&self) -> String;
fn as_slice(&self) -> &[u8];
}
pub trait Context {
fn get_algorithm_name(&self) -> &str;
fn sign(&self, message: &[u8], key: &dyn PrivateKey) -> Result<String, Error>;
fn verify(&self, signature: &str, message: &[u8], key: &dyn PublicKey) -> Result<bool, Error>;
fn get_public_key(&self, private_key: &dyn PrivateKey) -> Result<Box<dyn PublicKey>, Error>;
fn new_random_private_key(&self) -> Result<Box<dyn PrivateKey>, Error>;
}
pub fn create_context(algorithm_name: &str) -> Result<Box<dyn Context>, Error> {
match algorithm_name {
"secp256k1" => Ok(Box::new(secp256k1::Secp256k1Context::new())),
_ => Err(Error::NoSuchAlgorithm(format!(
"no such algorithm: {}",
algorithm_name
))),
}
}
pub struct CryptoFactory<'a> {
context: &'a dyn Context,
}
impl<'a> CryptoFactory<'a> {
pub fn new(context: &'a dyn Context) -> Self {
CryptoFactory { context }
}
pub fn get_context(&self) -> &dyn Context {
self.context
}
pub fn new_signer(&self, key: &'a dyn PrivateKey) -> Signer {
Signer::new(self.context, key)
}
}
enum ContextAndKey<'a> {
ByRef(&'a dyn Context, &'a dyn PrivateKey),
ByBox(Box<dyn Context>, Box<dyn PrivateKey>),
}
pub struct Signer<'a> {
context_and_key: ContextAndKey<'a>,
}
impl<'a> Signer<'a> {
pub fn new(context: &'a dyn Context, key: &'a dyn PrivateKey) -> Self {
Signer {
context_and_key: ContextAndKey::ByRef(context, key),
}
}
pub fn new_boxed(context: Box<dyn Context>, key: Box<dyn PrivateKey>) -> Self {
Signer {
context_and_key: ContextAndKey::ByBox(context, key),
}
}
pub fn sign(&self, message: &[u8]) -> Result<String, Error> {
match &self.context_and_key {
ContextAndKey::ByRef(context, key) => context.sign(message, *key),
ContextAndKey::ByBox(context, key) => context.sign(message, key.as_ref()),
}
}
pub fn get_public_key(&self) -> Result<Box<dyn PublicKey>, Error> {
match &self.context_and_key {
ContextAndKey::ByRef(context, key) => context.get_public_key(*key),
ContextAndKey::ByBox(context, key) => context.get_public_key(key.as_ref()),
}
}
}
fn hex_str_to_bytes(s: &str) -> Result<Vec<u8>, Error> {
for (i, ch) in s.chars().enumerate() {
if !ch.is_digit(16) {
return Err(Error::ParseError(format!(
"invalid character position {}",
i
)));
}
}
let input: Vec<_> = s.chars().collect();
let decoded: Vec<u8> = input
.chunks(2)
.map(|chunk| {
((chunk[0].to_digit(16).unwrap() << 4) | (chunk[1].to_digit(16).unwrap())) as u8
})
.collect();
Ok(decoded)
}
fn bytes_to_hex_str(b: &[u8]) -> String {
b.iter()
.map(|b| format!("{:02x}", b))
.collect::<Vec<_>>()
.join("")
}
#[cfg(test)]
mod signing_test {
use super::create_context;
#[test]
fn no_such_algorithm() {
let result = create_context("invalid");
assert!(result.is_err())
}
}