diff --git a/generate-implementations.py b/generate-implementations.py index ba31f1a..edb4f0f 100755 --- a/generate-implementations.py +++ b/generate-implementations.py @@ -73,6 +73,7 @@ def generate_scheme(name, type, properties): name=name, type=type, insecure=properties.get('insecure', False), + supports_context=properties.get('supports_context', False), version=properties['version'], implementations=properties['implementations'], ) @@ -95,6 +96,7 @@ def generate_scheme(name, type, properties): render_template( target_dir, 'src/ffi.rs', 'scheme/src/ffi.rs.j2', insecure=properties.get('insecure', False), + supports_context=properties.get('supports_context', False), type=type, name=name, metadatas=metadatas, @@ -107,6 +109,7 @@ def generate_scheme(name, type, properties): "scheme/src/scheme.rs.j2", type=type, name=name, + supports_context=properties.get('supports_context', False), insecure=properties.get('insecure', False), scheme=scheme, ) @@ -116,6 +119,7 @@ def generate_scheme(name, type, properties): name=name, type=type, insecure=properties.get('insecure', False), + supports_context=properties.get('supports_context', False), notes=properties.get('notes', None), schemes=properties['schemes'], ) @@ -125,6 +129,7 @@ def generate_scheme(name, type, properties): name=name, type=type, insecure=properties.get('insecure', False), + supports_context=properties.get('supports_context', False), notes=properties.get('notes', None), schemes=properties['schemes'], ) diff --git a/pqcrypto-template/pqcrypto/examples/keygen.rs b/pqcrypto-template/pqcrypto/examples/keygen.rs index 3958221..7bd43e6 100644 --- a/pqcrypto-template/pqcrypto/examples/keygen.rs +++ b/pqcrypto-template/pqcrypto/examples/keygen.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::prelude::*; use pqcrypto::prelude::*; -use pqcrypto::sign::dilithium2::*; +use pqcrypto::sign::mldsa44::*; fn main() -> std::io::Result<()> { diff --git a/pqcrypto-template/pqcrypto/examples/signer.rs b/pqcrypto-template/pqcrypto/examples/signer.rs index 574c01b..fbc210c 100644 --- a/pqcrypto-template/pqcrypto/examples/signer.rs +++ b/pqcrypto-template/pqcrypto/examples/signer.rs @@ -2,7 +2,7 @@ use std::fs::{self, File}; use std::io::prelude::*; use pqcrypto::prelude::*; -use pqcrypto::sign::dilithium2::*; +use pqcrypto::sign::mldsa44::*; fn parseargs() -> (String, String, String) { let args: Vec = std::env::args().collect(); diff --git a/pqcrypto-template/pqcrypto/examples/verifier.rs b/pqcrypto-template/pqcrypto/examples/verifier.rs index 08444b8..4704c6a 100644 --- a/pqcrypto-template/pqcrypto/examples/verifier.rs +++ b/pqcrypto-template/pqcrypto/examples/verifier.rs @@ -1,7 +1,7 @@ use std::fs; use pqcrypto::prelude::*; -use pqcrypto::sign::dilithium2::*; +use pqcrypto::sign::mldsa44::*; fn parseargs() -> (String, String, String) { let args: Vec = std::env::args().collect(); diff --git a/pqcrypto-template/scheme/Cargo.toml.j2 b/pqcrypto-template/scheme/Cargo.toml.j2 index 1e144e7..5654d9a 100644 --- a/pqcrypto-template/scheme/Cargo.toml.j2 +++ b/pqcrypto-template/scheme/Cargo.toml.j2 @@ -12,11 +12,14 @@ keywords = ["cryptography", "post-quantum", "security"] categories = ["cryptography", "no-std"] [dependencies] -pqcrypto-internals = { path = "../pqcrypto-internals", version = "0.2" } +pqcrypto-internals = { path = "../pqcrypto-internals", version = "0.2.6" } pqcrypto-traits = { path = "../pqcrypto-traits", version = "{{ traits_version }}", default-features = false } libc = "0.2.0" serde = { version = "1.0", features = ["derive"], optional = true } serde-big-array = { version = "0.5.1", optional = true } +{% if supports_context %} +paste = "*" +{% endif %} [features] default = [{% if 'avx2' in implementations or 'avx' in implementations %}"avx2", {% endif %}{% if 'aesni' in implementations %}"aes", {% endif %}{% if 'aarch64' in implementations %}"neon", {% endif %}"std"] diff --git a/pqcrypto-template/scheme/src/ffi.rs.j2 b/pqcrypto-template/scheme/src/ffi.rs.j2 index dcf0b57..3a49522 100644 --- a/pqcrypto-template/scheme/src/ffi.rs.j2 +++ b/pqcrypto-template/scheme/src/ffi.rs.j2 @@ -154,6 +154,19 @@ extern "C" { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign(sm: *mut u8, smlen: *mut usize, msg: *const u8, len: usize, sk: *const u8) -> c_int; + {% if supports_context %} + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_x86_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_x86_aes)] + {% elif implementation == 'aarch64' %} + #[cfg(enable_aarch64_neon)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_ctx(sm: *mut u8, smlen: *mut usize, msg: *const u8, len: usize, ctx: *const u8, ctxlen: usize, sk: *const u8) -> c_int; + {% endif %} {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_x86_avx2)] {% elif implementation == 'aesni' %} @@ -165,6 +178,19 @@ extern "C" { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_open(m: *mut u8, mlen: *mut usize, sm: *const u8, smlen: usize, pk: *const u8) -> c_int; + {% if supports_context %} + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_x86_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_x86_aes)] + {% elif implementation == 'aarch64' %} + #[cfg(enable_aarch64_neon)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_open_ctx(m: *mut u8, mlen: *mut usize, sm: *const u8, smlen: usize, ctx: *const u8, ctxlen: usize, pk: *const u8) -> c_int; + {% endif %} {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_x86_avx2)] {% elif implementation == 'aesni' %} @@ -176,6 +202,19 @@ extern "C" { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_signature(sig: *mut u8, siglen: *mut usize, m: *const u8, mlen: usize, sk: *const u8) -> c_int; + {% if supports_context %} + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_x86_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_x86_aes)] + {% elif implementation == 'aarch64' %} + #[cfg(enable_aarch64_neon)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_signature_ctx(sig: *mut u8, siglen: *mut usize, m: *const u8, mlen: usize, ctx: *const u8, ctxlen: usize, sk: *const u8) -> c_int; + {% endif %} {% if implementation == 'avx2' or implementation == 'avx' %} #[cfg(enable_x86_avx2)] {% elif implementation == 'aesni' %} @@ -187,6 +226,20 @@ extern "C" { #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_verify(sig: *const u8, siglen: usize, m: *const u8, mlen: usize, pk: *const u8) -> c_int; + {% if supports_context %} + {% if implementation == 'avx2' or implementation == 'avx' %} + #[cfg(enable_x86_avx2)] + {% elif implementation == 'aesni' %} + #[cfg(enable_x86_aes)] + {% elif implementation == 'aarch64' %} + #[cfg(enable_aarch64_neon)] + {% endif %} + {% if insecure %} + #[deprecated(note = "Insecure cryptography, do not use in production")] + {% endif %} + pub fn PQCLEAN_{{ NS_NAME }}_crypto_sign_verify_ctx(sig: *const u8, siglen: usize, m: *const u8, mlen: usize, ctx: *const u8, ctxlen: usize, pk: *const u8) -> c_int; + {% endif %} + {% endif %} } {% endfor %} {# implementations #} @@ -295,26 +348,37 @@ mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { let mut detached_sig = vec![0u8; PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES]; let mut sm = Vec::with_capacity(mlen + PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES); let mut smlen = 0; + assert_eq!( 0, PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) ); + {% if supports_context %} + {% set ctx = "_ctx" %} + {% set ctxptrs = "core::ptr::null(), 0,"%} + {% else %} + {% set ctx = "" %} + {% set ctxptrs = "" %} + {% endif%} assert_eq!( 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign( + PQCLEAN_{{ NS_NAME }}_crypto_sign{{ctx}}( sm.as_mut_ptr(), &mut smlen as *mut usize, - msg.as_ptr(), mlen, sk.as_ptr()) + msg.as_ptr(), msg.len(), {{ ctxptrs }} + sk.as_ptr()), + "sign" ); sm.set_len(smlen); let mut unpacked_m = Vec::with_capacity(mlen + PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES); assert_eq!( 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_open( + PQCLEAN_{{ NS_NAME }}_crypto_sign_open{{ctx}}( unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, - sm.as_ptr(), sm.len(), - pk.as_ptr() - ) + sm.as_ptr(), sm.len(), {{ctxptrs}} + pk.as_ptr(), + ), + "sign_open" ); unpacked_m.set_len(mlen); assert_eq!(unpacked_m, msg); @@ -322,46 +386,96 @@ mod test_{{ scheme.name|nameize }}_{{ implementation|nameize }} { // check verification fails with wrong pk assert_eq!( 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk_alt.as_mut_ptr(), sk_alt.as_mut_ptr()) + PQCLEAN_{{ NS_NAME }}_crypto_sign_keypair(pk_alt.as_mut_ptr(), sk_alt.as_mut_ptr()), + "keypair" ); assert_eq!( -1, - PQCLEAN_{{ NS_NAME }}_crypto_sign_open( + PQCLEAN_{{ NS_NAME }}_crypto_sign_open{{ctx}}( unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, sm.as_ptr(), sm.len(), + {{ctxptrs}} pk_alt.as_ptr() - ) + ), + "sign_open" ); assert_eq!( 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_signature( + PQCLEAN_{{ NS_NAME }}_crypto_sign_signature{{ctx}}( detached_sig.as_mut_ptr(), &mut smlen as *mut usize, - msg.as_ptr(), msg.len(), - sk.as_ptr()) + msg.as_ptr(), msg.len(), {{ctxptrs}} + sk.as_ptr()), + "sign_signature" ); assert!(smlen <= PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES, "Signed message length should be ≤ CRYPTO_BYTES"); assert_eq!( 0, - PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( + PQCLEAN_{{ NS_NAME }}_crypto_sign_verify{{ctx}}( detached_sig.as_ptr(), smlen, - msg.as_ptr(), msg.len(), - pk.as_ptr()) + msg.as_ptr(), msg.len(), {{ctxptrs}} + pk.as_ptr()), + "sign_verify" ); assert_eq!( -1, - PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( + PQCLEAN_{{ NS_NAME }}_crypto_sign_verify{{ctx}}( detached_sig.as_ptr(), smlen, - msg.as_ptr(), msg.len(), - pk_alt.as_ptr()) + msg.as_ptr(), msg.len(), {{ctxptrs}} + pk_alt.as_ptr()), + "sign_verify alt pk" ); assert_eq!( -1, - PQCLEAN_{{ NS_NAME }}_crypto_sign_verify( + PQCLEAN_{{ NS_NAME }}_crypto_sign_verify{{ctx}}( detached_sig.as_ptr(), smlen, - msg.as_ptr(), msg.len()-1, - pk.as_ptr()) + msg.as_ptr(), msg.len()-1, {{ctxptrs}} + pk.as_ptr()), + "sign_verify wrong length" + ); + + {% if supports_context %} + let ctx = vec![1u8; 10]; + assert_eq!( + 0, + PQCLEAN_{{ NS_NAME }}_crypto_sign_ctx( + sm.as_mut_ptr(), &mut smlen as *mut usize, + msg.as_ptr(), msg.len(), + ctx.as_ptr(), ctx.len(), + sk.as_ptr()), + "Sign ctx call" + ); + sm.set_len(smlen); + assert!(smlen >= PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES, "too small sig"); + assert!(smlen <= PQCLEAN_{{ NS_NAME }}_CRYPTO_BYTES + msg.len(), "too big sig"); + + let mut mlen: usize = 0; + unpacked_m.clear(); + assert_eq!( + 0, + PQCLEAN_{{ NS_NAME }}_crypto_sign_open_ctx( + unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, + sm.as_ptr(), sm.len(), + ctx.as_ptr(), ctx.len(), + pk.as_ptr() + ), + "sign_open_ctx" + ); + unpacked_m.set_len(mlen); + assert!(unpacked_m == msg, "unequal messages"); + + let ctx_alt = vec![0u8; 10]; + assert_eq!( + -1, + PQCLEAN_{{ NS_NAME }}_crypto_sign_open_ctx( + unpacked_m.as_mut_ptr(), &mut mlen as *mut usize, + sm.as_ptr(), sm.len(), + ctx_alt.as_ptr(), ctx_alt.len(), + pk.as_ptr() + ), + "sign_open_ctx with alt context" ); + {% endif %} } } {% endif %} {# SIGN #} diff --git a/pqcrypto-template/scheme/src/lib.rs.j2 b/pqcrypto-template/scheme/src/lib.rs.j2 index bfdbe94..8410e85 100644 --- a/pqcrypto-template/scheme/src/lib.rs.j2 +++ b/pqcrypto-template/scheme/src/lib.rs.j2 @@ -50,6 +50,12 @@ pub use crate::{{ scheme_name }}::{ open as {{ scheme_name }}_open, detached_sign as {{ scheme_name }}_detached_sign, verify_detached_signature as {{ scheme_name }}_verify_detached_signature, + {% if supports_context %} + sign_ctx as {{ scheme_name }}_sign_ctx, + open_ctx as {{ scheme_name }}_open_ctx, + detached_sign_ctx as {{ scheme_name }}_detached_sign_ctx, + verify_detached_signature_ctx as {{ scheme_name }}_verify_detached_signature_ctx, + {% endif %} {% endif %} }; {% endfor %} diff --git a/pqcrypto-template/scheme/src/scheme.rs.j2 b/pqcrypto-template/scheme/src/scheme.rs.j2 index afc5d08..4371522 100644 --- a/pqcrypto-template/scheme/src/scheme.rs.j2 +++ b/pqcrypto-template/scheme/src/scheme.rs.j2 @@ -54,6 +54,10 @@ use alloc::vec::Vec; use pqcrypto_traits::sign as primitive; {% endif %} +{% if supports_context %} +use paste::paste; +{% endif %} + macro_rules! simple_struct { ($type: ident, $size: expr) => { #[derive(Clone, Copy)] @@ -402,6 +406,18 @@ macro_rules! gen_signature { let mut signed_msg = Vec::with_capacity(max_len); let mut smlen: usize = 0; unsafe { + {% if supports_context %} + paste! { + ffi::[<$variant _ctx>]( + signed_msg.as_mut_ptr(), + &mut smlen as *mut usize, + $msg.as_ptr(), + $msg.len(), + core::ptr::null(), 0, + $sk.0.as_ptr(), + ); + } + {% else %} ffi::$variant( signed_msg.as_mut_ptr(), &mut smlen as *mut usize, @@ -409,6 +425,7 @@ macro_rules! gen_signature { $msg.len(), $sk.0.as_ptr(), ); + {% endif %} debug_assert!(smlen <= max_len, "exceeded vector capacity"); signed_msg.set_len(smlen); } @@ -417,6 +434,32 @@ macro_rules! gen_signature { }; } +{% if supports_context %} +macro_rules! gen_signature_ctx { + ($variant:ident, $msg:ident, $ctx:ident, $sk:ident) => { + { + let max_len = $msg.len() + signature_bytes(); + let mut signed_msg = Vec::with_capacity(max_len); + let mut smlen: usize = 0; + unsafe { + ffi::$variant( + signed_msg.as_mut_ptr(), + &mut smlen as *mut usize, + $msg.as_ptr(), + $msg.len(), + $ctx.as_ptr(), + $ctx.len(), + $sk.0.as_ptr(), + ); + debug_assert!(smlen <= max_len, "exceeded vector capacity"); + signed_msg.set_len(smlen); + } + SignedMessage(signed_msg) + } + }; +} +{% endif %} + /// Sign the message and return the signed message. {% if insecure %} #[deprecated(note = "Insecure cryptography, do not use in production")] @@ -449,8 +492,79 @@ pub fn sign(msg: &[u8], sk: &SecretKey) -> SignedMessage { gen_signature!(PQCLEAN_{{ NS_NAME }}_crypto_sign, msg, sk) } +{% if supports_context %} +/// Sign the message for the provided context and return the signed message. +{% if insecure %} +#[deprecated(note = "Insecure cryptography, do not use in production")] +{% endif %} +pub fn sign_ctx(msg: &[u8], ctx: &[u8], sk: &SecretKey) -> SignedMessage { + {% if globals.x86_avx2 %} + #[cfg(all(enable_x86_avx2, feature = "avx2"))] + { + if std::is_x86_feature_detected!("avx2") { + return gen_signature_ctx!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_ctx, msg, ctx, sk); + } + } + {% endif %} + {% if globals.x86_aes %} + #[cfg(all(enable_x86_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return gen_signature_ctx!(PQCLEAN_{{ AES_NAME }}_crypto_sign_ctx, msg, ctx, sk); + } + } + {% endif %} + {% if globals.aarch64_neon %} + #[cfg(all(enable_aarch64_neon, feature = "neon"))] + { + if true { + return gen_signature_ctx!(PQCLEAN_{{ AARCH64_NAME }}_crypto_sign_ctx, msg, ctx, sk); + } + } + {% endif %} + gen_signature_ctx!(PQCLEAN_{{ NS_NAME }}_crypto_sign_ctx, msg, ctx, sk) +} +{% endif %} + macro_rules! open_signed { ($variant:ident, $sm:ident, $pk:ident) => { + { + let mut m: Vec = Vec::with_capacity($sm.len()); + let mut mlen: usize = 0; + match unsafe { + {% if supports_context %} + paste!{ ffi:: [<$variant _ctx>] ( + m.as_mut_ptr(), + &mut mlen as *mut usize, + $sm.0.as_ptr(), + $sm.len(), + core::ptr::null() as *const u8, 0, + $pk.0.as_ptr(), + )} + {% else %} + ffi::$variant( + m.as_mut_ptr(), + &mut mlen as *mut usize, + $sm.0.as_ptr(), + $sm.len(), + $pk.0.as_ptr(), + ) + {% endif %} + } { + 0 => { + unsafe { m.set_len(mlen) }; + Ok(m) + } + -1 => Err(primitive::VerificationError::InvalidSignature), + _ => Err(primitive::VerificationError::UnknownVerificationError), + } + } + }; +} + +{% if supports_context %} +macro_rules! open_signed_ctx { + ($variant:ident, $sm:ident, $ctx: ident, $pk:ident) => { { let mut m: Vec = Vec::with_capacity($sm.len()); let mut mlen: usize = 0; @@ -460,6 +574,8 @@ macro_rules! open_signed { &mut mlen as *mut usize, $sm.0.as_ptr(), $sm.len(), + $ctx.as_ptr(), + $ctx.len(), $pk.0.as_ptr(), ) } { @@ -473,6 +589,7 @@ macro_rules! open_signed { } }; } +{% endif %} /// Open the signed message and if verification succeeds return the message {% if insecure %} @@ -509,11 +626,61 @@ pub fn open( open_signed!(PQCLEAN_{{ NS_NAME }}_crypto_sign_open, sm, pk) } +{% if supports_context %} +/// Open the signed message and if verification succeeds return the message +{% if insecure %} +#[deprecated(note = "Insecure cryptography, do not use in production")] +{% endif %} +pub fn open_ctx( + sm: &SignedMessage, + ctx: &[u8], + pk: &PublicKey +) -> core::result::Result,primitive::VerificationError> { + {% if globals.x86_avx2 %} + #[cfg(all(enable_x86_avx2, feature = "avx2"))] + { + if std::is_x86_feature_detected!("avx2") { + return open_signed_ctx!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_open_ctx, sm, ctx, pk); + } + } + {% endif %} + {% if globals.x86_aes %} + #[cfg(all(enable_x86_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return open_signed_ctx!(PQCLEAN_{{ AES_NAME }}_crypto_sign_open_ctx, sm, ctx, pk); + } + } + {% endif %} + {% if globals.aarch64_neon %} + #[cfg(all(enable_aarch64_neon, feature = "neon"))] + { + if true { + return open_signed_ctx!(PQCLEAN_{{ AARCH64_NAME }}_crypto_sign_open_ctx, sm, ctx, pk); + } + } + {% endif %} + open_signed_ctx!(PQCLEAN_{{ NS_NAME }}_crypto_sign_open_ctx, sm, ctx, pk) +} +{% endif %} + macro_rules! detached_signature { ($variant:ident, $msg:ident, $sk:ident) => { { let mut sig = DetachedSignature::new(); unsafe { + {% if supports_context %} + paste! { + ffi:: [<$variant _ctx >]( + sig.0.as_mut_ptr(), + &mut sig.1 as *mut usize, + $msg.as_ptr(), + $msg.len(), + core::ptr::null(), 0, + $sk.0.as_ptr(), + ); + } + {% else %} ffi::$variant( sig.0.as_mut_ptr(), &mut sig.1 as *mut usize, @@ -521,12 +688,35 @@ macro_rules! detached_signature { $msg.len(), $sk.0.as_ptr(), ); + {% endif %} } sig } }; } +{% if supports_context %} +macro_rules! detached_signature_ctx { + ($variant:ident, $msg:ident, $ctx:ident, $sk:ident) => { + { + let mut sig = DetachedSignature::new(); + unsafe { + ffi::$variant( + sig.0.as_mut_ptr(), + &mut sig.1 as *mut usize, + $msg.as_ptr(), + $msg.len(), + $ctx.as_ptr(), + $ctx.len(), + $sk.0.as_ptr(), + ); + } + sig + } + }; +} +{% endif %} + {% if insecure %} #[deprecated(note = "Insecure cryptography, do not use in production")] {% endif %} @@ -559,10 +749,56 @@ pub fn detached_sign(msg: &[u8], sk: &SecretKey) -> DetachedSignature { detached_signature!(PQCLEAN_{{ NS_NAME }}_crypto_sign_signature, msg, sk) } +{% if supports_context %} +{% if insecure %} +#[deprecated(note = "Insecure cryptography, do not use in production")] +{% endif %} +/// Create a detached signature on the message +pub fn detached_sign_ctx(msg: &[u8], ctx: &[u8], sk: &SecretKey) -> DetachedSignature { + {% if globals.x86_avx2 %} + #[cfg(all(enable_x86_avx2, feature = "avx2"))] + { + if std::is_x86_feature_detected!("avx2") { + return detached_signature_ctx!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_signature_ctx, msg, ctx, sk); + } + } + {% endif %} + {% if globals.x86_aes %} + #[cfg(all(enable_x86_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return detached_signature_ctx!(PQCLEAN_{{ AES_NAME }}_crypto_sign_signature_ctx, msg, ctx, sk); + } + } + {% endif %} + {% if globals.aarch64_neon %} + #[cfg(all(enable_aarch64_neon, feature = "neon"))] + { + if true { + return detached_signature_ctx!(PQCLEAN_{{ AARCH64_NAME }}_crypto_sign_signature_ctx, msg, ctx, sk); + } + } + {% endif %} + detached_signature_ctx!(PQCLEAN_{{ NS_NAME }}_crypto_sign_signature_ctx, msg, ctx, sk) +} +{% endif %} + macro_rules! verify_detached_sig { ($variant:ident, $sig:ident, $msg:ident, $pk:ident) => { { let res = unsafe { + {% if supports_context %} + paste! { + ffi:: [<$variant _ctx >]( + $sig.0.as_ptr(), + $sig.1, + $msg.as_ptr(), + $msg.len(), + core::ptr::null(), 0, + $pk.0.as_ptr(), + ) + } + {% else %} ffi::$variant( $sig.0.as_ptr(), $sig.1, @@ -570,6 +806,7 @@ macro_rules! verify_detached_sig { $msg.len(), $pk.0.as_ptr(), ) + {% endif %} }; match res { 0 => Ok(()), @@ -580,6 +817,31 @@ macro_rules! verify_detached_sig { }; } +{% if supports_context %} +macro_rules! verify_detached_sig_ctx { + ($variant:ident, $sig:ident, $msg:ident, $ctx:ident, $pk:ident) => { + { + let res = unsafe { + ffi::$variant( + $sig.0.as_ptr(), + $sig.1, + $msg.as_ptr(), + $msg.len(), + $ctx.as_ptr(), + $ctx.len(), + $pk.0.as_ptr(), + ) + }; + match res { + 0 => Ok(()), + -1 => Err(primitive::VerificationError::InvalidSignature), + _ => Err(primitive::VerificationError::UnknownVerificationError), + } + } + }; +} +{% endif %} + /// Verify the detached signature {% if insecure %} #[deprecated(note = "Insecure cryptography, do not use in production")] @@ -612,6 +874,41 @@ pub fn verify_detached_signature(sig: &DetachedSignature, msg: &[u8], pk: &Publi verify_detached_sig!(PQCLEAN_{{ NS_NAME }}_crypto_sign_verify, sig, msg, pk) } +{% if supports_context %} + +/// Verify the detached signature +{% if insecure %} +#[deprecated(note = "Insecure cryptography, do not use in production")] +{% endif %} +pub fn verify_detached_signature_ctx(sig: &DetachedSignature, msg: &[u8], ctx: &[u8], pk: &PublicKey) -> core::result::Result<(), primitive::VerificationError> { + {% if globals.x86_avx2 %} + #[cfg(all(enable_x86_avx2, feature = "avx2"))] + { + if std::is_x86_feature_detected!("avx2") { + return verify_detached_sig_ctx!(PQCLEAN_{{ AVX2_NAME }}_crypto_sign_verify_ctx, sig, msg, ctx, pk); + } + } + {% endif %} + {% if globals.x86_aes %} + #[cfg(all(enable_x86_aes, feature = "aes"))] + { + if std::is_x86_feature_detected!("aes") { + return verify_detached_sig_ctx!(PQCLEAN_{{ AES_NAME }}_crypto_sign_verify_ctx, sig, msg, ctx, pk); + } + } + {% endif %} + {% if globals.aarch64_neon %} + #[cfg(all(enable_aarch64_neon, feature = "neon"))] + { + if true { + return verify_detached_sig_ctx!(PQCLEAN_{{ AARCH64_NAME }}_crypto_sign_verify_ctx, sig, msg, ctx, pk); + } + } + {% endif %} + verify_detached_sig_ctx!(PQCLEAN_{{ NS_NAME }}_crypto_sign_verify_ctx, sig, msg, ctx, pk) +} +{% endif %} + {% endif %} {# KEM or SIGN #} @@ -655,5 +952,36 @@ mod test { assert!(verify_detached_signature(&sig, &message, &pk).is_ok()); assert!(!verify_detached_signature(&sig, &message[..message.len()-1], &pk).is_ok()); } + + {% if supports_context %} + #[test] + pub fn test_sign_ctx() { + let mut rng = rand::thread_rng(); + let len: u16 = rng.gen(); + let ctx = (0..10).map(|_| rng.gen::()).collect::>(); + + let message = (0..len).map(|_| rng.gen::()).collect::>(); + let (pk, sk) = keypair(); + let sm = sign_ctx(&message, &ctx, &sk); + let verifiedmsg = open_ctx(&sm, &ctx, &pk).unwrap(); + assert!(verifiedmsg == message); + assert!(open(&sm, &pk).is_err()); + } + + #[test] + pub fn test_sign_detached_ctx() { + let mut rng = rand::thread_rng(); + let len: u16 = rng.gen(); + let message = (0..len).map(|_| rng.gen::()).collect::>(); + let ctx = (0..10).map(|_| rng.gen::()).collect::>(); + + let (pk, sk) = keypair(); + let sig = detached_sign_ctx(&message, &ctx, &sk); + assert!(verify_detached_signature_ctx(&sig, &message, &ctx, &pk).is_ok()); + assert!(!verify_detached_signature_ctx(&sig, &message[..message.len()-1], &ctx, &pk).is_ok()); + assert!(!verify_detached_signature_ctx(&sig, &message[..message.len()], &ctx[..ctx.len()-1], &pk).is_ok()); + assert!(!verify_detached_signature(&sig, &message[..message.len()-1], &pk).is_ok()); + } + {% endif %} {% endif %} {# KEM or SIGN #} }