fips203 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fips203-0.2.1/PKG-INFO ADDED
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.1
2
+ Name: fips203
3
+ Version: 0.2.1
4
+ Summary: ML-KEM (FIPS203) -- asymmetric, quantum-secure encryption (Initial Public Draft)
5
+ Author-email: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
6
+ Project-URL: Homepage, https://github.com/integritychain/fips203/tree/main/ffi/python
7
+ Project-URL: Repository, https://github.com/integritychain/fips203.git
8
+ Project-URL: Issues, https://github.com/integritychain/fips203/issues
9
+ Keywords: cryptography,encryption,FIPS,FIPS203,KEM,lattice,module-lattice,post-quantum
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Telecommunications Industry
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Topic :: Security :: Cryptography
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: License :: DFSG approved
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Description-Content-Type: text/markdown
24
+
25
+ # `fips203` Python module
26
+
27
+ This Python module provides an implementation of FIPS 203, the
28
+ Module-Lattice-based Key Encapsulation Mechanism Standard.
29
+
30
+ The underlying mechanism is intended to offer "post-quantum"
31
+ asymmetric encryption and decryption.
32
+
33
+ ## Example
34
+
35
+ The following example shows using the standard ML-KEM algorithm to
36
+ produce identical 32-byte shared secrets:
37
+
38
+ ```
39
+ from fips203 import ML_KEM_512
40
+
41
+ (encapsulation_key, decapsulation_key) = ML_KEM_512.keygen()
42
+ (ciphertext, shared_secret_1) = encapsulation_key.encaps()
43
+ shared_secret_2 = decapsulation_key.decaps(ciphertext)
44
+ assert(shared_secret_1 == shared_secret_2)
45
+ ```
46
+
47
+ Encapsulation keys, decapsulation keys, and ciphertexts can all be
48
+ serialized by accessing them as `bytes`, and deserialized by
49
+ initializing them with the appropriate size bytes object.
50
+
51
+ A serialization example:
52
+
53
+ ```
54
+ from fips203 import ML_KEM_768
55
+
56
+ (ek,dk) = ML_KEM_768.keygen()
57
+ with open('encapskey.bin', 'wb') as f:
58
+ f.write(bytes(ek))
59
+ with open('decapskey.bin', 'wb') as f:
60
+ f.write(bytes(dk))
61
+ ```
62
+
63
+ A deserialization example, followed by use:
64
+
65
+ ```
66
+ import fips203
67
+
68
+ with open('encapskey.bin', 'b') as f:
69
+ ekdata = f.read()
70
+
71
+ ek = fips203.EncapsulationKey(ekdata)
72
+ (ct, ss) = ek.Encaps()
73
+ ```
74
+
75
+ The expected sizes (in bytes) of the different objects in each
76
+ parameter set can be accessed with `EK_SIZE`, `DK_SIZE`, `CT_SIZE`,
77
+ and `SS_SIZE`:
78
+
79
+ ```
80
+ from fips203 import ML_KEM_768
81
+
82
+ print(f"ML-KEM-768 Ciphertext size (in bytes) is {ML_KEM_768.CT_SIZE}")
83
+ ```
84
+
85
+ ## Implementation Notes
86
+
87
+ This is a wrapper around libfips203, built from the Rust fips203-ffi crate.
88
+
89
+ If that library is not installed in the expected path for libraries on
90
+ your system, any attempt to use this module will fail.
91
+
92
+ This module should have reasonable type annotations and docstrings for
93
+ the public interface. If you discover a problem with type
94
+ annotations, or see a way that this kind of documentation could be
95
+ improved, please report it!
96
+
97
+ ## See Also
98
+
99
+ - https://doi.org/10.6028/NIST.FIPS.203.ipd
100
+ - https://github.com/integritychain/fips203
101
+
102
+ ## Bug Reporting
103
+
104
+ Please report issues at https://github.com/integritychain/fips203/issues
@@ -0,0 +1,80 @@
1
+ # `fips203` Python module
2
+
3
+ This Python module provides an implementation of FIPS 203, the
4
+ Module-Lattice-based Key Encapsulation Mechanism Standard.
5
+
6
+ The underlying mechanism is intended to offer "post-quantum"
7
+ asymmetric encryption and decryption.
8
+
9
+ ## Example
10
+
11
+ The following example shows using the standard ML-KEM algorithm to
12
+ produce identical 32-byte shared secrets:
13
+
14
+ ```
15
+ from fips203 import ML_KEM_512
16
+
17
+ (encapsulation_key, decapsulation_key) = ML_KEM_512.keygen()
18
+ (ciphertext, shared_secret_1) = encapsulation_key.encaps()
19
+ shared_secret_2 = decapsulation_key.decaps(ciphertext)
20
+ assert(shared_secret_1 == shared_secret_2)
21
+ ```
22
+
23
+ Encapsulation keys, decapsulation keys, and ciphertexts can all be
24
+ serialized by accessing them as `bytes`, and deserialized by
25
+ initializing them with the appropriate size bytes object.
26
+
27
+ A serialization example:
28
+
29
+ ```
30
+ from fips203 import ML_KEM_768
31
+
32
+ (ek,dk) = ML_KEM_768.keygen()
33
+ with open('encapskey.bin', 'wb') as f:
34
+ f.write(bytes(ek))
35
+ with open('decapskey.bin', 'wb') as f:
36
+ f.write(bytes(dk))
37
+ ```
38
+
39
+ A deserialization example, followed by use:
40
+
41
+ ```
42
+ import fips203
43
+
44
+ with open('encapskey.bin', 'b') as f:
45
+ ekdata = f.read()
46
+
47
+ ek = fips203.EncapsulationKey(ekdata)
48
+ (ct, ss) = ek.Encaps()
49
+ ```
50
+
51
+ The expected sizes (in bytes) of the different objects in each
52
+ parameter set can be accessed with `EK_SIZE`, `DK_SIZE`, `CT_SIZE`,
53
+ and `SS_SIZE`:
54
+
55
+ ```
56
+ from fips203 import ML_KEM_768
57
+
58
+ print(f"ML-KEM-768 Ciphertext size (in bytes) is {ML_KEM_768.CT_SIZE}")
59
+ ```
60
+
61
+ ## Implementation Notes
62
+
63
+ This is a wrapper around libfips203, built from the Rust fips203-ffi crate.
64
+
65
+ If that library is not installed in the expected path for libraries on
66
+ your system, any attempt to use this module will fail.
67
+
68
+ This module should have reasonable type annotations and docstrings for
69
+ the public interface. If you discover a problem with type
70
+ annotations, or see a way that this kind of documentation could be
71
+ improved, please report it!
72
+
73
+ ## See Also
74
+
75
+ - https://doi.org/10.6028/NIST.FIPS.203.ipd
76
+ - https://github.com/integritychain/fips203
77
+
78
+ ## Bug Reporting
79
+
80
+ Please report issues at https://github.com/integritychain/fips203/issues
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.1
2
+ Name: fips203
3
+ Version: 0.2.1
4
+ Summary: ML-KEM (FIPS203) -- asymmetric, quantum-secure encryption (Initial Public Draft)
5
+ Author-email: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
6
+ Project-URL: Homepage, https://github.com/integritychain/fips203/tree/main/ffi/python
7
+ Project-URL: Repository, https://github.com/integritychain/fips203.git
8
+ Project-URL: Issues, https://github.com/integritychain/fips203/issues
9
+ Keywords: cryptography,encryption,FIPS,FIPS203,KEM,lattice,module-lattice,post-quantum
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Telecommunications Industry
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: Topic :: Security :: Cryptography
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: License :: DFSG approved
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Description-Content-Type: text/markdown
24
+
25
+ # `fips203` Python module
26
+
27
+ This Python module provides an implementation of FIPS 203, the
28
+ Module-Lattice-based Key Encapsulation Mechanism Standard.
29
+
30
+ The underlying mechanism is intended to offer "post-quantum"
31
+ asymmetric encryption and decryption.
32
+
33
+ ## Example
34
+
35
+ The following example shows using the standard ML-KEM algorithm to
36
+ produce identical 32-byte shared secrets:
37
+
38
+ ```
39
+ from fips203 import ML_KEM_512
40
+
41
+ (encapsulation_key, decapsulation_key) = ML_KEM_512.keygen()
42
+ (ciphertext, shared_secret_1) = encapsulation_key.encaps()
43
+ shared_secret_2 = decapsulation_key.decaps(ciphertext)
44
+ assert(shared_secret_1 == shared_secret_2)
45
+ ```
46
+
47
+ Encapsulation keys, decapsulation keys, and ciphertexts can all be
48
+ serialized by accessing them as `bytes`, and deserialized by
49
+ initializing them with the appropriate size bytes object.
50
+
51
+ A serialization example:
52
+
53
+ ```
54
+ from fips203 import ML_KEM_768
55
+
56
+ (ek,dk) = ML_KEM_768.keygen()
57
+ with open('encapskey.bin', 'wb') as f:
58
+ f.write(bytes(ek))
59
+ with open('decapskey.bin', 'wb') as f:
60
+ f.write(bytes(dk))
61
+ ```
62
+
63
+ A deserialization example, followed by use:
64
+
65
+ ```
66
+ import fips203
67
+
68
+ with open('encapskey.bin', 'b') as f:
69
+ ekdata = f.read()
70
+
71
+ ek = fips203.EncapsulationKey(ekdata)
72
+ (ct, ss) = ek.Encaps()
73
+ ```
74
+
75
+ The expected sizes (in bytes) of the different objects in each
76
+ parameter set can be accessed with `EK_SIZE`, `DK_SIZE`, `CT_SIZE`,
77
+ and `SS_SIZE`:
78
+
79
+ ```
80
+ from fips203 import ML_KEM_768
81
+
82
+ print(f"ML-KEM-768 Ciphertext size (in bytes) is {ML_KEM_768.CT_SIZE}")
83
+ ```
84
+
85
+ ## Implementation Notes
86
+
87
+ This is a wrapper around libfips203, built from the Rust fips203-ffi crate.
88
+
89
+ If that library is not installed in the expected path for libraries on
90
+ your system, any attempt to use this module will fail.
91
+
92
+ This module should have reasonable type annotations and docstrings for
93
+ the public interface. If you discover a problem with type
94
+ annotations, or see a way that this kind of documentation could be
95
+ improved, please report it!
96
+
97
+ ## See Also
98
+
99
+ - https://doi.org/10.6028/NIST.FIPS.203.ipd
100
+ - https://github.com/integritychain/fips203
101
+
102
+ ## Bug Reporting
103
+
104
+ Please report issues at https://github.com/integritychain/fips203/issues
@@ -0,0 +1,7 @@
1
+ README.md
2
+ fips203.py
3
+ pyproject.toml
4
+ fips203.egg-info/PKG-INFO
5
+ fips203.egg-info/SOURCES.txt
6
+ fips203.egg-info/dependency_links.txt
7
+ fips203.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ fips203
@@ -0,0 +1,374 @@
1
+ '''FIPS 203 (ML-KEM) Asymmetric Post-Quantum Cryptography
2
+
3
+ This Python module provides an implementation of FIPS 203, the
4
+ Module-Lattice-based Key Encapsulation Mechanism Standard.
5
+
6
+ The underlying mechanism is intended to offer "post-quantum"
7
+ asymmetric encryption and decryption.
8
+
9
+ ## Example
10
+
11
+ The following example shows using the standard ML-KEM algorithm to
12
+ produce identical 32-byte shared secrets:
13
+
14
+ ```
15
+ from fips203 import ML_KEM_512
16
+
17
+ (encapsulation_key, decapsulation_key) = ML_KEM_512.keygen()
18
+ (ciphertext, shared_secret_1) = encapsulation_key.encaps()
19
+ shared_secret_2 = decapsulation_key.decaps(ciphertext)
20
+ assert(shared_secret_1 == shared_secret_2)
21
+ ```
22
+
23
+ Encapsulation keys, decapsulation keys, and ciphertexts can all be
24
+ serialized by accessing them as `bytes`, and deserialized by
25
+ initializing them with the appropriate size bytes object.
26
+
27
+ A serialization example:
28
+
29
+ ```
30
+ from fips203 import ML_KEM_768
31
+
32
+ (ek,dk) = ML_KEM_768.keygen()
33
+ with open('encapskey.bin', 'wb') as f:
34
+ f.write(bytes(ek))
35
+ with open('decapskey.bin', 'wb') as f:
36
+ f.write(bytes(dk))
37
+ ```
38
+
39
+ A deserialization example, followed by use:
40
+
41
+ ```
42
+ import fips203
43
+
44
+ with open('encapskey.bin', 'b') as f:
45
+ ekdata = f.read()
46
+
47
+ ek = fips203.EncapsulationKey(ekdata)
48
+ (ct, ss) = ek.Encaps()
49
+ ```
50
+
51
+ The expected sizes (in bytes) of the different objects in each
52
+ parameter set can be accessed with `EK_SIZE`, `DK_SIZE`, `CT_SIZE`,
53
+ and `SS_SIZE`:
54
+
55
+ ```
56
+ from fips203 import ML_KEM_768
57
+
58
+ print(f"ML-KEM-768 Ciphertext size (in bytes) is {ML_KEM_768.CT_SIZE}")
59
+ ```
60
+
61
+ ## Implementation Notes
62
+
63
+ This is a wrapper around libfips203, built from the Rust fips203-ffi crate.
64
+
65
+ If that library is not installed in the expected path for libraries on
66
+ your system, any attempt to use this module will fail.
67
+
68
+ This module should have reasonable type annotations and docstrings for
69
+ the public interface. If you discover a problem with type
70
+ annotations, or see a way that this kind of documentation could be
71
+ improved, please report it!
72
+
73
+ ## See Also
74
+
75
+ - https://doi.org/10.6028/NIST.FIPS.203.ipd
76
+ - https://github.com/integritychain/fips203
77
+
78
+ ## Bug Reporting
79
+
80
+ Please report issues at https://github.com/integritychain/fips203/issues
81
+ '''
82
+
83
+ '''__version__ should track package.version from ../Cargo.toml'''
84
+ __version__ = '0.2.1'
85
+ __author__ = 'Daniel Kahn Gillmor <dkg@fifthhorseman.net>'
86
+ __all__ = [
87
+ 'ML_KEM_512',
88
+ 'ML_KEM_768',
89
+ 'ML_KEM_1024',
90
+ 'Ciphertext',
91
+ 'EncapsulationKey',
92
+ 'DecapsulationKey',
93
+ ]
94
+
95
+ import ctypes
96
+ import ctypes.util
97
+ import enum
98
+ from typing import Tuple, Dict, Any, Union
99
+ from abc import ABC
100
+
101
+
102
+ class _SharedSecret(ctypes.Structure):
103
+ _fields_ = [('data', ctypes.c_uint8 * 32)]
104
+
105
+
106
+ class Err(enum.IntEnum):
107
+ OK = 0
108
+ NULL_PTR_ERROR = 1
109
+ SERIALIZATION_ERROR = 2
110
+ DESERIALIZATION_ERROR = 3
111
+ KEYGEN_ERROR = 4
112
+ ENCAPSULATION_ERROR = 5
113
+ DECAPSULATION_ERROR = 6
114
+
115
+
116
+ class Ciphertext():
117
+ '''ML-KEM Ciphertext
118
+
119
+ Serialize this object by asking for it as `bytes`.
120
+
121
+ You can convert it to a 32-byte shared secret by passing it to the
122
+ Decaps() function of the appropriate Decapsulation Key.
123
+
124
+ '''
125
+ def __init__(self, data: Union[bytes, int]) -> None:
126
+ '''Create ML-KEM Ciphertext from bytes (or strength level).'''
127
+ if isinstance(data, bytes):
128
+ self._strength = _ML_KEM.strength_from_length('CT_SIZE', len(data))
129
+ elif isinstance(data, int):
130
+ self._strength = data
131
+ else:
132
+ raise Exception("Initialize ML-KEM Ciphertext object with "
133
+ f"bytes or a strength level, not {type(data)}")
134
+ self._ffi = _ML_KEM.strength(self._strength)
135
+ self._ct = self._ffi['Ciphertext']()
136
+
137
+ if isinstance(data, bytes):
138
+ self._set(data)
139
+
140
+ def __repr__(self) -> str:
141
+ return f'<ML-KEM-{self._strength} Ciphertext>'
142
+
143
+ def __bytes__(self) -> bytes:
144
+ return bytes(self._ct.data)
145
+
146
+ def _set(self, data: bytes) -> None:
147
+ if len(data) != len(self._ct.data):
148
+ raise ValueError(f"Expected {len(self._ct.data)} bytes, "
149
+ f"got {len(data)}")
150
+ for i in range(len(data)):
151
+ self._ct.data[i] = data[i]
152
+
153
+
154
+ class EncapsulationKey():
155
+ '''ML-KEM Encapsulation Key
156
+
157
+ Serialize this object by asking for it as `bytes`.
158
+
159
+ Produce a Ciphertext and a 32-byte shared secret by invoking
160
+ Encaps() on it.
161
+ '''
162
+ def __init__(self, data: Union[bytes, int]) -> None:
163
+ '''Create ML-KEM Encapsulation Key from bytes (or strength level).'''
164
+ if isinstance(data, bytes):
165
+ self._strength = _ML_KEM.strength_from_length('EK_SIZE', len(data))
166
+ elif isinstance(data, int):
167
+ self._strength = data
168
+ else:
169
+ raise Exception("Initialize ML-KEM Encapsulation Key with "
170
+ f"bytes or a strength level, not {type(data)}")
171
+ self._ffi = _ML_KEM.strength(self._strength)
172
+ self._ek = self._ffi['EncapsKey']()
173
+ if isinstance(data, bytes):
174
+ self._set(data)
175
+
176
+ def __repr__(self) -> str:
177
+ return f'<ML-KEM-{self._strength} Encapsulation Key>'
178
+
179
+ def __bytes__(self) -> bytes:
180
+ return bytes(self._ek.data)
181
+
182
+ def _set(self, data: bytes) -> None:
183
+ if len(data) != len(self._ek.data):
184
+ raise ValueError(f"Expected {len(self._ek.data)} bytes, "
185
+ f"got {len(data)}")
186
+ for i in range(len(data)):
187
+ self._ek.data[i] = data[i]
188
+
189
+ def encaps(self) -> Tuple[Ciphertext, bytes]:
190
+ '''Produce a new Ciphertext and corresponding 32-byte shared secret.'''
191
+ ct = Ciphertext(self._strength)
192
+ ss = _SharedSecret()
193
+ ret = Err(self._ffi['encaps'](ctypes.byref(self._ek),
194
+ ctypes.byref(ct._ct),
195
+ ctypes.byref(ss)))
196
+ if ret is not Err.OK:
197
+ raise Exception(f"ml_kem_{self._strength}_encaps() "
198
+ f"returned {ret} ({ret.name})")
199
+ return (ct, bytes(ss.data))
200
+
201
+
202
+ class DecapsulationKey():
203
+ '''ML-KEM Decapsulation Key
204
+
205
+ Serialize this object by asking for it as `bytes`.
206
+
207
+ Produce a 32-byte shared secret from a Ciphertext by invoking
208
+ Decaps() on it.
209
+ '''
210
+ def __init__(self, data: Union[bytes, int]) -> None:
211
+ '''Create ML-KEM Decapsulation Key from bytes (or strength level).'''
212
+ if isinstance(data, bytes):
213
+ self._strength = _ML_KEM.strength_from_length('DK_SIZE', len(data))
214
+ elif isinstance(data, int):
215
+ self._strength = data
216
+ else:
217
+ raise Exception("Initialize ML-KEM Encapsulation Key with bytes "
218
+ f"or a strength level, not {type(data)}")
219
+ self._ffi = _ML_KEM.strength(self._strength)
220
+ self._dk = self._ffi['DecapsKey']()
221
+ if isinstance(data, bytes):
222
+ self._set(data)
223
+
224
+ def __repr__(self) -> str:
225
+ return f'<ML-KEM-{self._strength} Decapsulation Key>'
226
+
227
+ def __bytes__(self) -> bytes:
228
+ return bytes(self._dk.data)
229
+
230
+ def _set(self, data: bytes) -> None:
231
+ if len(data) != len(self._dk.data):
232
+ raise ValueError(f"Expected {len(self._dk.data)} bytes, "
233
+ f"got {len(data)}")
234
+ for i in range(len(data)):
235
+ self._dk.data[i] = data[i]
236
+
237
+ def decaps(self, ct: Ciphertext) -> bytes:
238
+ '''Get 32-byte shared secret corresponding to the given Ciphertext.'''
239
+ if self._strength != ct._strength:
240
+ raise Exception(f"Cannot decapsulate {ct} with {self}")
241
+ ss = _SharedSecret()
242
+ ret = Err(self._ffi['decaps'](ctypes.byref(self._dk),
243
+ ctypes.byref(ct._ct),
244
+ ctypes.byref(ss)))
245
+ if ret is not Err.OK:
246
+ raise Exception(f"ml_kem_{self._strength}_decaps() "
247
+ f"returned {ret} ({ret.name})")
248
+ return bytes(ss.data)
249
+
250
+
251
+ class _ML_KEM():
252
+ params: Dict[int, Dict[str, int]] = {
253
+ 512: {
254
+ 'EK_SIZE': 800,
255
+ 'DK_SIZE': 1632,
256
+ 'CT_SIZE': 768,
257
+ },
258
+ 768: {
259
+ 'EK_SIZE': 1184,
260
+ 'DK_SIZE': 2400,
261
+ 'CT_SIZE': 1088,
262
+ },
263
+ 1024: {
264
+ 'EK_SIZE': 1568,
265
+ 'DK_SIZE': 3168,
266
+ 'CT_SIZE': 1568,
267
+ },
268
+ }
269
+ lib = ctypes.CDLL(ctypes.util.find_library('fips203'))
270
+ if not hasattr(lib, 'ml_kem_512_keygen'): lib = ctypes.CDLL("../target/debug/libfips203.so")
271
+
272
+ # use Any below because i don't know how to specify the type of the FuncPtr
273
+ ffi: Dict[int, Dict[str, Any]] = {}
274
+
275
+ @classmethod
276
+ def strength(cls, level: int) -> Dict[str, Any]:
277
+ if level not in cls.ffi:
278
+ class _EncapsKey(ctypes.Structure):
279
+ _fields_ = [('data', ctypes.c_uint8 *
280
+ cls.params[level]['EK_SIZE'])]
281
+
282
+ class _DecapsKey(ctypes.Structure):
283
+ _fields_ = [('data', ctypes.c_uint8 *
284
+ cls.params[level]['DK_SIZE'])]
285
+
286
+ class _Ciphertext(ctypes.Structure):
287
+ _fields_ = [('data', ctypes.c_uint8 *
288
+ cls.params[level]['CT_SIZE'])]
289
+ ffi: Dict[str, Any] = {}
290
+
291
+ ffi['keygen'] = cls.lib[f'ml_kem_{level}_keygen']
292
+ ffi['keygen'].argtypes = [ctypes.POINTER(_EncapsKey),
293
+ ctypes.POINTER(_DecapsKey)]
294
+ ffi['keygen'].restype = ctypes.c_uint8
295
+
296
+ ffi['encaps'] = cls.lib[f'ml_kem_{level}_encaps']
297
+ ffi['encaps'].argtypes = [ctypes.POINTER(_EncapsKey),
298
+ ctypes.POINTER(_Ciphertext),
299
+ ctypes.POINTER(_SharedSecret)]
300
+ ffi['encaps'].restype = ctypes.c_uint8
301
+
302
+ ffi['decaps'] = cls.lib[f'ml_kem_{level}_decaps']
303
+ ffi['decaps'].argtypes = [ctypes.POINTER(_DecapsKey),
304
+ ctypes.POINTER(_Ciphertext),
305
+ ctypes.POINTER(_SharedSecret)]
306
+ ffi['decaps'].restype = ctypes.c_uint8
307
+
308
+ ffi['EncapsKey'] = _EncapsKey
309
+ ffi['DecapsKey'] = _DecapsKey
310
+ ffi['Ciphertext'] = _Ciphertext
311
+
312
+ cls.ffi[level] = ffi
313
+
314
+ return cls.ffi[level]
315
+
316
+ @classmethod
317
+ def strength_from_length(cls, object_type: str, object_len: int) -> int:
318
+ for strength in cls.params:
319
+ if cls.params[strength][object_type] == object_len:
320
+ return strength
321
+ raise Exception(f"No ML-KEM parameter set has {object_type} "
322
+ f"of {object_len} bytes")
323
+
324
+ @classmethod
325
+ def _keygen(cls, strength: int) -> Tuple[EncapsulationKey,
326
+ DecapsulationKey]:
327
+ ek = EncapsulationKey(strength)
328
+ dk = DecapsulationKey(strength)
329
+
330
+ ret = Err(cls.strength(strength)['keygen'](ctypes.byref(ek._ek),
331
+ ctypes.byref(dk._dk)))
332
+ if ret is not Err.OK:
333
+ raise Exception(f"ml_kem_{strength}_keygen() returned "
334
+ f"{ret} ({ret.name})")
335
+ return (ek, dk)
336
+
337
+
338
+ class ML_KEM(ABC):
339
+ '''Abstract base class for all ML-KEM (FIPS 203) parameter sets.'''
340
+
341
+ _strength: int
342
+ EK_SIZE: int
343
+ DK_SIZE: int
344
+ CT_SIZE: int
345
+ SS_SIZE: int = 32
346
+
347
+ @classmethod
348
+ def keygen(cls) -> Tuple[EncapsulationKey, DecapsulationKey]:
349
+ '''Generate a pair of Encapsulation and Decapsulation Keys.'''
350
+ return _ML_KEM._keygen(cls._strength)
351
+
352
+
353
+ class ML_KEM_512(ML_KEM):
354
+ '''ML-KEM-512 (FIPS 203) Implementation.'''
355
+ _strength: int = 512
356
+ EK_SIZE: int = 800
357
+ DK_SIZE: int = 1632
358
+ CT_SIZE: int = 768
359
+
360
+
361
+ class ML_KEM_768(ML_KEM):
362
+ '''ML-KEM-768 (FIPS 203) Implementation.'''
363
+ _strength: int = 768
364
+ EK_SIZE: int = 1184
365
+ DK_SIZE: int = 2400
366
+ CT_SIZE: int = 1088
367
+
368
+
369
+ class ML_KEM_1024(ML_KEM):
370
+ '''ML-KEM-1024 (FIPS 203) Implementation.'''
371
+ _strength: int = 1024
372
+ EK_SIZE: int = 1568
373
+ DK_SIZE: int = 3168
374
+ CT_SIZE: int = 1568
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fips203"
7
+ dynamic = ["version"]
8
+ description = "ML-KEM (FIPS203) -- asymmetric, quantum-secure encryption (Initial Public Draft)"
9
+ authors = [{name = "Daniel Kahn Gillmor", email = "dkg@fifthhorseman.net"}]
10
+ keywords = [
11
+ "cryptography",
12
+ "encryption",
13
+ "FIPS",
14
+ "FIPS203",
15
+ "KEM",
16
+ "lattice",
17
+ "module-lattice",
18
+ "post-quantum",
19
+ ]
20
+ # README.md duplicates the module docstring.
21
+ # I do not know how to keep them automatically in sync
22
+ readme = "README.md"
23
+ # only dependencies are having the libfips203 shared object available
24
+ # everything else is from the stdlib
25
+ dependencies = []
26
+ classifiers = [
27
+ "Development Status :: 4 - Beta",
28
+ "Intended Audience :: Developers",
29
+ "Intended Audience :: Telecommunications Industry",
30
+ "Intended Audience :: Information Technology",
31
+ "Topic :: Security :: Cryptography",
32
+ "License :: OSI Approved :: MIT License",
33
+ # Apache 2.0 license is not in the list of known classifiers
34
+ # So the dual licensing of this module is not adequately represented
35
+ # "License :: OSI Approved :: Apache 2.0 License",
36
+ "License :: DFSG approved",
37
+ "Programming Language :: Python :: 3",
38
+ "Programming Language :: Python :: 3.9",
39
+ "Programming Language :: Python :: 3.10",
40
+ "Programming Language :: Python :: 3.11",
41
+ "Programming Language :: Python :: 3.12",
42
+ "Programming Language :: Python :: 3.13",
43
+ ]
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/integritychain/fips203/tree/main/ffi/python"
47
+ Repository = "https://github.com/integritychain/fips203.git"
48
+ Issues = "https://github.com/integritychain/fips203/issues"
49
+
50
+ [tool.setuptools.dynamic]
51
+ version = {attr = "fips203.__version__"}
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+