diff options
author | Ed Merks | 2022-02-12 16:00:13 +0000 |
---|---|---|
committer | Ed Merks | 2022-02-13 05:41:41 +0000 |
commit | f149fd1be884ae15e951f9e447f0ce5b89315c20 (patch) | |
tree | 590fcf672c6acdc9794df34e050bcb2150d82eb4 | |
parent | ebb22636c086e0cc23c1263303840ac5fc17a54b (diff) | |
download | rt.equinox.p2-f149fd1be884ae15e951f9e447f0ce5b89315c20.tar.gz rt.equinox.p2-f149fd1be884ae15e951f9e447f0ce5b89315c20.tar.xz rt.equinox.p2-f149fd1be884ae15e951f9e447f0ce5b89315c20.zip |
Bug 578166 - Signing information apparent in the Plug-ins tab of the
About dialog
Introduce org.eclipse.equinox.internal.p2.ui.KeySigningInfoFactory to
provide the adapter for the AboutBundleData.ExtendedSigningInfo
interface. The signature and key information is gathered from the
artifact metadata of the bundle pool repository of the installation.
Change-Id: I578b62cb0a20d4141d38c2b587e39ae8bc0dfd4d
Signed-off-by: Ed Merks <ed.merks@gmail.com>
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.p2/+/190744
9 files changed, 211 insertions, 4 deletions
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java index c5f9d0196..383fd74b8 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPPublicKeyStore.java @@ -25,7 +25,7 @@ import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; public class PGPPublicKeyStore { - private Map<String, PGPPublicKey> keys = new HashMap<>(); + private Map<String, PGPPublicKey> keys = new LinkedHashMap<>(); public PGPPublicKey addKey(PGPPublicKey key) { if (key == null) { diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java index 6fc1dc597..740e06c2b 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java @@ -88,6 +88,13 @@ public final class PGPSignatureVerifier extends ProcessingStep { return res; } + public static PGPPublicKeyStore getKeys(IArtifactDescriptor artifact) { + PGPPublicKeyStore keyStore = new PGPPublicKeyStore(); + String keyText = artifact.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME); + PGPPublicKeyStore.readPublicKeys(keyText).stream().forEach(keyStore::addKey); + return keyStore; + } + @Override public void initialize(IProvisioningAgent agent, IProcessingStepDescriptor descriptor, IArtifactDescriptor context) { diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF index 76a2520ae..290732d27 100644 --- a/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.extensionlocation/META-INF/MANIFEST.MF @@ -2,11 +2,11 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.equinox.p2.extensionlocation;singleton:=true -Bundle-Version: 1.4.0.qualifier +Bundle-Version: 1.4.100.qualifier Bundle-Activator: org.eclipse.equinox.internal.p2.extensionlocation.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin -Export-Package: org.eclipse.equinox.internal.p2.extensionlocation;x-friends:="org.eclipse.equinox.p2.reconciler.dropins,org.eclipse.equinox.p2.ui.importexport" +Export-Package: org.eclipse.equinox.internal.p2.extensionlocation;x-friends:="org.eclipse.equinox.p2.reconciler.dropins,org.eclipse.equinox.p2.ui,org.eclipse.equinox.p2.ui.importexport" Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)", org.eclipse.equinox.p2.metadata Bundle-RequiredExecutionEnvironment: JavaSE-11 diff --git a/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml b/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml index 474ce108b..d6352193a 100644 --- a/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml +++ b/bundles/org.eclipse.equinox.p2.extensionlocation/pom.xml @@ -9,6 +9,6 @@ </parent> <groupId>org.eclipse.equinox</groupId> <artifactId>org.eclipse.equinox.p2.extensionlocation</artifactId> - <version>1.4.0-SNAPSHOT</version> + <version>1.4.100-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF index f4c597204..b66eb2ce6 100644 --- a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF @@ -43,6 +43,7 @@ Import-Package: javax.xml.parsers, org.eclipse.equinox.internal.p2.artifact.repository, org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.internal.p2.director, + org.eclipse.equinox.internal.p2.extensionlocation, org.eclipse.equinox.internal.p2.metadata, org.eclipse.equinox.internal.p2.metadata.repository, org.eclipse.equinox.internal.p2.operations, diff --git a/bundles/org.eclipse.equinox.p2.ui/plugin.xml b/bundles/org.eclipse.equinox.p2.ui/plugin.xml index 6f6031e2e..87e8645fd 100644 --- a/bundles/org.eclipse.equinox.p2.ui/plugin.xml +++ b/bundles/org.eclipse.equinox.p2.ui/plugin.xml @@ -12,6 +12,13 @@ <adapter type="org.eclipse.equinox.p2.repository.metadata.IMetadataRepository"/> <adapter type="org.eclipse.equinox.p2.repository.artifact.IArtifactRepository"/> </factory> + <factory + adaptableType="org.eclipse.ui.internal.about.AboutPluginsPage" + class="org.eclipse.equinox.internal.p2.ui.KeySigningInfoFactory"> + <adapter + type="org.eclipse.ui.internal.about.AboutBundleData$ExtendedSigningInfo"> + </adapter> + </factory> </extension> <extension diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java new file mode 100644 index 000000000..782dff926 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/KeySigningInfoFactory.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * 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.internal.p2.ui; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import org.bouncycastle.openpgp.*; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPPublicKeyStore; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.repository.artifact.*; +import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; +import org.eclipse.ui.internal.about.AboutBundleData; +import org.eclipse.ui.internal.about.AboutPluginsPage; +import org.osgi.framework.Bundle; + +/** + * A factory used by the {@link AboutPluginsPage} to provide extended signing + * information, in particular information about PGP signing. + * + * @since 2.7.400 + */ +public class KeySigningInfoFactory implements IAdapterFactory { + + private static final Class<?>[] CLASSES = new Class[] { AboutBundleData.ExtendedSigningInfo.class }; + + @Override + public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) { + if (adapterType == AboutBundleData.ExtendedSigningInfo.class) { + return adapterType.cast(new AboutBundleData.ExtendedSigningInfo() { + private final Map<File, Map<PGPSignature, PGPPublicKey>> bundlePoolArtficactSigningDetails = getBundlePoolArtficactPGPSigningDetails(); + + @Override + public boolean isSigned(Bundle bundle) { + return getDetails(bundle) != null; + } + + @Override + public String getSigningType(Bundle bundle) { + return ProvUIMessages.KeySigningInfoFactory_PGPSigningType; + } + + @Override + public String getSigningDetails(Bundle bundle) { + Map<PGPSignature, PGPPublicKey> details = getDetails(bundle); + if (details != null) { + PGPPublicKeyService keyService = getKeyService(); + List<String> lines = new ArrayList<>(); + for (PGPPublicKey key : details.values()) { + if (keyService != null) { + // Be sure to normalize/enhance the key so we properly don't show + // self-signatures. + key = keyService.addKey(key); + } + if (!lines.isEmpty()) { + lines.add(""); //$NON-NLS-1$ + } + addDetails(key, lines, ""); //$NON-NLS-1$ + if (keyService != null) { + Set<PGPPublicKey> verifiedCertifications = keyService.getVerifiedCertifications(key); + boolean first = true; + for (PGPPublicKey verifyingKey : verifiedCertifications) { + /// Don't show self-signatures. + if (!verifyingKey.equals(key)) { + if (first) { + lines.add(" " + ProvUIMessages.KeySigningInfoFactory_KeySignersSection); //$NON-NLS-1$ + first = false; + } + addDetails(verifyingKey, lines, " "); //$NON-NLS-1$ + } + } + } + } + return String.join("\n", lines); //$NON-NLS-1$ + } + return null; + } + + @Override + public Date getSigningTime(Bundle bundle) { + Map<PGPSignature, PGPPublicKey> details = getDetails(bundle); + return details == null ? null : details.keySet().iterator().next().getCreationTime(); + } + + private void addDetails(PGPPublicKey key, List<String> lines, String indentation) { + lines.add(indentation + ProvUIMessages.KeySigningInfoFactory_FingerprintItem + + PGPPublicKeyService.toHex(key.getFingerprint()).toUpperCase(Locale.ROOT)); + for (Iterator<String> userIDs = key.getUserIDs(); userIDs.hasNext();) { + lines.add(indentation + ProvUIMessages.KeySigningInfoFactory_UserIDItem + userIDs.next()); + } + } + + private PGPPublicKeyService getKeyService() { + IProvisioningAgent agent = org.eclipse.equinox.internal.p2.extensionlocation.Activator + .getCurrentAgent(); + return agent == null ? null : agent.getService(PGPPublicKeyService.class); + } + + private Map<PGPSignature, PGPPublicKey> getDetails(Bundle bundle) { + try { + File bundleFile = FileLocator.getBundleFile(bundle); + Map<PGPSignature, PGPPublicKey> details = bundlePoolArtficactSigningDetails.get(bundleFile); + return details; + } catch (IOException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + return null; + } + } + }); + } + + return null; + } + + @Override + public Class<?>[] getAdapterList() { + return CLASSES; + } + + /** + * Returns a map from artifact files to the PGP signature/key pairs that were + * used to {@link PGPSignatureVerifier#close() verify} the artifact while it was + * being downloaded. + * + * @return a map of all the PGP signed artifact files to their signature/key + * pairs. + */ + private static Map<File, Map<PGPSignature, PGPPublicKey>> getBundlePoolArtficactPGPSigningDetails() { + Map<File, Map<PGPSignature, PGPPublicKey>> result = new LinkedHashMap<>(); + try { + // Look up artifact metadata for all the bundle pool repository of the + // installation. + IFileArtifactRepository bundlePoolRepository = org.eclipse.equinox.internal.p2.extensionlocation.Activator + .getBundlePoolRepository(); + IQueryResult<IArtifactKey> allArtifactKeys = bundlePoolRepository.query(ArtifactKeyQuery.ALL_KEYS, + new NullProgressMonitor()); + for (IArtifactKey key : allArtifactKeys) { + for (IArtifactDescriptor descriptor : bundlePoolRepository.getArtifactDescriptors(key)) { + File file = bundlePoolRepository.getArtifactFile(descriptor); + if (file != null) { + try { + Collection<PGPSignature> signatures = PGPSignatureVerifier.getSignatures(descriptor); + if (!signatures.isEmpty()) { + Map<PGPSignature, PGPPublicKey> details = new LinkedHashMap<>(); + PGPPublicKeyStore keys = PGPSignatureVerifier.getKeys(descriptor); + for (PGPSignature signature : signatures) { + Collection<PGPPublicKey> signingKeys = keys.getKeys(signature.getKeyID()); + if (!signingKeys.isEmpty()) { + // There is vanishingly small chance that two keys with colliding key IDs were + // used on two different signatures on the same artifact. + details.put(signature, signingKeys.iterator().next()); + } + } + if (!details.isEmpty()) { + result.put(file, details); + } + } + } catch (IOException | PGPException | RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + } + } + } + } + } catch (RuntimeException e) { + ProvUIActivator.getDefault().getLog() + .log(new Status(IStatus.ERROR, ProvUIActivator.PLUGIN_ID, e.getMessage(), e)); + } + return result; + } +} diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java index af37549e2..e3804b987 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java @@ -247,6 +247,10 @@ public class ProvUIMessages extends NLS { public static String SelectableIUsPage_Deselect_All; public static String InstallRemediationPage_Title; public static String InstallRemediationPage_Description; + public static String KeySigningInfoFactory_FingerprintItem; + public static String KeySigningInfoFactory_KeySignersSection; + public static String KeySigningInfoFactory_PGPSigningType; + public static String KeySigningInfoFactory_UserIDItem; public static String UpdateRemediationPage_Title; public static String UpdateRemediationPage_Description; public static String RemediationPage_SubDescription; diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties index c9fdafb75..26fc36307 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties @@ -257,6 +257,10 @@ RepositoryElement_NotFound=This repository is currently not available. RepositoryTracker_DuplicateLocation=Duplicate location MetadataRepositoryElement_RepositoryLoadError=Error loading repository {0} IUViewQueryContext_NoCategorizedItemsDescription=You can uncheck the 'Group items by category' check box to see items without categories. +KeySigningInfoFactory_FingerprintItem=Fingerprint= +KeySigningInfoFactory_KeySignersSection=Key Signers: +KeySigningInfoFactory_PGPSigningType=PGP Public Key +KeySigningInfoFactory_UserIDItem=UserID= QueriedElementWrapper_NoCategorizedItemsExplanation=There are no categorized items QueriedElementWrapper_NoItemsExplanation=There are no items available QueriedElementWrapper_SiteNotFound=Could not find {0} |