Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration of Falcon DSA into VM #1068

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assembly/src/ast/nodes/advice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum AdviceInjectorNode {
InsertMem,
InsertHdword,
InsertHdwordImm { domain: u8 },
FalconSign,
bobbinth marked this conversation as resolved.
Show resolved Hide resolved
}

impl From<&AdviceInjectorNode> for AdviceInjector {
Expand Down Expand Up @@ -57,6 +58,7 @@ impl From<&AdviceInjectorNode> for AdviceInjector {
InsertHdwordImm { domain } => Self::HdwordToMap {
domain: Felt::from(*domain),
},
FalconSign => Self::FalconSign,
}
}
}
Expand All @@ -76,6 +78,7 @@ impl fmt::Display for AdviceInjectorNode {
InsertMem => write!(f, "insert_mem"),
InsertHdword => write!(f, "insert_hdword"),
InsertHdwordImm { domain } => write!(f, "insert_hdword.{domain}"),
FalconSign => write!(f, "falcon_sign"),
}
}
}
Expand All @@ -94,6 +97,7 @@ const PUSH_MTNODE: u8 = 7;
const INSERT_MEM: u8 = 8;
const INSERT_HDWORD: u8 = 9;
const INSERT_HDWORD_IMM: u8 = 10;
const FALCON_SIGN: u8 = 11;

impl Serializable for AdviceInjectorNode {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
Expand All @@ -119,6 +123,7 @@ impl Serializable for AdviceInjectorNode {
target.write_u8(INSERT_HDWORD_IMM);
target.write_u8(*domain);
}
FalconSign => target.write_u8(FALCON_SIGN),
}
}
}
Expand Down Expand Up @@ -152,6 +157,7 @@ impl Deserializable for AdviceInjectorNode {
let domain = source.read_u8()?;
Ok(AdviceInjectorNode::InsertHdwordImm { domain })
}
FALCON_SIGN => Ok(AdviceInjectorNode::FalconSign),
val => Err(DeserializationError::InvalidValue(val.to_string())),
}
}
Expand Down
5 changes: 5 additions & 0 deletions assembly/src/ast/parsers/adv_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ pub fn parse_adv_inject(op: &Token) -> Result<Node, ParsingError> {
}
_ => return Err(ParsingError::extra_param(op)),
},
"falcon_sign" => match op.num_parts() {
2 => AdvInject(FalconSign),
_ => return Err(ParsingError::extra_param(op)),
},
bobbinth marked this conversation as resolved.
Show resolved Hide resolved

_ => return Err(ParsingError::invalid_op(op)),
};

Expand Down
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ std = ["math/std", "winter-utils/std"]

[dependencies]
math = { package = "winter-math", version = "0.6", default-features = false }
crypto = { package = "miden-crypto", git = "https://github.com/0xPolygonMiden/crypto.git", branch = "next", default-features = false }
miden-crypto = {git = "https://github.com/0xPolygonMiden/crypto", branch = "al-bindings_second_attempt" }
winter-crypto = { package = "winter-crypto", version = "0.6", default-features = false }
winter-utils = { package = "winter-utils", version = "0.6", default-features = false }

Expand Down
10 changes: 7 additions & 3 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ extern crate alloc;
pub mod chiplets;
pub mod errors;

pub use ::crypto::{Word, ONE, WORD_SIZE, ZERO};
pub use ::miden_crypto::{Word, ONE, WORD_SIZE, ZERO};
pub mod crypto {
pub mod merkle {
pub use ::crypto::merkle::{
pub use ::miden_crypto::merkle::{
DefaultMerkleStore, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath,
MerkleStore, MerkleTree, Mmr, MmrPeaks, NodeIndex, PartialMerkleTree,
RecordingMerkleStore, SimpleSmt, StoreNode, TieredSmt,
};
}

pub mod hash {
pub use ::crypto::hash::{
pub use ::miden_crypto::hash::{
blake::{Blake3Digest, Blake3_160, Blake3_192, Blake3_256},
rpo::{Rpo256, RpoDigest},
ElementHasher, Hasher,
Expand All @@ -28,6 +28,10 @@ pub mod crypto {
pub mod random {
pub use crate::random::*;
}

pub mod dsa {
pub use ::miden_crypto::falcon::*;
}
}

pub use math::{
Expand Down
4 changes: 4 additions & 0 deletions core/src/operations/decorators/advice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ pub enum AdviceInjector {
/// Where KEY is computed as hash(A || B, domain), where domain is provided via the immediate
/// value.
HdwordToMap { domain: Felt },

/// TODO
FalconSign,
}

impl fmt::Display for AdviceInjector {
Expand All @@ -204,6 +207,7 @@ impl fmt::Display for AdviceInjector {
Self::SmtGet => write!(f, "smt_get"),
Self::MemToMap => write!(f, "mem_to_map"),
Self::HdwordToMap { domain } => write!(f, "hdword_to_map.{domain}"),
Self::FalconSign => write!(f, "falcon_sign"),
}
}
}
2 changes: 1 addition & 1 deletion core/src/random.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{crypto::hash::Rpo256, utils::collections::Vec, Felt, FieldElement};
use crypto::{hash::rpo::RpoDigest, Word, ZERO};
use math::StarkField;
use miden_crypto::{hash::rpo::RpoDigest, Word, ZERO};

// RE-EXPORTS
// ================================================================================================
Expand Down
2 changes: 1 addition & 1 deletion core/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub use winter_utils::{
ByteWriter, Deserializable, DeserializationError, Serializable, SliceReader,
};

pub use crypto::utils::collections;
pub use miden_crypto::utils::collections;

pub mod math {
pub use math::{batch_inversion, log2};
Expand Down
7 changes: 7 additions & 0 deletions processor/src/advice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ pub trait AdviceProvider {
/// are replaced with the specified values.
fn insert_into_map(&mut self, key: Word, values: Vec<Felt>) -> Result<(), ExecutionError>;

/// TODO
fn falcon_sign(&self, pub_key: Word, msg: Word) -> Result<Vec<Felt>, ExecutionError>;
bobbinth marked this conversation as resolved.
Show resolved Hide resolved

// ADVICE MAP
// --------------------------------------------------------------------------------------------
/// Returns a reference to the value(s) associated with the specified key in the advice map.
Expand Down Expand Up @@ -285,4 +288,8 @@ where
fn advance_clock(&mut self) {
T::advance_clock(self)
}

fn falcon_sign(&self, pub_key: Word, msg: Word) -> Result<Vec<Felt>, ExecutionError> {
T::falcon_sign(self, pub_key, msg)
}
}
94 changes: 94 additions & 0 deletions processor/src/advice/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use super::{
AdviceInputs, AdviceProvider, AdviceSource, BTreeMap, ExecutionError, Felt, IntoBytes, KvMap,
MerklePath, MerkleStore, NodeIndex, RecordingMap, RpoDigest, StarkField, StoreNode, Vec, Word,
};
use vm_core::crypto::dsa::{
elements_as_bytes, Polynomial, PublicKeyBytes, SecretKey, SecretKeyBytes, PK_LEN, SK_LEN,
};

// TYPE ALIASES
// ================================================================================================
Expand Down Expand Up @@ -106,6 +109,63 @@ where
Ok(())
}

fn falcon_sign(&self, pub_key: Word, msg: Word) -> Result<Vec<Felt>, ExecutionError> {
let pk_sk = self
.map
.get(&pub_key.into_bytes())
.ok_or(ExecutionError::AdviceKeyNotFound(pub_key))?;

if pk_sk.len() != (PK_LEN + SK_LEN) {
return Err(ExecutionError::AdviceStackReadFailed(0));
}

// To generate a signature, we need the expanded key as well as the secret key
let pk_exp = pk_sk[..PK_LEN].to_vec();
let pk_exp: PublicKeyBytes = vec_felt_to_u8(&pk_exp)
.try_into()
.expect("Should not fail as we've checked the length of the combined vector");
bobbinth marked this conversation as resolved.
Show resolved Hide resolved

let sk = pk_sk[PK_LEN..].to_vec();
let sk: SecretKeyBytes = vec_felt_to_u8(&sk)
.try_into()
.expect("Should not fail as we've checked the length of the combined vector");
let sk = SecretKey::new(sk);

// We need to convert the message to a byte array
let msg_u64 = vec_felt_to_u64(&msg);
let msg = elements_as_bytes(&msg_u64);

// We can now generate the signature
let sig = sk.sign(msg, pk_exp);

// The signature is composed of a nonce and a polynomial s2

// We first convert the nonce, a [40; u8], to 8 field elements.
let nonce = sig.nonce();
let nonce = convert_nonce(nonce);

// We convert the signature to a polynomial
let s2: Polynomial = (&sig).into();

// We also need in the VM the expanded key corresponding to the public key the was provided
// via the operand stack
let h: Polynomial = Polynomial::from_pubkey(&pk_exp);

// Lastly, for the probabilistic product routine that is part of the verification procedure,
// we need to compute the product of the expanded key and the signature polynomial in
// the ring of polynomials with coefficients in the Miden field.
let pi = Polynomial::mul_modulo_p(&h, &s2);

// We now push the nonce, the expanded key, the signature polynomial, and the product of the
// expanded key and the signature polynomial to the advice stack.
let mut result: Vec<Felt> = nonce.iter().map(|a| Felt::new(*a)).collect::<Vec<Felt>>();
result.extend(h.inner().iter().map(|a| Felt::new(*a as u64)).collect::<Vec<Felt>>());
result.extend(s2.inner().iter().map(|&a| Felt::new(a as u64)).collect::<Vec<Felt>>());
result.extend(pi.iter().map(|&a| Felt::new(a)).collect::<Vec<Felt>>());
result.reverse();
Ok(result)
}

// ADVICE MAP
// --------------------------------------------------------------------------------------------
fn get_mapped_values(&self, key: &[u8; 32]) -> Option<&[Felt]> {
Expand Down Expand Up @@ -271,6 +331,10 @@ impl AdviceProvider for MemAdviceProvider {
self.provider.insert_into_map(key, values)
}

fn falcon_sign(&self, pub_key: Word, msg: Word) -> Result<Vec<Felt>, ExecutionError> {
self.provider.falcon_sign(pub_key, msg)
}

fn get_mapped_values(&self, key: &[u8; 32]) -> Option<&[Felt]> {
self.provider.get_mapped_values(key)
}
Expand Down Expand Up @@ -396,6 +460,10 @@ impl AdviceProvider for RecAdviceProvider {
self.provider.insert_into_map(key, values)
}

fn falcon_sign(&self, pub_key: Word, msg: Word) -> Result<Vec<Felt>, ExecutionError> {
self.provider.falcon_sign(pub_key, msg)
}

fn get_mapped_values(&self, key: &[u8; 32]) -> Option<&[Felt]> {
self.provider.get_mapped_values(key)
}
Expand Down Expand Up @@ -468,3 +536,29 @@ impl RecAdviceProvider {
(proof, stack, map, store.into())
}
}

// HELPERS
// ================================================================================================

fn vec_felt_to_u8(felts: &[Felt]) -> Vec<u8> {
felts.iter().map(|f| f.as_int() as u8).collect()
}
fn vec_felt_to_u64(felts: &[Felt]) -> Vec<u64> {
felts.iter().map(|f| f.as_int()).collect()
}
fn convert_nonce(nonce: &[u8]) -> Vec<u64> {
let mut result = vec![];
for i in 0..8 {
result.push(u64::from_le_bytes([
nonce[i * 5],
nonce[i * 5 + 1],
nonce[i * 5 + 2],
nonce[i * 5 + 3],
nonce[i * 5 + 4],
0,
0,
0,
]));
}
result
}
45 changes: 45 additions & 0 deletions processor/src/decorators/adv_stack_injectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,51 @@ where

Ok(())
}

/// Pushes values onto the advice stack which are required for a Falcon signature verification.
/// These are:
///
/// 1. The nonce represented as 8 field elements.
/// 2. The expanded public key represented as the coefficients of a polynomial of degree < 512.
/// 3. The signature represented as the coefficients of a polynomial of degree < 512.
/// 4. The product of the above two polynomials in the ring of polynomials with coefficients
/// in the Miden field.
///
/// Inputs:
/// Operand stack: [PK, MSG, ...]
/// Advice stack: [...]
/// Advice map: {PK: [pk_raw, sk_raw]}
///
/// Outputs:
/// Operand stack: [PK, MSG, ...]
/// Advice stack: [NONCE1, NONCE0, h, s2, pi]
/// Advice map: {PK: [pk_raw, sk_raw]}
///
/// Where:
/// - PK is the digest of an expanded public.
/// - MSG is the digest of the message to be signed.
/// - [NONCE0, NONCE1] is a double-word representing a 40 bit nonce that is used in the Falcon
/// hash-to-point algorithm.
/// - h is the polynomial representing the expanded public key corresponding to the digest PK.
/// - s2 is the polynomial representing the signature with the secret key associated to PK on
/// the message MSG.
/// - pi is the product of the above two polynomials.
/// - pk_raw are raw bytes of the expanded public key.
/// - sk_raw are raw bytes of the secret key.
///
/// # Errors
/// Will return an error if either:
/// - The advice map does not contain an entry with key PK.
/// - The advice map entry under key PK is not a vector of the expected length.
pub(super) fn push_falcon_signature(&mut self) -> Result<(), ExecutionError> {
let pub_key = self.stack.get_word(0);
let msg = self.stack.get_word(1);
let result: Vec<Felt> = self.advice_provider.falcon_sign(pub_key, msg)?;
for r in result {
self.advice_provider.push_stack(AdviceSource::Value(r))?;
}
Ok(())
}
}

// HELPER FUNCTIONS
Expand Down
1 change: 1 addition & 0 deletions processor/src/decorators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ where
AdviceInjector::SmtGet => self.push_smtget_inputs(),
AdviceInjector::MemToMap => self.insert_mem_values_into_adv_map(),
AdviceInjector::HdwordToMap { domain } => self.insert_hdword_into_adv_map(*domain),
AdviceInjector::FalconSign => self.push_falcon_signature(),
}
}

Expand Down
Loading