-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #847 from sigstore/add_key_parsers_to_tuf_package
Add tuf specific key/signature handlers
- Loading branch information
Showing
10 changed files
with
593 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* Copyright 2023 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package fuzzing; | ||
|
||
import com.code_intelligence.jazzer.api.FuzzedDataProvider; | ||
import dev.sigstore.tuf.encryption.Verifiers; | ||
import dev.sigstore.tuf.model.ImmutableKey; | ||
import dev.sigstore.tuf.model.Key; | ||
import java.io.IOException; | ||
import java.security.InvalidKeyException; | ||
import java.util.Map; | ||
|
||
public class TufVerifierFuzzer { | ||
public static void fuzzerTestOneInput(FuzzedDataProvider data) { | ||
try { | ||
String keyType = data.consumeString(10); | ||
String scheme = data.consumeString(20); | ||
String keyData = data.consumeRemainingAsString(); | ||
|
||
Key key = | ||
ImmutableKey.builder() | ||
.keyType(keyType) | ||
.keyVal(Map.of("public", keyData)) | ||
.scheme(scheme) | ||
.build(); | ||
|
||
Verifiers.newVerifier(key); | ||
} catch (IOException | InvalidKeyException e) { | ||
// known exceptions | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
sigstore-java/src/main/java/dev/sigstore/tuf/encryption/EcdsaVerifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright 2022 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package dev.sigstore.tuf.encryption; | ||
|
||
import java.security.InvalidKeyException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.PublicKey; | ||
import java.security.Signature; | ||
import java.security.SignatureException; | ||
|
||
/** ECDSA verifier, instantiated in {@link Verifiers}. */ | ||
class EcdsaVerifier implements Verifier { | ||
|
||
private final PublicKey publicKey; | ||
|
||
EcdsaVerifier(PublicKey publicKey) { | ||
this.publicKey = publicKey; | ||
} | ||
|
||
@Override | ||
public boolean verify(byte[] artifact, byte[] signature) | ||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { | ||
var verifier = Signature.getInstance("SHA256withECDSA"); | ||
verifier.initVerify(publicKey); | ||
verifier.update(artifact); | ||
return verifier.verify(signature); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
sigstore-java/src/main/java/dev/sigstore/tuf/encryption/Ed25519Verifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2022 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package dev.sigstore.tuf.encryption; | ||
|
||
import java.security.InvalidKeyException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.PublicKey; | ||
import java.security.Signature; | ||
import java.security.SignatureException; | ||
|
||
/** Ed25519 verifier, instantiated by {@link Verifiers}. */ | ||
class Ed25519Verifier implements Verifier { | ||
|
||
private final PublicKey publicKey; | ||
|
||
Ed25519Verifier(PublicKey publicKey) { | ||
this.publicKey = publicKey; | ||
} | ||
|
||
/** EdDSA verifiers hash implicitly for ed25519 keys. */ | ||
@Override | ||
public boolean verify(byte[] artifact, byte[] signature) | ||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { | ||
var verifier = Signature.getInstance("Ed25519"); | ||
verifier.initVerify(publicKey); | ||
verifier.update(artifact); | ||
return verifier.verify(signature); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
sigstore-java/src/main/java/dev/sigstore/tuf/encryption/RsaPssVerifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright 2022 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package dev.sigstore.tuf.encryption; | ||
|
||
import java.security.InvalidKeyException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.PublicKey; | ||
import java.security.Signature; | ||
import java.security.SignatureException; | ||
|
||
/** RSA verifier using PSS and MGF1, instantiated by {@link Verifiers}. */ | ||
class RsaPssVerifier implements Verifier { | ||
|
||
private final PublicKey publicKey; | ||
|
||
RsaPssVerifier(PublicKey publicKey) { | ||
this.publicKey = publicKey; | ||
} | ||
|
||
@Override | ||
public boolean verify(byte[] artifact, byte[] signature) | ||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { | ||
var verifier = Signature.getInstance("SHA256withRSAandMGF1"); | ||
verifier.initVerify(publicKey); | ||
verifier.update(artifact); | ||
return verifier.verify(signature); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
sigstore-java/src/main/java/dev/sigstore/tuf/encryption/Verifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright 2022 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package dev.sigstore.tuf.encryption; | ||
|
||
import java.security.InvalidKeyException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.SignatureException; | ||
|
||
/** A verifier interface specifying verification for a raw artifact (no hashing). */ | ||
public interface Verifier { | ||
|
||
/** | ||
* Verify an artifact. Implementations may hash the artifact with sha256 before verifying unless | ||
* they have an implicit hashing algorithm. | ||
* | ||
* @param artifact the artifact that was signed | ||
* @param signature the signature associated with the artifact | ||
* @return true if the signature is valid, false otherwise | ||
*/ | ||
boolean verify(byte[] artifact, byte[] signature) | ||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException; | ||
} |
113 changes: 113 additions & 0 deletions
113
sigstore-java/src/main/java/dev/sigstore/tuf/encryption/Verifiers.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Copyright 2024 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package dev.sigstore.tuf.encryption; | ||
|
||
import dev.sigstore.tuf.model.Key; | ||
import java.io.IOException; | ||
import java.io.StringReader; | ||
import java.security.InvalidKeyException; | ||
import java.security.PublicKey; | ||
import java.security.Security; | ||
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | ||
import org.bouncycastle.crypto.params.ECKeyParameters; | ||
import org.bouncycastle.crypto.params.RSAKeyParameters; | ||
import org.bouncycastle.crypto.util.PublicKeyFactory; | ||
import org.bouncycastle.jce.provider.BouncyCastleProvider; | ||
import org.bouncycastle.openssl.PEMParser; | ||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; | ||
import org.bouncycastle.util.encoders.DecoderException; | ||
import org.bouncycastle.util.encoders.Hex; | ||
|
||
public class Verifiers { | ||
|
||
static { | ||
Security.addProvider(new BouncyCastleProvider()); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface Supplier { | ||
Verifier newVerifier(Key key) throws IOException, InvalidKeyException; | ||
} | ||
|
||
public static Verifier newVerifier(Key key) throws IOException, InvalidKeyException { | ||
|
||
PublicKey publicKey = parsePublicKey(key); | ||
if (key.getKeyType().equals("rsa") && key.getScheme().equals("rsassa-pss-sha256")) { | ||
return new RsaPssVerifier(publicKey); | ||
} | ||
if (isEcdsaKey(key) && key.getScheme().equals("ecdsa-sha2-nistp256")) { | ||
return new EcdsaVerifier(publicKey); | ||
} | ||
if (key.getKeyType().equals("ed25519") && key.getScheme().equals("ed25519")) { | ||
return new Ed25519Verifier(publicKey); | ||
} | ||
throw new InvalidKeyException( | ||
"Unsupported tuf key type and scheme combination: " | ||
+ key.getKeyType() | ||
+ "/" | ||
+ key.getScheme()); | ||
} | ||
|
||
private static PublicKey parsePublicKey(Key key) throws IOException, InvalidKeyException { | ||
var keyType = key.getKeyType(); | ||
if (keyType.equals("rsa") || isEcdsaKey(key)) { | ||
try (PEMParser pemParser = new PEMParser(new StringReader(key.getKeyVal().get("public")))) { | ||
var keyObj = pemParser.readObject(); // throws DecoderException | ||
if (keyObj == null) { | ||
throw new InvalidKeyException( | ||
"tuf " + key.getKeyType() + " keys must be a single PEM encoded section"); | ||
} | ||
if (keyObj instanceof SubjectPublicKeyInfo) { | ||
var keyInfo = PublicKeyFactory.createKey((SubjectPublicKeyInfo) keyObj); | ||
if ((keyType.equals("rsa") && keyInfo instanceof RSAKeyParameters) | ||
|| (isEcdsaKey(key) && keyInfo instanceof ECKeyParameters)) { | ||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); | ||
return converter.getPublicKey((SubjectPublicKeyInfo) keyObj); | ||
} | ||
} | ||
throw new InvalidKeyException( | ||
"Could not parse PEM section into " + keyType + " public key"); | ||
} catch (DecoderException e) { | ||
throw new InvalidKeyException("Could not parse PEM section in " + keyType + " public key"); | ||
} | ||
} | ||
// tuf allows raw keys only for ed25519 (non PEM): | ||
// https://github.com/theupdateframework/specification/blob/c51875f445d8a57efca9dadfbd5dbdece06d87e6/tuf-spec.md#key-objects--file-formats-keys | ||
else if (keyType.equals("ed25519")) { | ||
byte[] keyContents; | ||
try { | ||
keyContents = Hex.decode(key.getKeyVal().get("public")); | ||
} catch (DecoderException e) { | ||
throw new InvalidKeyException("Could not parse hex encoded ed25519 public key"); | ||
} | ||
var params = | ||
new SubjectPublicKeyInfo( | ||
new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), keyContents); | ||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); | ||
return converter.getPublicKey(params); | ||
} else { | ||
throw new InvalidKeyException("Unsupported tuf key type" + key.getKeyType()); | ||
} | ||
} | ||
|
||
// this is a hack to handle keytypes of ecdsa-sha2-nistp256 | ||
// context: https://github.com/awslabs/tough/issues/754 | ||
private static boolean isEcdsaKey(Key key) { | ||
return key.getKeyType().equals("ecdsa-sha2-nistp256") || key.getKeyType().equals("ecdsa"); | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
sigstore-java/src/test/java/dev/sigstore/tuf/encryption/EcdsaVerifierTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright 2024 The Sigstore Authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package dev.sigstore.tuf.encryption; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.security.KeyPair; | ||
import java.security.KeyPairGenerator; | ||
import java.security.Security; | ||
import java.security.Signature; | ||
import org.bouncycastle.jce.provider.BouncyCastleProvider; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class EcdsaVerifierTest { | ||
|
||
private static final byte[] CONTENT = "abcdef".getBytes(StandardCharsets.UTF_8); | ||
|
||
@Test | ||
public void testVerify_ECDSA() throws Exception { | ||
Security.addProvider(new BouncyCastleProvider()); | ||
|
||
var keyPair = genKeyPair(); | ||
var signature = genSignature(keyPair); | ||
var verifier = new EcdsaVerifier(keyPair.getPublic()); | ||
Assertions.assertTrue(verifier.verify(CONTENT, signature)); | ||
} | ||
|
||
private KeyPair genKeyPair() throws Exception { | ||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA"); | ||
keyGen.initialize(256); | ||
return keyGen.generateKeyPair(); | ||
} | ||
|
||
private byte[] genSignature(KeyPair keyPair) throws Exception { | ||
Signature signature = Signature.getInstance("SHA256withECDSA"); | ||
signature.initSign(keyPair.getPrivate()); | ||
signature.update(CONTENT); | ||
return signature.sign(); | ||
} | ||
} |
Oops, something went wrong.