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 +104 -0
- fips203-0.2.1/README.md +80 -0
- fips203-0.2.1/fips203.egg-info/PKG-INFO +104 -0
- fips203-0.2.1/fips203.egg-info/SOURCES.txt +7 -0
- fips203-0.2.1/fips203.egg-info/dependency_links.txt +1 -0
- fips203-0.2.1/fips203.egg-info/top_level.txt +1 -0
- fips203-0.2.1/fips203.py +374 -0
- fips203-0.2.1/pyproject.toml +51 -0
- fips203-0.2.1/setup.cfg +4 -0
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
|
fips203-0.2.1/README.md
ADDED
|
@@ -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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fips203
|
fips203-0.2.1/fips203.py
ADDED
|
@@ -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__"}
|
fips203-0.2.1/setup.cfg
ADDED