diff options
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2')
3 files changed, 363 insertions, 23 deletions
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactDescriptor.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactDescriptor.java index 9b5cd4d9b..2403402ac 100644 --- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactDescriptor.java +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/IArtifactDescriptor.java @@ -23,7 +23,7 @@ import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; * descriptor defines the artifact it contains, as well as any processing steps that * must be performed when the artifact is transferred out of the repository (such * as decompression, error checking, etc). - * + * * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. Instead subclass the {@link ArtifactDescriptor} class * @since 2.0 @@ -34,37 +34,36 @@ public interface IArtifactDescriptor { * An artifact descriptor property (value "download.size") indicating the number * of bytes that will be transferred when this artifact is transferred out of the repository. */ - public static final String DOWNLOAD_SIZE = "download.size"; //$NON-NLS-1$ + String DOWNLOAD_SIZE = "download.size"; //$NON-NLS-1$ /** * An artifact descriptor property (value "artifact.size") indicating the size in * bytes of the artifact in its native format (after processing steps have been applied). */ - public static final String ARTIFACT_SIZE = "artifact.size"; //$NON-NLS-1$ + String ARTIFACT_SIZE = "artifact.size"; //$NON-NLS-1$ /** * A prefix of an artifact descriptor properties storing checksum * of the artifact bytes that are transferred. * @since 2.4 */ - public static final String DOWNLOAD_CHECKSUM = "download.checksum"; //$NON-NLS-1$ + String DOWNLOAD_CHECKSUM = "download.checksum"; //$NON-NLS-1$ /** * An artifact descriptor property (value "download.md5") indicating the MD5 * checksum of the artifact bytes that are transferred. * @see #DOWNLOAD_CHECKSUM */ - @Deprecated - public static final String DOWNLOAD_MD5 = "download.md5"; //$NON-NLS-1$ + @Deprecated String DOWNLOAD_MD5 = "download.md5"; //$NON-NLS-1$ /** - * An artifact descriptor property (value "download.contentType") indicating the + * An artifact descriptor property (value "download.contentType") indicating the * content type of the artifact bytes that are transferred. */ - public static final String DOWNLOAD_CONTENTTYPE = "download.contentType"; //$NON-NLS-1$ + String DOWNLOAD_CONTENTTYPE = "download.contentType"; //$NON-NLS-1$ /** * An content type (value "application/zip") indicating the content is a zip file. */ - public static final String TYPE_ZIP = "application/zip"; //$NON-NLS-1$ + String TYPE_ZIP = "application/zip"; //$NON-NLS-1$ /** * A prefix of an artifact descriptor property storing list of @@ -72,7 +71,7 @@ public interface IArtifactDescriptor { * been applied). * @since 2.4 */ - public static final String ARTIFACT_CHECKSUM = "artifact.checksum"; //$NON-NLS-1$ + String ARTIFACT_CHECKSUM = "artifact.checksum"; //$NON-NLS-1$ /** * An artifact descriptor property (value "artifact.md5") indicating the MD5 @@ -80,59 +79,61 @@ public interface IArtifactDescriptor { * been applied). * @see #ARTIFACT_CHECKSUM */ - @Deprecated - public static final String ARTIFACT_MD5 = "artifact.md5"; //$NON-NLS-1$ + @Deprecated String ARTIFACT_MD5 = "artifact.md5"; //$NON-NLS-1$ /** * An artifact descriptor property (value "format") indicating the storage format * of the artifact in the repository. * @see #FORMAT_PACKED */ - public static final String FORMAT = "format"; //$NON-NLS-1$ + String FORMAT = "format"; //$NON-NLS-1$ /** * A property value for the {@link #FORMAT} artifact descriptor property (value "packed") * indicating the storage format is using pack200 compression. * @see #FORMAT + * @noreference This field is not intended to be referenced by clients. + * + * @deprecated See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=572043">bug</a> for details. */ - public static final String FORMAT_PACKED = "packed"; //$NON-NLS-1$ + @Deprecated(forRemoval = true, since = "2.5.0") String FORMAT_PACKED = "packed"; //$NON-NLS-1$ /** * Return the key for the artifact described by this descriptor. * @return the key associated with this descriptor */ - public abstract IArtifactKey getArtifactKey(); + IArtifactKey getArtifactKey(); /** - * Return the value of the given property in this descriptor <code>null</code> + * Return the value of the given property in this descriptor <code>null</code> * is returned if no such property exists * @param key the property key to look for * @return the value of the given property or <code>null</code> */ - public abstract String getProperty(String key); + String getProperty(String key); /** * Returns a read-only collection of the properties of the artifact descriptor. * @return the properties of this artifact descriptor. */ - public Map<String, String> getProperties(); + Map<String, String> getProperties(); - /** + /** * Return the list of processing steps associated with this descriptor. * An empty set of steps implies that this descriptor describes a complete * copy of the artifact in its native form. If one or more steps are present, * they may be performed when the artifact is transferred from the repository * that contains it. - * + * * @return the list of processing steps for this descriptor */ - public abstract IProcessingStepDescriptor[] getProcessingSteps(); + IProcessingStepDescriptor[] getProcessingSteps(); /** * Return the artifact repository that holds the artifact described by this descriptor. * <code>null</code> is returned if this descriptor is not held in a repository. - * + * * @return the repository holding this artifact or <code>null</code> if none. */ - public abstract IArtifactRepository getRepository(); + IArtifactRepository getRepository(); } diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java new file mode 100644 index 000000000..b5cc328f5 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/artifact/spi/IArtifactUIServices.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.p2.repository.artifact.spi; + +import java.io.File; +import java.security.cert.Certificate; +import java.util.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.eclipse.equinox.p2.core.UIServices; +import org.eclipse.equinox.p2.core.UIServices.TrustInfo; +import org.eclipse.equinox.p2.metadata.IArtifactKey; + +/** + * An interface optionally implemented by {@link UIServices} to provide richer + * information for the users. In particular, the users often wish to know which + * artifacts are signed by which certificates or which keys when they are + * {@link UIServices#getTrustInfo(Certificate[][], Collection, String[]) + * prompted} whether to trust such certificates or keys. + * + * @since 2.6 + * + * @see UIServices#getTrustInfo(Certificate[][], Collection, String[]) + */ +public interface IArtifactUIServices { + + /** + * Opens a UI prompt to capture information about trusted content. + * + * @param untrustedCertificateChains a map from untrusted certificate chains to + * the set of keys of the artifacts signed by + * that chain. + * @param untrustedPGPKeys a map of untrusted PGP public keys to the + * set of keys of the artifacts signed by that + * key. + * @param unsignedArtifacts a set of keys of the artifacts that are not + * signed. + * @param artifactFiles a map from artifact keys to the file + * associated with that artifact key. + * + * @return the TrustInfo that describes the user's choices for trusting + * certificates, keys, and unsigned content. + * + * @see #getTrustInfo(UIServices, Map, Map, Set, Map) + * @see UIServices + */ + TrustInfo getTrustInfo( // + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificateChains, // + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifactFiles); + + /** + * Opens a UI prompt to capture information about trusted content. This + * implementation is useful for delegating to an old-style + * {@link UIServices#getTrustInfo(Certificate[][], Collection, String[]) + * UIServices implementation} that does not support the IArtifactUIServices + * interface. + * + * @param uiServices the delegate UI services. + * @param untrustedCertificateChains a map from untrusted certificate chains to + * the set of keys of the artifacts signed by + * that chain. + * @param untrustedPGPKeys a map of untrusted PGP public keys to the + * set of keys of the artifacts signed by that + * key. + * @param unsignedArtifacts a set of keys of the artifacts that are not + * signed. + * @param artifactFiles a map from artifact keys to the file + * associated with that artifact key. + * + * @return the TrustInfo that describes the user's choices for trusting + * certificates, keys, and unsigned content. + * + * @see #getTrustInfo(Map, Map, Set, Map) + * @see UIServices#getTrustInfo(Certificate[][], Collection, String[]) + */ + static TrustInfo getTrustInfo(UIServices uiServices, // + Map<List<Certificate>, Set<IArtifactKey>> untrustedCertificateChains, // + Map<PGPPublicKey, Set<IArtifactKey>> untrustedPGPKeys, // + Set<IArtifactKey> unsignedArtifacts, // + Map<IArtifactKey, File> artifactFiles) { + Certificate[][] unTrustedCertificateChainsArray = untrustedCertificateChains.keySet().stream() + .map(c -> c.toArray(Certificate[]::new)).toArray(Certificate[][]::new); + String[] details = unsignedArtifacts.isEmpty() ? null + : unsignedArtifacts.stream().map(artifactFiles::get).map(Objects::toString).toArray(String[]::new); + return uiServices.getTrustInfo(unTrustedCertificateChainsArray, untrustedPGPKeys.keySet(), details); + } +} diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java new file mode 100644 index 000000000..6fbdff286 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/p2/repository/spi/PGPPublicKeyService.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * Copyright (c) 2022 Eclipse contributors and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.equinox.p2.repository.spi; + +import java.util.*; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.encoders.Hex; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.core.spi.IAgentServiceFactory; + +/** + * A service for managing and searching {@link PGPPublicKey keys}. + * Implementations may make use of a + * <a href="https://datatracker.ietf.org/doc/html/draft-shaw-openpgp-hkp-00">key + * server</a> to fetch up-to-date information about keys. Implementations should + * generally provide support for caching and efficient lookup of keys, + * especially lookup based on {@link PGPPublicKey#getKeyID() key ID} because + * signatures generally use {@link PGPSignature#getKeyID() key IDs} and this is + * the primary use case. + * + * <p> + * Implementors of this service are responsible for registering the + * implementation with the {@link IProvisioningAgent provisioning agent} either + * {@link IProvisioningAgent#registerService(String, Object) explicitly} or via + * an {@link IAgentServiceFactory agent service factory}. + * </p> + * + * @see PGPPublicKey#getKeyID() + * @see PGPSignature#getKeyID() + * @see IAgentServiceFactory + * @see IProvisioningAgent#registerService(String, Object) + * + * @since 2.6 + */ +public abstract class PGPPublicKeyService { + /** + * The name used for obtaining a reference to the key service. + * + * @see IProvisioningAgent#getService(Class) + * @see IProvisioningAgent#getService(String) + */ + public static final String SERVICE_NAME = PGPPublicKeyService.class.getName(); + + /** + * Returns the key associated with the given + * {@link PGPPublicKey#getFingerprint() fingerprint}. + * + * @param fingerprint the fingerprint for which to search. + * @return the key with the matching fingerprint. + * + * @see PGPPublicKey#getFingerprint() + */ + public PGPPublicKey getKey(byte[] fingerprint) { + return getKey(toHex(fingerprint)); + } + + /** + * Returns the key associated with the given + * {@link PGPPublicKey#getFingerprint() fingerprint} represented as a + * {@link #toHex(byte[]) hexadecimal} string value. + * + * @param fingerprint the fingerprint for which to search. + * @return the key with the matching fingerprint. + * + * @see PGPPublicKey#getFingerprint() + * @see #toHex(byte[]) + */ + public abstract PGPPublicKey getKey(String fingerprint); + + /** + * Returns the keys associated with the given {@link PGPPublicKey#getKeyID() key + * ID}. In general, key ID collisions are possible so implementations must be + * tolerant of that. + * + * @param keyID the key ID for which to search. + * @return the keys with the matching key IDs. + * + * @see PGPPublicKey#getKeyID() + * @see PGPSignature#getKeyID() + */ + public abstract Collection<PGPPublicKey> getKeys(long keyID); + + /** + * Adds the given key to this key service. An implementations may fetch more + * up-to-date information about this key from a key server and may return a + * different key than the one passed in here. In general an implementation may + * also return an existing key, with the same fingerprint, already known to the + * key service. + * + * @param key the key to add. + * @return the normalized key available in this key service. + */ + public abstract PGPPublicKey addKey(PGPPublicKey key); + + /** + * Returns the set of keys that have been verified to have signed the given key. + * These are the links in the web of trust. + * + * @param key the key for which to find keys that have signed it. + * @return the set of keys that have been verified to have signed the given key. + * + * @see PGPSignature#verifyCertification(String, PGPPublicKey) + * @see PGPSignature#verifyCertification(PGPPublicKey, PGPPublicKey) + */ + public abstract Set<PGPPublicKey> getVerifiedCertifications(PGPPublicKey key); + + /** + * If this key has a revocation signature that is verified to have been signed + * by the public key of that revocation signature, this returns the + * {@link PGPSignature#getCreationTime() creation time} of that signature, + * otherwise it returns <code>null</code>. + * + * @param key the key to test for revocation. + * @return when this key was verifiably revoked, or <code>null</code> if it is + * not revoked. + * + * @see PGPSignature#getKeyID() + * @see PGPPublicKey#hasRevocation() + * @see PGPSignature#getCreationTime() + * @see PGPSignature#KEY_REVOCATION + * @see PGPSignature#SUBKEY_REVOCATION + */ + public abstract Date getVerifiedRevocationDate(PGPPublicKey key); + + /** + * Returns whether the signature's {@link PGPSignature creation time} is before + * the key's {@link #getVerifiedRevocationDate(PGPPublicKey) verified revocation + * date}, if that key has one. + * + * @param signature the signature to test. + * @param key the corresponding key of this signature against which to + * test. + * @return <code>true</code> if the signature was created before the key was + * revoked or if the key is not revoked, <code>false</code> otherwise. + * + * @throws IllegalArgumentException if the signature's + * {@link PGPSignature#getKeyID() key} is not + * the same as the key's + * {@link PGPPublicKey#getKeyID() key ID} + */ + public boolean isCreatedBeforeRevocation(PGPSignature signature, PGPPublicKey key) { + if (signature.getKeyID() != key.getKeyID()) { + throw new IllegalArgumentException("The signature's key ID must be the same as the key's key ID"); //$NON-NLS-1$ + } + Date verifiedRevocationDate = getVerifiedRevocationDate(key); + if (verifiedRevocationDate != null) { + long signatureCreationTime = signature.getCreationTime().getTime(); + if (signatureCreationTime >= verifiedRevocationDate.getTime()) { + return false; + } + } + return true; + } + + /** + * Returns the hexadecimal representation of the key's + * {@link PGPPublicKey#getFingerprint() fingerprint}. + * + * @param key the key for which to get the hexadecimal fingerprint. + * + * @return the hexadecimal representation of the key's fingerprint. + */ + public static String toHexFingerprint(PGPPublicKey key) { + return Hex.toHexString(key.getFingerprint()); + } + + /** + * Returns the hexadecimal representation of the given bytes. + * + * @param bytes the bytes to convert to a hexadecimal representation. + * @return the hexadecimal representation of the given bytes. + */ + public static String toHex(byte[] bytes) { + return Hex.toHexString(bytes); + } + + /** + * Returns the hexadecimal representation of the given long value, typically a + * key ID, padded with leading zeros to a length of 16. + * + * @param keyID the long value, typically a key ID, to convert to a hexadecimal + * representation. + * @return the hexadecimal representation of the given long value, padded with + * leading zeros. + */ + public static String toHex(long keyID) { + return String.format("%1$016x", keyID); //$NON-NLS-1$ + } + + /** + * If the signature's {@link PGPSignature#getCreationTime() creation time} is + * before the key's {@link PGPPublicKey#getCreationTime() creation time}, this + * returns a negative value equal to the number of milliseconds that the + * signature was created before the key was created. If the key has a + * {@link PGPPublicKey#getValidSeconds() validity period}, i.e., if the key + * expires, and the signature's creation time is after the key's expiration, + * returns a positive value equal to the number of milliseconds that the + * signature was created after the key's expiration. Otherwise, the signature + * was created during the period of time that the key was valid and this returns + * <code>0</code>. + * + * @param signature the signature to test. + * @param key the corresponding key of this signature against which to + * test. + * @return a negative value representing the number of milliseconds the + * signature was created before the key was created, a positive value + * representing the number of milliseconds the signature as created + * after the key expired, or <code>0</code> if the signature was created + * during the valid period of the key. + * @throws IllegalArgumentException if the signature's + * {@link PGPSignature#getKeyID() key} is not + * the same as the key's + * {@link PGPPublicKey#getKeyID() key ID} + */ + public static long compareSignatureTimeToKeyValidityTime(PGPSignature signature, PGPPublicKey key) { + if (signature.getKeyID() != key.getKeyID()) { + throw new IllegalArgumentException("The signature's key ID must be the same as the key's key ID"); //$NON-NLS-1$ + } + long keyCreationTime = key.getCreationTime().getTime(); + long signatureCreationTime = signature.getCreationTime().getTime(); + long delta = signatureCreationTime - keyCreationTime; + if (delta < 0) { + return delta; + } + long validSeconds = key.getValidSeconds(); + if (validSeconds != 0) { + delta = signatureCreationTime - (keyCreationTime + validSeconds * 1000); + if (delta > 0) { + return delta; + } + } + return 0; + } +} |