diff --git a/noir_stdlib/src/embedded_curve_ops.nr b/noir_stdlib/src/embedded_curve_ops.nr index d93b4f41cf0..38bc1764b64 100644 --- a/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir_stdlib/src/embedded_curve_ops.nr @@ -71,6 +71,21 @@ impl EmbeddedCurveScalar { let (a,b) = crate::field::bn254::decompose(scalar); EmbeddedCurveScalar { lo: a, hi: b } } + + //Bytes to scalar: take the first (after the specified offset) 16 bytes of the input as the lo value, and the next 16 bytes as the hi value + #[field(bn254)] + fn from_bytes(bytes: [u8; 64], offset: u32) -> EmbeddedCurveScalar { + let mut v = 1; + let mut lo = 0 as Field; + let mut hi = 0 as Field; + for i in 0..16 { + lo = lo + (bytes[offset+31 - i] as Field) * v; + hi = hi + (bytes[offset+15 - i] as Field) * v; + v = v * 256; + } + let sig_s = crate::embedded_curve_ops::EmbeddedCurveScalar { lo, hi }; + sig_s + } } impl Eq for EmbeddedCurveScalar { diff --git a/noir_stdlib/src/schnorr.nr b/noir_stdlib/src/schnorr.nr index 24ca514025c..336041fec19 100644 --- a/noir_stdlib/src/schnorr.nr +++ b/noir_stdlib/src/schnorr.nr @@ -1,3 +1,6 @@ +use crate::collections::vec::Vec; +use crate::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar}; + #[foreign(schnorr_verify)] // docs:start:schnorr_verify pub fn verify_signature( @@ -20,3 +23,65 @@ pub fn verify_signature_slice( // docs:end:schnorr_verify_slice {} +pub fn verify_signature_noir(public_key: EmbeddedCurvePoint, signature: [u8; 64], message: [u8; N]) -> bool { + //scalar lo/hi from bytes + let sig_s = EmbeddedCurveScalar::from_bytes(signature, 0); + let sig_e = EmbeddedCurveScalar::from_bytes(signature, 32); + // pub_key is on Grumpkin curve + let mut is_ok = (public_key.y * public_key.y == public_key.x * public_key.x * public_key.x - 17) + & (!public_key.is_infinite); + + if ((sig_s.lo != 0) | (sig_s.hi != 0)) & ((sig_e.lo != 0) | (sig_e.hi != 0)) { + let (r_is_infinite, result) = calculate_signature_challenge(public_key, sig_s, sig_e, message); + + is_ok = !r_is_infinite; + for i in 0..32 { + is_ok &= result[i] == signature[32 + i]; + } + } + is_ok +} + +pub fn assert_valid_signature(public_key: EmbeddedCurvePoint, signature: [u8; 64], message: [u8; N]) { + //scalar lo/hi from bytes + let sig_s = EmbeddedCurveScalar::from_bytes(signature, 0); + let sig_e = EmbeddedCurveScalar::from_bytes(signature, 32); + + // assert pub_key is on Grumpkin curve + assert(public_key.y * public_key.y == public_key.x * public_key.x * public_key.x - 17); + assert(public_key.is_infinite == false); + // assert signature is not null + assert((sig_s.lo != 0) | (sig_s.hi != 0)); + assert((sig_e.lo != 0) | (sig_e.hi != 0)); + + let (r_is_infinite, result) = calculate_signature_challenge(public_key, sig_s, sig_e, message); + + assert(!r_is_infinite); + for i in 0..32 { + assert(result[i] == signature[32 + i]); + } +} + +fn calculate_signature_challenge( + public_key: EmbeddedCurvePoint, + sig_s: EmbeddedCurveScalar, + sig_e: EmbeddedCurveScalar, + message: [u8; N] +) -> (bool, [u8; 32]) { + let g1 = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false }; + let r = crate::embedded_curve_ops::multi_scalar_mul([g1, public_key], [sig_s, sig_e]); + // compare the _hashes_ rather than field elements modulo r + let pedersen_hash = crate::hash::pedersen_hash([r.x, public_key.x, public_key.y]); + let pde: [u8; 32] = pedersen_hash.to_be_bytes(); + + let mut hash_input = [0; N + 32]; + for i in 0..32 { + hash_input[i] = pde[i]; + } + for i in 0..N { + hash_input[32+i] = message[i]; + } + + let result = crate::hash::blake2s(hash_input); + (r.is_infinite, result) +} diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr index b64078e6b46..835ea2ffb1f 100644 --- a/test_programs/execution_success/schnorr/src/main.nr +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -12,11 +12,6 @@ fn main( // Regression for issue #2421 // We want to make sure that we can accurately verify a signature whose message is a slice vs. an array let message_field_bytes: [u8; 10] = message_field.to_be_bytes(); - let mut message2 = [0; 42]; - for i in 0..10 { - assert(message[i] == message_field_bytes[i]); - message2[i] = message[i]; - } // Is there ever a situation where someone would want // to ensure that a signature was invalid? @@ -27,102 +22,7 @@ fn main( let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); assert(valid_signature); let pub_key = embedded_curve_ops::EmbeddedCurvePoint { x: pub_key_x, y: pub_key_y, is_infinite: false }; - let valid_signature = verify_signature_noir(pub_key, signature, message2); + let valid_signature = std::schnorr::verify_signature_noir(pub_key, signature, message); assert(valid_signature); - assert_valid_signature(pub_key, signature, message2); -} - -// TODO: to put in the stdlib once we have numeric generics -// Meanwhile, you have to use a message with 32 additional bytes: -// If you want to verify a signature on a message of 10 bytes, you need to pass a message of length 42, -// where the first 10 bytes are the one from the original message (the other bytes are not used) -pub fn verify_signature_noir( - public_key: embedded_curve_ops::EmbeddedCurvePoint, - signature: [u8; 64], - message: [u8; M] -) -> bool { - let N = message.len() - 32; - - //scalar lo/hi from bytes - let sig_s = bytes_to_scalar(signature, 0); - let sig_e = bytes_to_scalar(signature, 32); - // pub_key is on Grumpkin curve - let mut is_ok = (public_key.y * public_key.y == public_key.x * public_key.x * public_key.x - 17) - & (!public_key.is_infinite); - - if ((sig_s.lo != 0) | (sig_s.hi != 0)) & ((sig_e.lo != 0) | (sig_e.hi != 0)) { - let g1 = embedded_curve_ops::EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false }; - let r = embedded_curve_ops::multi_scalar_mul([g1, public_key], [sig_s, sig_e]); - // compare the _hashes_ rather than field elements modulo r - let pedersen_hash = std::hash::pedersen_hash([r.x, public_key.x, public_key.y]); - let mut hash_input = [0; M]; - let pde: [u8; 32] = pedersen_hash.to_be_bytes(); - - for i in 0..32 { - hash_input[i] = pde[i]; - } - for i in 0..N { - hash_input[32+i] = message[i]; - } - let result = std::hash::blake2s(hash_input); - - is_ok = !r.is_infinite; - for i in 0..32 { - if result[i] != signature[32 + i] { - is_ok = false; - } - } - } - is_ok -} - -pub fn bytes_to_scalar(bytes: [u8; 64], offset: u32) -> embedded_curve_ops::EmbeddedCurveScalar { - let mut v = 1; - let mut lo = 0 as Field; - let mut hi = 0 as Field; - for i in 0..16 { - lo = lo + (bytes[offset+31 - i] as Field) * v; - hi = hi + (bytes[offset+15 - i] as Field) * v; - v = v * 256; - } - let sig_s = embedded_curve_ops::EmbeddedCurveScalar { lo, hi }; - sig_s -} - -pub fn assert_valid_signature( - public_key: embedded_curve_ops::EmbeddedCurvePoint, - signature: [u8; 64], - message: [u8; M] -) { - let N = message.len() - 32; - //scalar lo/hi from bytes - let sig_s = bytes_to_scalar(signature, 0); - let sig_e = bytes_to_scalar(signature, 32); - - // assert pub_key is on Grumpkin curve - assert(public_key.y * public_key.y == public_key.x * public_key.x * public_key.x - 17); - assert(public_key.is_infinite == false); - // assert signature is not null - assert((sig_s.lo != 0) | (sig_s.hi != 0)); - assert((sig_e.lo != 0) | (sig_e.hi != 0)); - - let g1 = embedded_curve_ops::EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false }; - let r = embedded_curve_ops::multi_scalar_mul([g1, public_key], [sig_s, sig_e]); - // compare the _hashes_ rather than field elements modulo r - let pedersen_hash = std::hash::pedersen_hash([r.x, public_key.x, public_key.y]); - let mut hash_input = [0; M]; - let pde: [u8; 32] = pedersen_hash.to_be_bytes(); - - for i in 0..32 { - hash_input[i] = pde[i]; - } - for i in 0..N { - hash_input[32+i] = message[i]; - } - let result = std::hash::blake2s(hash_input); - - assert(!r.is_infinite); - for i in 0..32 { - assert(result[i] == signature[32 + i]); - } + std::schnorr::assert_valid_signature(pub_key, signature, message); }