17
17
18
18
import static dev .sigstore .json .GsonSupplier .GSON ;
19
19
20
- import com .google .api .client .http .GenericUrl ;
21
- import com .google .api .client .json .gson .GsonFactory ;
22
20
import com .google .common .annotations .VisibleForTesting ;
23
21
import dev .sigstore .encryption .Keys ;
24
22
import dev .sigstore .encryption .signers .Verifiers ;
25
- import dev .sigstore .http .HttpClients ;
26
- import dev .sigstore .http .ImmutableHttpParams ;
27
23
import dev .sigstore .tuf .model .*;
28
24
import java .io .IOException ;
29
- import java .net .URL ;
30
- import java .nio .charset .StandardCharsets ;
31
25
import java .nio .file .Files ;
32
26
import java .nio .file .Path ;
33
27
import java .security .InvalidKeyException ;
52
46
*/
53
47
public class TufClient {
54
48
55
- private static final int MAX_META_BYTES = 99 * 1024 ; // 99 KB
56
49
private static final int MAX_UPDATES =
57
50
1024 ; // Limit the update loop to retrieve a max of 1024 subsequent versions as expressed in
58
51
// 5.3.3 of spec.
59
52
60
53
private Clock clock ;
61
54
private Verifiers .Supplier verifiers ;
62
-
63
- TufClient (Clock clock , Verifiers .Supplier verifiers ) {
55
+ private MetaFetcher fetcher ;
56
+ private ZonedDateTime updateStartTime ;
57
+ private Path trustedRootPath ;
58
+ private TufLocalStore localStore ;
59
+
60
+ TufClient (
61
+ Clock clock ,
62
+ Verifiers .Supplier verifiers ,
63
+ MetaFetcher fetcher ,
64
+ Path trustedRootPath ,
65
+ TufLocalStore localStore ) {
64
66
this .clock = clock ;
65
67
this .verifiers = verifiers ;
68
+ this .fetcher = fetcher ;
69
+ this .trustedRootPath = trustedRootPath ;
70
+ this .localStore = localStore ;
71
+ this .fetcher = fetcher ;
66
72
}
67
73
68
- TufClient (Verifiers .Supplier verifiers ) {
69
- this (Clock .systemUTC (), verifiers );
70
- }
71
-
72
- TufClient () {
73
- this (Clock .systemUTC (), Verifiers ::newVerifier );
74
+ public static Builder builder () {
75
+ return new Builder ();
74
76
}
75
77
76
- private ZonedDateTime updateStartTime ;
77
-
78
78
// https://theupdateframework.github.io/specification/latest/#detailed-client-workflow
79
- public void updateRoot (Path trustedRootPath , URL mirror , TufLocalStore localStore )
79
+ public void updateRoot ()
80
80
throws IOException , RootExpiredException , NoSuchAlgorithmException , InvalidKeySpecException ,
81
81
InvalidKeyException , MetaFileExceedsMaxException , RoleVersionException ,
82
82
SignatureVerificationException {
@@ -98,36 +98,12 @@ public void updateRoot(Path trustedRootPath, URL mirror, TufLocalStore localStor
98
98
// 5.3.3) download $version+1.root.json from mirror url (eventually obtained from remote.json
99
99
// or map.json) up MAX_META_BYTES. If the file is not available, or we have reached
100
100
// MAX_UPDATES number of root metadata files go to step 5.3.10
101
- String nextVersionFileName = nextVersion + ".root.json" ;
102
- GenericUrl nextVersionUrl = new GenericUrl (mirror + "/" + nextVersionFileName );
103
- var req =
104
- HttpClients .newHttpTransport (ImmutableHttpParams .builder ().build ())
105
- .createRequestFactory (
106
- request -> {
107
- request .setParser (GsonFactory .getDefaultInstance ().createJsonObjectParser ());
108
- })
109
- .buildGetRequest (nextVersionUrl );
110
- req .getHeaders ().setAccept ("application/json; api-version=2.0" );
111
- req .getHeaders ().setContentType ("application/json" );
112
- req .setThrowExceptionOnExecuteError (false );
113
- var resp = req .execute ();
114
- if (resp .getStatusCode () == 404 ) {
101
+ var newRootMaybe = fetcher .getRootAtVersion (nextVersion );
102
+ if (newRootMaybe .isEmpty ()) {
115
103
// No newer versions, go to 5.3.10.
116
104
break ;
117
105
}
118
- if (resp .getStatusCode () != 200 ) {
119
- throw new TufException (
120
- String .format (
121
- "Unexpected return from mirror. Status code: %s, status message: %s"
122
- + resp .getStatusCode ()
123
- + resp .getStatusMessage ()));
124
- }
125
- byte [] rootBytes = resp .getContent ().readNBytes (MAX_META_BYTES );
126
- if (rootBytes .length == MAX_META_BYTES && resp .getContent ().read () != -1 ) {
127
- throw new MetaFileExceedsMaxException (nextVersionUrl .toString (), MAX_META_BYTES );
128
- }
129
- var newRoot = GSON .get ().fromJson (new String (rootBytes , StandardCharsets .UTF_8 ), Root .class );
130
-
106
+ var newRoot = newRootMaybe .get ();
131
107
// 5.3.4) we have a valid next version of the root.json. Check that the file has been signed
132
108
// by:
133
109
// a) a threshold (from step 2) of keys specified in the trusted metadata
@@ -163,7 +139,7 @@ public void updateRoot(Path trustedRootPath, URL mirror, TufLocalStore localStor
163
139
// otherwise throw error.
164
140
ZonedDateTime expires = trustedRoot .getSignedMeta ().getExpiresAsDate ();
165
141
if (expires .isBefore (updateStartTime )) {
166
- throw new RootExpiredException (mirror . toString (), updateStartTime , expires );
142
+ throw new RootExpiredException (fetcher . getSource (), updateStartTime , expires );
167
143
}
168
144
// 5.3.11) If the timestamp and / or snapshot keys have been rotated, then delete the trusted
169
145
// timestamp and snapshot metadata files.
@@ -230,7 +206,7 @@ void verifyDelegate(
230
206
public void updateTimestamp () {
231
207
// 1) download the timestamp.json bytes up to few 10s of K max.
232
208
233
- // 2) verify against threshold of keys as specified in trusted root, json
209
+ // 2) verify against threshold of keys as specified in trusted root. json
234
210
235
211
// 3) check that version of new timestamp.json is higher or equal than current, else fail.
236
212
// 3.2) check that timestamp.snapshot.version <= timestamp.version or fail
@@ -283,4 +259,42 @@ public void updateTargets() {
283
259
284
260
// Done!!
285
261
}
262
+
263
+ public static class Builder {
264
+ private Clock clock = Clock .systemUTC ();
265
+ private Verifiers .Supplier verifiers = Verifiers ::newVerifier ;
266
+
267
+ private MetaFetcher fetcher ;
268
+ private Path trustedRootPath ;
269
+ private TufLocalStore localStore ;
270
+
271
+ public Builder setClock (Clock clock ) {
272
+ this .clock = clock ;
273
+ return this ;
274
+ }
275
+
276
+ public Builder setVerifiers (Verifiers .Supplier verifiers ) {
277
+ this .verifiers = verifiers ;
278
+ return this ;
279
+ }
280
+
281
+ public Builder setLocalStore (TufLocalStore store ) {
282
+ this .localStore = store ;
283
+ return this ;
284
+ }
285
+
286
+ public Builder setTrustedRootPath (Path trustedRootPath ) {
287
+ this .trustedRootPath = trustedRootPath ;
288
+ return this ;
289
+ }
290
+
291
+ public Builder setFetcher (MetaFetcher fetcher ) {
292
+ this .fetcher = fetcher ;
293
+ return this ;
294
+ }
295
+
296
+ public TufClient build () {
297
+ return new TufClient (clock , verifiers , fetcher , trustedRootPath , localStore );
298
+ }
299
+ }
286
300
}
0 commit comments