use mc_core::keys::SubaddressViewPrivate;
use mc_crypto_digestible::MerlinTranscript;
use mc_crypto_keys::{RistrettoPrivate, RistrettoSignature};
use rand_core::{
block::{BlockRng, BlockRngCore},
SeedableRng,
};
use rand_hc::Hc128Core;
use schnorrkel_og::{context::attach_rng, SecretKey as SchnorrkelPrivate};
#[inline(never)]
fn schnorrkel_nonce(private_key: &RistrettoPrivate, context: &[u8], message: &[u8]) -> [u8; 32] {
let mut transcript = MerlinTranscript::new(b"SigningNonce");
transcript.append_message(b"context", context);
transcript.append_message(b"private", private_key.as_ref());
transcript.append_message(b"message", message);
let mut nonce = [0u8; 32];
transcript.challenge_bytes(b"nonce", &mut nonce);
nonce
}
#[inline(never)]
fn schnorrkel_secret(private_key: &RistrettoPrivate, nonce: &[u8; 32]) -> SchnorrkelPrivate {
let mut secret_bytes = [0u8; 64];
secret_bytes[0..32].copy_from_slice(private_key.as_ref());
secret_bytes[32..64].copy_from_slice(&nonce[..]);
SchnorrkelPrivate::from_bytes(&secret_bytes).unwrap()
}
#[inline(never)]
fn schnorrkel_sign(
private_key: &RistrettoPrivate,
context: &[u8],
message: &[u8],
) -> RistrettoSignature {
let nonce = schnorrkel_nonce(private_key, context, message);
let secret_key = schnorrkel_secret(private_key, &nonce);
let keypair = secret_key.to_keypair();
let mut t = MerlinTranscript::new(b"SigningContext");
t.append_message(b"", context);
t.append_message(b"sign-bytes", message);
let mut core = Hc128Core::from_seed(nonce);
let container = Hc128CoreContainer(&mut core);
let mut csprng = BlockRng::new(container);
let mut transcript = attach_rng(t, &mut csprng);
RistrettoSignature::from(keypair.sign(&mut transcript))
}
struct Hc128CoreContainer<'a>(&'a mut Hc128Core);
impl<'a> BlockRngCore for Hc128CoreContainer<'a> {
type Item = u32;
type Results = [u32; 16];
fn generate(&mut self, results: &mut Self::Results) {
self.0.generate(results);
}
}
impl<'a> rand_core::CryptoRng for Hc128CoreContainer<'a> {}
const CONTEXT: &[u8] = b"Fog authority signature";
#[inline(never)]
pub fn sign_authority(
private_key: &SubaddressViewPrivate,
spki_bytes: &[u8],
) -> RistrettoSignature {
schnorrkel_sign(private_key.as_ref(), CONTEXT, spki_bytes)
}
#[cfg(test)]
mod test {
use mc_account_keys::{AccountKey, DEFAULT_SUBADDRESS_INDEX};
use mc_crypto_keys::RistrettoPublic;
use mc_fog_sig_authority::{Signer, Verifier};
use mc_util_from_random::FromRandom;
use rand_core::OsRng;
use ledger_mob_apdu::tx::FogId;
use super::*;
use crate::engine::FogCert;
const FOGS: &[FogId] = &[
FogId::MobMain,
FogId::MobTest,
FogId::SignalMain,
FogId::SignalTest,
];
#[test]
fn schnorrkel_sign_verify() {
for _i in 0..10 {
let k = RistrettoPrivate::from_random(&mut OsRng {});
let p = RistrettoPublic::from(&k);
let spki = FogId::MobMain.spki();
let s1 = k.sign_authority(spki).unwrap();
p.verify_authority(spki, &s1).unwrap();
let s2 = sign_authority(&k.into(), spki);
p.verify_authority(spki, &s2).unwrap();
assert_eq!(s1, s2);
}
}
#[test]
fn fog_authority_sigs() {
for f in FOGS {
for _i in 0..10 {
let spki = f.spki();
let a = AccountKey::random(&mut OsRng {}).with_fog(f.url(), "", spki);
let subaddr = a.subaddress(DEFAULT_SUBADDRESS_INDEX);
let view_private = a.default_subaddress_view_private();
let sig: [u8; 64] = sign_authority(&view_private.into(), spki).into();
subaddr
.view_public_key()
.verify_authority(spki, &RistrettoSignature::try_from(&sig[..]).unwrap())
.unwrap();
assert_eq!(
sig,
subaddr.fog_authority_sig().unwrap(),
"Fog authority signature mismatch"
);
}
}
}
}