#![deny(missing_docs)]
#![deny(unsafe_code)]
use anyhow::ensure;
use clap::ValueEnum;
use essential_types::{convert::word_4_from_u8_32, Hash, Word};
use serde::{Deserialize, Serialize};
use sha2::Digest;
pub use ed25519_dalek;
pub use secp256k1;
#[derive(ValueEnum, Clone, Copy, Debug)]
pub enum Encoding {
Bytes,
Hex,
HexUpper,
Base64,
Base64UrlNoPad,
}
#[derive(ValueEnum, Clone, Copy, Debug)]
pub enum Padding {
Start,
End,
}
#[derive(Clone, Copy)]
pub enum Key {
Secp256k1(secp256k1::SecretKey),
Ed25519(ed25519_dalek::SecretKey),
}
#[derive(Clone, Copy, Debug)]
pub enum PublicKey {
Secp256k1(secp256k1::PublicKey),
Ed25519(ed25519_dalek::VerifyingKey),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Signature {
Secp256k1(secp256k1::ecdsa::RecoverableSignature),
Ed25519(ed25519_dalek::Signature),
}
pub fn sign_postcard<T: Serialize>(data: &T, private_key: &Key) -> anyhow::Result<Signature> {
let data = postcard_bytes(data)?;
let hash = hash_bytes(&data)?;
sign_hash(hash, private_key)
}
pub fn sign_postcard_with_padding<T: Serialize>(
data: &T,
padding: Padding,
private_key: &Key,
) -> anyhow::Result<Signature> {
let data = postcard_bytes_with_padding(data, padding)?;
let hash = hash_bytes(&data)?;
sign_hash(hash, private_key)
}
pub fn sign_words(data: &[Word], private_key: &Key) -> anyhow::Result<Signature> {
let hash = hash_words(data);
sign_hash(hash, private_key)
}
pub fn sign_bytes_with_padding(
data: Vec<u8>,
padding: Padding,
private_key: &Key,
) -> anyhow::Result<Signature> {
let data = align_to_word(data, padding);
let hash = hash_bytes(&data)?;
sign_hash(hash, private_key)
}
pub fn sign_aligned_bytes(data: &[u8], private_key: &Key) -> anyhow::Result<Signature> {
ensure!(is_word_aligned(data), "Data is not word aligned");
let hash = hash_bytes(data)?;
sign_hash(hash, private_key)
}
pub fn sign_bytes_unchecked(data: &[u8], private_key: &Key) -> anyhow::Result<Signature> {
let hash = hash_bytes(data)?;
sign_hash(hash, private_key)
}
pub fn sign_hash(hash: Hash, private_key: &Key) -> anyhow::Result<Signature> {
match private_key {
Key::Secp256k1(private_key) => {
let sig = essential_sign::sign_hash(hash, private_key);
let sig = secp256k1::ecdsa::RecoverableSignature::from_compact(
&sig.0,
secp256k1::ecdsa::RecoveryId::from_i32(sig.1 as i32)?,
)?;
Ok(Signature::Secp256k1(sig))
}
Key::Ed25519(_) => todo!(),
}
}
pub fn read_file(path: &std::path::Path) -> anyhow::Result<Vec<u8>> {
use std::io::Read;
let mut file = std::fs::File::open(path)?;
let mut data = Vec::new();
file.read_to_end(&mut data)?;
Ok(data)
}
#[derive(Deserialize, Serialize)]
struct Bytes(#[serde(with = "serde_bytes")] Vec<u8>);
pub fn decode_str(data: String, encoding: Encoding) -> anyhow::Result<Vec<u8>> {
match encoding {
Encoding::Bytes => {
let Bytes(data) = serde_json::from_str(&data)?;
Ok(data)
}
Encoding::Hex | Encoding::HexUpper => Ok(hex::decode(data)?),
Encoding::Base64 => {
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
Ok(STANDARD.decode(data.as_bytes())?)
}
Encoding::Base64UrlNoPad => {
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
Ok(URL_SAFE_NO_PAD.decode(data.as_bytes())?)
}
}
}
pub fn encode_str(data: Vec<u8>, encoding: Encoding) -> anyhow::Result<String> {
match encoding {
Encoding::Bytes => Ok(serde_json::to_string(&Bytes(data))?),
Encoding::Hex => Ok(hex::encode(data)),
Encoding::HexUpper => Ok(hex::encode_upper(data)),
Encoding::Base64 => {
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
Ok(STANDARD.encode(data))
}
Encoding::Base64UrlNoPad => {
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
Ok(URL_SAFE_NO_PAD.encode(data))
}
}
}
pub fn into_words(data: Vec<u8>, padding: Padding) -> Vec<Word> {
let data = align_to_word(data, padding);
data.chunks(8)
.map(|chunk| {
essential_types::convert::word_from_bytes(
chunk.try_into().expect("This can't fail because of chunks"),
)
})
.collect::<Vec<Word>>()
}
pub fn align_to_word(data: Vec<u8>, padding: Padding) -> Vec<u8> {
if is_word_aligned(&data) {
data
} else {
pad_bytes(data, padding)
}
}
pub fn is_word_aligned(data: &[u8]) -> bool {
data.len() % 8 == 0
}
pub fn pad_bytes(mut data: Vec<u8>, padding: Padding) -> Vec<u8> {
match padding {
Padding::Start => {
let len = data.len();
let pad = 8 - len % 8;
let mut padded = vec![0; pad];
padded.extend(data);
padded
}
Padding::End => {
let len = data.len();
let pad = 8 - len % 8;
data.extend(std::iter::repeat(0).take(pad));
data
}
}
}
pub fn hash_bytes(data: &[u8]) -> anyhow::Result<Hash> {
let mut hasher = <sha2::Sha256 as sha2::Digest>::new();
hasher.update(data);
Ok(hasher.finalize().into())
}
pub fn hash_words(data: &[Word]) -> Hash {
essential_hash::hash_words(data)
}
pub fn to_essential_signature(
sig: secp256k1::ecdsa::RecoverableSignature,
) -> anyhow::Result<essential_types::Signature> {
let (rec_id, data) = sig.serialize_compact();
Ok(essential_types::Signature(
data,
rec_id.to_i32().try_into()?,
))
}
pub fn signature_to_aligned_bytes(sig: &Signature) -> Vec<u8> {
match sig {
Signature::Secp256k1(sig) => essential_sign::encode::signature_as_bytes(sig).to_vec(),
Signature::Ed25519(sig) => sig.to_bytes().to_vec(),
}
}
pub fn signature_to_bytes(sig: &Signature) -> anyhow::Result<Vec<u8>> {
match sig {
Signature::Secp256k1(sig) => {
let (rec_id, data) = sig.serialize_compact();
let mut bytes = data.to_vec();
let rec_id = rec_id.to_i32();
let rec_id: u8 = rec_id.try_into()?;
bytes.push(rec_id);
Ok(bytes)
}
Signature::Ed25519(sig) => Ok(sig.to_bytes().to_vec()),
}
}
pub fn signed_set_to_bytes(
signed_set: &essential_types::contract::SignedContract,
) -> anyhow::Result<Vec<u8>> {
Ok(serde_json::to_vec(signed_set)?)
}
pub fn signature_to_words(sig: &Signature) -> Vec<Word> {
match sig {
Signature::Secp256k1(sig) => essential_sign::encode::signature(sig).to_vec(),
Signature::Ed25519(_) => todo!(),
}
}
pub fn public_key_to_words(key: &PublicKey) -> Vec<Word> {
match key {
PublicKey::Secp256k1(key) => essential_sign::encode::public_key(key).to_vec(),
PublicKey::Ed25519(key) => word_4_from_u8_32(key.to_bytes()).to_vec(),
}
}
pub fn postcard_bytes_with_padding<T: Serialize>(
data: &T,
padding: Padding,
) -> anyhow::Result<Vec<u8>> {
let data = postcard::to_allocvec(data)?;
Ok(align_to_word(data, padding))
}
pub fn postcard_bytes<T: Serialize>(data: &T) -> anyhow::Result<Vec<u8>> {
Ok(postcard::to_allocvec(data)?)
}
pub fn public_key(private_key: &Key) -> PublicKey {
match private_key {
Key::Secp256k1(key) => {
let secp = secp256k1::Secp256k1::new();
PublicKey::Secp256k1(key.public_key(&secp))
}
Key::Ed25519(key) => {
let key = ed25519_dalek::SigningKey::from_bytes(key);
PublicKey::Ed25519(key.verifying_key())
}
}
}