#![forbid(unsafe_code)]
use oxicrypto_core::{CryptoError, SecretKey, Signer, Vec, Verifier};
use k256::schnorr::{Signature, SigningKey, VerifyingKey};
use sha2::{Digest, Sha256};
pub const SECRET_KEY_LEN: usize = 32;
pub const PUBLIC_KEY_LEN: usize = 32;
pub const SIGNATURE_LEN: usize = 64;
const ZERO_AUX: [u8; 32] = [0u8; 32];
#[derive(Debug, Default, Clone, Copy)]
pub struct SchnorrBip340;
impl SchnorrBip340 {
#[must_use = "result must be checked"]
pub fn parse_secret_key(sk: &[u8]) -> Result<SecretKey<SECRET_KEY_LEN>, CryptoError> {
let secret = SecretKey::<SECRET_KEY_LEN>::from_slice(sk)?;
let _ = signing_key_from_secret(&secret)?;
Ok(secret)
}
#[must_use = "result must be checked"]
pub fn derive_public_key(&self, sk: &[u8]) -> Result<[u8; PUBLIC_KEY_LEN], CryptoError> {
let secret = SecretKey::<SECRET_KEY_LEN>::from_slice(sk)?;
let signing_key = signing_key_from_secret(&secret)?;
Ok(verifying_key_to_xonly(signing_key.verifying_key()))
}
#[must_use = "result must be checked"]
pub fn parse_public_key(pk: &[u8]) -> Result<[u8; PUBLIC_KEY_LEN], CryptoError> {
let verifying_key = verifying_key_from_xonly(pk)?;
Ok(verifying_key_to_xonly(&verifying_key))
}
#[must_use = "signature result must be checked"]
pub fn sign_with_aux(
&self,
sk: &[u8],
msg: &[u8],
aux_rand: &[u8; 32],
) -> Result<[u8; SIGNATURE_LEN], CryptoError> {
let secret = SecretKey::<SECRET_KEY_LEN>::from_slice(sk)?;
let signing_key = signing_key_from_secret(&secret)?;
let signature = signing_key
.sign_raw(msg, aux_rand)
.map_err(|_| CryptoError::Sign)?;
Ok(signature.to_bytes())
}
#[must_use = "verification result must be checked"]
pub fn verify_message(&self, pk: &[u8], msg: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
let verifying_key = verifying_key_from_xonly(pk)?;
let signature = parse_signature(sig)?;
verifying_key
.verify_raw(msg, &signature)
.map_err(|_| CryptoError::Sign)
}
#[must_use = "signature result must be checked"]
pub fn sign_sha256(&self, sk: &[u8], msg: &[u8]) -> Result<[u8; SIGNATURE_LEN], CryptoError> {
let digest = sha256(msg);
self.sign_with_aux(sk, &digest, &ZERO_AUX)
}
#[must_use = "verification result must be checked"]
pub fn verify_sha256(&self, pk: &[u8], msg: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
let digest = sha256(msg);
self.verify_message(pk, &digest, sig)
}
}
impl Signer for SchnorrBip340 {
fn name(&self) -> &'static str {
"Schnorr-BIP340"
}
fn signature_len(&self) -> usize {
SIGNATURE_LEN
}
fn sign(&self, sk: &[u8], msg: &[u8], sig_out: &mut [u8]) -> Result<usize, CryptoError> {
if sig_out.len() < SIGNATURE_LEN {
return Err(CryptoError::BufferTooSmall);
}
let signature = self.sign_with_aux(sk, msg, &ZERO_AUX)?;
sig_out[..SIGNATURE_LEN].copy_from_slice(&signature);
Ok(SIGNATURE_LEN)
}
}
impl Verifier for SchnorrBip340 {
fn name(&self) -> &'static str {
"Schnorr-BIP340"
}
fn verify(&self, pk: &[u8], msg: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
self.verify_message(pk, msg, sig)
}
}
fn signing_key_from_secret(secret: &SecretKey<SECRET_KEY_LEN>) -> Result<SigningKey, CryptoError> {
let field_bytes: &k256::FieldBytes = secret.as_bytes().into();
SigningKey::from_bytes(field_bytes).map_err(|_| CryptoError::InvalidKey)
}
fn verifying_key_from_xonly(pk: &[u8]) -> Result<VerifyingKey, CryptoError> {
if pk.len() != PUBLIC_KEY_LEN {
return Err(CryptoError::InvalidKey);
}
VerifyingKey::from_slice(pk).map_err(|_| CryptoError::InvalidKey)
}
fn verifying_key_to_xonly(vk: &VerifyingKey) -> [u8; PUBLIC_KEY_LEN] {
let field_bytes = vk.to_bytes();
let mut out = [0u8; PUBLIC_KEY_LEN];
out.copy_from_slice(field_bytes.as_slice());
out
}
fn parse_signature(sig: &[u8]) -> Result<Signature, CryptoError> {
if sig.len() != SIGNATURE_LEN {
return Err(CryptoError::InvalidTag);
}
Signature::try_from(sig).map_err(|_| CryptoError::InvalidTag)
}
fn sha256(msg: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(msg);
let digest = hasher.finalize();
let mut out = [0u8; 32];
out.copy_from_slice(&digest);
out
}
#[must_use = "signature result must be checked"]
pub fn schnorr_bip340_sign_with_aux(
sk: &[u8],
msg: &[u8],
aux_rand: &[u8; 32],
) -> Result<Vec<u8>, CryptoError> {
let scheme = SchnorrBip340;
Ok(scheme.sign_with_aux(sk, msg, aux_rand)?.to_vec())
}