ml_kem 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d0131894b513113fb5492afe072bfec6ac2ba8d64531dbbd9c712cf419aff3a
4
- data.tar.gz: f61a2aa77bef7d515d8b55c10dc66f07342b408d8884fbeca88e3d1fe673d4c1
3
+ metadata.gz: 291966b70102237cc2d2c36410dffafb2aef96f16381f0c4f25bb52b54e2666d
4
+ data.tar.gz: a12f02cda9894cd988451e38a6eebb4bb6cd0b889f1ae261a8fd64fc5cf3882d
5
5
  SHA512:
6
- metadata.gz: 83a36606aa8c28521590dcd2c10a60f74b9e76e90562c60f2b1b3af188294567736c45ba128d40a322e63d65640461be8720004b7bd72a553f4db427655d9573
7
- data.tar.gz: 1bf7d2be9236d916e4e5043f2377c630632a48277459e17ca521ba5890b61c54c31591c4fef6e86ab2bb22d12ee4c3e3cfefc9cd84c84953775cdf30746796bf
6
+ metadata.gz: 5b06d18dbf5a67425607e6cfadb21681d19ed0cee9efa8ad516e67874487fec7fd4ed93e079bbf923457824e309d4af8da9396019e8473c257210032c0b944d9
7
+ data.tar.gz: b683231323c1a52032acf6d084cf2e5ce2b1b508109b58e01ee340139091f16ba73915b7f2bbfc01a786922c7c65e32c7085ff9122b87426609467d577c1a259
data/lib/ml_kem/cli.rb ADDED
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'ml_kem'
5
+ require 'base64'
6
+
7
+ module MLKEM
8
+ # Command Line Interface (CLI) for ML-KEM key encapsulation mechanism.
9
+ # Supports key generation, encapsulation, and decapsulation via Thor commands.
10
+ #
11
+ # @example Generate keys
12
+ # $ mlkem keygen -p pub.pem -s priv.pem
13
+ #
14
+ # @example Encapsulate
15
+ # $ mlkem encaps -p pub.pem -c ct.txt -k secret.key
16
+ #
17
+ # @example Decapsulate
18
+ # $ mlkem decaps -s priv.pem -c ct.txt -k secret.key
19
+ #
20
+ # @since 0.1.0
21
+ class CLI < Thor
22
+ # Ensures the CLI exits with a non-zero status code on failure
23
+ def self.exit_on_failure?
24
+ true
25
+ end
26
+
27
+ # Global option for selecting the ML-KEM variant
28
+ class_option :variant,
29
+ aliases: '-v',
30
+ type: :string,
31
+ default: 'ml_kem_768',
32
+ desc: 'Select the ML-KEM variant (ml_kem_512, ml_kem_768, ml_kem_1024)'
33
+
34
+ desc "keygen", "Generate a key pair"
35
+ option :pk, aliases: "-p", type: :string, default: "public_key.pem", desc: "Output file for the public key"
36
+ option :sk, aliases: "-s", type: :string, default: "private_key.pem", desc: "Output file for the private key"
37
+ #
38
+ # Generates a public/private key pair and stores them in PEM format.
39
+ #
40
+ # @option options [String] :pk Output path for the public key.
41
+ # @option options [String] :sk Output path for the private key.
42
+ #
43
+ # @example
44
+ # $ mlkem keygen -p pub.pem -s priv.pem
45
+ def keygen
46
+ mlkem = create_mlkem_instance
47
+ public_key, private_key = mlkem.keygen
48
+
49
+ File.write(options[:pk], encode_pem(public_key, "PUBLIC KEY"))
50
+ File.write(options[:sk], encode_pem(private_key, "PRIVATE KEY"))
51
+
52
+ puts "Keys generated:\n Public Key: #{options[:pk]}\n Private Key: #{options[:sk]}"
53
+ end
54
+
55
+ desc "encaps", "Encapsulate a secret using the public key"
56
+ option :pk, aliases: "-p", type: :string, required: true, desc: "Input file containing the public key (for encapsulation)"
57
+ option :sharedkey, aliases: "-k", type: :string, default: "shared_secret.key", desc: "Output file for the shared secret"
58
+ option :ciphertext, aliases: "-c", type: :string, default: "ciphertext.txt", desc: "Output file for the ciphertext"
59
+ #
60
+ # Encapsulates a shared secret using the given public key.
61
+ #
62
+ # @option options [String] :pk Path to PEM-encoded public key file.
63
+ # @option options [String] :sharedkey Output path for the base64-encoded shared secret.
64
+ # @option options [String] :ciphertext Output path for the base64-encoded ciphertext.
65
+ #
66
+ # @example
67
+ # $ mlkem encaps -p pub.pem -c ct.txt -k secret.key
68
+ def encaps
69
+ mlkem = create_mlkem_instance
70
+ public_key_pem = File.read(options[:pk])
71
+ public_key = decode_pem(public_key_pem)
72
+
73
+ secret, ciphertext = mlkem.encaps(public_key)
74
+
75
+ File.write(options[:ciphertext], Base64.strict_encode64(ciphertext))
76
+ File.write(options[:sharedkey], Base64.strict_encode64(secret))
77
+
78
+ puts "Encapsulation complete:\n Ciphertext: #{options[:ciphertext]}\n Shared Secret: #{options[:sharedkey]}"
79
+ end
80
+
81
+ desc "decaps", "Decapsulate a ciphertext using the private key"
82
+ option :sk, aliases: "-s", type: :string, required: true, desc: "Input file containing the private key (for decapsulation)"
83
+ option :ciphertext, aliases: "-c", type: :string, required: true, desc: "Ciphertext file to decapsulate"
84
+ option :sharedkey, aliases: "-k", type: :string, default: "shared_secret.key", desc: "Output file for the shared secret"
85
+ #
86
+ # Decapsulates a ciphertext to recover the shared secret using the private key.
87
+ #
88
+ # @option options [String] :sk Path to PEM-encoded private key file.
89
+ # @option options [String] :ciphertext Path to base64-encoded ciphertext file.
90
+ # @option options [String] :sharedkey Output path for the base64-encoded shared secret.
91
+ #
92
+ # @example
93
+ # $ mlkem decaps -s priv.pem -c ct.txt -k secret.key
94
+ def decaps
95
+ mlkem = create_mlkem_instance
96
+ private_key_pem = File.read(options[:sk])
97
+ private_key = decode_pem(private_key_pem)
98
+
99
+ ciphertext_base64 = File.read(options[:ciphertext])
100
+ ciphertext = Base64.decode64(ciphertext_base64)
101
+
102
+ secret = mlkem.decaps(private_key, ciphertext)
103
+
104
+ File.write(options[:sharedkey], Base64.strict_encode64(secret))
105
+
106
+ puts "Decapsulation complete. Shared secret written to #{options[:sharedkey]}"
107
+ end
108
+
109
+ private
110
+
111
+ # Initializes the ML-KEM instance using the provided variant.
112
+ #
113
+ # @return [MLKEM::MLKEM] Instance for encryption operations.
114
+ # @raise [ArgumentError] if the variant is not recognized.
115
+ def create_mlkem_instance
116
+ variant = options[:variant].to_sym
117
+ valid_variants = [:ml_kem_512, :ml_kem_768, :ml_kem_1024]
118
+
119
+ unless valid_variants.include?(variant)
120
+ raise ArgumentError, "Invalid variant: #{options[:variant]}. Valid options are: #{valid_variants.join(', ')}"
121
+ end
122
+
123
+ ::MLKEM::MLKEM.new(variant: variant)
124
+ end
125
+
126
+ # Decodes a PEM string into binary data.
127
+ #
128
+ # @param [String] pem_str The PEM-encoded key string.
129
+ # @return [String] Raw binary key data.
130
+ def decode_pem(pem_str)
131
+ base64_body = pem_str.lines.reject { |line|
132
+ line =~ /-----BEGIN|-----END/ || line.strip.empty?
133
+ }.join
134
+ Base64.decode64(base64_body)
135
+ end
136
+
137
+ # Encodes binary data into PEM format.
138
+ #
139
+ # @param [String] bin_str The binary data.
140
+ # @param [String] label The PEM label (e.g., "PUBLIC KEY").
141
+ # @return [String] PEM-formatted string.
142
+ def encode_pem(bin_str, label)
143
+ encoded = Base64.strict_encode64(bin_str).scan(/.{1,64}/).join("\n")
144
+ "-----BEGIN #{label}-----\n#{encoded}\n-----END #{label}-----\n"
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ module MLKEM
5
+ # MLKEM::Constants module holds all static cryptographic parameters and tables
6
+ # used throughout the ML-KEM implementation.
7
+ #
8
+ # These include:
9
+ # - Approved parameter sets for ML-KEM (Table 2 of the specification)
10
+ # - Modulus (Q)
11
+ # - Polynomial degree (N)
12
+ # - Precomputed values for the Number-Theoretic Transform (NTT)
13
+ #
14
+ # @see https://csrc.nist.gov/pubs/fips/203/final NIST FIPS 203 - ML-KEM Specification
15
+ #
16
+ # @since 0.1.0
17
+ module Constants
18
+ ##
19
+ # Approved parameter sets for ML-KEM.
20
+ # These are tuples of the form [k, eta1, eta2, du, dv] where:
21
+ # - k : number of polynomials in the public key
22
+ # - eta1 : noise sampling parameter for secret key
23
+ # - eta2 : noise sampling parameter for encryption
24
+ # - du : number of bits revealed in the ciphertext part u
25
+ # - dv : number of bits revealed in the ciphertext part v
26
+ #
27
+ # @return [Hash{String => Array<Integer>}]
28
+ PARAM_SETS = {
29
+ 'ML-KEM-512' => [2, 3, 2, 10, 4],
30
+ 'ML-KEM-768' => [3, 2, 2, 10, 4],
31
+ 'ML-KEM-1024' => [4, 2, 2, 11, 5]
32
+ }.freeze
33
+
34
+ ##
35
+ # Modulus used for all arithmetic operations in the ring Z_q.
36
+ #
37
+ # @return [Integer]
38
+ Q = 3329
39
+
40
+ ##
41
+ # Degree of the polynomials used in ML-KEM, i.e., number of coefficients.
42
+ #
43
+ # @return [Integer]
44
+ N = 256
45
+
46
+ ##
47
+ # Precomputed roots of unity for the Number-Theoretic Transform (NTT),
48
+ # as required by Appendix A of the ML-KEM specification.
49
+ #
50
+ # These are used for forward NTT operations.
51
+ #
52
+ # @return [Array<Integer>]
53
+ ZETA_NTT = [
54
+ 1, 1729, 2580, 3289, 2642, 630, 1897, 848,
55
+ 1062, 1919, 193, 797, 2786, 3260, 569, 1746,
56
+ 296, 2447, 1339, 1476, 3046, 56, 2240, 1333,
57
+ 1426, 2094, 535, 2882, 2393, 2879, 1974, 821,
58
+ 289, 331, 3253, 1756, 1197, 2304, 2277, 2055,
59
+ 650, 1977, 2513, 632, 2865, 33, 1320, 1915,
60
+ 2319, 1435, 807, 452, 1438, 2868, 1534, 2402,
61
+ 2647, 2617, 1481, 648, 2474, 3110, 1227, 910,
62
+ 17, 2761, 583, 2649, 1637, 723, 2288, 1100,
63
+ 1409, 2662, 3281, 233, 756, 2156, 3015, 3050,
64
+ 1703, 1651, 2789, 1789, 1847, 952, 1461, 2687,
65
+ 939, 2308, 2437, 2388, 733, 2337, 268, 641,
66
+ 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645,
67
+ 1063, 319, 2773, 757, 2099, 561, 2466, 2594,
68
+ 2804, 1092, 403, 1026, 1143, 2150, 2775, 886,
69
+ 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154
70
+ ].freeze
71
+
72
+ ##
73
+ # Precomputed values used in point-wise multiplication during the NTT.
74
+ # These include both positive and negative roots of unity.
75
+ #
76
+ # @return [Array<Integer>]
77
+ ZETA_MUL = [
78
+ 17, -17, 2761, -2761, 583, -583, 2649, -2649,
79
+ 1637, -1637, 723, -723, 2288, -2288, 1100, -1100,
80
+ 1409, -1409, 2662, -2662, 3281, -3281, 233, -233,
81
+ 756, -756, 2156, -2156, 3015, -3015, 3050, -3050,
82
+ 1703, -1703, 1651, -1651, 2789, -2789, 1789, -1789,
83
+ 1847, -1847, 952, -952, 1461, -1461, 2687, -2687,
84
+ 939, -939, 2308, -2308, 2437, -2437, 2388, -2388,
85
+ 733, -733, 2337, -2337, 268, -268, 641, -641,
86
+ 1584, -1584, 2298, -2298, 2037, -2037, 3220, -3220,
87
+ 375, -375, 2549, -2549, 2090, -2090, 1645, -1645,
88
+ 1063, -1063, 319, -319, 2773, -2773, 757, -757,
89
+ 2099, -2099, 561, -561, 2466, -2466, 2594, -2594,
90
+ 2804, -2804, 1092, -1092, 403, -403, 1026, -1026,
91
+ 1143, -1143, 2150, -2150, 2775, -2775, 886, -886,
92
+ 1722, -1722, 1212, -1212, 1874, -1874, 1029, -1029,
93
+ 2110, -2110, 2935, -2935, 885, -885, 2154, -2154
94
+ ].freeze
95
+ end
96
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MLKEM
4
+ # Core module containing the Public Key Encryption (K-PKE) implementation and the ML-KEM internal logic in two separate classes.
5
+ #
6
+ # @since 0.1.0
7
+ module Core
8
+ # Implements the Public Key Encryption (K-PKE) as specified in ML-KEM (Kyber).
9
+ # Provides methods for key generation, encryption, and decryption using lattice-based NTT arithmetic.
10
+ #
11
+ # @since 0.1.0
12
+ class KPKE
13
+ # Initializes the KPKE engine with given parameters.
14
+ #
15
+ # @param [Integer] k Security level (2, 3, or 4 for ML-KEM-512/768/1024)
16
+ # @param [Integer] eta1 Noise parameter for secret and error
17
+ # @param [Integer] eta2 Noise parameter for encryption errors
18
+ # @param [Integer] du Compression bits for u
19
+ # @param [Integer] dv Compression bits for v
20
+ # @param [Integer] q Prime modulus
21
+ def initialize(k, eta1, eta2, du, dv, q = Constants::Q)
22
+ @k = k
23
+ @eta1 = eta1
24
+ @eta2 = eta2
25
+ @du = du
26
+ @dv = dv
27
+ @q = q
28
+
29
+ @poly_ops = Math::Polynomial.new(@q)
30
+ @ntt_ops = Math::NTT.new(@q)
31
+ @sampling = Math::Sampling.new(@q)
32
+ end
33
+
34
+ # Key generation algorithm (Algorithm 13).
35
+ #
36
+ # @param [String] d Random seed (32 bytes)
37
+ # @return [Array<String>] [public_key, secret_key] both as binary strings
38
+ def keygen(d)
39
+ rho, sig = Crypto::SymmetricPrimitives.g(d + [@k].pack('C'))
40
+ n = 0
41
+
42
+ a = Array.new(@k) { Array.new(@k) }
43
+ @k.times do |i|
44
+ @k.times do |j|
45
+ a[i][j] = @sampling.sample_ntt(rho + [j, i].pack('CC'))
46
+ end
47
+ end
48
+
49
+ s = Array.new(@k)
50
+ e = Array.new(@k)
51
+ @k.times do |i|
52
+ s[i] = @sampling.sample_poly_cbd(@eta1, Crypto::SymmetricPrimitives.prf(@eta1, sig, n))
53
+ n += 1
54
+ e[i] = @sampling.sample_poly_cbd(@eta1, Crypto::SymmetricPrimitives.prf(@eta1, sig, n))
55
+ n += 1
56
+ end
57
+
58
+ s.map! { |v| @ntt_ops.ntt(v) }
59
+ e.map! { |v| @ntt_ops.ntt(v) }
60
+
61
+ t = e.dup
62
+ @k.times do |i|
63
+ @k.times do |j|
64
+ t[i] = @poly_ops.add(t[i], @ntt_ops.multiply_ntts(a[i][j], s[j]))
65
+ end
66
+ end
67
+
68
+ ek_pke = Math::ByteOperations.byte_encode(12, t, @q) + rho
69
+ dk_pke = Math::ByteOperations.byte_encode(12, s, @q)
70
+
71
+ [ek_pke, dk_pke]
72
+ end
73
+
74
+ # Encryption algorithm (Algorithm 14).
75
+ #
76
+ # @param [String] ek_pke Public key
77
+ # @param [String] m Message (32 bytes of encoded bits)
78
+ # @param [String] r Randomness (32-byte seed)
79
+ # @return [String] Ciphertext
80
+ def encrypt(ek_pke, m, r)
81
+ n = 0
82
+
83
+ t = Array.new(@k)
84
+ @k.times do |i|
85
+ t[i] = Math::ByteOperations.byte_decode(12, ek_pke[(384 * i)...(384 * (i + 1))], @q)
86
+ end
87
+ rho = ek_pke[(384 * @k)...(384 * @k + 32)]
88
+
89
+ a = Array.new(@k) { Array.new(@k) }
90
+ @k.times do |i|
91
+ @k.times do |j|
92
+ a[i][j] = @sampling.sample_ntt(rho + [j, i].pack('CC'))
93
+ end
94
+ end
95
+
96
+ y = Array.new(@k)
97
+ e1 = Array.new(@k)
98
+ @k.times do |i|
99
+ y[i] = @sampling.sample_poly_cbd(@eta1, Crypto::SymmetricPrimitives.prf(@eta1, r, n))
100
+ n += 1
101
+ e1[i] = @sampling.sample_poly_cbd(@eta2, Crypto::SymmetricPrimitives.prf(@eta2, r, n))
102
+ n += 1
103
+ end
104
+ e2 = @sampling.sample_poly_cbd(@eta2, Crypto::SymmetricPrimitives.prf(@eta2, r, n))
105
+
106
+ y.map! { |v| @ntt_ops.ntt(v) }
107
+
108
+ u = Array.new(@k) { Array.new(256, 0) }
109
+ @k.times do |i|
110
+ @k.times do |j|
111
+ u[i] = @poly_ops.add(u[i], @ntt_ops.multiply_ntts(a[j][i], y[j]))
112
+ end
113
+ end
114
+
115
+ @k.times do |i|
116
+ u[i] = @ntt_ops.inverse_ntt(u[i])
117
+ u[i] = @poly_ops.add(u[i], e1[i])
118
+ end
119
+
120
+ mu = @poly_ops.decompress(1, Math::ByteOperations.byte_decode(1, m, @q))
121
+
122
+ v = Array.new(256, 0)
123
+ @k.times do |i|
124
+ v = @poly_ops.add(v, @ntt_ops.multiply_ntts(t[i], y[i]))
125
+ end
126
+ v = @ntt_ops.inverse_ntt(v)
127
+ v = @poly_ops.add(v, e2)
128
+ v = @poly_ops.add(v, mu)
129
+
130
+ c1 = ''
131
+ @k.times do |i|
132
+ c1 += Math::ByteOperations.byte_encode(@du, @poly_ops.compress(@du, u[i]), @q)
133
+ end
134
+ c2 = Math::ByteOperations.byte_encode(@dv, @poly_ops.compress(@dv, v), @q)
135
+
136
+ c1 + c2
137
+ end
138
+
139
+ # Decryption algorithm (Algorithm 15).
140
+ #
141
+ # @param [String] dk_pke Secret key
142
+ # @param [String] c Ciphertext
143
+ # @return [String] Recovered message (32-byte encoded bitstring)
144
+ def decrypt(dk_pke, c)
145
+ c1 = c[0...(32 * @du * @k)]
146
+ c2 = c[(32 * @du * @k)...(32 * (@du * @k + @dv))]
147
+
148
+ up = Array.new(@k)
149
+ @k.times do |i|
150
+ up[i] = @poly_ops.decompress(@du,
151
+ Math::ByteOperations.byte_decode(@du, c1[(32 * @du * i)...(32 * @du * (i + 1))], @q))
152
+ end
153
+
154
+ vp = @poly_ops.decompress(@dv, Math::ByteOperations.byte_decode(@dv, c2, @q))
155
+
156
+ s = Array.new(@k)
157
+ @k.times do |i|
158
+ s[i] = Math::ByteOperations.byte_decode(12, dk_pke[(384 * i)...(384 * (i + 1))], @q)
159
+ end
160
+
161
+ w = Array.new(256, 0)
162
+ @k.times do |i|
163
+ w = @poly_ops.add(w, @ntt_ops.multiply_ntts(s[i], @ntt_ops.ntt(up[i])))
164
+ end
165
+ w = @poly_ops.subtract(vp, @ntt_ops.inverse_ntt(w))
166
+
167
+ Math::ByteOperations.byte_encode(1, @poly_ops.compress(1, w), @q)
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MLKEM
4
+ module Core
5
+ # Internal implementation of ML-KEM (Kyber) encapsulation and decapsulation algorithms.
6
+ # Wraps K-PKE logic and adds hashing and key derivation per ML-KEM specification.
7
+ #
8
+ # Algorithms implemented:
9
+ # - Algorithm 16: ML-KEM.KeyGen_internal
10
+ # - Algorithm 17: ML-KEM.Encaps_internal
11
+ # - Algorithm 18: ML-KEM.Decaps_internal
12
+ #
13
+ # @since 0.1.0
14
+ class MLKEMInternal
15
+ # Constructs the internal ML-KEM engine.
16
+ #
17
+ # @param [Integer] k Security level (1, 3, or 5)
18
+ # @param [Integer] eta1 Noise parameter η₁
19
+ # @param [Integer] eta2 Noise parameter η₂
20
+ # @param [Integer] du Compression bits for u
21
+ # @param [Integer] dv Compression bits for v
22
+ def initialize(k, eta1, eta2, du, dv)
23
+ @k = k
24
+ @eta1 = eta1
25
+ @eta2 = eta2
26
+ @du = du
27
+ @dv = dv
28
+ @q = Constants::Q
29
+ @kpke = KPKE.new(@k, @eta1, @eta2, @du, @dv, @q)
30
+ end
31
+
32
+ # ML-KEM Key Generation (Algorithm 16).
33
+ #
34
+ # @param [String] d A 32-byte seed for public key derivation.
35
+ # @param [String] z A 32-byte random secret value.
36
+ # @return [Array<String>] [ek, dk] Public and private key pair.
37
+ #
38
+ # @example
39
+ # ek, dk = kem.keygen_internal(seed, z)
40
+ def keygen_internal(d, z)
41
+ ek_pke, dk_pke = @kpke.keygen(d)
42
+ ek = ek_pke
43
+ dk = dk_pke + ek + Crypto::SymmetricPrimitives.h(ek) + z
44
+ [ek, dk]
45
+ end
46
+
47
+ # ML-KEM Encapsulation (Algorithm 17).
48
+ #
49
+ # @param [String] ek Public key.
50
+ # @param [String] m A 32-byte message (uniformly random).
51
+ # @return [Array<String>] [k, c] Shared secret and ciphertext.
52
+ #
53
+ # @example
54
+ # k, c = kem.encaps_internal(ek, m)
55
+ def encaps_internal(ek, m)
56
+ k, r = Crypto::SymmetricPrimitives.g(m + Crypto::SymmetricPrimitives.h(ek))
57
+ c = @kpke.encrypt(ek, m, r)
58
+ [k, c]
59
+ end
60
+
61
+ # ML-KEM Decapsulation (Algorithm 18).
62
+ #
63
+ # @param [String] dk Private key.
64
+ # @param [String] c Ciphertext.
65
+ # @return [String] Shared secret (32 bytes).
66
+ #
67
+ # @example
68
+ # k = kem.decaps_internal(dk, c)
69
+ def decaps_internal(dk, c)
70
+ dk_pke = dk[0...(384 * @k)]
71
+ ek_pke = dk[(384 * @k)...(768 * @k + 32)]
72
+ h_val = dk[(768 * @k + 32)...(768 * @k + 64)]
73
+ z = dk[(768 * @k + 64)...(768 * @k + 96)]
74
+
75
+ mp = @kpke.decrypt(dk_pke, c)
76
+ kp, rp = Crypto::SymmetricPrimitives.g(mp + h_val)
77
+ kk = Crypto::SymmetricPrimitives.j(z + c)
78
+ cp = @kpke.encrypt(ek_pke, mp, rp)
79
+
80
+ c == cp ? kp : kk
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sha3'
4
+
5
+ module MLKEM
6
+ # Module containing an adjusted version of the Kyber hash functions
7
+ # used for cryptographic operations in ML-KEM.
8
+ #
9
+ # @since 0.1.0
10
+ module Crypto
11
+ # Provides cryptographic hash functions used in ML-KEM (Kyber),
12
+ # including SHAKE128, SHAKE256, SHA3-256, and SHA3-512.
13
+ #
14
+ #
15
+ # @since 0.1.0
16
+ class HashFunctions
17
+ class << self
18
+ # Computes the SHAKE128 extendable-output function (XOF).
19
+ #
20
+ # @param [String] data Input byte string.
21
+ # @param [Integer] length Number of output bytes to produce.
22
+ # @return [String] XOF output of the specified length.
23
+ #
24
+ # @example
25
+ # out = HashFunctions.shake128("seed", 32)
26
+ def shake128(data, length)
27
+ shake = SHA3::Digest::SHAKE_128.new
28
+ shake << data
29
+ shake.squeeze(length)
30
+ end
31
+
32
+ # Computes the SHAKE256 extendable-output function (XOF).
33
+ #
34
+ # @param [String] data Input byte string.
35
+ # @param [Integer] length Number of output bytes to produce.
36
+ # @return [String] XOF output of the specified length.
37
+ #
38
+ # @example
39
+ # out = HashFunctions.shake256("key", 64)
40
+ def shake256(data, length)
41
+ shake = SHA3::Digest::SHAKE_256.new
42
+ shake << data
43
+ shake.squeeze(length)
44
+ end
45
+
46
+ # Computes the SHA3-256 hash of the given data.
47
+ #
48
+ # @param [String] data Input byte string.
49
+ # @return [String] 32-byte hash digest.
50
+ #
51
+ # @example
52
+ # digest = HashFunctions.sha3_256("message")
53
+ def sha3_256(data)
54
+ digest = SHA3::Digest.new(:sha3_256)
55
+ digest.update(data)
56
+ digest.digest
57
+ end
58
+
59
+ # Computes the SHA3-512 hash of the given data.
60
+ #
61
+ # @param [String] data Input byte string.
62
+ # @return [String] 64-byte hash digest.
63
+ #
64
+ # @example
65
+ # digest = HashFunctions.sha3_512("message")
66
+ def sha3_512(data)
67
+ digest = SHA3::Digest.new(:sha3_512)
68
+ digest.update(data)
69
+ digest.digest
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sha3'
4
+
5
+ module MLKEM
6
+ module Crypto
7
+ # Provides symmetric cryptographic primitives as defined by ML-KEM (Kyber),
8
+ # including hash functions h, g, j and a pseudorandom function (PRF).
9
+ #
10
+ # These are used for key derivation, random generation, and message hashing.
11
+ #
12
+ # @since 0.1.0
13
+ class SymmetricPrimitives
14
+ class << self
15
+ # Hash function h: SHA3-256
16
+ #
17
+ # @param [String] x Input data to hash.
18
+ # @return [String] 32-byte digest.
19
+ #
20
+ # @example
21
+ # digest = SymmetricPrimitives.h("message")
22
+ def h(x)
23
+ HashFunctions.sha3_256(x)
24
+ end
25
+
26
+ # Hash function g: SHA3-512, split into two 32-byte outputs.
27
+ #
28
+ # @param [String] x Input data to hash.
29
+ # @return [Array<String>] An array containing two 32-byte strings.
30
+ #
31
+ # @example
32
+ # g1, g2 = SymmetricPrimitives.g("seed")
33
+ def g(x)
34
+ hash = HashFunctions.sha3_512(x)
35
+ [hash[0...32], hash[32...64]]
36
+ end
37
+
38
+ # Hash function j: SHAKE256 with 32-byte output.
39
+ #
40
+ # @param [String] s Input data to hash.
41
+ # @return [String] 32-byte XOF output.
42
+ #
43
+ # @example
44
+ # result = SymmetricPrimitives.j("domain")
45
+ def j(s)
46
+ HashFunctions.shake256(s, 32)
47
+ end
48
+
49
+ # Pseudorandom Function (PRF)
50
+ #
51
+ # Uses SHAKE256 to expand `s || b` into `eta * 64` bytes of pseudorandom data.
52
+ #
53
+ # @param [Integer] eta Security parameter (e.g., 2 or 3).
54
+ # @param [String] s Secret seed.
55
+ # @param [Integer] b A single byte to include in domain separation.
56
+ # @return [String] Pseudorandom byte string of length `eta * 64`.
57
+ #
58
+ # @example
59
+ # prf_output = SymmetricPrimitives.prf(3, "key", 0x01)
60
+ def prf(eta, s, b)
61
+ input = s + [b].pack('C*')
62
+ HashFunctions.shake256(input, eta * 64)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MLKEM
4
+ # Math module containing all the math related operations to NTT, byte operations, polynomial and sampling.
5
+ #
6
+ # @since 0.1.0
7
+ module Math
8
+ # Byte and bit manipulation operations used in the ML-KEM standard (2024).
9
+ #
10
+ # Provides methods to convert between bit arrays and byte strings,
11
+ # and to encode and decode arrays according to given parameters.
12
+ #
13
+ # @since 0.1.0
14
+ class ByteOperations
15
+ # Converts an array of bits into a byte string.
16
+ #
17
+ # Implements Algorithm 3, BitsToBytes(b).
18
+ #
19
+ # @param [Array<Integer>] b An array of bits (0 or 1).
20
+ # @return [String] The resulting byte string.
21
+ #
22
+ # @example
23
+ # bits = [1,0,1,0,1,0,1,0]
24
+ # ByteOperations.bits_to_bytes(bits) # => "\x55" (byte representation)
25
+ def self.bits_to_bytes(b)
26
+ l = b.length
27
+ a = Array.new(l / 8, 0)
28
+ (0...l).step(8) do |i|
29
+ x = 0
30
+ 8.times do |j|
31
+ x += b[i + j] << j
32
+ end
33
+ a[i / 8] = x
34
+ end
35
+ a.pack('C*')
36
+ end
37
+
38
+ # Converts a byte string into an array of bits.
39
+ #
40
+ # Implements Algorithm 4, BytesToBits(B).
41
+ #
42
+ # @param [String] b A byte string.
43
+ # @return [Array<Integer>] The resulting array of bits (0 or 1).
44
+ #
45
+ # @example
46
+ # bytes = "\x55"
47
+ # ByteOperations.bytes_to_bits(bytes) # => [1,0,1,0,1,0,1,0]
48
+ def self.bytes_to_bits(b)
49
+ l = b.length
50
+ a = Array.new(8 * l, 0)
51
+ (0...(8 * l)).step(8) do |i|
52
+ x = b.bytes[i / 8]
53
+ 8.times do |j|
54
+ a[i + j] = (x >> j) & 1
55
+ end
56
+ end
57
+ a
58
+ end
59
+
60
+ # Encodes an array of values into a byte string using dimension d and modulus q.
61
+ #
62
+ # Implements Algorithm 5, ByteEncode_d(F).
63
+ #
64
+ # @param [Integer] d Number of bits per value.
65
+ # @param [Array<Integer>, Array<Array<Integer>>] f Array of values or array of arrays to encode.
66
+ # @param [Integer] q Modulus for values (kept for compatibility; unused internally).
67
+ # @return [String] The encoded byte string.
68
+ #
69
+ # @example
70
+ # d = 8
71
+ # f = Array.new(256) { |i| i }
72
+ # ByteOperations.byte_encode(d, f, 256) # => encoded string
73
+ #
74
+ # @example Encoding multiple arrays:
75
+ # f_multi = [Array.new(256, 1), Array.new(256, 2)]
76
+ # ByteOperations.byte_encode(d, f_multi, 256) # => concatenated encoded string
77
+ def self.byte_encode(d, f, q)
78
+ if f.first.is_a?(Array)
79
+ return f.map { |x| byte_encode(d, x, q) }.join
80
+ end
81
+
82
+ b = Array.new(256 * d, 0)
83
+
84
+ 256.times do |i|
85
+ a = f[i]
86
+ d.times do |j|
87
+ b[d * i + j] = (a >> j) & 1
88
+ end
89
+ end
90
+ bits_to_bytes(b)
91
+ end
92
+
93
+ # Decodes a byte string into an array of values using dimension d and modulus q.
94
+ #
95
+ # Implements Algorithm 6, ByteDecode_d(B).
96
+ #
97
+ # @param [Integer] d Number of bits per value.
98
+ # @param [String] b Encoded byte string.
99
+ # @param [Integer] q Modulus for values.
100
+ # @return [Array<Integer>] The decoded array of values.
101
+ #
102
+ # @example
103
+ # d = 8
104
+ # encoded = ByteOperations.byte_encode(d, Array.new(256, 42), 256)
105
+ # ByteOperations.byte_decode(d, encoded, 256) # => Array with value 42 repeated
106
+ def self.byte_decode(d, b, q)
107
+ bits_array = bytes_to_bits(b)
108
+ f = Array.new(256, 0)
109
+
110
+ 256.times do |i|
111
+ a = 0
112
+ d.times do |j|
113
+ a += bits_array[d * i + j] << j
114
+ end
115
+ f[i] = a % q
116
+ end
117
+ f
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MLKEM
4
+ module Math
5
+ # Implements the Number Theoretic Transform (NTT) and related operations
6
+ # as defined in the ML-KEM standard (2024), used for polynomial arithmetic
7
+ # in the ring Z_q[X]/(X^256 + 1).
8
+ #
9
+ # Includes forward and inverse NTT transforms, pointwise multiplication,
10
+ # and the base case multiplication algorithm.
11
+ #
12
+ # @since 0.1.0
13
+ class NTT
14
+ # Initializes an NTT instance with a modulus `q`.
15
+ #
16
+ # @param [Integer] q The modulus for all modular arithmetic.
17
+ #
18
+ # @example
19
+ # ntt = NTT.new
20
+ def initialize(q = Constants::Q)
21
+ @q = q
22
+ end
23
+
24
+ # Applies the forward Number Theoretic Transform to a polynomial.
25
+ #
26
+ # Implements Algorithm 9, NTT(f).
27
+ #
28
+ # @param [Array<Integer>] f A 256-element array representing the polynomial.
29
+ # @return [Array<Integer>] The transformed polynomial.
30
+ #
31
+ # @example
32
+ # transformed = ntt.ntt(original_poly)
33
+ def ntt(f)
34
+ f = f.dup
35
+ i = 1
36
+ le = 128
37
+
38
+ while le >= 2
39
+ (0...256).step(2 * le) do |st|
40
+ ze = Constants::ZETA_NTT[i]
41
+ i += 1
42
+ (st...(st + le)).each do |j|
43
+ t = (ze * f[j + le]) % @q
44
+ f[j + le] = (f[j] - t) % @q
45
+ f[j] = (f[j] + t) % @q
46
+ end
47
+ end
48
+ le /= 2
49
+ end
50
+
51
+ f
52
+ end
53
+
54
+ # Applies the inverse Number Theoretic Transform to a polynomial.
55
+ #
56
+ # Implements Algorithm 10, NTT^<sup>-1</sup>(f).
57
+ #
58
+ # @param [Array<Integer>] f A 256-element array in the NTT domain.
59
+ # @return [Array<Integer>] The inverse-transformed polynomial.
60
+ #
61
+ # @example
62
+ # original = ntt.inverse_ntt(transformed_poly)
63
+ def inverse_ntt(f)
64
+ f = f.dup
65
+ i = 127
66
+ le = 2
67
+
68
+ while le <= 128
69
+ (0...256).step(2 * le) do |st|
70
+ ze = Constants::ZETA_NTT[i]
71
+ i -= 1
72
+ (st...(st + le)).each do |j|
73
+ t = f[j]
74
+ f[j] = (t + f[j + le]) % @q
75
+ f[j + le] = (ze * (f[j + le] - t)) % @q
76
+ end
77
+ end
78
+ le *= 2
79
+ end
80
+
81
+ f.map { |x| (x * 3303) % @q }
82
+ end
83
+
84
+ # Performs pointwise multiplication in the NTT domain.
85
+ #
86
+ # Implements Algorithm 11, MultiplyNTTs(~f, ~g).
87
+ #
88
+ # @param [Array<Integer>] f First polynomial in NTT form.
89
+ # @param [Array<Integer>] g Second polynomial in NTT form.
90
+ # @return [Array<Integer>] Result of the pointwise multiplication.
91
+ #
92
+ # @example
93
+ # product = ntt.multiply_ntts(ntt_f, ntt_g)
94
+ def multiply_ntts(f, g)
95
+ h = []
96
+ (0...256).step(2) do |ii|
97
+ h.concat(base_case_multiply(f[ii], f[ii + 1], g[ii], g[ii + 1],
98
+ Constants::ZETA_MUL[ii / 2]))
99
+ end
100
+ h
101
+ end
102
+
103
+ # Performs the base case multiplication for two polynomial coefficient pairs.
104
+ #
105
+ # Implements Algorithm 12, BaseCaseMultiply(a0, a1, b0, b1, gamma).
106
+ #
107
+ # @param [Integer] a0 Coefficient a_0
108
+ # @param [Integer] a1 Coefficient a_1
109
+ # @param [Integer] b0 Coefficient b_0
110
+ # @param [Integer] b1 Coefficient b_1
111
+ # @param [Integer] gam Precomputed gamma value
112
+ # @return [Array<Integer>] The resulting coefficients [c0, c1]
113
+ #
114
+ # @example
115
+ # ntt.base_case_multiply(3, 4, 5, 6, gamma) # => [expected_c0, expected_c1]
116
+ def base_case_multiply(a0, a1, b0, b1, gam)
117
+ c0 = (a0 * b0 + a1 * b1 * gam) % @q
118
+ c1 = (a0 * b1 + a1 * b0) % @q
119
+ [c0, c1]
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MLKEM
4
+ module Math
5
+ # Implements polynomial arithmetic and compression/decompression
6
+ # operations used in ML-KEM (Kyber) cryptographic schemes.
7
+ #
8
+ # Provides basic modular operations such as addition and subtraction
9
+ # as well as lossy compression methods used to reduce bandwidth.
10
+ #
11
+ # @since 0.1.0
12
+ class Polynomial
13
+ # Initializes a Polynomial instance with a modulus q.
14
+ #
15
+ # @param [Integer] q The modulus used for polynomial arithmetic.
16
+ #
17
+ # @example
18
+ # poly = Polynomial.new
19
+ def initialize(q = Constants::Q)
20
+ @q = q
21
+ end
22
+
23
+ # Adds two polynomials coefficient-wise modulo q.
24
+ #
25
+ # @param [Array<Integer>] f First polynomial.
26
+ # @param [Array<Integer>] g Second polynomial.
27
+ # @return [Array<Integer>] Resulting polynomial (f + g) mod q.
28
+ #
29
+ # @example
30
+ # result = poly.add([1, 2], [3, 4]) # => [4, 6]
31
+ def add(f, g)
32
+ f.zip(g).map { |a, b| (a + b) % @q }
33
+ end
34
+
35
+ # Subtracts one polynomial from another coefficient-wise modulo q.
36
+ #
37
+ # @param [Array<Integer>] f Minuend polynomial.
38
+ # @param [Array<Integer>] g Subtrahend polynomial.
39
+ # @return [Array<Integer>] Resulting polynomial (f - g) mod q.
40
+ #
41
+ # @example
42
+ # result = poly.subtract([5, 3], [2, 1]) # => [3, 2]
43
+ def subtract(f, g)
44
+ f.zip(g).map { |a, b| (a - b) % @q }
45
+ end
46
+
47
+ # Compresses the coefficients of a polynomial to `d` bits.
48
+ #
49
+ # Lossy operation used to reduce size during transmission.
50
+ #
51
+ # @param [Integer] d Number of bits to compress to.
52
+ # @param [Array<Integer>] xv Polynomial coefficients.
53
+ # @return [Array<Integer>] Compressed coefficients.
54
+ #
55
+ # @example
56
+ # compressed = poly.compress(4, [0, 1000, 2000])
57
+ def compress(d, xv)
58
+ xv.map do |x|
59
+ (((x << d) + (@q - 1) / 2) / @q) % (1 << d)
60
+ end
61
+ end
62
+
63
+ # Decompresses `d`-bit values back into approximate polynomial coefficients.
64
+ #
65
+ # Inverse of `#compress`, though lossy.
66
+ #
67
+ # @param [Integer] d Number of bits the coefficients were compressed to.
68
+ # @param [Array<Integer>] yv Compressed coefficients.
69
+ # @return [Array<Integer>] Approximate original coefficients.
70
+ #
71
+ # @example
72
+ # decompressed = poly.decompress(4, [0, 5, 10])
73
+ def decompress(d, yv)
74
+ yv.map do |y|
75
+ (@q * y + (1 << (d - 1))) >> d
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sha3'
4
+
5
+ module MLKEM
6
+ module Math
7
+ # Implements sampling algorithms used in ML-KEM (Kyber),
8
+ # including NTT-domain sampling and centered binomial distribution (CBD).
9
+ #
10
+ # These routines are used to generate polynomial coefficients
11
+ # from uniformly random byte arrays or XOF outputs.
12
+ #
13
+ # @since 0.1.0
14
+ class Sampling
15
+ # Initializes the Sampling object with a modulus `q`.
16
+ #
17
+ # @param [Integer] q The modulus used in coefficient arithmetic.
18
+ #
19
+ # @example
20
+ # sampling = Sampling.new
21
+ def initialize(q = Constants::Q)
22
+ @q = q
23
+ end
24
+
25
+ # Samples a polynomial in the NTT domain from a byte string using SHAKE128.
26
+ #
27
+ # Implements Algorithm 7, SampleNTT(B).
28
+ #
29
+ # This uses rejection sampling to select values uniformly < q from SHAKE128 output.
30
+ #
31
+ # @param [String] b Input byte string to expand into polynomial coefficients.
32
+ # @return [Array<Integer>] A 256-element array of sampled coefficients < q.
33
+ #
34
+ # @example
35
+ # sampled = sampling.sample_ntt(seed_bytes)
36
+ def sample_ntt(b)
37
+ xof_data = Crypto::HashFunctions.shake128(b, 1024)
38
+ j = 0
39
+ a = []
40
+ i = 0
41
+
42
+ while j < 256 && i < xof_data.length - 2
43
+ c = xof_data.bytes[i, 3]
44
+ d1 = c[0] + 256 * (c[1] % 16)
45
+ d2 = (c[1] / 16) + 16 * c[2]
46
+
47
+ if d1 < @q
48
+ a << d1
49
+ j += 1
50
+ end
51
+
52
+ if d2 < @q && j < 256
53
+ a << d2
54
+ j += 1
55
+ end
56
+
57
+ i += 3
58
+ end
59
+
60
+ a
61
+ end
62
+
63
+ # Samples a polynomial using centered binomial distribution (CBD).
64
+ #
65
+ # Implements Algorithm 8, SamplePolyCBD_eta(B).
66
+ #
67
+ # The result is a noise polynomial used in ML-KEM.
68
+ #
69
+ # @param [Integer] eta CBD parameter (usually 2 or 3).
70
+ # @param [String] b Byte string used as input entropy.
71
+ # @return [Array<Integer>] A 256-element array of polynomial coefficients modulo q.
72
+ #
73
+ # @example
74
+ # noise = sampling.sample_poly_cbd(3, seed_bytes)
75
+ def sample_poly_cbd(eta, b)
76
+ b_bits = Math::ByteOperations.bytes_to_bits(b)
77
+ f = Array.new(256, 0)
78
+
79
+ 256.times do |i|
80
+ x = b_bits[(2 * i * eta)...((2 * i + 1) * eta)].sum
81
+ y = b_bits[((2 * i + 1) * eta)...((2 * i + 2) * eta)].sum
82
+ f[i] = (x - y) % @q
83
+ end
84
+
85
+ f
86
+ end
87
+ end
88
+ end
89
+ end
@@ -6,5 +6,5 @@
6
6
  # @author MarioRgzLpz <https://github.com/MarioRgzLpz>
7
7
  # @since 0.1.0
8
8
  module MLKEM
9
- VERSION = "0.1.0"
9
+ VERSION = "0.1.1"
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ml_kem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - MarioRgzLpz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-03 00:00:00.000000000 Z
11
+ date: 2025-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sha3
@@ -30,56 +30,70 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.3'
33
+ version: 1.3.2
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.3'
40
+ version: 1.3.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: yard
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.9'
47
+ version: 0.9.37
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.9'
54
+ version: 0.9.37
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '5.22'
61
+ version: 5.25.5
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '5.22'
68
+ version: 5.25.5
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '13.3'
75
+ version: 13.3.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '13.3'
82
+ version: 13.3.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: irb
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.15.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.15.2
83
97
  description: A Ruby gem providing an implementation of the ML-KEM (formerly Kyber)
84
98
  key-encapsulation mechanism for post-quantum cryptography standards.
85
99
  email:
@@ -91,11 +105,19 @@ extra_rdoc_files: []
91
105
  files:
92
106
  - LICENSE.txt
93
107
  - README.md
94
- - Rakefile
95
108
  - exe/mlkem
96
109
  - lib/ml_kem.rb
110
+ - lib/ml_kem/cli.rb
111
+ - lib/ml_kem/constants.rb
112
+ - lib/ml_kem/core/k_pke.rb
113
+ - lib/ml_kem/core/ml_kem_internal.rb
114
+ - lib/ml_kem/crypto/hash_functions.rb
115
+ - lib/ml_kem/crypto/symmetric_primitives.rb
116
+ - lib/ml_kem/math/byte_operations.rb
117
+ - lib/ml_kem/math/ntt.rb
118
+ - lib/ml_kem/math/polynomial.rb
119
+ - lib/ml_kem/math/sampling.rb
97
120
  - lib/ml_kem/version.rb
98
- - sig/ml_kem.rbs
99
121
  homepage: https://github.com/MarioRgzLpz/ml_kem
100
122
  licenses:
101
123
  - MIT
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "minitest/test_task"
5
-
6
- Minitest::TestTask.create
7
-
8
- task default: :test
data/sig/ml_kem.rbs DELETED
@@ -1,4 +0,0 @@
1
- module MlKem
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end