Verifiers.java
/*
* 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");
}
}