Skip to content

Commit bea6565

Browse files
committed
Add context string signature APIs
Signed-off-by: Spencer Wilson <spencer.wilson@uwaterloo.ca>
1 parent c45cdca commit bea6565

File tree

1 file changed

+136
-1
lines changed

1 file changed

+136
-1
lines changed

oqs/src/sig.rs

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! for the list of supported algorithms.
55
use alloc::vec::Vec;
66

7-
use core::ptr::NonNull;
7+
use core::ptr::{null, NonNull};
88

99
#[cfg(not(feature = "std"))]
1010
use cstr_core::CStr;
@@ -24,6 +24,8 @@ newtype_buffer!(Signature, SignatureRef);
2424

2525
/// Message type
2626
pub type Message = [u8];
27+
/// Context string type
28+
pub type CtxStr = [u8];
2729

2830
macro_rules! implement_sigs {
2931
{ $(($feat: literal) $sig: ident: $oqs_id: ident),* $(,)? } => (
@@ -67,6 +69,53 @@ macro_rules! implement_sigs {
6769
sig.verify(&message, &signature, &pk)
6870
}
6971

72+
#[test]
73+
#[cfg(feature = $feat)]
74+
fn test_signing_with_empty_context_string() -> Result<()> {
75+
crate::init();
76+
let message = [0u8; 100];
77+
let ctx_str: [u8; 0] = [];
78+
let sig = Sig::new(Algorithm::$sig)?;
79+
let (pk, sk) = sig.keypair()?;
80+
let signature = sig.sign_with_ctx_str(&message, &ctx_str, &sk)?;
81+
sig.verify_with_ctx_str(&message, &signature, &ctx_str, &pk)
82+
}
83+
84+
#[test]
85+
#[cfg(feature = $feat)]
86+
fn test_signing_with_nonempty_context_string() -> Result<()> {
87+
crate::init();
88+
let message = [0u8; 100];
89+
let ctx_str = [0u8; 100];
90+
let sig = Sig::new(Algorithm::$sig)?;
91+
let (pk, sk) = sig.keypair()?;
92+
if sig.has_ctx_str_support() {
93+
let signature = sig.sign_with_ctx_str(&message, &ctx_str, &sk)?;
94+
sig.verify_with_ctx_str(&message, &signature, &ctx_str, &pk)
95+
} else {
96+
let sig_result = sig.sign_with_ctx_str(&message, &ctx_str, &sk);
97+
// Expect a generic error
98+
let sig_result: Result<()> = match sig_result {
99+
Err(Error::Error) => Ok(()),
100+
Ok(_) => Err(Error::Error),
101+
Err(e) => Err(e)
102+
};
103+
if sig_result.is_ok() {
104+
// get a valid signature with which to test verify
105+
let signature = sig.sign(&message, &sk)?;
106+
// Expect a generic error
107+
match sig.verify_with_ctx_str(&message, &signature, &ctx_str, &pk) {
108+
Err(Error::Error) => Ok(()),
109+
Ok(_) => Err(Error::Error),
110+
Err(e) => Err(e)
111+
112+
}
113+
} else {
114+
sig_result
115+
}
116+
}
117+
}
118+
70119
#[test]
71120
fn test_enabled() {
72121
crate::init();
@@ -261,6 +310,12 @@ impl Sig {
261310
sig.euf_cma
262311
}
263312

313+
/// Does this algorithm support signing with a context string?
314+
pub fn has_ctx_str_support(&self) -> bool {
315+
let sig = unsafe { self.sig.as_ref() };
316+
sig.sig_with_ctx_support
317+
}
318+
264319
/// Length of the public key
265320
pub fn length_public_key(&self) -> usize {
266321
let sig = unsafe { self.sig.as_ref() };
@@ -356,6 +411,47 @@ impl Sig {
356411
Ok(sig)
357412
}
358413

414+
/// Sign a message with a context string
415+
pub fn sign_with_ctx_str<'a, S: Into<SecretKeyRef<'a>>>(
416+
&self,
417+
message: &Message,
418+
ctx_str: &CtxStr,
419+
sk: S,
420+
) -> Result<Signature> {
421+
let sk = sk.into();
422+
let sig = unsafe { self.sig.as_ref() };
423+
let func = sig.sign_with_ctx_str.unwrap();
424+
let mut sig = Signature {
425+
bytes: Vec::with_capacity(sig.length_signature),
426+
};
427+
let mut sig_len = 0;
428+
// For algorithms without context string support, liboqs
429+
// expects the context to be NULL. Converting an empty
430+
// slice to a pointer doesn't actually do this.
431+
let ctx_str_ptr = if !ctx_str.is_empty() {
432+
ctx_str.as_ptr()
433+
} else {
434+
null()
435+
};
436+
let status = unsafe {
437+
func(
438+
sig.bytes.as_mut_ptr(),
439+
&mut sig_len,
440+
message.as_ptr(),
441+
message.len(),
442+
ctx_str_ptr,
443+
ctx_str.len(),
444+
sk.bytes.as_ptr(),
445+
)
446+
};
447+
status_to_result(status)?;
448+
// This is safe to do as it's initialised now.
449+
unsafe {
450+
sig.bytes.set_len(sig_len);
451+
}
452+
Ok(sig)
453+
}
454+
359455
/// Verify a message
360456
pub fn verify<'a, 'b>(
361457
&self,
@@ -383,4 +479,43 @@ impl Sig {
383479
};
384480
status_to_result(status)
385481
}
482+
483+
/// Verify a message with a context string
484+
pub fn verify_with_ctx_str<'a, 'b>(
485+
&self,
486+
message: &Message,
487+
signature: impl Into<SignatureRef<'a>>,
488+
ctx_str: &CtxStr,
489+
pk: impl Into<PublicKeyRef<'b>>,
490+
) -> Result<()> {
491+
let signature = signature.into();
492+
let pk = pk.into();
493+
if signature.bytes.len() > self.length_signature()
494+
|| pk.bytes.len() != self.length_public_key()
495+
{
496+
return Err(Error::InvalidLength);
497+
}
498+
let sig = unsafe { self.sig.as_ref() };
499+
let func = sig.verify_with_ctx_str.unwrap();
500+
// For algorithms without context string support, liboqs
501+
// expects the context to be NULL. Converting an empty
502+
// slice to a pointer doesn't actually do this.
503+
let ctx_str_ptr = if !ctx_str.is_empty() {
504+
ctx_str.as_ptr()
505+
} else {
506+
null()
507+
};
508+
let status = unsafe {
509+
func(
510+
message.as_ptr(),
511+
message.len(),
512+
signature.bytes.as_ptr(),
513+
signature.len(),
514+
ctx_str_ptr,
515+
ctx_str.len(),
516+
pk.bytes.as_ptr(),
517+
)
518+
};
519+
status_to_result(status)
520+
}
386521
}

0 commit comments

Comments
 (0)