15
15
*/
16
16
package dev .sigstore .bundle ;
17
17
18
+ import com .google .common .collect .Iterables ;
18
19
import com .google .protobuf .ByteString ;
19
20
import com .google .protobuf .util .JsonFormat ;
20
21
import dev .sigstore .KeylessSignature ;
21
- import dev .sigstore .encryption . certificates . Certificates ;
22
+ import dev .sigstore .proto . ProtoMutators ;
22
23
import dev .sigstore .proto .bundle .v1 .Bundle ;
23
24
import dev .sigstore .proto .bundle .v1 .VerificationMaterial ;
24
25
import dev .sigstore .proto .common .v1 .HashAlgorithm ;
25
26
import dev .sigstore .proto .common .v1 .HashOutput ;
26
27
import dev .sigstore .proto .common .v1 .LogId ;
27
28
import dev .sigstore .proto .common .v1 .MessageSignature ;
28
29
import dev .sigstore .proto .common .v1 .X509Certificate ;
29
- import dev .sigstore .proto .common .v1 .X509CertificateChain ;
30
30
import dev .sigstore .proto .rekor .v1 .Checkpoint ;
31
31
import dev .sigstore .proto .rekor .v1 .InclusionPromise ;
32
32
import dev .sigstore .proto .rekor .v1 .InclusionProof ;
39
39
import java .io .IOException ;
40
40
import java .io .Reader ;
41
41
import java .security .cert .CertPath ;
42
- import java .security .cert .Certificate ;
43
42
import java .security .cert .CertificateEncodingException ;
44
43
import java .security .cert .CertificateException ;
45
- import java .security .cert .CertificateFactory ;
46
- import java .util .ArrayList ;
47
44
import java .util .Base64 ;
48
45
import java .util .List ;
46
+ import java .util .Optional ;
49
47
import java .util .stream .Collectors ;
50
48
import org .bouncycastle .util .encoders .Hex ;
51
49
59
57
class BundleFactoryInternal {
60
58
static final JsonFormat .Printer JSON_PRINTER = JsonFormat .printer ();
61
59
60
+ private static final String BUNDLE_V_0_1 = "application/vnd.dev.sigstore.bundle+json;version=0.1" ;
61
+ private static final String BUNDLE_V_0_2 = "application/vnd.dev.sigstore.bundle+json;version=0.2" ;
62
+ private static final String BUNDLE_V_0_3 = "application/vnd.dev.sigstore.bundle+json;version=0.3" ;
63
+ private static final List <String > SUPPORTED_MEDIA_TYPES =
64
+ List .of (BUNDLE_V_0_1 , BUNDLE_V_0_2 , BUNDLE_V_0_3 );
65
+
62
66
/**
63
67
* Generates Sigstore Bundle Builder from {@link KeylessSignature}. This might be useful in case
64
68
* you want to add additional information to the bundle.
@@ -72,7 +76,7 @@ static Bundle.Builder createBundleBuilder(KeylessSignature signingResult) {
72
76
"keyless signature must have artifact digest when serializing to bundle" );
73
77
}
74
78
return Bundle .newBuilder ()
75
- .setMediaType ("application/vnd.dev.sigstore.bundle+json;version=0.2" )
79
+ .setMediaType (BUNDLE_V_0_3 )
76
80
.setVerificationMaterial (buildVerificationMaterial (signingResult ))
77
81
.setMessageSignature (
78
82
MessageSignature .newBuilder ()
@@ -85,29 +89,18 @@ static Bundle.Builder createBundleBuilder(KeylessSignature signingResult) {
85
89
86
90
private static VerificationMaterial .Builder buildVerificationMaterial (
87
91
KeylessSignature signingResult ) {
88
- var builder =
89
- VerificationMaterial .newBuilder ()
90
- .setX509CertificateChain (
91
- X509CertificateChain .newBuilder ()
92
- .addAllCertificates (
93
- signingResult .getCertPath ().getCertificates ().stream ()
94
- .map (
95
- c -> {
96
- byte [] encoded ;
97
- try {
98
- encoded = c .getEncoded ();
99
- } catch (CertificateEncodingException e ) {
100
- throw new IllegalArgumentException (
101
- "Cannot encode certificate " + c , e );
102
- }
103
- return X509Certificate .newBuilder ()
104
- .setRawBytes (ByteString .copyFrom (encoded ))
105
- .build ();
106
- })
107
- .collect (Collectors .toList ())));
108
- if (signingResult .getEntry ().isPresent ()) {
109
- builder .addTlogEntries (buildTlogEntries (signingResult .getEntry ().get ()));
92
+ X509Certificate cert ;
93
+ var javaCert = Iterables .getLast (signingResult .getCertPath ().getCertificates ());
94
+ try {
95
+ cert = ProtoMutators .fromCert ((java .security .cert .X509Certificate ) javaCert );
96
+ } catch (CertificateEncodingException ce ) {
97
+ throw new IllegalArgumentException ("Cannot encode certificate " + javaCert , ce );
98
+ }
99
+ var builder = VerificationMaterial .newBuilder ().setCertificate (cert );
100
+ if (signingResult .getEntry ().isEmpty ()) {
101
+ throw new IllegalArgumentException ("A log entry must be present in the signing result" );
110
102
}
103
+ builder .addTlogEntries (buildTlogEntries (signingResult .getEntry ().get ()));
111
104
return builder ;
112
105
}
113
106
@@ -135,10 +128,13 @@ private static TransparencyLogEntry.Builder buildTlogEntries(RekorEntry entry) {
135
128
private static void addInclusionProof (
136
129
TransparencyLogEntry .Builder transparencyLogEntry , RekorEntry entry ) {
137
130
RekorEntry .InclusionProof inclusionProof =
138
- entry .getVerification ().getInclusionProof ().orElse (null );
139
- if (inclusionProof == null ) {
140
- return ;
141
- }
131
+ entry
132
+ .getVerification ()
133
+ .getInclusionProof ()
134
+ .orElseThrow (
135
+ () ->
136
+ new IllegalArgumentException (
137
+ "An inclusion proof must be present in the log entry in the signing result" ));
142
138
transparencyLogEntry .setInclusionProof (
143
139
InclusionProof .newBuilder ()
144
140
.setLogIndex (inclusionProof .getLogIndex ())
@@ -156,51 +152,47 @@ static KeylessSignature readBundle(Reader jsonReader) throws BundleParseExceptio
156
152
try {
157
153
JsonFormat .parser ().merge (jsonReader , bundleBuilder );
158
154
} catch (IOException ioe ) {
159
- throw new BundleParseException ("Could not read bundle json" , ioe );
155
+ throw new BundleParseException ("Could not process bundle json" , ioe );
160
156
}
161
- Bundle bundle = bundleBuilder .build ();
162
157
163
- // TODO: only allow v0.2 bundles at some point, we will only be producing v0.2 bundles
164
- // TODO: in our GA release.
165
- // var supportedMediaType = "application/vnd.dev.sigstore.bundle+json;version=0.2";
166
- // if (!supportedMediaType.equals(bundle.getMediaType())) {
167
- // throw new BundleParseException(
168
- // "Unsupported media type '"
169
- // + bundle.getMediaType()
170
- // + "', only '"
171
- // + supportedMediaType
172
- // + "' is supported");
173
- // }
158
+ Bundle bundle = bundleBuilder .build ();
159
+ if (!SUPPORTED_MEDIA_TYPES .contains (bundle .getMediaType ())) {
160
+ throw new BundleParseException ("Unsupported bundle media type: " + bundle .getMediaType ());
161
+ }
174
162
175
163
if (bundle .getVerificationMaterial ().getTlogEntriesCount () == 0 ) {
176
164
throw new BundleParseException ("Could not find any tlog entries in bundle json" );
177
165
}
178
166
var bundleEntry = bundle .getVerificationMaterial ().getTlogEntries (0 );
167
+ RekorEntry .InclusionProof inclusionProof = null ;
179
168
if (!bundleEntry .hasInclusionProof ()) {
180
- throw new BundleParseException ("Could not find an inclusion proof" );
169
+ if (!bundle .getMediaType ().equals (BUNDLE_V_0_1 )) {
170
+ throw new BundleParseException ("Could not find an inclusion proof" );
171
+ }
172
+ } else {
173
+ var bundleInclusionProof = bundleEntry .getInclusionProof ();
174
+
175
+ inclusionProof =
176
+ ImmutableInclusionProof .builder ()
177
+ .logIndex (bundleInclusionProof .getLogIndex ())
178
+ .rootHash (Hex .toHexString (bundleInclusionProof .getRootHash ().toByteArray ()))
179
+ .treeSize (bundleInclusionProof .getTreeSize ())
180
+ .checkpoint (bundleInclusionProof .getCheckpoint ().getEnvelope ())
181
+ .addAllHashes (
182
+ bundleInclusionProof .getHashesList ().stream ()
183
+ .map (ByteString ::toByteArray )
184
+ .map (Hex ::toHexString )
185
+ .collect (Collectors .toList ()))
186
+ .build ();
181
187
}
182
- var bundleInclusionProof = bundleEntry .getInclusionProof ();
183
-
184
- ImmutableInclusionProof inclusionProof =
185
- ImmutableInclusionProof .builder ()
186
- .logIndex (bundleInclusionProof .getLogIndex ())
187
- .rootHash (Hex .toHexString (bundleInclusionProof .getRootHash ().toByteArray ()))
188
- .treeSize (bundleInclusionProof .getTreeSize ())
189
- .checkpoint (bundleInclusionProof .getCheckpoint ().getEnvelope ())
190
- .addAllHashes (
191
- bundleInclusionProof .getHashesList ().stream ()
192
- .map (ByteString ::toByteArray )
193
- .map (Hex ::toHexString )
194
- .collect (Collectors .toList ()))
195
- .build ();
196
188
197
189
var verification =
198
190
ImmutableVerification .builder ()
199
191
.signedEntryTimestamp (
200
192
Base64 .getEncoder ()
201
193
.encodeToString (
202
194
bundleEntry .getInclusionPromise ().getSignedEntryTimestamp ().toByteArray ()))
203
- .inclusionProof (inclusionProof )
195
+ .inclusionProof (Optional . ofNullable ( inclusionProof ) )
204
196
.build ();
205
197
206
198
var rekorEntry =
@@ -214,6 +206,10 @@ static KeylessSignature readBundle(Reader jsonReader) throws BundleParseExceptio
214
206
.verification (verification )
215
207
.build ();
216
208
209
+ if (bundle .hasDsseEnvelope ()) {
210
+ throw new BundleParseException ("DSSE envelope signatures are not supported by this client" );
211
+ }
212
+
217
213
var digest = new byte [] {};
218
214
if (bundle .getMessageSignature ().hasMessageDigest ()) {
219
215
var hashAlgorithm = bundle .getMessageSignature ().getMessageDigest ().getAlgorithm ();
@@ -228,27 +224,24 @@ static KeylessSignature readBundle(Reader jsonReader) throws BundleParseExceptio
228
224
digest = bundle .getMessageSignature ().getMessageDigest ().getDigest ().toByteArray ();
229
225
}
230
226
227
+ CertPath certPath ;
231
228
try {
232
- return KeylessSignature . builder ()
233
- . digest ( digest )
234
- . certPath (
235
- toCertPath (
236
- bundle . getVerificationMaterial (). getX509CertificateChain (). getCertificatesList ()))
237
- . signature ( bundle . getMessageSignature (). getSignature (). toByteArray ())
238
- . entry ( rekorEntry )
239
- . build ();
229
+ if ( bundle . getVerificationMaterial (). hasCertificate ()) {
230
+ certPath =
231
+ ProtoMutators . toCertPath ( List . of ( bundle . getVerificationMaterial (). getCertificate ()));
232
+ } else {
233
+ certPath =
234
+ ProtoMutators . toCertPath (
235
+ bundle . getVerificationMaterial (). getX509CertificateChain (). getCertificatesList ());
236
+ }
240
237
} catch (CertificateException ce ) {
241
238
throw new BundleParseException ("Could not parse bundle certificate chain" , ce );
242
239
}
243
- }
244
-
245
- private static CertPath toCertPath (List <X509Certificate > certificates )
246
- throws CertificateException {
247
- CertificateFactory cf = CertificateFactory .getInstance ("X.509" );
248
- List <Certificate > converted = new ArrayList <>(certificates .size ());
249
- for (var cert : certificates ) {
250
- converted .add (Certificates .fromDer (cert .getRawBytes ().toByteArray ()));
251
- }
252
- return cf .generateCertPath (converted );
240
+ return KeylessSignature .builder ()
241
+ .digest (digest )
242
+ .certPath (certPath )
243
+ .signature (bundle .getMessageSignature ().getSignature ().toByteArray ())
244
+ .entry (rekorEntry )
245
+ .build ();
253
246
}
254
247
}
0 commit comments