Skip to content

Commit 1268925

Browse files
authored
Merge pull request #796 from sigstore/tuf-optional-hashes
TargetData should work on sha256 or sha512
2 parents a4104d5 + d19d61a commit 1268925

25 files changed

+177
-12
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ jobs:
5454
- name: Setup Gradle
5555
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
5656

57-
# tests that hit staging are current disabled due to flakiness (-PskipStaging)
57+
# if you need to skip staging due to flakiness use "-PskipStaging"
5858
- name: Test sigstore-java
59-
run: ./gradlew build -PskipStaging
59+
run: ./gradlew build
6060

6161
- name: Ensure sigstore-java self signing still works
6262
run: ./gradlew sigstore-java:publishToMavenLocal -Prelease -PskipPgpSigning

sigstore-java/src/main/java/dev/sigstore/tuf/Updater.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,14 +438,18 @@ void downloadTargets(Targets targets)
438438
String targetName = entry.getKey();
439439
// 8) If target is missing metadata fail.
440440
// Note: This can't actually happen due to the way GSON is setup the targets.json would fail
441-
// to parse. Leaving
442-
// this code in in-case we eventually allow it in de-serialization.
441+
// to parse. Leaving this code in in-case we eventually allow it in de-serialization.
443442
if (entry.getValue() == null) {
444443
throw new TargetMetadataMissingException(targetName);
445444
}
446445
TargetMeta.TargetData targetData = entry.getValue();
447446
// 9) Download target up to length specified in bytes. verify against hash.
448-
var versionedTargetName = targetData.getHashes().getSha512() + "." + targetName;
447+
String versionedTargetName;
448+
if (targetData.getHashes().getSha512() != null) {
449+
versionedTargetName = targetData.getHashes().getSha512() + "." + targetName;
450+
} else {
451+
versionedTargetName = targetData.getHashes().getSha256() + "." + targetName;
452+
}
449453

450454
var targetBytes =
451455
fetcher.fetchResource("targets/" + versionedTargetName, targetData.getLength());

sigstore-java/src/main/java/dev/sigstore/tuf/model/TargetMeta.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,22 @@ interface TargetData {
4141
/** Custom application specific metadata about the target. */
4242
Optional<Custom> getCustom();
4343

44-
/** Hash values of the target metadata. */
44+
/**
45+
* Hash values of the target metadata. One or both of sha256 or sha512 is required to be
46+
* present.
47+
*/
4548
Hashes getHashes();
4649

4750
/** Length in bytes of the metadata. */
4851
int getLength();
52+
53+
@Value.Check
54+
default void check() {
55+
if (getHashes().getSha256() == null && getHashes().getSha512() == null) {
56+
throw new IllegalStateException(
57+
"No hashes (sha256 or sha512) found for target data: " + this);
58+
}
59+
}
4960
}
5061

5162
/** Field to store use-case specific labels/data. */

sigstore-java/src/test/java/dev/sigstore/tuf/UpdaterTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static dev.sigstore.testkit.tuf.TestResources.UPDATER_REAL_TRUSTED_ROOT;
1919
import static dev.sigstore.testkit.tuf.TestResources.UPDATER_SYNTHETIC_TRUSTED_ROOT;
20+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2021
import static org.junit.jupiter.api.Assertions.assertEquals;
2122
import static org.junit.jupiter.api.Assertions.assertNotNull;
2223
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -658,6 +659,31 @@ public void testTargetsDownload_success()
658659
assertTrue(updater.getLocalStore().getTargetFile("test2.txt") != null);
659660
}
660661

662+
// Ensure we accept sha256 or sha512 on hashes for targets
663+
@Test
664+
public void testTargetsDownload_sha256Only()
665+
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
666+
setupMirror(
667+
"synthetic/targets-sha256-or-sha512",
668+
"1.root.json",
669+
"2.root.json",
670+
"2.snapshot.json",
671+
"1.targets.json",
672+
"timestamp.json",
673+
"targets/2dff935df7d1e1221ef52c753091c487c6fdaabbb0b0e2b193764de8cd7c1222776c61d7ef21f20a4d031a6a6bfa631713df7c4f71b4ee21d362152d4618d514.test2.txt",
674+
"targets/55f8718109829bf506b09d8af615b9f107a266e19f7a311039d1035f180b22d4.test.txt");
675+
var UPDATER_ROOT =
676+
Path.of(
677+
Resources.getResource("dev/sigstore/tuf/synthetic/targets-sha256-or-sha512/root.json")
678+
.getPath());
679+
var updater = createTimeStaticUpdater(localStorePath, UPDATER_ROOT);
680+
var root = updater.updateRoot();
681+
var timestamp = updater.updateTimestamp(root);
682+
var snapshot = updater.updateSnapshot(root, timestamp.get());
683+
var targets = updater.updateTargets(root, snapshot);
684+
assertDoesNotThrow(() -> updater.downloadTargets(targets));
685+
}
686+
661687
// End to end sanity test on the actual prod sigstore repo.
662688
@Test
663689
public void testUpdate_fromProdData()

sigstore-java/src/test/java/dev/sigstore/tuf/model/TestTufJsonLoading.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@
2020
import static org.junit.jupiter.api.Assertions.assertNotNull;
2121

2222
import com.google.common.io.Resources;
23+
import com.google.gson.JsonSyntaxException;
24+
import dev.sigstore.tuf.model.TargetMeta.TargetData;
2325
import java.io.IOException;
2426
import java.io.Reader;
2527
import java.nio.charset.Charset;
2628
import java.time.ZonedDateTime;
2729
import java.util.List;
2830
import java.util.Map;
31+
import org.junit.jupiter.api.Assertions;
2932
import org.junit.jupiter.api.Test;
3033

3134
public class TestTufJsonLoading {
@@ -163,12 +166,37 @@ public void loadTargetsJson() throws IOException {
163166
assertEquals("Expired", custom.getSigstoreMeta().getStatus());
164167
assertEquals("https://fulcio.dev", custom.getSigstoreMeta().getUri().get());
165168
assertEquals("Fulcio", custom.getSigstoreMeta().getUsage());
166-
assertEquals(
167-
"f360c53b2e13495a628b9b8096455badcb6d375b185c4816d95a5d746ff29908",
168-
targetData.getHashes().getSha256());
169-
assertEquals(
170-
"0713252a7fd17f7f3ab12f88a64accf2eb14b8ad40ca711d7fe8b4ecba3b24db9e9dffadb997b196d3867b8f9ff217faf930d80e4dab4e235c7fc3f07be69224",
171-
targetData.getHashes().getSha512());
172169
assertEquals(744, targetData.getLength());
173170
}
171+
172+
@Test
173+
public void loadTargetData_oneHash() {
174+
Assertions.assertDoesNotThrow(
175+
() ->
176+
GSON.get()
177+
.fromJson(
178+
"{\"custom\":{\"sigstore\":{\"status\":\"Active\",\"usage\":\"CTFE\"}},\"hashes\":{\"sha256\": \"7fcb94a5d0ed541260473b990b99a6c39864c1fb16f3f3e594a5a3cebbfe138a\"},\"length\":177}",
179+
TargetData.class));
180+
Assertions.assertDoesNotThrow(
181+
() ->
182+
GSON.get()
183+
.fromJson(
184+
"{\"custom\":{\"sigstore\":{\"status\":\"Active\",\"usage\":\"CTFE\"}},\"hashes\":{\"sha512\": \"4b20747d1afe2544238ad38cc0cc3010921b177d60ac743767e0ef675b915489bd01a36606c0ff83c06448622d7160f0d866c83d20f0c0f44653dcc3f9aa0bd4\"},\"length\":177}",
185+
TargetData.class));
186+
}
187+
188+
@Test
189+
public void loadTargetData_failNoHashes() {
190+
var error =
191+
Assertions.assertThrows(
192+
JsonSyntaxException.class,
193+
() ->
194+
GSON.get()
195+
.fromJson(
196+
"{\"custom\":{\"sigstore\":{\"status\":\"Active\",\"usage\":\"CTFE\"}},\"hashes\":{},\"length\":177}",
197+
TargetData.class));
198+
Assertions.assertEquals(
199+
"No hashes (sha256 or sha512) found for target data: TargetData{custom=Custom{sigstoreMeta=SigstoreMeta{status=Active, usage=CTFE}}, hashes=Hashes{}, length=177}",
200+
error.getCause().getMessage());
201+
}
174202
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"signed":{"_type":"root","spec_version":"1.0","version":1,"expires":"2024-11-20T19:22:57Z","keys":{"09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZd80ry+aWsYUuNgxh6A9MqZX7I03\nnP0sEfTHTBKYn+mHvmd7iNE3jT6ccTmvSmPR593Y59zPxuxVxVxNluI/Sw==\n-----END PUBLIC KEY-----\n"}},"1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+iObJwvwNaRlcYTWQm06PwkG/JXZ\nHbs3NbF6q88yX8/9cFWkPM7fe6ywqnUPvjB3SB/TRBA247JOorXq9GnkoA==\n-----END PUBLIC KEY-----\n"}},"5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEidXjF3GT4NNDHaqLnAHmKHNcYM+v\nCtWgjXPUxZ/ra6X3JWDIAxySTW8kIxqBDuZjdPx2Cb3iGsYKvv1uU2ACTw==\n-----END PUBLIC KEY-----\n"}},"f48d3de6cc0c9f9dbb6b8af8f3de96a1f12d24c9f5980f2d8ce7afdf19b07e03":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDYVTdtFRilKwjrgFU4BmfC+3dN7B\nnxvAzhcHNKr1BtgjlbD4ih10lM3mMfMy6xXEIgHG08hpz12NePg1JPaHFw==\n-----END PUBLIC KEY-----\n"}}},"roles":{"root":{"keyids":["09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759"],"threshold":1},"snapshot":{"keyids":["1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482"],"threshold":1},"targets":{"keyids":["5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5"],"threshold":1},"timestamp":{"keyids":["f48d3de6cc0c9f9dbb6b8af8f3de96a1f12d24c9f5980f2d8ce7afdf19b07e03"],"threshold":1}},"consistent_snapshot":false},"signatures":[{"keyid":"09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759","sig":"304502203b45b43d7049423433231073438bead62726d99266a80c1ba5b3a7fb7866fca3022100a0b1e10b9c3d0b92e9d3277f36e3154485996f83be6463e530b622e5af74bcc7"}]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"signed":{"_type":"targets","spec_version":"1.0","version":1,"expires":"2024-11-22T21:34:24Z","targets":{"test.txt":{"length":10,"hashes":{"sha256":"55f8718109829bf506b09d8af615b9f107a266e19f7a311039d1035f180b22d4"}},"test2.txt":{"length":6,"hashes":{"sha512":"2dff935df7d1e1221ef52c753091c487c6fdaabbb0b0e2b193764de8cd7c1222776c61d7ef21f20a4d031a6a6bfa631713df7c4f71b4ee21d362152d4618d514"}}}},"signatures":[{"keyid":"5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5","sig":"304602210092fbb2a4cfc04497f640314d41207d79a17ea5b3331faf157e24da738c124dbc022100b237f54ea3d3fb5687b3e47fbc3549a216dbda9c0ab05e94196f2ab6990894da"}]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"signed":{"_type":"root","spec_version":"1.0","version":2,"expires":"2024-12-20T21:37:40Z","keys":{"09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZd80ry+aWsYUuNgxh6A9MqZX7I03\nnP0sEfTHTBKYn+mHvmd7iNE3jT6ccTmvSmPR593Y59zPxuxVxVxNluI/Sw==\n-----END PUBLIC KEY-----\n"}},"1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+iObJwvwNaRlcYTWQm06PwkG/JXZ\nHbs3NbF6q88yX8/9cFWkPM7fe6ywqnUPvjB3SB/TRBA247JOorXq9GnkoA==\n-----END PUBLIC KEY-----\n"}},"5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEidXjF3GT4NNDHaqLnAHmKHNcYM+v\nCtWgjXPUxZ/ra6X3JWDIAxySTW8kIxqBDuZjdPx2Cb3iGsYKvv1uU2ACTw==\n-----END PUBLIC KEY-----\n"}},"61491d12afe41a1fc2c2204046d0857f9950115b4f2dfff758bb556c8fa38b2c":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwQbpOfC51o+7QQYdEKgRGyA53MAk\n+kpoHN4u+vBiRX6gtElBG+08uHZ+hYGVuZkrYS3RY2e4FE6S8V67aYVg2g==\n-----END PUBLIC KEY-----\n"}},"f48d3de6cc0c9f9dbb6b8af8f3de96a1f12d24c9f5980f2d8ce7afdf19b07e03":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDYVTdtFRilKwjrgFU4BmfC+3dN7B\nnxvAzhcHNKr1BtgjlbD4ih10lM3mMfMy6xXEIgHG08hpz12NePg1JPaHFw==\n-----END PUBLIC KEY-----\n"}}},"roles":{"root":{"keyids":["09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759","61491d12afe41a1fc2c2204046d0857f9950115b4f2dfff758bb556c8fa38b2c"],"threshold":2},"snapshot":{"keyids":["1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482"],"threshold":1},"targets":{"keyids":["5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5"],"threshold":1},"timestamp":{"keyids":["f48d3de6cc0c9f9dbb6b8af8f3de96a1f12d24c9f5980f2d8ce7afdf19b07e03"],"threshold":1}},"consistent_snapshot":false},"signatures":[{"keyid":"09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759","sig":"3046022100aceb849b04e33e1ff3bfa2ed7ee9fee11bcfb1e2e99ebcb1b3ae945215b8a42c022100c157930d70bc3d94075f1bc7e2b0d42cee5ea5c0adca235e5f4ce16ce88b2c46"},{"keyid":"61491d12afe41a1fc2c2204046d0857f9950115b4f2dfff758bb556c8fa38b2c","sig":"304502204f83ccbcfaf1e086b02172f0d0d13bdf8fb96fda3b6e4c6e71934af30099211802210085ed4b98bb8f93784286dd80eb8d272686ac5a9c89c589f59bffa0dac4a1ce48"}]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"signed":{"_type":"snapshot","spec_version":"1.0","version":2,"expires":"2024-08-29T20:36:55Z","meta":{"targets.json":{"length":642,"hashes":{"sha512":"4cdbc10a77607ab1effe79645e367c61d1195af568995b9ce0dec5fd684af4359198add5597e92e61aa8f2c96598ed48552847ad14fe35354868446b60a0e498"},"version":1}}},"signatures":[{"keyid":"1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482","sig":"30460221008fa6b790fb813c483b9672afc045b5bd3e0c719bb8ddf63ab7f97ae632015a3b022100bcaa73f6b2de9e67cdd1d336cb31a0d86f73ec935d3bd03a9fd0a08fbb516754"}]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"signed":{"_type":"root","spec_version":"1.0","version":2,"expires":"2024-12-20T21:37:40Z","keys":{"09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZd80ry+aWsYUuNgxh6A9MqZX7I03\nnP0sEfTHTBKYn+mHvmd7iNE3jT6ccTmvSmPR593Y59zPxuxVxVxNluI/Sw==\n-----END PUBLIC KEY-----\n"}},"1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+iObJwvwNaRlcYTWQm06PwkG/JXZ\nHbs3NbF6q88yX8/9cFWkPM7fe6ywqnUPvjB3SB/TRBA247JOorXq9GnkoA==\n-----END PUBLIC KEY-----\n"}},"5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEidXjF3GT4NNDHaqLnAHmKHNcYM+v\nCtWgjXPUxZ/ra6X3JWDIAxySTW8kIxqBDuZjdPx2Cb3iGsYKvv1uU2ACTw==\n-----END PUBLIC KEY-----\n"}},"61491d12afe41a1fc2c2204046d0857f9950115b4f2dfff758bb556c8fa38b2c":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwQbpOfC51o+7QQYdEKgRGyA53MAk\n+kpoHN4u+vBiRX6gtElBG+08uHZ+hYGVuZkrYS3RY2e4FE6S8V67aYVg2g==\n-----END PUBLIC KEY-----\n"}},"f48d3de6cc0c9f9dbb6b8af8f3de96a1f12d24c9f5980f2d8ce7afdf19b07e03":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDYVTdtFRilKwjrgFU4BmfC+3dN7B\nnxvAzhcHNKr1BtgjlbD4ih10lM3mMfMy6xXEIgHG08hpz12NePg1JPaHFw==\n-----END PUBLIC KEY-----\n"}}},"roles":{"root":{"keyids":["09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759","61491d12afe41a1fc2c2204046d0857f9950115b4f2dfff758bb556c8fa38b2c"],"threshold":2},"snapshot":{"keyids":["1dec63309978fb7e4f8264f403798fce160174c65ac85398ae39b07daeaa2482"],"threshold":1},"targets":{"keyids":["5e284914a20f614e375b4f82808333a687afeee15d7f43d187dc173353682fd5"],"threshold":1},"timestamp":{"keyids":["f48d3de6cc0c9f9dbb6b8af8f3de96a1f12d24c9f5980f2d8ce7afdf19b07e03"],"threshold":1}},"consistent_snapshot":false},"signatures":[{"keyid":"09511bfa91a3dc7186789e61110733724c18b8debf2255623ed3202b70acd759","sig":"3046022100aceb849b04e33e1ff3bfa2ed7ee9fee11bcfb1e2e99ebcb1b3ae945215b8a42c022100c157930d70bc3d94075f1bc7e2b0d42cee5ea5c0adca235e5f4ce16ce88b2c46"},{"keyid":"61491d12afe41a1fc2c2204046d0857f9950115b4f2dfff758bb556c8fa38b2c","sig":"304502204f83ccbcfaf1e086b02172f0d0d13bdf8fb96fda3b6e4c6e71934af30099211802210085ed4b98bb8f93784286dd80eb8d272686ac5a9c89c589f59bffa0dac4a1ce48"}]}

0 commit comments

Comments
 (0)