pq_crypto 0.5.0 → 0.5.2

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: 4f34f4ae9f34414fbbc1f6ffc63de9c24306a6a432138e36442c1e43661fa59e
4
- data.tar.gz: 6b2a754c55b9a1be4706cf5d835ee08844b54d75d96d43611f22c8c4f68cb10f
3
+ metadata.gz: 2274795113da1599b664af11b1393a86d68f1886f3536e0a65385b04b8ce03fe
4
+ data.tar.gz: 386fe1093ba0b28a910195a40d7698e0cef91f9f57fc90b3a1aaac3eec4451a5
5
5
  SHA512:
6
- metadata.gz: 0b72822b4b645f891e8f87693004f736070b9bba37432a48ffc840ec1a114865ba691db7c1ffc12aab7643369c3883892d9fbcf3c4f7cf895b5938d2ee650b1c
7
- data.tar.gz: 13c0263600408685f5d484528032305c48e3e3d551799abad990167c9a9d14d0287aabb70fd88dc00f72d6236f15e70c3d9f6e9572ad02276e11674aafd87774
6
+ metadata.gz: f9f73a9d575d9931020e1674f96adafe08816a1f7183b0b948194fcd2fb4da106afb854525ef544409b47bde34c987ac0eae6ce54440b7562f70faf5807446c3
7
+ data.tar.gz: c5e54b45ab8461bd1edd2b3e4e14f0092267232bc3bf1436841488fa7a604fdf66056dfd160767ec2d47b0a4afe37cb660a46a54466677994a3c1d73269959d1
@@ -1,12 +1,30 @@
1
1
  name: CI
2
2
 
3
3
  on:
4
- push:
5
- branches: ["**"]
6
4
  pull_request:
5
+ push:
6
+ branches: [main]
7
7
 
8
8
  jobs:
9
+ vendor-verify:
10
+ name: vendor-verify
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Checkout
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: "3.4"
21
+ bundler-cache: true
22
+
23
+ - name: Verify vendored sources against pinned tree_sha256
24
+ run: bundle exec rake vendor:verify
25
+
9
26
  test:
27
+ needs: vendor-verify
10
28
  runs-on: ${{ matrix.os }}
11
29
 
12
30
  strategy:
@@ -30,6 +48,9 @@ jobs:
30
48
  with:
31
49
  go-version: "1.26.0"
32
50
 
51
+ - name: Verify vendored sources
52
+ run: bundle exec rake vendor:verify
53
+
33
54
  - name: Compile extension
34
55
  run: bundle exec rake compile
35
56
 
@@ -37,6 +58,7 @@ jobs:
37
58
  run: bundle exec rake test
38
59
 
39
60
  interop-openssl-3-5-required:
61
+ needs: vendor-verify
40
62
  name: interop-openssl-3.5-required
41
63
  runs-on: ubuntu-24.04
42
64
 
@@ -77,6 +99,9 @@ jobs:
77
99
  with:
78
100
  go-version: "1.26.0"
79
101
 
102
+ - name: Verify vendored sources
103
+ run: bundle exec rake vendor:verify
104
+
80
105
  - name: Compile extension
81
106
  run: bundle exec rake compile
82
107
 
@@ -91,3 +116,50 @@ jobs:
91
116
  echo "OpenSSL 3.5 interop tests must NOT skip on this matrix entry."
92
117
  exit 1
93
118
  fi
119
+
120
+ linux-native-backend:
121
+ needs: test
122
+ name: linux-native-backend
123
+ runs-on: ubuntu-latest
124
+ env:
125
+ PQCRYPTO_NATIVE_ASM: "1"
126
+
127
+ steps:
128
+ - name: Checkout
129
+ uses: actions/checkout@v4
130
+
131
+ - name: Set up Ruby
132
+ uses: ruby/setup-ruby@v1
133
+ with:
134
+ ruby-version: "3.4"
135
+ bundler-cache: true
136
+
137
+ - name: Verify vendored sources
138
+ run: bundle exec rake vendor:verify
139
+
140
+ - name: Compile extension with native x86_64 backend
141
+ run: |
142
+ rm -rf tmp lib/pqcrypto/pqcrypto_secure.so
143
+ bundle exec rake compile
144
+
145
+ - name: Smoke native backend
146
+ run: |
147
+ bundle exec ruby -Ilib -e '
148
+ require "pq_crypto"
149
+
150
+ kem = PQCrypto::KEM.generate(:ml_kem_768)
151
+ enc = kem.public_key.encapsulate
152
+ raise "ML-KEM mismatch" unless kem.secret_key.decapsulate(enc.ciphertext) == enc.shared_secret
153
+
154
+ sig = PQCrypto::Signature.generate(:ml_dsa_65)
155
+ msg = "linux-native-backend".b
156
+ signature = sig.secret_key.sign(msg)
157
+ raise "ML-DSA verify failed" unless sig.public_key.verify(msg, signature)
158
+ '
159
+
160
+ - name: Verify AVX2 native symbols
161
+ run: |
162
+ set -euxo pipefail
163
+ so="lib/pqcrypto/pqcrypto_secure.so"
164
+ test -f "$so"
165
+ nm "$so" | grep -E "pqcr_(mlkem|mldsa).*avx2|keccak.*avx2"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.2] - 2026-05-06
4
+
5
+ ### Build
6
+
7
+ - Added Linux/OpenSSL discovery via `OPENSSL_ROOT_DIR`, `OPENSSL_DIR`, and `pkg-config`.
8
+ - Preserved `$(CFLAGS)`/`$(CCDLFLAGS)` for vendored native objects so Linux shared-object builds keep `-fPIC`.
9
+ - Added opt-in Linux x86_64 native-backend support through `PQCRYPTO_NATIVE_ASM=1`.
10
+ - Added x86_64 AVX2 vendor flags for mlkem-native/mldsa-native when native backends are explicitly enabled.
11
+ - Added separate `PQCRYPTO_NATIVE_ARITH` and `PQCRYPTO_NATIVE_FIPS202` switches for native arithmetic and Keccak/FIPS202 backends.
12
+ - Kept AArch64 native asm enabled by default; verified macOS arm64 still builds ML-KEM/ML-DSA native asm paths.
13
+
14
+ ### CI
15
+
16
+ - Added a Linux native-backend job that compiles with `PQCRYPTO_NATIVE_ASM=1`, runs ML-KEM/ML-DSA smoke checks, and verifies AVX2 symbols in the extension.
17
+
18
+ ## [0.5.1] - 2026-05-04
19
+
20
+ ### Performance
21
+
22
+ - Enabled native asm/SIMD paths for ML-DSA/ML-KEM where available.
23
+ - Reduced Hybrid KEM X25519 overhead by reusing expanded/native key state and avoiding repeated private/public key reconstruction.
24
+ - Moved hot crypto calls under `rb_nogvl` where applicable.
25
+ - Optimized PEM export path by replacing BIO/streaming base64 with direct encode.
26
+ - Reduced small-buffer streaming overhead by avoiding unnecessary `malloc`/`nogvl` work for tiny chunks.
27
+
28
+ ### Vendoring
29
+
30
+ - Committed `mlkem-native` and `mldsa-native` sources to the repository. Builds no longer require git or network.
31
+ - Pinned upstream by `commit` + `tree_sha256` in `script/vendor_libs.rb`; manifest signed with `manifest_sha256`.
32
+ - Deterministic vendor tree (no symlinks, dotfiles, normalized mtime/permissions).
33
+ - `script/vendor_libs.rb` now supports `--verify`, `--sync`, `--bump`.
34
+
35
+ ### Build
36
+
37
+ - `extconf.rb` no longer auto-fetches upstream. Missing vendor aborts the build; opt in with `PQCRYPTO_AUTO_VENDOR=1`.
38
+
39
+ ### Rakefile
40
+
41
+ - Added `vendor:verify`, `vendor:sync`, `vendor:bump`. Default task runs `vendor:verify` before `compile` and `test`.
42
+
43
+ ### CI
44
+
45
+ - Added `vendor-verify` gating job; `vendor:verify` also runs in each test job before compile.
46
+
47
+ ### Repository
48
+
49
+ - `.gitattributes` enforces `eol=lf` and marks vendor as binary (CRLF protection).
50
+ - `.gitignore` no longer hides `sempls/`.
51
+
3
52
  ## [0.5.0] - 2026-05-04
4
53
 
5
54
  ### Changed — native backend migration
@@ -39,14 +39,85 @@ if SANITIZE && !SANITIZE.strip.empty?
39
39
  $LDFLAGS << " -fsanitize=#{sanitize}"
40
40
  end
41
41
 
42
- NATIVE_ASM = (ENV["PQCRYPTO_NATIVE_ASM"] || "0") == "1"
42
+ def host_cpu
43
+ RbConfig::CONFIG.fetch("host_cpu", "")
44
+ end
45
+
46
+ def host_os
47
+ RbConfig::CONFIG.fetch("host_os", "")
48
+ end
49
+
50
+ def aarch64_host?
51
+ host_cpu =~ /\A(?:arm64|aarch64)\z/i
52
+ end
53
+
54
+ def x86_64_host?
55
+ host_cpu =~ /\A(?:x86_64|amd64|x64)\z/i
56
+ end
57
+
58
+ def native_asm_supported_by_default?
59
+ return false if host_os =~ /mswin|mingw|cygwin/i
60
+
61
+ aarch64_host?
62
+ end
63
+
64
+ def env_bool(name, default)
65
+ value = ENV[name]
66
+ return default if value.nil? || value.strip.empty? || value.strip.downcase == "auto"
67
+
68
+ case value.strip.downcase
69
+ when "1", "true", "yes", "on"
70
+ true
71
+ when "0", "false", "no", "off"
72
+ false
73
+ else
74
+ abort "Invalid #{name}=#{value.inspect}; use 1, 0, true, false, or auto"
75
+ end
76
+ end
77
+
78
+ NATIVE_ASM = env_bool("PQCRYPTO_NATIVE_ASM", native_asm_supported_by_default?)
79
+ NATIVE_ARITH = env_bool("PQCRYPTO_NATIVE_ARITH", NATIVE_ASM)
80
+ NATIVE_FIPS202 = env_bool("PQCRYPTO_NATIVE_FIPS202", NATIVE_ASM)
81
+
82
+ X86_VENDOR_ARCH_FLAGS = "-mavx2 -mbmi -mbmi2 -mpopcnt -maes -mssse3 -msse4.1 -msse4.2"
83
+
84
+ VENDOR_C_ARCH_FLAGS = +""
85
+ VENDOR_ASM_ARCH_FLAGS = +""
86
+
87
+ if x86_64_host? && (NATIVE_ARITH || NATIVE_FIPS202)
88
+ VENDOR_C_ARCH_FLAGS << "#{X86_VENDOR_ARCH_FLAGS} -fno-tree-vectorize"
89
+ VENDOR_ASM_ARCH_FLAGS << X86_VENDOR_ARCH_FLAGS
90
+ end
91
+
92
+ if ENV["PQCRYPTO_NATIVE_TUNE"] == "1"
93
+ VENDOR_C_ARCH_FLAGS << " -march=native -mtune=native"
94
+ VENDOR_ASM_ARCH_FLAGS << " -march=native -mtune=native"
95
+ end
43
96
 
44
97
  def configure_compiler_environment
45
- return unless RUBY_PLATFORM.include?("darwin")
98
+ if RUBY_PLATFORM.include?("darwin")
99
+ dir_config("homebrew", "/opt/homebrew")
100
+ $CPPFLAGS << " -I/opt/homebrew/include"
101
+ $LDFLAGS << " -L/opt/homebrew/lib"
102
+ return
103
+ end
46
104
 
47
- dir_config("homebrew", "/opt/homebrew")
48
- $CPPFLAGS << " -I/opt/homebrew/include"
49
- $LDFLAGS << " -L/opt/homebrew/lib"
105
+ openssl_root = ENV["OPENSSL_ROOT_DIR"] || ENV["OPENSSL_DIR"]
106
+ if openssl_root && !openssl_root.strip.empty? && File.directory?(openssl_root)
107
+ $CPPFLAGS << " -I#{openssl_root}/include"
108
+ %w[lib64 lib].each do |suffix|
109
+ libdir = File.join(openssl_root, suffix)
110
+ next unless File.directory?(libdir)
111
+
112
+ $LDFLAGS << " -L#{libdir} -Wl,-rpath,#{libdir}"
113
+ break
114
+ end
115
+ elsif find_executable("pkg-config")
116
+ cflags = `pkg-config --cflags openssl 2>/dev/null`.strip
117
+ libs = `pkg-config --libs-only-L openssl 2>/dev/null`.strip
118
+ $CPPFLAGS << " #{cflags}" unless cflags.empty?
119
+ $LDFLAGS << " #{libs}" unless libs.empty?
120
+ end
50
121
  end
51
122
 
52
123
  def native_vendor_sources_for(vendor_dir)
@@ -66,28 +137,25 @@ def vendor_script_path
66
137
  end
67
138
 
68
139
  def run_vendor_script!(vendor_dir)
69
- script = vendor_script_path
70
- abort <<~MSG unless File.exist?(script)
71
- PQ Code Package vendored sources are missing and script/vendor_libs.rb was not packaged.
140
+ abort <<~MSG if ENV["PQCRYPTO_AUTO_VENDOR"] != "1"
141
+ PQ Code Package vendored sources are missing.
72
142
 
73
143
  Expected:
74
144
  #{native_vendor_sources_for(vendor_dir).join("\n ")}
75
145
 
76
- Rebuild the gem from a repository that includes script/vendor_libs.rb, or run
77
- script/vendor_libs.rb before building the gem package.
78
- MSG
79
-
80
- abort <<~MSG if ENV["PQCRYPTO_AUTO_VENDOR"] == "0"
81
- PQ Code Package vendored sources are missing and PQCRYPTO_AUTO_VENDOR=0 was set.
146
+ The vendor tree is committed to the repository and shipped with the gem.
147
+ If it is missing, the source tree is incomplete or corrupted.
82
148
 
83
- Expected:
84
- #{native_vendor_sources_for(vendor_dir).join("\n ")}
85
-
86
- Run:
149
+ To fetch upstream sources at the pinned commits run:
87
150
  ruby script/vendor_libs.rb
151
+
152
+ Or to allow extconf.rb to do this for you set PQCRYPTO_AUTO_VENDOR=1.
88
153
  MSG
89
154
 
90
- puts "PQ Code Package native sources are missing; vendoring now..."
155
+ script = vendor_script_path
156
+ abort "PQ Code Package vendored sources are missing and script/vendor_libs.rb was not packaged." unless File.exist?(script)
157
+
158
+ puts "PQ Code Package native sources are missing; vendoring now (PQCRYPTO_AUTO_VENDOR=1)..."
91
159
  ok = system(RbConfig.ruby, script)
92
160
  abort <<~MSG unless ok
93
161
  Failed to vendor PQ Code Package native sources.
@@ -210,10 +278,8 @@ def native_flags(kind, level, shared:)
210
278
  flags << "-D#{prefix}_CONFIG_NAMESPACE_PREFIX=#{ns}"
211
279
  flags << "-D#{prefix}_CONFIG_NO_SUPERCOP"
212
280
  flags << (shared ? "-D#{prefix}_CONFIG_MULTILEVEL_WITH_SHARED" : "-D#{prefix}_CONFIG_MULTILEVEL_NO_SHARED")
213
- if NATIVE_ASM
214
- flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_ARITH"
215
- flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202"
216
- end
281
+ flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_ARITH" if NATIVE_ARITH
282
+ flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202" if NATIVE_FIPS202
217
283
  flags.join(" ")
218
284
  end
219
285
 
@@ -237,11 +303,11 @@ def inject_native_sources!(config)
237
303
  build_rules << <<~RULE
238
304
  #{object}: #{source}
239
305
  $(ECHO) compiling #{source} [#{kind}-#{level}]
240
- $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{VENDOR_ONLY_CFLAGS} #{flags} $(COUTFLAG)$@ -c $(CSRCFLAG)$<
306
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(CCDLFLAGS) #{VENDOR_ONLY_CFLAGS} #{VENDOR_C_ARCH_FLAGS} #{flags} $(COUTFLAG)$@ -c $(CSRCFLAG)$<
241
307
  RULE
242
308
  end
243
309
 
244
- if NATIVE_ASM
310
+ if NATIVE_ARITH || NATIVE_FIPS202
245
311
  [
246
312
  [:mlkem, "512", config[:mlkem_asm], true],
247
313
  [:mlkem, "768", config[:mlkem_asm], false],
@@ -258,7 +324,7 @@ def inject_native_sources!(config)
258
324
  build_rules << <<~RULE
259
325
  #{object}: #{source}
260
326
  $(ECHO) assembling #{source} [#{kind}-#{level}]
261
- $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{VENDOR_ONLY_CFLAGS} #{flags} $(COUTFLAG)$@ -c $(CSRCFLAG)$<
327
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(CCDLFLAGS) #{VENDOR_ONLY_CFLAGS} #{VENDOR_ASM_ARCH_FLAGS} #{flags} $(COUTFLAG)$@ -c $(CSRCFLAG)$<
262
328
  RULE
263
329
  end
264
330
  end
@@ -288,7 +354,15 @@ native_config = native_vendor_config(vendor_dir)
288
354
  puts "OpenSSL: system"
289
355
  puts "ML-KEM: mlkem-native vendored"
290
356
  puts "ML-DSA: mldsa-native vendored"
291
- puts "Native asm backends: #{NATIVE_ASM ? 'enabled' : 'disabled'}"
357
+ puts "Host CPU: #{host_cpu} (#{host_os})"
358
+ puts "Native asm auto/forced: #{NATIVE_ASM ? 'enabled' : 'disabled'}"
359
+ puts "Native arithmetic backend: #{NATIVE_ARITH ? 'enabled' : 'disabled'}"
360
+ puts "Native FIPS202 backend: #{NATIVE_FIPS202 ? 'enabled' : 'disabled'}"
361
+ puts "Vendor C arch flags: #{VENDOR_C_ARCH_FLAGS.empty? ? '(none)' : VENDOR_C_ARCH_FLAGS}"
362
+ puts "Vendor ASM arch flags: #{VENDOR_ASM_ARCH_FLAGS.empty? ? '(none)' : VENDOR_ASM_ARCH_FLAGS}"
363
+ if x86_64_host? && (NATIVE_ARITH || NATIVE_FIPS202)
364
+ puts "x86_64 native backend: AVX2 build flags enabled"
365
+ end
292
366
  puts "PQClean fallback: removed"
293
367
  puts "Output: pqcrypto/pqcrypto_secure"
294
368
  puts "===================================="
@@ -78,6 +78,9 @@ int pqcr_mlkem1024_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
78
78
  int pqcr_mlkem1024_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
79
79
  int pqcr_mlkem1024_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
80
80
 
81
+ void pqcr_mlkem_shake256(uint8_t *output, size_t outlen, const uint8_t *input, size_t inlen);
82
+ void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
83
+
81
84
  /* mldsa-native symbols: namespace prefix pqcr_mldsa + level suffix. */
82
85
  int pqcr_mldsa44_keypair(uint8_t *pk, uint8_t *sk);
83
86
  int pqcr_mldsa44_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);