kpqc 0.2.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +20 -8
- data/THIRD_PARTY_NOTICES.md +4 -4
- data/kpqc.gemspec +1 -1
- data/lib/kpqc/api.rb +1 -1
- data/lib/kpqc/ntruplus.rb +1 -1
- data/lib/kpqc/smaugt.rb +1 -1
- data/lib/kpqc/types.rb +2 -2
- data/lib/kpqc/version.rb +1 -1
- data/test/kat_test.rb +108 -52
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46c577c95e4c31c71f815b922990271f4352b3f84b84ff66b010598ec2421c55
|
|
4
|
+
data.tar.gz: e59071e5a75c838071b0d47c574194c49689f573857d35426855bca6f33f7a29
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c68fe659266446fe1ce738d4b29370d49f72827c91d4f3efed1e8339905e53c253c752d44530a6542b471e57edd49bbc157e1e3c0fbfe4f6a5951c208924cfb
|
|
7
|
+
data.tar.gz: f606ae127792ab6b3826898d001a7e9db0fb109c62b3c5bdd6a7e17fd7d12c302983bbafcb80ea8f935e6e8a25f7df4756350ed4fc82f5970556b61d29299510
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# KpqC
|
|
2
2
|
|
|
3
|
-
KpqC provides synchronous Ruby APIs for AIMer
|
|
3
|
+
KpqC provides synchronous Ruby APIs for the AIMer and HAETAE signature schemes
|
|
4
|
+
and the NTRU+ and SMAUG-T key encapsulation mechanisms (KEMs).
|
|
4
5
|
|
|
5
6
|
## Runtime support
|
|
6
7
|
|
|
@@ -23,8 +24,8 @@ are not compatible with this version.
|
|
|
23
24
|
| --- | --- | --- |
|
|
24
25
|
| **AIMer** | Signature | `aimer128f`, `aimer128s`, `aimer192f`, `aimer192s`, `aimer256f`, `aimer256s` |
|
|
25
26
|
| **HAETAE** | Signature | `haetae2`, `haetae3`, `haetae5` |
|
|
26
|
-
| **NTRU+** |
|
|
27
|
-
| **SMAUG‑T** |
|
|
27
|
+
| **NTRU+** | KEM | `ntruplus768`, `ntruplus864`, `ntruplus1152` |
|
|
28
|
+
| **SMAUG‑T** | KEM | `smaugt128`, `smaugt192`, `smaugt256`, `timer` |
|
|
28
29
|
|
|
29
30
|
Requiring an algorithm family keeps the entry point focused:
|
|
30
31
|
|
|
@@ -68,7 +69,7 @@ puts valid # true
|
|
|
68
69
|
Verification fails when the supplied context does not match the one used for
|
|
69
70
|
signing.
|
|
70
71
|
|
|
71
|
-
###
|
|
72
|
+
### KEM
|
|
72
73
|
|
|
73
74
|
A KEM creates a shared secret for a sender and a recipient. The public key may
|
|
74
75
|
be distributed; the secret key and resulting shared secret must remain private.
|
|
@@ -102,8 +103,8 @@ require "kpqc/smaugt"
|
|
|
102
103
|
|
|
103
104
|
signer = KpqC::Aimer.aimer256s
|
|
104
105
|
another_signer = KpqC::Haetae.haetae5
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
kem = KpqC::NtruPlus.ntruplus1152
|
|
107
|
+
another_kem = KpqC::SmaugT.timer
|
|
107
108
|
```
|
|
108
109
|
|
|
109
110
|
All named algorithms are also available from the package root:
|
|
@@ -112,7 +113,7 @@ All named algorithms are also available from the package root:
|
|
|
112
113
|
require "kpqc"
|
|
113
114
|
|
|
114
115
|
signer = KpqC.aimer192f
|
|
115
|
-
|
|
116
|
+
kem = KpqC.ntruplus864
|
|
116
117
|
```
|
|
117
118
|
|
|
118
119
|
## Data and failures
|
|
@@ -138,7 +139,7 @@ All sizes are in bytes.
|
|
|
138
139
|
| `haetae3` | 1,472 | 2,112 | 2,349 |
|
|
139
140
|
| `haetae5` | 2,080 | 2,752 | 2,948 |
|
|
140
141
|
|
|
141
|
-
####
|
|
142
|
+
#### KEM
|
|
142
143
|
|
|
143
144
|
| Algorithm | Public key | Secret key | Ciphertext | Shared secret |
|
|
144
145
|
| --- | ---: | ---: | ---: | ---: |
|
|
@@ -155,6 +156,17 @@ Methods reject values of the wrong type or size. Signature verification returns
|
|
|
155
156
|
SMAUG-T performs implicit rejection and returns a replacement secret instead;
|
|
156
157
|
that value will not equal the sender's shared secret.
|
|
157
158
|
|
|
159
|
+
## Known-answer tests
|
|
160
|
+
|
|
161
|
+
The test suite validates all 1,600 KAT records in
|
|
162
|
+
[KpqC/kpqc-test-vectors at commit d75490bf824f](https://github.com/KpqC/kpqc-test-vectors/tree/d75490bf824faa4b148cd0b901a2eb13198fe0da).
|
|
163
|
+
With `kpqc-test-vectors` checked out beside `kpqc-ruby`, run:
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
KPQC_TEST_VECTORS=../kpqc-test-vectors \
|
|
167
|
+
bundle exec rake test TEST=test/kat_test.rb
|
|
168
|
+
```
|
|
169
|
+
|
|
158
170
|
## Distribution
|
|
159
171
|
|
|
160
172
|
The published gem includes Ruby modules, native extension sources, and the
|
data/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
This package includes code from the following upstream reference
|
|
4
4
|
implementations:
|
|
5
5
|
|
|
6
|
-
- [AIMer](https://github.com/samsungsds-opensource/AIMer/tree/
|
|
7
|
-
- [HAETAE](https://github.com/CryptoLabInc/HAETAE/tree/
|
|
8
|
-
- [NTRU+](https://github.com/ntruplus/ntruplus/tree/
|
|
9
|
-
- [SMAUG-T](https://github.com/CryptoLabInc/SMAUG-T/tree/
|
|
6
|
+
- [AIMer](https://github.com/samsungsds-opensource/AIMer/tree/734cc76fa4695ca8c8eaa070508db8ff62205e68), Copyright (c) 2022–2026 SAMSUNG SDS
|
|
7
|
+
- [HAETAE](https://github.com/CryptoLabInc/HAETAE/tree/743c31df48183fc8c8a39a4f50a634da1c4af03a), Copyright (c) 2026 Team HAETAE
|
|
8
|
+
- [NTRU+](https://github.com/ntruplus/ntruplus/tree/3991b2ae08d6f0008d37e41b8aceaaab27b4ec89), Copyright (c) 2024–2026 NTRU+ TEAM
|
|
9
|
+
- [SMAUG-T](https://github.com/CryptoLabInc/SMAUG-T/tree/bbc463cd788eae36c6d155cef667ac9dbd9054cd), Copyright (c) 2026 Team SMAUG-T
|
|
10
10
|
|
|
11
11
|
These components are distributed under the MIT License. Their original license
|
|
12
12
|
texts are included under `vendor/` and with the package.
|
data/kpqc.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.summary = "Ruby APIs for AIMer, HAETAE, NTRU+, and SMAUG-T."
|
|
12
12
|
spec.description =
|
|
13
13
|
"Synchronous Ruby bindings for the bundled AIMer and HAETAE signature " \
|
|
14
|
-
"schemes and NTRU+ and SMAUG-T key
|
|
14
|
+
"schemes and NTRU+ and SMAUG-T key encapsulation mechanisms (KEMs)."
|
|
15
15
|
spec.homepage = "https://github.com/KpqC/kpqc-ruby"
|
|
16
16
|
spec.license = "MIT"
|
|
17
17
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
data/lib/kpqc/api.rb
CHANGED
data/lib/kpqc/ntruplus.rb
CHANGED
data/lib/kpqc/smaugt.rb
CHANGED
data/lib/kpqc/types.rb
CHANGED
|
@@ -23,7 +23,7 @@ module KpqC
|
|
|
23
23
|
alias to_s inspect
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# The result of a
|
|
26
|
+
# The result of a KEM operation.
|
|
27
27
|
class EncapsulatedSecret
|
|
28
28
|
attr_reader :ciphertext, :shared_secret
|
|
29
29
|
|
|
@@ -69,7 +69,7 @@ module KpqC
|
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# Byte sizes for a
|
|
72
|
+
# Byte sizes for a KEM parameter set.
|
|
73
73
|
class KemSizes
|
|
74
74
|
attr_reader :public_key, :secret_key, :ciphertext, :shared_secret
|
|
75
75
|
|
data/lib/kpqc/version.rb
CHANGED
data/test/kat_test.rb
CHANGED
|
@@ -4,6 +4,25 @@ require_relative "test_helper"
|
|
|
4
4
|
require_relative "kat_drbg"
|
|
5
5
|
|
|
6
6
|
class KnownAnswerTest < Minitest::Test
|
|
7
|
+
VECTOR_PATHS = {
|
|
8
|
+
"aimer128f" => "aimer/128f",
|
|
9
|
+
"aimer128s" => "aimer/128s",
|
|
10
|
+
"aimer192f" => "aimer/192f",
|
|
11
|
+
"aimer192s" => "aimer/192s",
|
|
12
|
+
"aimer256f" => "aimer/256f",
|
|
13
|
+
"aimer256s" => "aimer/256s",
|
|
14
|
+
"haetae2" => "haetae/mode2",
|
|
15
|
+
"haetae3" => "haetae/mode3",
|
|
16
|
+
"haetae5" => "haetae/mode5",
|
|
17
|
+
"ntruplus768" => "ntruplus/768",
|
|
18
|
+
"ntruplus864" => "ntruplus/864",
|
|
19
|
+
"ntruplus1152" => "ntruplus/1152",
|
|
20
|
+
"smaugt128" => "smaugt/mode1",
|
|
21
|
+
"smaugt192" => "smaugt/mode3",
|
|
22
|
+
"smaugt256" => "smaugt/mode5",
|
|
23
|
+
"timer" => "smaugt/modet"
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
7
26
|
def test_aes_256_primitive
|
|
8
27
|
key = binary(
|
|
9
28
|
"000102030405060708090a0b0c0d0e0f" \
|
|
@@ -15,69 +34,107 @@ class KnownAnswerTest < Minitest::Test
|
|
|
15
34
|
end
|
|
16
35
|
|
|
17
36
|
def test_signature_parameter_sets
|
|
37
|
+
total = 0
|
|
18
38
|
SIGNATURE_ALGORITHMS.each do |algorithm|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
vectors = read_vectors(native_name(algorithm))
|
|
40
|
+
vectors.each do |vector|
|
|
41
|
+
keys = nil
|
|
42
|
+
native = algorithm.send(:native)
|
|
43
|
+
random = KatDrbg::CtrDrbg.new(binary(vector.fetch("seed")))
|
|
44
|
+
native.__set_randombytes(lambda { |length| random.random_bytes(length) })
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
keys = algorithm.generate_key_pair
|
|
48
|
+
assert_equal binary(vector.fetch("pk")), keys.public_key, algorithm.id
|
|
49
|
+
assert_equal binary(vector.fetch("sk")), keys.secret_key, algorithm.id
|
|
50
|
+
|
|
51
|
+
context = signature_context(algorithm, random)
|
|
52
|
+
message = binary(vector.fetch("msg"))
|
|
53
|
+
signature = algorithm.sign(message, keys.secret_key, context: context)
|
|
54
|
+
expected = if vector.key?("sig")
|
|
55
|
+
binary(vector.fetch("sig"))
|
|
56
|
+
else
|
|
57
|
+
binary(vector.fetch("sm")).byteslice(message.bytesize..-1)
|
|
58
|
+
end
|
|
59
|
+
assert_equal expected, signature, algorithm.id
|
|
60
|
+
assert algorithm.verify(
|
|
61
|
+
message, signature, keys.public_key, context: context
|
|
62
|
+
), algorithm.id
|
|
63
|
+
ensure
|
|
64
|
+
native.__reset_randombytes
|
|
65
|
+
keys.clear! if keys
|
|
66
|
+
end
|
|
67
|
+
total += 1
|
|
68
|
+
end
|
|
69
|
+
assert_equal expected_vector_count, vectors.length, algorithm.id
|
|
43
70
|
end
|
|
71
|
+
assert_equal expected_vector_count * SIGNATURE_ALGORITHMS.length, total
|
|
44
72
|
end
|
|
45
73
|
|
|
46
|
-
def
|
|
74
|
+
def test_kem_parameter_sets
|
|
75
|
+
total = 0
|
|
47
76
|
KEM_ALGORITHMS.each do |algorithm|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
vectors = read_vectors(native_name(algorithm))
|
|
78
|
+
vectors.each do |vector|
|
|
79
|
+
keys = nil
|
|
80
|
+
encapsulated = nil
|
|
81
|
+
native = algorithm.send(:native)
|
|
82
|
+
random = KatDrbg::CtrDrbg.new(binary(vector.fetch("seed")))
|
|
83
|
+
native.__set_randombytes(lambda { |length| random.random_bytes(length) })
|
|
84
|
+
|
|
85
|
+
begin
|
|
86
|
+
keys = algorithm.generate_key_pair
|
|
87
|
+
assert_equal binary(vector.fetch("pk")), keys.public_key, algorithm.id
|
|
88
|
+
assert_equal binary(vector.fetch("sk")), keys.secret_key, algorithm.id
|
|
89
|
+
|
|
90
|
+
encapsulated = algorithm.encapsulate(keys.public_key)
|
|
91
|
+
assert_equal binary(vector.fetch("ct")), encapsulated.ciphertext,
|
|
92
|
+
algorithm.id
|
|
93
|
+
assert_equal binary(vector.fetch("ss")), encapsulated.shared_secret,
|
|
94
|
+
algorithm.id
|
|
95
|
+
assert_equal encapsulated.shared_secret,
|
|
96
|
+
algorithm.decapsulate(
|
|
97
|
+
encapsulated.ciphertext, keys.secret_key
|
|
98
|
+
), algorithm.id
|
|
99
|
+
ensure
|
|
100
|
+
native.__reset_randombytes
|
|
101
|
+
keys.clear! if keys
|
|
102
|
+
encapsulated.clear! if encapsulated
|
|
103
|
+
end
|
|
104
|
+
total += 1
|
|
105
|
+
end
|
|
106
|
+
assert_equal expected_vector_count, vectors.length, algorithm.id
|
|
70
107
|
end
|
|
108
|
+
assert_equal expected_vector_count * KEM_ALGORITHMS.length, total
|
|
71
109
|
end
|
|
72
110
|
|
|
73
111
|
private
|
|
74
112
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
|
|
113
|
+
def read_vectors(name)
|
|
114
|
+
root = ENV["KPQC_TEST_VECTORS"]
|
|
115
|
+
path = if root
|
|
116
|
+
File.join(root, VECTOR_PATHS.fetch(name), "kat.rsp")
|
|
117
|
+
else
|
|
118
|
+
File.expand_path("kat/#{name}.rsp", __dir__)
|
|
119
|
+
end
|
|
120
|
+
vectors = []
|
|
121
|
+
fields = {}
|
|
122
|
+
File.foreach(path) do |line|
|
|
78
123
|
match = /\A([a-z]+) = ([0-9A-Fa-f]*)\s*\z/.match(line)
|
|
79
|
-
|
|
124
|
+
next unless match
|
|
125
|
+
|
|
126
|
+
if match[1] == "count" && !fields.empty?
|
|
127
|
+
vectors << fields
|
|
128
|
+
fields = {}
|
|
129
|
+
end
|
|
130
|
+
fields[match[1]] = match[2]
|
|
80
131
|
end
|
|
132
|
+
vectors << fields unless fields.empty?
|
|
133
|
+
vectors
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def expected_vector_count
|
|
137
|
+
ENV["KPQC_TEST_VECTORS"] ? 100 : 1
|
|
81
138
|
end
|
|
82
139
|
|
|
83
140
|
def signature_context(algorithm, random)
|
|
@@ -96,4 +153,3 @@ class KnownAnswerTest < Minitest::Test
|
|
|
96
153
|
[value].pack("H*")
|
|
97
154
|
end
|
|
98
155
|
end
|
|
99
|
-
|
metadata
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kpqc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- osuolfou
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-10-09 00:00:00.000000000 Z
|
|
11
12
|
dependencies: []
|
|
12
13
|
description: Synchronous Ruby bindings for the bundled AIMer and HAETAE signature
|
|
13
|
-
schemes and NTRU+ and SMAUG-T key
|
|
14
|
+
schemes and NTRU+ and SMAUG-T key encapsulation mechanisms (KEMs).
|
|
14
15
|
email:
|
|
15
16
|
- osuolfou@naver.com
|
|
16
17
|
executables: []
|
|
@@ -235,6 +236,7 @@ metadata:
|
|
|
235
236
|
bug_tracker_uri: https://github.com/KpqC/kpqc-ruby/issues
|
|
236
237
|
changelog_uri: https://github.com/KpqC/kpqc-ruby/blob/main/CHANGELOG.md
|
|
237
238
|
rubygems_mfa_required: 'true'
|
|
239
|
+
post_install_message:
|
|
238
240
|
rdoc_options: []
|
|
239
241
|
require_paths:
|
|
240
242
|
- lib
|
|
@@ -249,7 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
249
251
|
- !ruby/object:Gem::Version
|
|
250
252
|
version: '0'
|
|
251
253
|
requirements: []
|
|
252
|
-
rubygems_version:
|
|
254
|
+
rubygems_version: 3.0.3.1
|
|
255
|
+
signing_key:
|
|
253
256
|
specification_version: 4
|
|
254
257
|
summary: Ruby APIs for AIMer, HAETAE, NTRU+, and SMAUG-T.
|
|
255
258
|
test_files: []
|