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 +4 -4
- data/.github/workflows/ci.yml +74 -2
- data/CHANGELOG.md +49 -0
- data/ext/pqcrypto/extconf.rb +101 -27
- data/ext/pqcrypto/pqcrypto_native_api.h +3 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +234 -9
- data/ext/pqcrypto/pqcrypto_secure.c +295 -218
- data/ext/pqcrypto/pqcrypto_secure.h +12 -3
- data/ext/pqcrypto/pqcrypto_version.h +1 -1
- data/ext/pqcrypto/vendor/.vendored +4 -2
- data/lib/pq_crypto/hybrid_kem.rb +10 -1
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +4 -0
- data/script/vendor_libs.rb +218 -73
- metadata +1 -1
|
@@ -48,6 +48,8 @@ typedef struct {
|
|
|
48
48
|
uint8_t x25519_pk[X25519_PUBLICKEYBYTES];
|
|
49
49
|
} hybrid_expanded_secret_key_t;
|
|
50
50
|
|
|
51
|
+
#define HYBRID_EXPANDED_SECRETKEYBYTES (sizeof(hybrid_expanded_secret_key_t))
|
|
52
|
+
|
|
51
53
|
typedef struct {
|
|
52
54
|
uint8_t mlkem_ct[MLKEM_CIPHERTEXTBYTES];
|
|
53
55
|
uint8_t x25519_ephemeral[X25519_PUBLICKEYBYTES];
|
|
@@ -176,9 +178,10 @@ const char *pq_version(void);
|
|
|
176
178
|
#define PQ_MLKEM_SHAREDSECRETBYTES MLKEM_SHAREDSECRETBYTES
|
|
177
179
|
|
|
178
180
|
#define PQ_HYBRID_PUBLICKEYBYTES HYBRID_PUBLICKEYBYTES
|
|
179
|
-
#define PQ_HYBRID_SECRETKEYBYTES
|
|
180
|
-
#define
|
|
181
|
-
#define
|
|
181
|
+
#define PQ_HYBRID_SECRETKEYBYTES HYBRID_SECRETKEYBYTES
|
|
182
|
+
#define PQ_HYBRID_EXPANDED_SECRETKEYBYTES HYBRID_EXPANDED_SECRETKEYBYTES
|
|
183
|
+
#define PQ_HYBRID_CIPHERTEXTBYTES HYBRID_CIPHERTEXTBYTES
|
|
184
|
+
#define PQ_HYBRID_SHAREDSECRETBYTES HYBRID_SHAREDSECRETBYTES
|
|
182
185
|
|
|
183
186
|
#define PQ_MLDSA_PUBLICKEYBYTES MLDSA_PUBLICKEYBYTES
|
|
184
187
|
#define PQ_MLDSA_SECRETKEYBYTES MLDSA_SECRETKEYBYTES
|
|
@@ -203,6 +206,12 @@ void pq_mu_builder_release(void *state);
|
|
|
203
206
|
int pq_hybrid_kem_keypair(uint8_t *public_key, uint8_t *secret_key);
|
|
204
207
|
int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
|
|
205
208
|
const uint8_t *public_key);
|
|
209
|
+
int pq_hybrid_kem_expand_secret_key(uint8_t *expanded_secret_key, const uint8_t *secret_key);
|
|
210
|
+
int pq_hybrid_kem_decapsulate_expanded(uint8_t *shared_secret, const uint8_t *ciphertext,
|
|
211
|
+
const uint8_t *expanded_secret_key);
|
|
212
|
+
int pq_hybrid_kem_decapsulate_expanded_pkey(uint8_t *shared_secret, const uint8_t *ciphertext,
|
|
213
|
+
const uint8_t *expanded_secret_key,
|
|
214
|
+
void *x25519_private_pkey);
|
|
206
215
|
int pq_hybrid_kem_decapsulate(uint8_t *shared_secret, const uint8_t *ciphertext,
|
|
207
216
|
const uint8_t *secret_key);
|
|
208
217
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
# pq_crypto vendor manifest. Do not edit by hand. Regenerate with: ruby script/vendor_libs.rb
|
|
1
2
|
backend=PQ Code Package native only
|
|
2
3
|
pqclean=removed
|
|
3
4
|
mlkem_native_repo=https://github.com/pq-code-package/mlkem-native.git
|
|
4
5
|
mlkem_native_ref=v1.1.0
|
|
5
6
|
mlkem_native_commit=d2cae2be522a67bfae26100fdb520576f1b2ef90
|
|
6
|
-
mlkem_native_tree_sha256=
|
|
7
|
+
mlkem_native_tree_sha256=c225de87a69e6d6360cddc4b5839b03e65fa9d5a1112a5f19700c905b7e74512
|
|
7
8
|
mldsa_native_repo=https://github.com/pq-code-package/mldsa-native.git
|
|
8
9
|
mldsa_native_ref=v1.0.0-beta
|
|
9
10
|
mldsa_native_commit=db65535319d9750d75d34c6d170677415f9d2c46
|
|
10
|
-
mldsa_native_tree_sha256=
|
|
11
|
+
mldsa_native_tree_sha256=3b2cb648dade4540191f08d606b422042bf781fb37b434934ab02b58a0121f5c
|
|
12
|
+
manifest_sha256=aeb28860537e30f4da0d28dc2961ba6bb06e700195a56f1648e5caddf1b6e1be
|
data/lib/pq_crypto/hybrid_kem.rb
CHANGED
|
@@ -79,13 +79,22 @@ module PQCrypto
|
|
|
79
79
|
|
|
80
80
|
class SecretKey < KEM::SecretKey
|
|
81
81
|
def decapsulate(ciphertext)
|
|
82
|
-
PQCrypto.__send__(:
|
|
82
|
+
PQCrypto.__send__(:native_hybrid_kem_decapsulate_expanded_object, String(ciphertext).b, expanded_key_for_native)
|
|
83
83
|
rescue ArgumentError => e
|
|
84
84
|
raise InvalidCiphertextError, e.message
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def wipe!
|
|
88
|
+
@expanded_key = nil
|
|
89
|
+
super
|
|
90
|
+
end
|
|
91
|
+
|
|
87
92
|
private
|
|
88
93
|
|
|
94
|
+
def expanded_key_for_native
|
|
95
|
+
@expanded_key ||= PQCrypto.__send__(:native_hybrid_kem_expand_secret_key_object, @bytes)
|
|
96
|
+
end
|
|
97
|
+
|
|
89
98
|
def validate_length!
|
|
90
99
|
expected = HybridKEM.details(@algorithm).fetch(:secret_key_bytes)
|
|
91
100
|
raise InvalidKeyError, "Invalid hybrid KEM secret key length" unless @bytes.bytesize == expected
|
data/lib/pq_crypto/version.rb
CHANGED
data/lib/pq_crypto.rb
CHANGED
|
@@ -63,7 +63,11 @@ module PQCrypto
|
|
|
63
63
|
ml_kem_1024_decapsulate
|
|
64
64
|
hybrid_kem_keypair
|
|
65
65
|
hybrid_kem_encapsulate
|
|
66
|
+
hybrid_kem_expand_secret_key
|
|
67
|
+
hybrid_kem_expand_secret_key_object
|
|
66
68
|
hybrid_kem_decapsulate
|
|
69
|
+
hybrid_kem_decapsulate_expanded
|
|
70
|
+
hybrid_kem_decapsulate_expanded_object
|
|
67
71
|
sign_keypair
|
|
68
72
|
sign
|
|
69
73
|
verify
|
data/script/vendor_libs.rb
CHANGED
|
@@ -4,48 +4,106 @@
|
|
|
4
4
|
require "digest"
|
|
5
5
|
require "fileutils"
|
|
6
6
|
require "open3"
|
|
7
|
+
require "optparse"
|
|
7
8
|
require "tmpdir"
|
|
8
9
|
|
|
9
10
|
VENDOR_DIR = File.expand_path("../ext/pqcrypto/vendor", __dir__)
|
|
10
11
|
MANIFEST_PATH = File.join(VENDOR_DIR, ".vendored")
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
PINS = {
|
|
13
14
|
mlkem: {
|
|
14
15
|
repo: "https://github.com/pq-code-package/mlkem-native.git",
|
|
15
16
|
ref: "v1.1.0",
|
|
17
|
+
commit: "d2cae2be522a67bfae26100fdb520576f1b2ef90",
|
|
18
|
+
tree_sha256: "c225de87a69e6d6360cddc4b5839b03e65fa9d5a1112a5f19700c905b7e74512",
|
|
16
19
|
target: "mlkem-native",
|
|
17
20
|
source_dir: "mlkem"
|
|
18
21
|
},
|
|
19
22
|
mldsa: {
|
|
20
23
|
repo: "https://github.com/pq-code-package/mldsa-native.git",
|
|
21
24
|
ref: "v1.0.0-beta",
|
|
25
|
+
commit: "db65535319d9750d75d34c6d170677415f9d2c46",
|
|
26
|
+
tree_sha256: "3b2cb648dade4540191f08d606b422042bf781fb37b434934ab02b58a0121f5c",
|
|
22
27
|
target: "mldsa-native",
|
|
23
28
|
source_dir: "mldsa"
|
|
24
29
|
}
|
|
25
30
|
}.freeze
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
VENDORED_DOCS = %w[LICENSE LICENSE.txt README.md SECURITY.md BUILDING.md RELEASE.md META.yml].freeze
|
|
33
|
+
NORMALIZED_MTIME = Time.utc(2000, 1, 1).freeze
|
|
34
|
+
MANIFEST_HEADER = "# pq_crypto vendor manifest. Do not edit by hand. Regenerate with: ruby script/vendor_libs.rb"
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
options = { mode: :sync }
|
|
37
|
+
OptionParser.new do |opts|
|
|
38
|
+
opts.banner = "Usage: vendor_libs.rb [--verify | --sync | --bump]"
|
|
39
|
+
opts.on("--verify", "Verify existing vendor tree against pinned tree_sha256 (no network)") { options[:mode] = :verify }
|
|
40
|
+
opts.on("--sync", "Re-clone at pinned commits and rebuild vendor tree (idempotent)") { options[:mode] = :sync }
|
|
41
|
+
opts.on("--bump", "Re-clone and print new tree_sha256 values to update PINS in this file") { options[:mode] = :bump }
|
|
42
|
+
end.parse!
|
|
35
43
|
|
|
36
44
|
def sh!(cmd)
|
|
37
|
-
puts "+ #{cmd.join(" ")}"
|
|
38
45
|
system(*cmd) || abort("command failed: #{cmd.join(" ")}")
|
|
39
46
|
end
|
|
40
47
|
|
|
48
|
+
def capture!(*cmd)
|
|
49
|
+
out, status = Open3.capture2(*cmd)
|
|
50
|
+
abort("command failed: #{cmd.join(" ")}") unless status.success?
|
|
51
|
+
out.strip
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def vendor_candidate?(path)
|
|
55
|
+
return false if File.symlink?(path)
|
|
56
|
+
return false unless File.file?(path)
|
|
57
|
+
return false if path.split(File::SEPARATOR).any? { |seg| seg.start_with?(".") && seg != "." && seg != ".." }
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def normalize_tree!(directory)
|
|
62
|
+
Dir.glob(File.join(directory, "**", "*"), File::FNM_DOTMATCH).each do |path|
|
|
63
|
+
base = File.basename(path)
|
|
64
|
+
next if base == "." || base == ".."
|
|
65
|
+
next if File.symlink?(path)
|
|
66
|
+
if File.file?(path)
|
|
67
|
+
File.chmod(0o644, path)
|
|
68
|
+
File.utime(NORMALIZED_MTIME, NORMALIZED_MTIME, path)
|
|
69
|
+
elsif File.directory?(path)
|
|
70
|
+
File.chmod(0o755, path)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
File.utime(NORMALIZED_MTIME, NORMALIZED_MTIME, directory) if File.directory?(directory)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def copy_sources!(source_root, target_root, source_dir)
|
|
77
|
+
required = File.join(source_root, source_dir)
|
|
78
|
+
abort "missing required upstream directory: #{required}" unless Dir.exist?(required)
|
|
79
|
+
|
|
80
|
+
FileUtils.rm_rf(File.join(target_root, source_dir))
|
|
81
|
+
FileUtils.mkdir_p(File.join(target_root, source_dir))
|
|
82
|
+
|
|
83
|
+
Dir.glob(File.join(required, "**", "*"), File::FNM_DOTMATCH).sort.each do |path|
|
|
84
|
+
next unless vendor_candidate?(path)
|
|
85
|
+
relative = path.sub(/\A#{Regexp.escape(required)}\//, "")
|
|
86
|
+
dest = File.join(target_root, source_dir, relative)
|
|
87
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
88
|
+
FileUtils.cp(path, dest, preserve: false)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
VENDORED_DOCS.each do |relative|
|
|
92
|
+
src = File.join(source_root, relative)
|
|
93
|
+
next if File.symlink?(src)
|
|
94
|
+
next unless File.file?(src)
|
|
95
|
+
FileUtils.cp(src, File.join(target_root, relative), preserve: false)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
41
99
|
def tree_sha256_for(directory)
|
|
42
100
|
entries = Dir.glob(File.join(directory, "**", "*"), File::FNM_DOTMATCH)
|
|
43
|
-
.reject { |
|
|
101
|
+
.reject { |p| File.directory?(p) || File.symlink?(p) || %w[. ..].include?(File.basename(p)) }
|
|
44
102
|
.sort
|
|
45
103
|
|
|
46
104
|
digest = Digest::SHA256.new
|
|
47
105
|
entries.each do |path|
|
|
48
|
-
relative = path.
|
|
106
|
+
relative = path.sub(/\A#{Regexp.escape(directory)}\/?/, "")
|
|
49
107
|
digest << relative << "\0"
|
|
50
108
|
digest << File.binread(path)
|
|
51
109
|
digest << "\0"
|
|
@@ -53,79 +111,166 @@ def tree_sha256_for(directory)
|
|
|
53
111
|
digest.hexdigest
|
|
54
112
|
end
|
|
55
113
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
def vendor_one(name, pin)
|
|
115
|
+
target = File.join(VENDOR_DIR, pin[:target])
|
|
116
|
+
FileUtils.rm_rf(target)
|
|
117
|
+
FileUtils.mkdir_p(target)
|
|
118
|
+
|
|
119
|
+
Dir.mktmpdir("pqcrypto-#{name}-") do |tmpdir|
|
|
120
|
+
clone_dir = File.join(tmpdir, pin[:target])
|
|
121
|
+
sh!(["git", "clone", "--depth", "1", "--branch", pin[:ref], pin[:repo], clone_dir])
|
|
122
|
+
actual_commit = capture!("git", "-C", clone_dir, "rev-parse", "HEAD")
|
|
123
|
+
|
|
124
|
+
if actual_commit != pin[:commit]
|
|
125
|
+
sh!(["git", "-C", clone_dir, "fetch", "--depth", "1", "origin", pin[:commit]])
|
|
126
|
+
sh!(["git", "-C", clone_dir, "checkout", "--detach", pin[:commit]])
|
|
127
|
+
actual_commit = capture!("git", "-C", clone_dir, "rev-parse", "HEAD")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
abort "commit mismatch for #{name}: expected #{pin[:commit]}, got #{actual_commit}" unless actual_commit == pin[:commit]
|
|
131
|
+
|
|
132
|
+
copy_sources!(clone_dir, target, pin[:source_dir])
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
normalize_tree!(target)
|
|
136
|
+
tree_sha256_for(target)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def manifest_body_lines(results)
|
|
140
|
+
lines = ["backend=PQ Code Package native only", "pqclean=removed"]
|
|
141
|
+
results.each do |name, data|
|
|
142
|
+
prefix = "#{name}_native"
|
|
143
|
+
lines << "#{prefix}_repo=#{data[:repo]}"
|
|
144
|
+
lines << "#{prefix}_ref=#{data[:ref]}"
|
|
145
|
+
lines << "#{prefix}_commit=#{data[:commit]}"
|
|
146
|
+
lines << "#{prefix}_tree_sha256=#{data[:tree_sha256]}"
|
|
147
|
+
end
|
|
148
|
+
lines
|
|
71
149
|
end
|
|
72
150
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
151
|
+
def manifest_signature(body_lines)
|
|
152
|
+
Digest::SHA256.hexdigest(body_lines.join("\n") + "\n")
|
|
153
|
+
end
|
|
76
154
|
|
|
77
|
-
|
|
78
|
-
|
|
155
|
+
def write_manifest(results)
|
|
156
|
+
body = manifest_body_lines(results)
|
|
157
|
+
sig = manifest_signature(body)
|
|
158
|
+
content = ([MANIFEST_HEADER] + body + ["manifest_sha256=#{sig}"]).join("\n") + "\n"
|
|
159
|
+
File.write(MANIFEST_PATH, content)
|
|
160
|
+
File.chmod(0o644, MANIFEST_PATH)
|
|
161
|
+
File.utime(NORMALIZED_MTIME, NORMALIZED_MTIME, MANIFEST_PATH)
|
|
162
|
+
end
|
|
79
163
|
|
|
80
|
-
|
|
81
|
-
|
|
164
|
+
def parse_manifest(path)
|
|
165
|
+
return { kv: {}, body: [], signature: nil } unless File.exist?(path)
|
|
166
|
+
body = []
|
|
167
|
+
kv = {}
|
|
168
|
+
signature = nil
|
|
169
|
+
File.readlines(path, chomp: true).each do |line|
|
|
170
|
+
next if line.start_with?("#")
|
|
171
|
+
next if line.empty?
|
|
172
|
+
if line.start_with?("manifest_sha256=")
|
|
173
|
+
signature = line.split("=", 2).last
|
|
174
|
+
else
|
|
175
|
+
body << line
|
|
176
|
+
k, v = line.split("=", 2)
|
|
177
|
+
kv[k] = v if k && v
|
|
178
|
+
end
|
|
82
179
|
end
|
|
180
|
+
{ kv: kv, body: body, signature: signature }
|
|
83
181
|
end
|
|
84
182
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
target = File.join(VENDOR_DIR, config[:target])
|
|
183
|
+
case options[:mode]
|
|
184
|
+
when :verify
|
|
185
|
+
manifest = parse_manifest(MANIFEST_PATH)
|
|
186
|
+
failures = []
|
|
90
187
|
|
|
91
|
-
|
|
188
|
+
if manifest[:signature].nil?
|
|
189
|
+
failures << "manifest: missing manifest_sha256 line"
|
|
190
|
+
else
|
|
191
|
+
expected_sig = manifest_signature(manifest[:body])
|
|
192
|
+
failures << "manifest: signature mismatch (manifest_sha256=#{manifest[:signature]}, computed=#{expected_sig})" if expected_sig != manifest[:signature]
|
|
193
|
+
end
|
|
92
194
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
195
|
+
PINS.each do |name, pin|
|
|
196
|
+
target = File.join(VENDOR_DIR, pin[:target])
|
|
197
|
+
unless Dir.exist?(target)
|
|
198
|
+
failures << "#{name}: vendor directory missing (#{target})"
|
|
199
|
+
next
|
|
200
|
+
end
|
|
96
201
|
|
|
97
|
-
|
|
98
|
-
|
|
202
|
+
manifest_commit = manifest[:kv]["#{name}_native_commit"]
|
|
203
|
+
if manifest_commit != pin[:commit]
|
|
204
|
+
failures << "#{name}: manifest commit (#{manifest_commit.inspect}) != PINS commit (#{pin[:commit]})"
|
|
205
|
+
end
|
|
99
206
|
|
|
100
|
-
|
|
207
|
+
manifest_tree = manifest[:kv]["#{name}_native_tree_sha256"]
|
|
208
|
+
if manifest_tree != pin[:tree_sha256]
|
|
209
|
+
failures << "#{name}: manifest tree_sha256 (#{manifest_tree.inspect}) != PINS tree_sha256 (#{pin[:tree_sha256]})"
|
|
210
|
+
end
|
|
101
211
|
|
|
102
|
-
|
|
212
|
+
actual_tree = tree_sha256_for(target)
|
|
213
|
+
if actual_tree != pin[:tree_sha256]
|
|
214
|
+
failures << "#{name}: filesystem tree_sha256 (#{actual_tree}) != PINS tree_sha256 (#{pin[:tree_sha256]})"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if failures.empty?
|
|
219
|
+
puts "vendor verify: ok"
|
|
220
|
+
exit 0
|
|
221
|
+
else
|
|
222
|
+
failures.each { |f| warn f }
|
|
223
|
+
exit 1
|
|
103
224
|
end
|
|
104
|
-
end
|
|
105
225
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
226
|
+
when :sync
|
|
227
|
+
if Dir.exist?(VENDOR_DIR) && File.exist?(MANIFEST_PATH)
|
|
228
|
+
manifest = parse_manifest(MANIFEST_PATH)
|
|
229
|
+
sig_ok = manifest[:signature] && manifest_signature(manifest[:body]) == manifest[:signature]
|
|
230
|
+
pins_ok = sig_ok && PINS.all? do |name, pin|
|
|
231
|
+
target = File.join(VENDOR_DIR, pin[:target])
|
|
232
|
+
Dir.exist?(target) &&
|
|
233
|
+
manifest[:kv]["#{name}_native_commit"] == pin[:commit] &&
|
|
234
|
+
manifest[:kv]["#{name}_native_tree_sha256"] == pin[:tree_sha256] &&
|
|
235
|
+
tree_sha256_for(target) == pin[:tree_sha256]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if pins_ok
|
|
239
|
+
puts "vendor already at pinned commits and tree_sha256; nothing to do"
|
|
240
|
+
exit 0
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
FileUtils.rm_rf(VENDOR_DIR)
|
|
245
|
+
FileUtils.mkdir_p(VENDOR_DIR)
|
|
246
|
+
|
|
247
|
+
results = {}
|
|
248
|
+
PINS.each do |name, pin|
|
|
249
|
+
actual_tree = vendor_one(name, pin)
|
|
250
|
+
if actual_tree != pin[:tree_sha256]
|
|
251
|
+
abort "#{name}: tree_sha256 drift (PINS=#{pin[:tree_sha256]}, actual=#{actual_tree}). " \
|
|
252
|
+
"Upstream changed under the pinned commit, or the vendor algorithm changed. " \
|
|
253
|
+
"If intentional, run with --bump to print new pins."
|
|
254
|
+
end
|
|
255
|
+
results[name] = pin.merge(tree_sha256: actual_tree)
|
|
256
|
+
end
|
|
257
|
+
write_manifest(results)
|
|
258
|
+
puts "vendor sync: ok"
|
|
259
|
+
results.each { |name, data| puts " #{name}: commit=#{data[:commit]} tree_sha256=#{data[:tree_sha256]}" }
|
|
260
|
+
|
|
261
|
+
when :bump
|
|
262
|
+
FileUtils.rm_rf(VENDOR_DIR)
|
|
263
|
+
FileUtils.mkdir_p(VENDOR_DIR)
|
|
264
|
+
|
|
265
|
+
results = {}
|
|
266
|
+
PINS.each do |name, pin|
|
|
267
|
+
actual_tree = vendor_one(name, pin)
|
|
268
|
+
results[name] = pin.merge(tree_sha256: actual_tree)
|
|
269
|
+
end
|
|
270
|
+
write_manifest(results)
|
|
271
|
+
|
|
272
|
+
puts "Update PINS in script/vendor_libs.rb to:"
|
|
273
|
+
results.each do |name, data|
|
|
274
|
+
puts " PINS[:#{name}][:tree_sha256] = #{data[:tree_sha256].inspect}"
|
|
275
|
+
end
|
|
276
|
+
end
|