| /******************************************************************************* |
| * 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; |
| } |
| } |