Skip to content

Commit 9a10f58

Browse files
committed
ML-DSA: Add optional context to signing and verification
1 parent 5b13e52 commit 9a10f58

3 files changed

Lines changed: 68 additions & 18 deletions

File tree

scripts/build_ffi.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,9 @@ def build_ffi(local_wolfssl, features):
10311031
int wc_dilithium_export_public(dilithium_key* key, byte* out, word32* outLen);
10321032
int wc_dilithium_import_public(const byte* in, word32 inLen, dilithium_key* key);
10331033
int wc_dilithium_sign_msg(const byte* msg, word32 msgLen, byte* sig, word32* sigLen, dilithium_key* key, WC_RNG* rng);
1034+
int wc_dilithium_sign_ctx_msg(const byte* ctx, byte ctxLen, const byte* msg, word32 msgLen, byte* sig, word32* sigLen, dilithium_key* key, WC_RNG* rng);
10341035
int wc_dilithium_verify_msg(const byte* sig, word32 sigLen, const byte* msg, word32 msgLen, int* res, dilithium_key* key);
1036+
int wc_dilithium_verify_ctx_msg(const byte* sig, word32 sigLen, const byte* ctx, word32 ctxLen, const byte* msg, word32 msgLen, int* res, dilithium_key* key);
10351037
typedef dilithium_key MlDsaKey;
10361038
int wc_MlDsaKey_GetPrivLen(MlDsaKey* key, int* len);
10371039
int wc_MlDsaKey_GetPubLen(MlDsaKey* key, int* len);

tests/test_mldsa.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,21 @@ def test_sign_verify(mldsa_type, rng):
134134
# Verify with wrong message
135135
wrong_message = b"This is a wrong message for ML-DSA signature"
136136
assert not mldsa_pub.verify(signature, wrong_message)
137+
138+
# Verify with ctx for signature generated without
139+
ctx = b"This is a test context for ML-DSA signature"
140+
wrong_ctx = b"This is a wrong context for ML-DSA signature"
141+
assert not mldsa_pub.verify(signature, message, ctx=wrong_ctx)
142+
143+
# Sign a message with context
144+
signature = mldsa_priv.sign(message, rng, ctx=ctx)
145+
assert len(signature) == mldsa_priv.sig_size
146+
147+
# Verify the signature by MlDsaPrivate
148+
assert mldsa_priv.verify(signature, message, ctx=ctx)
149+
150+
# Verify the signature by MlDsaPublic
151+
assert mldsa_pub.verify(signature, message, ctx=ctx)
152+
153+
# Verify with wrong ctx
154+
assert not mldsa_pub.verify(signature, message, ctx=wrong_ctx)

wolfcrypt/ciphers.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,27 +2124,42 @@ def _encode_pub_key(self):
21242124

21252125
return _ffi.buffer(pub_key, out_size[0])[:]
21262126

2127-
def verify(self, signature, message):
2127+
def verify(self, signature, message, ctx=None):
21282128
"""
21292129
:param signature: signature to be verified
21302130
:type signature: bytes or str
21312131
:param message: message to be verified
21322132
:type message: bytes or str
2133+
:param ctx: context (optional)
2134+
:type ctx: None for no context, str or bytes otherwise
21332135
:return: True if the verification is successful, False otherwise
21342136
:rtype: bool
21352137
"""
21362138
sig_bytestype = t2b(signature)
21372139
msg_bytestype = t2b(message)
21382140
res = _ffi.new("int *")
21392141

2140-
ret = _lib.wc_dilithium_verify_msg(
2141-
_ffi.from_buffer(sig_bytestype),
2142-
len(sig_bytestype),
2143-
_ffi.from_buffer(msg_bytestype),
2144-
len(msg_bytestype),
2145-
res,
2146-
self.native_object,
2147-
)
2142+
if ctx is not None:
2143+
ctx_bytestype = t2b(ctx)
2144+
ret = _lib.wc_dilithium_verify_ctx_msg(
2145+
_ffi.from_buffer(sig_bytestype),
2146+
len(sig_bytestype),
2147+
_ffi.from_buffer(ctx_bytestype),
2148+
len(ctx_bytestype),
2149+
_ffi.from_buffer(msg_bytestype),
2150+
len(msg_bytestype),
2151+
res,
2152+
self.native_object,
2153+
)
2154+
else:
2155+
ret = _lib.wc_dilithium_verify_msg(
2156+
_ffi.from_buffer(sig_bytestype),
2157+
len(sig_bytestype),
2158+
_ffi.from_buffer(msg_bytestype),
2159+
len(msg_bytestype),
2160+
res,
2161+
self.native_object,
2162+
)
21482163

21492164
if ret < 0: # pragma: no cover
21502165
raise WolfCryptError("wc_dilithium_verify_msg() error (%d)" % ret)
@@ -2246,12 +2261,14 @@ def decode_key(self, priv_key, pub_key=None):
22462261
if pub_key is not None:
22472262
self._decode_pub_key(pub_key)
22482263

2249-
def sign(self, message, rng=Random()):
2264+
def sign(self, message, rng=Random(), ctx=None):
22502265
"""
22512266
:param message: message to be signed
22522267
:type message: bytes or str
22532268
:param rng: random number generator for sign
22542269
:type rng: Random
2270+
:param ctx: context (optional)
2271+
:type ctx: None for no context, str or bytes otherwise
22552272
:return: signature
22562273
:rtype: bytes
22572274
"""
@@ -2261,14 +2278,27 @@ def sign(self, message, rng=Random()):
22612278
out_size = _ffi.new("word32 *")
22622279
out_size[0] = in_size
22632280

2264-
ret = _lib.wc_dilithium_sign_msg(
2265-
_ffi.from_buffer(msg_bytestype),
2266-
len(msg_bytestype),
2267-
signature,
2268-
out_size,
2269-
self.native_object,
2270-
rng.native_object,
2271-
)
2281+
if ctx is not None:
2282+
ctx_bytestype = t2b(ctx)
2283+
ret = _lib.wc_dilithium_sign_ctx_msg(
2284+
_ffi.from_buffer(ctx_bytestype),
2285+
len(ctx_bytestype),
2286+
_ffi.from_buffer(msg_bytestype),
2287+
len(msg_bytestype),
2288+
signature,
2289+
out_size,
2290+
self.native_object,
2291+
rng.native_object,
2292+
)
2293+
else:
2294+
ret = _lib.wc_dilithium_sign_msg(
2295+
_ffi.from_buffer(msg_bytestype),
2296+
len(msg_bytestype),
2297+
signature,
2298+
out_size,
2299+
self.native_object,
2300+
rng.native_object,
2301+
)
22722302

22732303
if ret < 0: # pragma: no cover
22742304
raise WolfCryptError("wc_dilithium_sign_msg() error (%d)" % ret)

0 commit comments

Comments
 (0)