Skip to content

Commit

Permalink
First pass at implementing TUF sync algorithm for root.json.
Browse files Browse the repository at this point in the history
WIP towards #60

Signed-off-by: Patrick Flynn <patrick@chainguard.dev>
  • Loading branch information
Patrick Flynn committed Sep 9, 2022
1 parent cb60f1d commit df0ba4b
Show file tree
Hide file tree
Showing 47 changed files with 2,762 additions and 153 deletions.
72 changes: 64 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 @@ -105,6 +104,63 @@ 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 https://theupdateframework.github.io/specification/latest/index.html#role-role}
*
* @param contents
* @param scheme
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
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 the bytes contained in publicK
// 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;
}
}

0 comments on commit df0ba4b

Please sign in to comment.