Skip to content

Commit d19d61a

Browse files
committed
TargetData should work on sha256 or sha512
Only of them is required by spec, so if one's missing we should continue to work and determine the target file correctly based on the available hash. Signed-off-by: Appu Goundan <[email protected]>
1 parent a4104d5 commit d19d61a

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)