36
36
import dev .sigstore .rekor .client .HashedRekordRequest ;
37
37
import dev .sigstore .rekor .client .RekorClient ;
38
38
import dev .sigstore .rekor .client .RekorParseException ;
39
+ import dev .sigstore .rekor .client .RekorResponse ;
39
40
import dev .sigstore .rekor .client .RekorVerificationException ;
40
41
import dev .sigstore .rekor .client .RekorVerifier ;
41
42
import dev .sigstore .tuf .SigstoreTufClient ;
51
52
import java .security .spec .InvalidKeySpecException ;
52
53
import java .time .Duration ;
53
54
import java .util .ArrayList ;
55
+ import java .util .Collections ;
54
56
import java .util .List ;
55
57
import java .util .Map ;
56
58
import java .util .concurrent .locks .ReentrantReadWriteLock ;
@@ -77,6 +79,7 @@ public class KeylessSigner implements AutoCloseable {
77
79
private final RekorClient rekorClient ;
78
80
private final RekorVerifier rekorVerifier ;
79
81
private final OidcClients oidcClients ;
82
+ private final List <OidcIdentity > oidcIdentities ;
80
83
private final Signer signer ;
81
84
private final Duration minSigningCertificateLifetime ;
82
85
@@ -99,13 +102,15 @@ private KeylessSigner(
99
102
RekorClient rekorClient ,
100
103
RekorVerifier rekorVerifier ,
101
104
OidcClients oidcClients ,
105
+ List <OidcIdentity > oidcIdentities ,
102
106
Signer signer ,
103
107
Duration minSigningCertificateLifetime ) {
104
108
this .fulcioClient = fulcioClient ;
105
109
this .fulcioVerifier = fulcioVerifier ;
106
110
this .rekorClient = rekorClient ;
107
111
this .rekorVerifier = rekorVerifier ;
108
112
this .oidcClients = oidcClients ;
113
+ this .oidcIdentities = oidcIdentities ;
109
114
this .signer = signer ;
110
115
this .minSigningCertificateLifetime = minSigningCertificateLifetime ;
111
116
}
@@ -129,6 +134,7 @@ public static Builder builder() {
129
134
public static class Builder {
130
135
private SigstoreTufClient sigstoreTufClient ;
131
136
private OidcClients oidcClients ;
137
+ private List <OidcIdentity > oidcIdentities = Collections .emptyList ();
132
138
private Signer signer ;
133
139
private Duration minSigningCertificateLifetime = DEFAULT_MIN_SIGNING_CERTIFICATE_LIFETIME ;
134
140
@@ -144,6 +150,16 @@ public Builder oidcClients(OidcClients oidcClients) {
144
150
return this ;
145
151
}
146
152
153
+ /**
154
+ * An allow list OIDC identities to be used during signing. If the OidcClients are misconfigured
155
+ * or pick up unexpected credentials, this should prevent signing from proceeding
156
+ */
157
+ @ CanIgnoreReturnValue
158
+ public Builder allowedOidcIdentities (List <OidcIdentity > oidcIdentities ) {
159
+ this .oidcIdentities = ImmutableList .copyOf (oidcIdentities );
160
+ return this ;
161
+ }
162
+
147
163
@ CanIgnoreReturnValue
148
164
public Builder signer (Signer signer ) {
149
165
this .signer = signer ;
@@ -185,6 +201,7 @@ public KeylessSigner build()
185
201
rekorClient ,
186
202
rekorVerifier ,
187
203
oidcClients ,
204
+ oidcIdentities ,
188
205
signer ,
189
206
minSigningCertificateLifetime );
190
207
}
@@ -225,11 +242,7 @@ public Builder sigstoreStagingDefaults() throws IOException, NoSuchAlgorithmExce
225
242
* @return a list of keyless singing results.
226
243
*/
227
244
@ CheckReturnValue
228
- public List <KeylessSignature > sign (List <byte []> artifactDigests )
229
- throws OidcException , NoSuchAlgorithmException , SignatureException , InvalidKeyException ,
230
- UnsupportedAlgorithmException , CertificateException , IOException ,
231
- FulcioVerificationException , RekorVerificationException , InterruptedException ,
232
- RekorParseException , InvalidKeySpecException {
245
+ public List <KeylessSignature > sign (List <byte []> artifactDigests ) throws KeylessSignerException {
233
246
234
247
if (artifactDigests .size () == 0 ) {
235
248
throw new IllegalArgumentException ("Require one or more digests" );
@@ -238,12 +251,30 @@ public List<KeylessSignature> sign(List<byte[]> artifactDigests)
238
251
var result = ImmutableList .<KeylessSignature >builder ();
239
252
240
253
for (var artifactDigest : artifactDigests ) {
241
- var signature = signer .signDigest (artifactDigest );
254
+ byte [] signature ;
255
+ try {
256
+ signature = signer .signDigest (artifactDigest );
257
+ } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException ex ) {
258
+ throw new KeylessSignerException ("Failed to sign artifact" , ex );
259
+ }
242
260
243
261
// Technically speaking, it is unlikely the certificate will expire between signing artifacts
244
262
// However, files might be large, and it might take time to talk to Rekor
245
263
// so we check the certificate expiration here.
246
- renewSigningCertificate ();
264
+ try {
265
+ renewSigningCertificate ();
266
+ } catch (FulcioVerificationException
267
+ | UnsupportedAlgorithmException
268
+ | OidcException
269
+ | IOException
270
+ | InterruptedException
271
+ | InvalidKeyException
272
+ | NoSuchAlgorithmException
273
+ | SignatureException
274
+ | CertificateException ex ) {
275
+ throw new KeylessSignerException ("Failed to obtain signing certificate" , ex );
276
+ }
277
+
247
278
CertPath signingCert ;
248
279
byte [] signingCertPemBytes ;
249
280
lock .readLock ().lock ();
@@ -260,8 +291,19 @@ public List<KeylessSignature> sign(List<byte[]> artifactDigests)
260
291
var rekorRequest =
261
292
HashedRekordRequest .newHashedRekordRequest (
262
293
artifactDigest , signingCertPemBytes , signature );
263
- var rekorResponse = rekorClient .putEntry (rekorRequest );
264
- rekorVerifier .verifyEntry (rekorResponse .getEntry ());
294
+
295
+ RekorResponse rekorResponse ;
296
+ try {
297
+ rekorResponse = rekorClient .putEntry (rekorRequest );
298
+ } catch (RekorParseException | IOException ex ) {
299
+ throw new KeylessSignerException ("Failed to put entry in rekor" , ex );
300
+ }
301
+
302
+ try {
303
+ rekorVerifier .verifyEntry (rekorResponse .getEntry ());
304
+ } catch (RekorVerificationException ex ) {
305
+ throw new KeylessSignerException ("Failed to validate rekor response after signing" , ex );
306
+ }
265
307
266
308
result .add (
267
309
KeylessSignature .builder ()
@@ -277,7 +319,7 @@ public List<KeylessSignature> sign(List<byte[]> artifactDigests)
277
319
private void renewSigningCertificate ()
278
320
throws InterruptedException , CertificateException , IOException , UnsupportedAlgorithmException ,
279
321
NoSuchAlgorithmException , InvalidKeyException , SignatureException ,
280
- FulcioVerificationException , OidcException {
322
+ FulcioVerificationException , OidcException , KeylessSignerException {
281
323
// Check if the certificate is still valid
282
324
lock .readLock ().lock ();
283
325
try {
@@ -300,6 +342,18 @@ private void renewSigningCertificate()
300
342
signingCert = null ;
301
343
signingCertPemBytes = null ;
302
344
OidcToken tokenInfo = oidcClients .getIDToken ();
345
+
346
+ // check if we have an allow list and if so, ensure the provided token is in there
347
+ if (!oidcIdentities .isEmpty ()) {
348
+ var obtainedToken = OidcIdentity .from (tokenInfo );
349
+ if (!oidcIdentities .contains (OidcIdentity .from (tokenInfo ))) {
350
+ throw new KeylessSignerException (
351
+ "Obtained Oidc Token "
352
+ + obtainedToken
353
+ + " does not match any identities in allow list" );
354
+ }
355
+ }
356
+
303
357
CertPath signingCert =
304
358
fulcioClient .signingCertificate (
305
359
CertificateRequest .newCertificateRequest (
@@ -324,11 +378,7 @@ private void renewSigningCertificate()
324
378
* @return a keyless singing results.
325
379
*/
326
380
@ CheckReturnValue
327
- public KeylessSignature sign (byte [] artifactDigest )
328
- throws FulcioVerificationException , RekorVerificationException , UnsupportedAlgorithmException ,
329
- CertificateException , NoSuchAlgorithmException , SignatureException , IOException ,
330
- OidcException , InvalidKeyException , InterruptedException , RekorParseException ,
331
- InvalidKeySpecException {
381
+ public KeylessSignature sign (byte [] artifactDigest ) throws KeylessSignerException {
332
382
return sign (List .of (artifactDigest )).get (0 );
333
383
}
334
384
@@ -339,18 +389,18 @@ public KeylessSignature sign(byte[] artifactDigest)
339
389
* @return a map of artifacts and their keyless singing results.
340
390
*/
341
391
@ CheckReturnValue
342
- public Map <Path , KeylessSignature > signFiles (List <Path > artifacts )
343
- throws FulcioVerificationException , RekorVerificationException , UnsupportedAlgorithmException ,
344
- CertificateException , NoSuchAlgorithmException , SignatureException , IOException ,
345
- OidcException , InvalidKeyException , InterruptedException , RekorParseException ,
346
- InvalidKeySpecException {
392
+ public Map <Path , KeylessSignature > signFiles (List <Path > artifacts ) throws KeylessSignerException {
347
393
if (artifacts .size () == 0 ) {
348
394
throw new IllegalArgumentException ("Require one or more paths" );
349
395
}
350
396
var digests = new ArrayList <byte []>(artifacts .size ());
351
397
for (var artifact : artifacts ) {
352
398
var artifactByteSource = com .google .common .io .Files .asByteSource (artifact .toFile ());
353
- digests .add (artifactByteSource .hash (Hashing .sha256 ()).asBytes ());
399
+ try {
400
+ digests .add (artifactByteSource .hash (Hashing .sha256 ()).asBytes ());
401
+ } catch (IOException ex ) {
402
+ throw new KeylessSignerException ("Failed to hash artifact " + artifact );
403
+ }
354
404
}
355
405
var signingResult = sign (digests );
356
406
var result = ImmutableMap .<Path , KeylessSignature >builder ();
@@ -367,11 +417,7 @@ public Map<Path, KeylessSignature> signFiles(List<Path> artifacts)
367
417
* @return a keyless singing results.
368
418
*/
369
419
@ CheckReturnValue
370
- public KeylessSignature signFile (Path artifact )
371
- throws FulcioVerificationException , RekorVerificationException , UnsupportedAlgorithmException ,
372
- CertificateException , NoSuchAlgorithmException , SignatureException , IOException ,
373
- OidcException , InvalidKeyException , InterruptedException , RekorParseException ,
374
- InvalidKeySpecException {
420
+ public KeylessSignature signFile (Path artifact ) throws KeylessSignerException {
375
421
return signFiles (List .of (artifact )).get (artifact );
376
422
}
377
423
}
0 commit comments