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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62b5c05c4a099ed480898de92399ea5bc5afcdcd15aef2192773187bf51cb779
4
- data.tar.gz: aa315f890bf4242423749e2e864b381f91145a6c5ab15d55e3e632495506e556
3
+ metadata.gz: 46c577c95e4c31c71f815b922990271f4352b3f84b84ff66b010598ec2421c55
4
+ data.tar.gz: e59071e5a75c838071b0d47c574194c49689f573857d35426855bca6f33f7a29
5
5
  SHA512:
6
- metadata.gz: cabb4f39af2279d6a0b4f47496e66beb09d6676e041da9be5c1e0e3633c0d326952d7639fe9a1477e05975609dd785546a8d4e35d0bf69e034344f3c79efcdd5
7
- data.tar.gz: 197e9fd75a10d840a9e53d1ed3f7eaac4941f0470df948ee7fd08443f8d9fc01073aaeb14062b0dd164ed8d77f06dabc553dccb99344e2af1d53d00e553676fb
6
+ metadata.gz: 6c68fe659266446fe1ce738d4b29370d49f72827c91d4f3efed1e8339905e53c253c752d44530a6542b471e57edd49bbc157e1e3c0fbfe4f6a5951c208924cfb
7
+ data.tar.gz: f606ae127792ab6b3826898d001a7e9db0fb109c62b3c5bdd6a7e17fd7d12c302983bbafcb80ea8f935e6e8a25f7df4756350ed4fc82f5970556b61d29299510
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1
4
+
5
+ - Standardized KEM terminology and pinned upstream source revisions.
6
+ - Validated all supported parameter sets against the shared KAT repository.
7
+
3
8
  ## 0.2.0
4
9
 
5
10
  - Updated the bundled reference implementations.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # KpqC
2
2
 
3
- KpqC provides synchronous Ruby APIs for AIMer, HAETAE, NTRU+, and SMAUG-T.
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+** | Key encapsulation | `ntruplus768`, `ntruplus864`, `ntruplus1152` |
27
- | **SMAUG‑T** | Key encapsulation | `smaugt128`, `smaugt192`, `smaugt256`, `timer` |
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
- ### Key encapsulation
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
- key_exchange = KpqC::NtruPlus.ntruplus1152
106
- another_key_exchange = KpqC::SmaugT.timer
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
- key_exchange = KpqC.ntruplus864
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
- #### Key encapsulation
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
@@ -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/5f786ccd572d7bc2f92cf9f662e2c6cab2c45fa2), Copyright (c) 2022–2026 SAMSUNG SDS
7
- - [HAETAE](https://github.com/CryptoLabInc/HAETAE/tree/3130c9e26670a3406af31e2aa73ef73b223acf49), Copyright (c) 2026 Team HAETAE
8
- - [NTRU+](https://github.com/ntruplus/ntruplus/tree/9b607231246fc2eb8b93634ff4bb94ecf1f62c75), Copyright (c) 2024–2026 NTRU+ TEAM
9
- - [SMAUG-T](https://github.com/CryptoLabInc/SMAUG-T/tree/3cd19f3c8531a95c06010846a832b0e8ece4f939), Copyright (c) 2026 Team SMAUG-T
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-encapsulation mechanisms."
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
@@ -103,7 +103,7 @@ module KpqC
103
103
 
104
104
  end
105
105
 
106
- # A key-encapsulation algorithm and parameter set.
106
+ # A KEM algorithm and parameter set.
107
107
  class KeyEncapsulationAlgorithm
108
108
  include AlgorithmSupport
109
109
 
data/lib/kpqc/ntruplus.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require_relative "api"
4
4
 
5
5
  module KpqC
6
- # NTRU+ key-encapsulation parameter sets.
6
+ # NTRU+ KEM parameter sets.
7
7
  module NtruPlus
8
8
  NTRUPLUS768 = KeyEncapsulationAlgorithm.new(
9
9
  "NTRU+768", KemSizes.new(1_152, 2_336, 1_152, 32), "ntruplus768"
data/lib/kpqc/smaugt.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require_relative "api"
4
4
 
5
5
  module KpqC
6
- # SMAUG-T and TiMER key-encapsulation parameter sets.
6
+ # SMAUG-T and TiMER KEM parameter sets.
7
7
  module SmaugT
8
8
  SMAUGT128 = KeyEncapsulationAlgorithm.new(
9
9
  "SMAUG-T128", KemSizes.new(672, 832, 672, 32), "smaugt128"
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 key-encapsulation operation.
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 key-encapsulation parameter set.
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KpqC
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
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
- vector = read_vector(native_name(algorithm))
20
- native = algorithm.send(:native)
21
- random = KatDrbg::CtrDrbg.new(binary(vector.fetch("seed")))
22
- native.__set_randombytes(lambda { |length| random.random_bytes(length) })
23
-
24
- keys = algorithm.generate_key_pair
25
- assert_equal binary(vector.fetch("pk")), keys.public_key, algorithm.id
26
- assert_equal binary(vector.fetch("sk")), keys.secret_key, algorithm.id
27
-
28
- context = signature_context(algorithm, random)
29
- message = binary(vector.fetch("msg"))
30
- signature = algorithm.sign(message, keys.secret_key, context: context)
31
- expected = if vector.key?("sig")
32
- binary(vector.fetch("sig"))
33
- else
34
- binary(vector.fetch("sm")).byteslice(message.bytesize..-1)
35
- end
36
- assert_equal expected, signature, algorithm.id
37
- assert algorithm.verify(
38
- message, signature, keys.public_key, context: context
39
- ), algorithm.id
40
- ensure
41
- native.__reset_randombytes if native
42
- keys.clear! if keys
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 test_key_encapsulation_parameter_sets
74
+ def test_kem_parameter_sets
75
+ total = 0
47
76
  KEM_ALGORITHMS.each do |algorithm|
48
- vector = read_vector(native_name(algorithm))
49
- native = algorithm.send(:native)
50
- random = KatDrbg::CtrDrbg.new(binary(vector.fetch("seed")))
51
- native.__set_randombytes(lambda { |length| random.random_bytes(length) })
52
-
53
- keys = algorithm.generate_key_pair
54
- assert_equal binary(vector.fetch("pk")), keys.public_key, algorithm.id
55
- assert_equal binary(vector.fetch("sk")), keys.secret_key, algorithm.id
56
-
57
- encapsulated = algorithm.encapsulate(keys.public_key)
58
- assert_equal binary(vector.fetch("ct")), encapsulated.ciphertext,
59
- algorithm.id
60
- assert_equal binary(vector.fetch("ss")), encapsulated.shared_secret,
61
- algorithm.id
62
- assert_equal encapsulated.shared_secret,
63
- algorithm.decapsulate(
64
- encapsulated.ciphertext, keys.secret_key
65
- ), algorithm.id
66
- ensure
67
- native.__reset_randombytes if native
68
- keys.clear! if keys
69
- encapsulated.clear! if encapsulated
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 read_vector(name)
76
- path = File.expand_path("kat/#{name}.rsp", __dir__)
77
- File.readlines(path).each_with_object({}) do |line, fields|
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
- fields[match[1]] = match[2] if match
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.0
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: 1980-01-02 00:00:00.000000000 Z
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-encapsulation mechanisms.
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: 4.0.20
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: []