Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TUF Client root resource syncing. #123

Merged
merged 14 commits into from
Sep 13, 2022
3 changes: 2 additions & 1 deletion sigstore-java/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies {
implementation("io.grpc:grpc-stub")
runtimeOnly("io.grpc:grpc-netty-shaded")
compileOnly("org.apache.tomcat:annotations-api:6.0.53") // java 9+ only

implementation("commons-codec:commons-codec:1.15")
implementation("com.google.code.gson:gson:2.9.1")
implementation("org.bouncycastle:bcutil-jdk18on:1.71.1")
implementation("org.bouncycastle:bcpkix-jdk18on:1.71.1")
Expand All @@ -47,6 +47,7 @@ dependencies {
testImplementation("no.nav.security:mock-oauth2-server:0.5.1")
testImplementation("com.squareup.okhttp3:mockwebserver:4.10.0")
testImplementation("net.sourceforge.htmlunit:htmlunit:2.64.0")
testImplementation("org.eclipse.jetty:jetty-server:11.0.11")

implementation("javax.validation:validation-api:2.0.1.Final")
}
Expand Down
71 changes: 63 additions & 8 deletions sigstore-java/src/main/java/dev/sigstore/encryption/Keys.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,15 @@
*/
package dev.sigstore.encryption;

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

import com.google.common.annotations.VisibleForTesting;
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.PublicKey;
import java.security.Security;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.*;
import java.security.spec.*;
import java.util.logging.Logger;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
Expand All @@ -36,7 +32,10 @@
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.PublicKeyFactory;
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.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

Expand Down Expand Up @@ -89,6 +88,62 @@ public static PublicKey parsePublicKey(byte[] keyBytes)
return keyFactory.generatePublic(publicKeySpec);
}

/**
* 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 {
PublicKey publicKey = null;
switch (scheme) {
case "rsassa-pss-sha256":
throw new RuntimeException("rsassa-pss-sha256 not currently supported");
case "ed25519":
{
final KeyFactory kf = KeyFactory.getInstance("Ed25519");
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(contents);
publicKey = kf.generatePublic(keySpec);
break;
}
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)
ECNamedCurveSpec params =
new ECNamedCurveSpec("P-256", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = decodePoint(params.getCurve(), contents);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
publicKey = kf.generatePublic(pubKeySpec);
break;
}
}
return publicKey;
}

// https://stackoverflow.com/questions/42911637/get-publickey-from-key-bytes-not-knowing-the-key-algorithm
private static String extractKeyAlgorithm(AsymmetricKeyParameter keyParameters)
throws NoSuchAlgorithmException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static Verifier newVerifier(PublicKey publicKey) throws NoSuchAlgorithmEx
if (publicKey.getAlgorithm().equals("RSA")) {
return new RsaVerifier(publicKey);
}
if (publicKey.getAlgorithm().equals("EC")) {
if (publicKey.getAlgorithm().equals("EC") || publicKey.getAlgorithm().equals("ECDSA")) {
return new EcdsaVerifier(publicKey);
}
throw new NoSuchAlgorithmException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ public enum GsonSupplier implements Supplier<Gson> {
.registerTypeAdapterFactory(new GsonAdaptersKey())
.registerTypeAdapterFactory(new GsonAdaptersRoot())
.registerTypeAdapterFactory(new GsonAdaptersTargets())
.registerTypeAdapter(
LocalDateTime.class,
(JsonDeserializer<LocalDateTime>)
(json, type, jsonDeserializationContext) ->
ZonedDateTime.parse(json.getAsJsonPrimitive().getAsString())
.toLocalDateTime())
.disableHtmlEscaping()
.create();

Expand Down
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;

/**
* Thrown when the Meta File exceeds the max allowable file size as configured in the {@link
* TufClient}
*/
public class MetaFileExceedsMaxException extends TufException {

private String fileUrl;
private int maxSize;

public MetaFileExceedsMaxException(String fileUrl, int maxSize) {
super(
String.format(
"The file at %s exceeds the client's max file size limit (%d)", fileUrl, maxSize));
this.fileUrl = fileUrl;
this.maxSize = maxSize;
}

public String getFileUrl() {
return fileUrl;
}

public int getMaxSize() {
return maxSize;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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;

/** Thrown when the version of the latest downloaded role does not match the expectation. */
public class RoleVersionException extends TufException {
private int expectedVersion;
private int foundVersion;

public RoleVersionException(int expectedVersion, int foundVersion) {
super(String.format("Expected version %d but found version %d", expectedVersion, foundVersion));
this.expectedVersion = expectedVersion;
this.foundVersion = foundVersion;
}

public int getExpectedVersion() {
return expectedVersion;
}

public int getFoundVersion() {
return foundVersion;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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;

import java.time.ZonedDateTime;

/**
* Thrown when the local trusted root is expired and no valid root is found on the remote mirror.
*/
public class RootExpiredException extends TufException {
private String rootUrl;
private ZonedDateTime updateTime;
private ZonedDateTime rootExpirationTime;

public RootExpiredException(
String rootUrl, ZonedDateTime updateTime, ZonedDateTime rootExpirationTime) {
super(
String.format(
"Trusted root metadata is expired but no new versions are available at the "
+ "mirror URL:(%s)\n update start time: %tc\n expired time: %tc)",
rootUrl, updateTime, rootExpirationTime));
this.rootUrl = rootUrl;
this.updateTime = updateTime;
this.rootExpirationTime = rootExpirationTime;
}

public String getRootUrl() {
return rootUrl;
}

public ZonedDateTime getUpdateTime() {
return updateTime;
}

public ZonedDateTime getRootExpirationTime() {
return rootExpirationTime;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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;

/** Thrown when the metadata has not been signed by enough of the allowed keys. */
public class SignatureVerificationException extends TufException {
final int requiredSignatures, verifiedSignatures;

public SignatureVerificationException(int requiredSignatures, int verifiedSignatures) {
super(
String.format(
"The role has not been signed by enough keys. [Theshold: %d, Actual: %d]",
requiredSignatures, verifiedSignatures));
this.requiredSignatures = requiredSignatures;
this.verifiedSignatures = verifiedSignatures;
}
}
Loading