Skip to content

Commit

Permalink
Add all tuf key support
Browse files Browse the repository at this point in the history
- update embedded tuf roots to newer roots
- move key parsing into tuf package
- have separate tuf signature verifiers
- remove copy of prod data
- have all ed25519 key handling done by bc

Signed-off-by: Appu Goundan <[email protected]>
  • Loading branch information
loosebazooka committed Nov 13, 2024
1 parent 764abc9 commit ee0e0fe
Show file tree
Hide file tree
Showing 65 changed files with 809 additions and 2,912 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,30 @@
package fuzzing;

import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import dev.sigstore.encryption.Keys;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
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 TufKeysFuzzer {
public class TufVerifierFuzzer {
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
try {
String[] schemes = {"rsassa-pss-sha256", "ed25519", "ecdsa-sha2-nistp256", "ecdsa"};
String scheme = data.pickValue(schemes);
byte[] byteArray = data.consumeRemainingAsBytes();
String keyType = data.consumeString(10);
String scheme = data.consumeString(20);
String keyData = data.consumeRemainingAsString();

Keys.constructTufPublicKey(byteArray, scheme);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
Key key =
ImmutableKey.builder()
.keyType(keyType)
.keyVal(Map.of("public", keyData))
.scheme(scheme)
.build();

Verifiers.newVerifier(key);
} catch (IOException | InvalidKeyException e) {
// known exceptions
} catch (RuntimeException e) {
if (!e.toString().contains("not currently supported")) {
throw e;
}
}
}
}
85 changes: 1 addition & 84 deletions sigstore-java/src/main/java/dev/sigstore/encryption/Keys.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,22 @@
*/
package dev.sigstore.encryption;

import static org.bouncycastle.jce.ECPointUtil.decodePoint;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.encoders.DecoderException;
Expand Down Expand Up @@ -74,6 +64,7 @@ public static PublicKey parsePublicKey(byte[] keyBytes)
"sigstore public keys must be only a single PEM encoded public key");
}
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
converter.setProvider(BouncyCastleProvider.PROVIDER_NAME);
if (keyObj instanceof SubjectPublicKeyInfo) {
PublicKey pk = converter.getPublicKey((SubjectPublicKeyInfo) keyObj);
if (!SUPPORTED_KEY_TYPES.contains(pk.getAlgorithm())) {
Expand Down Expand Up @@ -115,78 +106,4 @@ public static PublicKey parsePkcs1RsaPublicKey(byte[] contents)
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(keySpec);
}

/**
* Valid values for scheme are:
*
* <ol>
* <li><a href="https://ed25519.cr.yp.to/">ed25519</a>
* <li><a
* href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">ecdsa-sha2-nistp256</a>
* </ol>
*
* @see <a
* href="https://theupdateframework.github.io/specification/latest/index.html#role-role">spec</a>
* @param contents keyBytes
* @param scheme signing scheme
* @return java {link PublicKey}
* @throws NoSuchAlgorithmException if we don't support the scheme provided
* @throws InvalidKeySpecException if the public key material is invalid
*/
public static PublicKey constructTufPublicKey(byte[] contents, String scheme)
throws NoSuchAlgorithmException, InvalidKeySpecException {
if (contents == null || contents.length == 0) {
throw new InvalidKeySpecException("key contents was empty");
}
switch (scheme) {
case "ed25519":
{
final KeyFactory kf = KeyFactory.getInstance("Ed25519");
X509EncodedKeySpec keySpec;
// tuf allows raw keys only for ed25519 (non PEM):
// https://github.com/theupdateframework/specification/blob/c51875f445d8a57efca9dadfbd5dbdece06d87e6/tuf-spec.md#key-objects--file-formats-keys
if (contents.length == 32) {
var params =
new SubjectPublicKeyInfo(
new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), contents);
try {
keySpec = new X509EncodedKeySpec(params.getEncoded());
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
keySpec = new X509EncodedKeySpec(contents);
}
return kf.generatePublic(keySpec);
}
case "ecdsa":
case "ecdsa-sha2-nistp256":
{
// spec for P-256 curve
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("P-256");
// create a KeyFactory with ECDSA (Elliptic Curve Diffie-Hellman) algorithm and use
// BouncyCastle as the provider
KeyFactory kf = null;
try {
kf = KeyFactory.getInstance("ECDSA", BouncyCastleProvider.PROVIDER_NAME);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
}

// code below just creates the public key from key contents using the curve parameters
// (spec variable)
try {
ECNamedCurveSpec params =
new ECNamedCurveSpec("P-256", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = decodePoint(params.getCurve(), contents);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
return kf.generatePublic(pubKeySpec);
} catch (IllegalArgumentException | NullPointerException ex) {
throw new InvalidKeySpecException("ecdsa key was not parseable", ex);
}
}
default:
throw new RuntimeException(scheme + " not currently supported");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@

/** Autodetection for verification algorithms based on public keys used. */
public class Verifiers {
@FunctionalInterface
public interface Supplier {
public Verifier newVerifier(PublicKey publicKey) throws NoSuchAlgorithmException;
}

/** Returns a new verifier for the provided public key to use during verification. */
public static Verifier newVerifier(PublicKey publicKey) throws NoSuchAlgorithmException {
if (publicKey.getAlgorithm().equals("RSA")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

@Value.Immutable
public interface CertificateRequest {
// Really Fuclio PublicKeyAlgorithm.RSA_PSS is not a PSS signature scheme, it's a PKCS1 scheme.
// https://github.com/sigstore/fulcio/issues/1858
Map<String, PublicKeyAlgorithm> SUPPORTED_ALGORITHMS =
ImmutableMap.of("EC", PublicKeyAlgorithm.ECDSA, "RSA", PublicKeyAlgorithm.RSA_PSS);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public java.security.PublicKey toJavaPublicKey()
if (getKeyDetails().equals("PKIX_ECDSA_P256_SHA_256")) {
return Keys.parsePkixPublicKey(getRawBytes(), "EC");
}
if (getKeyDetails().startsWith("PKIX_RSA")) {
return Keys.parsePkixPublicKey(getRawBytes(), "RSA");
}
if (getKeyDetails().equals("PKCS1_RSA_PKCS1V5")) {
return Keys.parsePkcs1RsaPublicKey(getRawBytes());
}
Expand Down
34 changes: 17 additions & 17 deletions sigstore-java/src/main/java/dev/sigstore/tuf/Updater.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.hash.Hashing;
import dev.sigstore.encryption.Keys;
import dev.sigstore.encryption.signers.Verifiers;
import dev.sigstore.tuf.encryption.Verifiers;
import dev.sigstore.tuf.model.*;
import dev.sigstore.tuf.model.TargetMeta.TargetData;
import dev.sigstore.tuf.model.Targets;
import dev.sigstore.tuf.model.Timestamp;
import dev.sigstore.tuf.model.TufMeta;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.time.Clock;
Expand Down Expand Up @@ -247,34 +247,34 @@ void verifyDelegate(
// look for the public key that matches the key ID and use it for verification.
var key = publicKeys.get(signature.getKeyId());
if (key != null) {
String publicKeyContents = key.getKeyVal().get("public");
PublicKey pubKey;
// TUF root version 4 and less is raw hex encoded key while 5+ is PEM.
// TODO([email protected]): remove hex handling code once we upgrade the trusted root
// to v5.
if (publicKeyContents.startsWith("-----BEGIN PUBLIC KEY-----")) {
pubKey = Keys.parsePublicKey(publicKeyContents.getBytes(StandardCharsets.UTF_8));
} else {
pubKey = Keys.constructTufPublicKey(Hex.decode(publicKeyContents), key.getScheme());
}
try {
// while we error on keys that are not readable, we are intentionally more permissive
// about signatures. If for ANY reason (except unparsed keys) we cannot validate a
// signature, we continue as long as we find enough valid signatures within the
// threshold. We still warn the user as this could be an indicator of data issues
byte[] signatureBytes = Hex.decode(signature.getSignature());
if (verifiers.newVerifier(pubKey).verify(verificationMaterial, signatureBytes)) {
if (verifiers.newVerifier(key).verify(verificationMaterial, signatureBytes)) {
goodSigs.add(signature.getKeyId());
} else {
log.log(
Level.FINE,
() ->
String.format(
Locale.ROOT,
"TUF: ignored failed signature verification: '%s' for keyid: '%s'",
signature.getSignature(),
signature.getKeyId()));
}
} catch (SignatureException e) {
log.log(
Level.FINE,
() ->
String.format(
Locale.ROOT,
"TUF: ignored unverifiable signature: '%s' for keyid: '%s'",
"TUF: ignored unverifiable signature: '%s' for keyid: '%s', because '%s'",
signature.getSignature(),
signature.getKeyId()));
signature.getKeyId(),
e.getMessage()));
} catch (DecoderException | NoSuchAlgorithmException | InvalidKeyException e) {
log.log(
Level.WARNING,
Expand Down
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);
}
}
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);
}
}
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);
}
}
Loading

0 comments on commit ee0e0fe

Please sign in to comment.