sigstore 0.2.1 → 0.2.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/README.md +1 -1
- data/lib/sigstore/internal/key.rb +5 -5
- data/lib/sigstore/internal/x509.rb +85 -27
- data/lib/sigstore/policy.rb +10 -2
- data/lib/sigstore/rekor/client.rb +8 -3
- data/lib/sigstore/signer.rb +14 -11
- data/lib/sigstore/trusted_root.rb +8 -9
- data/lib/sigstore/tuf/updater.rb +4 -2
- data/lib/sigstore/verifier.rb +12 -25
- data/lib/sigstore/version.rb +1 -1
- metadata +22 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: adbc9be5acca424a61e1be380b84e86ddfb7752ec1e7e347b974a6dc7a59b34e
|
|
4
|
+
data.tar.gz: 8448dc2e3b7db3ac9ce3d187337a18fb9615f8bc7262fe2a3c64e207fd236300
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0393afaefba1a158efd80e2e67653a218ef102db3848de301acefc99f5dbcd72c88ebc67cef4b11c573944d57169a2d0a2224cabfe0003899135ca5ba74282ca'
|
|
7
|
+
data.tar.gz: 92521025a92dbc5776caf13326e13e0988950e95c5206586674d01a80a25907110af6271dd045f76279e7f1a97fd1aea42e06d451514327e0384d2a7d1d57ebd
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sigstore
|
|
2
2
|
|
|
3
|
-
This is a pure Ruby implementation of the `sigstore verify` command from the [sigstore/cosign](https://sigstore.dev/projects/cosign) project. It is intended to be used as a library in other Ruby projects
|
|
3
|
+
This is a pure Ruby implementation of the `sigstore verify` command from the [sigstore/cosign](https://sigstore.dev/projects/cosign) project. It is intended to be used as a library in other Ruby projects or directly through a new `gem` subcommand. The project also contains a TUF client implementation, given TUF is a part of the sigstore verification flow.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -30,7 +30,11 @@ module Sigstore
|
|
|
30
30
|
key_type = "rsa"
|
|
31
31
|
key_schema = "rsa-pkcs1v15-sha256"
|
|
32
32
|
else
|
|
33
|
-
|
|
33
|
+
# Skip unrecognized key types instead of raising an error.
|
|
34
|
+
# This allows the library to work with newer trusted roots that include
|
|
35
|
+
# key types we don't yet support (e.g., PKIX_ED25519 for Rekor v2).
|
|
36
|
+
logger.warn { "Skipping unrecognized key type: #{key_details}" }
|
|
37
|
+
return nil
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
read(key_type, key_schema, key_bytes, key_id: OpenSSL::Digest::SHA256.hexdigest(key_bytes))
|
|
@@ -49,10 +53,6 @@ module Sigstore
|
|
|
49
53
|
RSA.new(key_type, schema, pkey, key_id:)
|
|
50
54
|
else
|
|
51
55
|
raise ArgumentError, "Unsupported key type #{key_type}"
|
|
52
|
-
end.tap do |key|
|
|
53
|
-
if RUBY_ENGINE == "jruby" && key.to_pem != key_bytes && key.to_der != key_bytes
|
|
54
|
-
raise Error::UnsupportedPlatform, "Key mismatch: #{key.to_pem.inspect} != #{key_bytes.inspect}"
|
|
55
|
-
end
|
|
56
56
|
end
|
|
57
57
|
rescue OpenSSL::PKey::PKeyError => e
|
|
58
58
|
raise OpenSSL::PKey::PKeyError, "Invalid key: #{e} for #{key_type} #{schema} #{key_id}"
|
|
@@ -20,6 +20,74 @@ require "openssl"
|
|
|
20
20
|
module Sigstore
|
|
21
21
|
module Internal
|
|
22
22
|
module X509
|
|
23
|
+
if RUBY_ENGINE == "jruby"
|
|
24
|
+
unless JOpenSSL::VERSION >= "0.15.3"
|
|
25
|
+
raise Error::UnsupportedPlatform, "JRuby support requires jruby-openssl >= 0.15.3, is #{JOpenSSL::VERSION}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.validate_chain(trust_roots, leaf, time)
|
|
29
|
+
cert_factory = java.security.cert.CertificateFactory.getInstance("X.509")
|
|
30
|
+
cert_factory.generateCertificate(java.io.ByteArrayInputStream.new(leaf.to_der.to_java_bytes))
|
|
31
|
+
target = leaf.openssl.to_java
|
|
32
|
+
|
|
33
|
+
trust_anchors = Set.new
|
|
34
|
+
intermediate_certs = []
|
|
35
|
+
trust_roots.each do |chain|
|
|
36
|
+
root = chain.last
|
|
37
|
+
|
|
38
|
+
trust_anchors << java.security.cert.TrustAnchor.new(root.openssl.to_java, nil)
|
|
39
|
+
chain[..-2].each do |cert|
|
|
40
|
+
intermediate_certs << cert.openssl.to_java
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
cert_store_parameters = java.security.cert.CollectionCertStoreParameters.new(intermediate_certs)
|
|
45
|
+
cert_store = java.security.cert.CertStore.getInstance("Collection", cert_store_parameters)
|
|
46
|
+
|
|
47
|
+
cert_selector = java.security.cert.X509CertSelector.new
|
|
48
|
+
cert_selector.setCertificate(target)
|
|
49
|
+
|
|
50
|
+
pkix_builder_parameters = java.security.cert.PKIXBuilderParameters.new(trust_anchors, cert_selector)
|
|
51
|
+
pkix_builder_parameters.setDate(time) if time
|
|
52
|
+
pkix_builder_parameters.setRevocationEnabled(false)
|
|
53
|
+
pkix_builder_parameters.addCertStore(cert_store)
|
|
54
|
+
|
|
55
|
+
cert_path_builder = java.security.cert.CertPathBuilder.getInstance("PKIX")
|
|
56
|
+
cert_path_result = cert_path_builder.build(pkix_builder_parameters)
|
|
57
|
+
chain = cert_path_result.cert_path.getCertificates.map do |cert|
|
|
58
|
+
der = String.from_java_bytes(cert.getEncoded).b
|
|
59
|
+
Certificate.read(der)
|
|
60
|
+
end
|
|
61
|
+
chain.shift # remove the cert itself
|
|
62
|
+
chain << Certificate.read(
|
|
63
|
+
String.from_java_bytes(cert_path_result.get_trust_anchor.getTrustedCert.getEncoded).b
|
|
64
|
+
)
|
|
65
|
+
[chain, nil]
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
def self.validate_chain(trust_roots, leaf, time)
|
|
69
|
+
store = OpenSSL::X509::Store.new
|
|
70
|
+
intermediate_certs = []
|
|
71
|
+
trust_roots.each do |chain|
|
|
72
|
+
store.add_cert(chain.last.openssl)
|
|
73
|
+
chain[..-2].each do |cert|
|
|
74
|
+
intermediate_certs << cert.openssl
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
store_ctx = OpenSSL::X509::StoreContext.new(store, leaf.openssl, intermediate_certs)
|
|
78
|
+
store_ctx.time = time if time
|
|
79
|
+
unless store_ctx.verify
|
|
80
|
+
return nil, VerificationFailure.new(
|
|
81
|
+
"failed to validate certificate from fulcio cert chain: #{store_ctx.error_string}"
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
chain = store_ctx.chain || raise(Error::InvalidCertificate, "no valid cert chain found")
|
|
86
|
+
chain.shift # remove the cert itself
|
|
87
|
+
[chain.map! { Certificate.new(_1) }, nil]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
23
91
|
class Certificate
|
|
24
92
|
extend Forwardable
|
|
25
93
|
|
|
@@ -149,7 +217,7 @@ module Sigstore
|
|
|
149
217
|
extended_key_usage = extension(Extension::ExtendedKeyUsage)
|
|
150
218
|
return false unless extended_key_usage
|
|
151
219
|
|
|
152
|
-
extended_key_usage.
|
|
220
|
+
extended_key_usage.precert?
|
|
153
221
|
end
|
|
154
222
|
end
|
|
155
223
|
|
|
@@ -236,7 +304,7 @@ module Sigstore
|
|
|
236
304
|
|
|
237
305
|
def parse_value(value)
|
|
238
306
|
unless value.is_a?(OpenSSL::ASN1::Sequence)
|
|
239
|
-
|
|
307
|
+
raise ArgumentError,
|
|
240
308
|
"Invalid extended key usage: #{value.inspect}"
|
|
241
309
|
end
|
|
242
310
|
|
|
@@ -248,10 +316,15 @@ module Sigstore
|
|
|
248
316
|
end
|
|
249
317
|
|
|
250
318
|
CODE_SIGNING = OpenSSL::ASN1::ObjectId.new("1.3.6.1.5.5.7.3.3")
|
|
319
|
+
PRECERT_PURPOSE = OpenSSL::ASN1::ObjectId.new("1.3.6.1.4.1.11129.2.4.4")
|
|
251
320
|
|
|
252
321
|
def code_signing?
|
|
253
322
|
purposes.any? { |purpose| purpose.oid == CODE_SIGNING.oid }
|
|
254
323
|
end
|
|
324
|
+
|
|
325
|
+
def precert?
|
|
326
|
+
purposes.any? { |purpose| purpose.oid == PRECERT_PURPOSE.oid }
|
|
327
|
+
end
|
|
255
328
|
end
|
|
256
329
|
|
|
257
330
|
class BasicConstraints < Extension
|
|
@@ -309,11 +382,15 @@ module Sigstore
|
|
|
309
382
|
@general_names = value.map do |general_name|
|
|
310
383
|
tag = general_name.tag
|
|
311
384
|
|
|
385
|
+
value = general_name.value
|
|
386
|
+
value = value.first if value.is_a?(Array) && value.size == 1
|
|
387
|
+
value = value.value if value.is_a?(OpenSSL::ASN1::OctetString)
|
|
388
|
+
|
|
312
389
|
case tag
|
|
313
390
|
when 1
|
|
314
|
-
[:otherName,
|
|
391
|
+
[:otherName, value]
|
|
315
392
|
when 6
|
|
316
|
-
[:uniformResourceIdentifier,
|
|
393
|
+
[:uniformResourceIdentifier, value]
|
|
317
394
|
else
|
|
318
395
|
raise Error::Unimplemented,
|
|
319
396
|
"Unhandled general name tag: #{tag}"
|
|
@@ -384,35 +461,17 @@ module Sigstore
|
|
|
384
461
|
|
|
385
462
|
private
|
|
386
463
|
|
|
387
|
-
if RUBY_VERSION >= "3.1"
|
|
388
|
-
def unpack_at(string, format, offset:)
|
|
389
|
-
string.unpack(format, offset:)
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
def unpack1_at(string, format, offset:)
|
|
393
|
-
string.unpack1(format, offset:)
|
|
394
|
-
end
|
|
395
|
-
else
|
|
396
|
-
def unpack_at(string, format, offset:)
|
|
397
|
-
string[offset..].unpack(format)
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
def unpack1_at(string, format, offset:)
|
|
401
|
-
string[offset..].unpack1(format)
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
|
|
405
464
|
# https://letsencrypt.org/2018/04/04/sct-encoding.html
|
|
406
465
|
def unpack_sct_list(string)
|
|
407
466
|
offset = 0
|
|
408
467
|
len = string.bytesize
|
|
409
468
|
list = []
|
|
410
469
|
while offset < len
|
|
411
|
-
sct_version, sct_log_id, sct_timestamp, sct_extensions_len =
|
|
470
|
+
sct_version, sct_log_id, sct_timestamp, sct_extensions_len = string.unpack("Ca32Q>n", offset:)
|
|
412
471
|
offset += 1 + 32 + 8 + 2
|
|
413
472
|
raise Error::Unimplemented, "expect sct version to be 0, got #{sct_version}" unless sct_version.zero?
|
|
414
473
|
|
|
415
|
-
sct_extensions_bytes =
|
|
474
|
+
sct_extensions_bytes = string.unpack1("a#{sct_extensions_len}", offset:).b
|
|
416
475
|
offset += sct_extensions_len
|
|
417
476
|
|
|
418
477
|
unless sct_extensions_len.zero?
|
|
@@ -420,10 +479,9 @@ module Sigstore
|
|
|
420
479
|
"sct_extensions_len=#{sct_extensions_len} not supported"
|
|
421
480
|
end
|
|
422
481
|
|
|
423
|
-
sct_signature_alg_hash, sct_signature_alg_sign, sct_signature_len =
|
|
424
|
-
offset:)
|
|
482
|
+
sct_signature_alg_hash, sct_signature_alg_sign, sct_signature_len = string.unpack("CCn", offset:)
|
|
425
483
|
offset += 1 + 1 + 2
|
|
426
|
-
sct_signature_bytes =
|
|
484
|
+
sct_signature_bytes = string.unpack1("a#{sct_signature_len}", offset:).b
|
|
427
485
|
offset += sct_signature_len
|
|
428
486
|
list << Timestamp.new(
|
|
429
487
|
version: sct_version,
|
data/lib/sigstore/policy.rb
CHANGED
|
@@ -38,8 +38,16 @@ module Sigstore
|
|
|
38
38
|
VerificationSuccess.new
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
ext
|
|
41
|
+
if RUBY_ENGINE == "jruby"
|
|
42
|
+
def ext_value(ext)
|
|
43
|
+
der = ext.to_der
|
|
44
|
+
seq = OpenSSL::ASN1.decode(der)
|
|
45
|
+
seq.value.last.value
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
def ext_value(ext)
|
|
49
|
+
ext.value
|
|
50
|
+
end
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
def oid
|
|
@@ -109,12 +109,17 @@ module Sigstore
|
|
|
109
109
|
raise ArgumentError, "Received multiple entries in response" if response.size != 1
|
|
110
110
|
|
|
111
111
|
_, result = response.first
|
|
112
|
+
canonicalized_body = Internal::Util.base64_decode(result.fetch("body"))
|
|
113
|
+
body = JSON.parse(canonicalized_body)
|
|
112
114
|
entry = V1::TransparencyLogEntry.new
|
|
113
|
-
entry.
|
|
114
|
-
entry.integrated_time = result.fetch("integratedTime")
|
|
115
|
+
entry.log_index = result.fetch("logIndex")
|
|
115
116
|
entry.log_id = Common::V1::LogId.new
|
|
116
117
|
entry.log_id.key_id = Internal::Util.hex_decode(result.fetch("logID"))
|
|
117
|
-
entry.
|
|
118
|
+
entry.kind_version = V1::KindVersion.new
|
|
119
|
+
entry.kind_version.kind = body.fetch("kind")
|
|
120
|
+
entry.kind_version.version = body.fetch("apiVersion")
|
|
121
|
+
entry.integrated_time = result.fetch("integratedTime")
|
|
122
|
+
entry.canonicalized_body = canonicalized_body
|
|
118
123
|
if (set = result.dig("verification", "signedEntryTimestamp"))
|
|
119
124
|
entry.inclusion_promise = V1::InclusionPromise.new
|
|
120
125
|
entry.inclusion_promise.signed_entry_timestamp = Internal::Util.base64_decode(set)
|
data/lib/sigstore/signer.rb
CHANGED
|
@@ -133,17 +133,20 @@ module Sigstore
|
|
|
133
133
|
# Perform certification path validation (RFC 5280 §6) of the returned certificate chain with the pre-distributed
|
|
134
134
|
# Fulcio root certificate(s) as a trust anchor.
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
now = Time.now
|
|
137
|
+
if leaf.not_before > now
|
|
138
|
+
unless leaf.not_before - now < 60
|
|
139
|
+
raise Error::Signing, "leaf certificate not yet valid: #{leaf.not_before.inspect} vs #{now.inspect}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
logger.warn do
|
|
143
|
+
"leaf certificate not yet valid: #{leaf.not_before.inspect} vs #{now.inspect}, sleeping until valid"
|
|
144
|
+
end
|
|
145
|
+
sleep(leaf.not_before - now)
|
|
142
146
|
end
|
|
143
147
|
|
|
144
|
-
chain =
|
|
145
|
-
chain
|
|
146
|
-
chain.map! { |cert| Internal::X509::Certificate.new(cert) }
|
|
148
|
+
chain, err = Internal::X509.validate_chain(@trusted_root.fulcio_cert_chains, leaf, nil)
|
|
149
|
+
raise Error::Signing, "failed to validate returned certificate chain: #{err.reason}" if err
|
|
147
150
|
|
|
148
151
|
logger.debug { "verified chain" }
|
|
149
152
|
|
|
@@ -175,7 +178,7 @@ module Sigstore
|
|
|
175
178
|
"certificate does not contain expected SAN #{expected_san}, got #{general_names}"
|
|
176
179
|
end
|
|
177
180
|
|
|
178
|
-
[leaf,
|
|
181
|
+
[leaf, chain.unshift(leaf)]
|
|
179
182
|
end
|
|
180
183
|
|
|
181
184
|
def sign_payload(payload, key)
|
|
@@ -267,7 +270,7 @@ module Sigstore
|
|
|
267
270
|
bundle.media_type = BundleType::BUNDLE_0_3.media_type
|
|
268
271
|
bundle.verification_material = Bundle::V1::VerificationMaterial.new
|
|
269
272
|
bundle.verification_material.certificate = Common::V1::X509Certificate.new
|
|
270
|
-
bundle.verification_material.certificate.raw_bytes = leaf_certificate.
|
|
273
|
+
bundle.verification_material.certificate.raw_bytes = leaf_certificate.to_der
|
|
271
274
|
bundle.verification_material.tlog_entries = tlog_entries
|
|
272
275
|
bundle.verification_material.timestamp_verification_data = timestamp_verification_data
|
|
273
276
|
bundle.message_signature = Sigstore::Common::V1::MessageSignature.new.tap do |ms|
|
|
@@ -59,13 +59,13 @@ module Sigstore
|
|
|
59
59
|
keys
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
Internal::X509::Certificate.read(raw_bytes)
|
|
62
|
+
def fulcio_cert_chains
|
|
63
|
+
chains = ca_keys(certificate_authorities, allow_expired: true).map do |certs|
|
|
64
|
+
certs.map { |raw_bytes| Internal::X509::Certificate.read(raw_bytes) }
|
|
65
65
|
end
|
|
66
|
-
raise Error::InvalidBundle, "Fulcio certificates not found in trusted root" if
|
|
66
|
+
raise Error::InvalidBundle, "Fulcio certificates not found in trusted root" if chains.none?(&:any?)
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
chains
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def tlog_for_signing
|
|
@@ -87,7 +87,8 @@ module Sigstore
|
|
|
87
87
|
|
|
88
88
|
tlogs.each do |transparency_log_instance|
|
|
89
89
|
key = transparency_log_instance.public_key
|
|
90
|
-
|
|
90
|
+
parsed_key = Internal::Key.from_key_details(key.key_details, key.raw_bytes)
|
|
91
|
+
yield parsed_key if parsed_key
|
|
91
92
|
end
|
|
92
93
|
end
|
|
93
94
|
|
|
@@ -97,9 +98,7 @@ module Sigstore
|
|
|
97
98
|
certificate_authorities.each do |ca|
|
|
98
99
|
next unless timerange_valid?(ca.valid_for, allow_expired:)
|
|
99
100
|
|
|
100
|
-
ca.cert_chain.certificates.
|
|
101
|
-
yield cert.raw_bytes
|
|
102
|
-
end
|
|
101
|
+
yield ca.cert_chain.certificates.map(&:raw_bytes)
|
|
103
102
|
end
|
|
104
103
|
end
|
|
105
104
|
|
data/lib/sigstore/tuf/updater.rb
CHANGED
|
@@ -21,8 +21,6 @@ require_relative "snapshot"
|
|
|
21
21
|
require_relative "targets"
|
|
22
22
|
require_relative "timestamp"
|
|
23
23
|
|
|
24
|
-
require "set"
|
|
25
|
-
|
|
26
24
|
module Sigstore::TUF
|
|
27
25
|
class Updater
|
|
28
26
|
include Sigstore::Loggable
|
|
@@ -74,6 +72,10 @@ module Sigstore::TUF
|
|
|
74
72
|
target_base_url ||= @target_base_url
|
|
75
73
|
raise ArgumentError, "No target_base_url set" unless target_base_url
|
|
76
74
|
|
|
75
|
+
if (cached_target = find_cached_target(target_info, filepath))
|
|
76
|
+
return cached_target
|
|
77
|
+
end
|
|
78
|
+
|
|
77
79
|
filepath ||= generate_target_file_path(target_info)
|
|
78
80
|
|
|
79
81
|
target_filepath = target_info.path
|
data/lib/sigstore/verifier.rb
CHANGED
|
@@ -28,9 +28,9 @@ module Sigstore
|
|
|
28
28
|
|
|
29
29
|
attr_reader :rekor_client
|
|
30
30
|
|
|
31
|
-
def initialize(rekor_client:,
|
|
31
|
+
def initialize(rekor_client:, fulcio_cert_chains:, timestamp_authorities:, rekor_keyring:, ct_keyring:)
|
|
32
32
|
@rekor_client = rekor_client
|
|
33
|
-
@
|
|
33
|
+
@fulcio_cert_chains = fulcio_cert_chains
|
|
34
34
|
@timestamp_authorities = timestamp_authorities
|
|
35
35
|
@rekor_keyring = rekor_keyring
|
|
36
36
|
@ct_keyring = ct_keyring
|
|
@@ -39,7 +39,7 @@ module Sigstore
|
|
|
39
39
|
def self.for_trust_root(trust_root:)
|
|
40
40
|
new(
|
|
41
41
|
rekor_client: Rekor::Client.new(url: trust_root.tlog_for_signing.base_url),
|
|
42
|
-
|
|
42
|
+
fulcio_cert_chains: trust_root.fulcio_cert_chains,
|
|
43
43
|
timestamp_authorities: trust_root.timestamp_authorities,
|
|
44
44
|
rekor_keyring: Internal::Keyring.new(keys: trust_root.rekor_keys),
|
|
45
45
|
ct_keyring: Internal::Keyring.new(keys: trust_root.ctfe_keys)
|
|
@@ -97,14 +97,6 @@ module Sigstore
|
|
|
97
97
|
|
|
98
98
|
timestamps << Time.at(entry.integrated_time).utc
|
|
99
99
|
|
|
100
|
-
# TODO: implement this step
|
|
101
|
-
|
|
102
|
-
store = OpenSSL::X509::Store.new
|
|
103
|
-
|
|
104
|
-
@fulcio_cert_chain.each do |cert|
|
|
105
|
-
store.add_cert(cert.openssl)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
100
|
# 3)
|
|
109
101
|
# The Verifier MUST perform certification path validation (RFC 5280 §6) of the certificate chain with the
|
|
110
102
|
# pre-distributed Fulcio root certificate(s) as a trust anchor, but with a fake “current time.”
|
|
@@ -112,19 +104,12 @@ module Sigstore
|
|
|
112
104
|
# timestamp from the Timestamping Service. If a timestamp from the Transparency Service is available, the Verifier
|
|
113
105
|
# MUST perform path validation using the timestamp from the Transparency Service. If both are available, the
|
|
114
106
|
# Verifier performs path validation twice. If either fails, verification fails.
|
|
115
|
-
chains = timestamps.map do |ts|
|
|
116
|
-
store_ctx = OpenSSL::X509::StoreContext.new(store, bundle.leaf_certificate.openssl)
|
|
117
|
-
store_ctx.time = ts
|
|
118
107
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
end
|
|
108
|
+
chains = timestamps.map do |ts|
|
|
109
|
+
chain, err = Internal::X509.validate_chain(@fulcio_cert_chains, bundle.leaf_certificate, ts)
|
|
110
|
+
return err if err
|
|
124
111
|
|
|
125
|
-
chain
|
|
126
|
-
chain.shift # remove the cert itself
|
|
127
|
-
chain.map! { Internal::X509::Certificate.new(_1) }
|
|
112
|
+
chain
|
|
128
113
|
end
|
|
129
114
|
|
|
130
115
|
chains.uniq! { |chain| chain.map(&:to_der) }
|
|
@@ -280,13 +265,15 @@ module Sigstore
|
|
|
280
265
|
issuer_cert = find_issuer_cert(chain)
|
|
281
266
|
issuer_pubkey = issuer_cert.public_key
|
|
282
267
|
unless issuer_cert.ca?
|
|
283
|
-
raise Error::InvalidCertificate, "Invalid issuer pubkey basicConstraint (not a CA): #{issuer_cert.
|
|
268
|
+
raise Error::InvalidCertificate, "Invalid issuer pubkey basicConstraint (not a CA): #{issuer_cert.to_pem}"
|
|
284
269
|
end
|
|
285
270
|
|
|
286
|
-
|
|
271
|
+
# TODO: use public_to_der when available
|
|
272
|
+
issuer_key_id = OpenSSL::Digest::SHA256.digest(issuer_pubkey.to_der)
|
|
287
273
|
end
|
|
288
274
|
|
|
289
275
|
digitally_signed = pack_digitally_signed(sct, certificate, issuer_key_id).b
|
|
276
|
+
|
|
290
277
|
ct_keyring.verify(key_id: sct.log_id, signature: sct.signature, data: digitally_signed)
|
|
291
278
|
end
|
|
292
279
|
|
|
@@ -328,7 +315,7 @@ module Sigstore
|
|
|
328
315
|
|
|
329
316
|
[issuer_key_id, len1, len2, len3, tbs_cert].pack("a32 CCC a#{tbs_cert_len}")
|
|
330
317
|
else
|
|
331
|
-
raise Error::Unimplemented, "only x509_entry and precert_entry supported, given #{sct
|
|
318
|
+
raise Error::Unimplemented, "only x509_entry and precert_entry supported, given #{sct.entry_type.inspect}"
|
|
332
319
|
end
|
|
333
320
|
|
|
334
321
|
[sct.version, 0, sct.timestamp, sct.entry_type, signed_entry, 0].pack(<<~PACK)
|
data/lib/sigstore/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sigstore
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Sigstore Authors
|
|
8
8
|
- Samuel Giddins
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2025-10-25 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: logger
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - ">="
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '0'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - ">="
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '0'
|
|
14
28
|
- !ruby/object:Gem::Dependency
|
|
15
29
|
name: net-http
|
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,9 +67,9 @@ dependencies:
|
|
|
53
67
|
- - ">="
|
|
54
68
|
- !ruby/object:Gem::Version
|
|
55
69
|
version: '0'
|
|
56
|
-
description:
|
|
70
|
+
description:
|
|
57
71
|
email:
|
|
58
|
-
-
|
|
72
|
+
-
|
|
59
73
|
- segiddins@segiddins.me
|
|
60
74
|
executables: []
|
|
61
75
|
extensions: []
|
|
@@ -106,7 +120,7 @@ metadata:
|
|
|
106
120
|
allowed_push_host: https://rubygems.org
|
|
107
121
|
homepage_uri: https://github.com/sigstore/sigstore-ruby
|
|
108
122
|
rubygems_mfa_required: 'true'
|
|
109
|
-
post_install_message:
|
|
123
|
+
post_install_message:
|
|
110
124
|
rdoc_options: []
|
|
111
125
|
require_paths:
|
|
112
126
|
- lib
|
|
@@ -114,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
114
128
|
requirements:
|
|
115
129
|
- - ">="
|
|
116
130
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: 3.
|
|
131
|
+
version: 3.2.0
|
|
118
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
133
|
requirements:
|
|
120
134
|
- - ">="
|
|
@@ -122,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
122
136
|
version: '0'
|
|
123
137
|
requirements: []
|
|
124
138
|
rubygems_version: 3.5.22
|
|
125
|
-
signing_key:
|
|
139
|
+
signing_key:
|
|
126
140
|
specification_version: 4
|
|
127
141
|
summary: A pure-ruby implementation of sigstore signature verification
|
|
128
142
|
test_files: []
|