| /******************************************************************************* |
| * Copyright (c) 2021 Red Hat Inc. 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.tests.artifact.processors; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URISyntaxException; |
| import java.nio.file.Files; |
| import java.util.Set; |
| import org.eclipse.core.runtime.FileLocator; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; |
| import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; |
| import org.eclipse.equinox.internal.provisional.p2.repository.DefaultPGPPublicKeyService; |
| import org.eclipse.equinox.p2.core.ProvisionException; |
| import org.eclipse.equinox.p2.metadata.Version; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; |
| import org.eclipse.equinox.p2.repository.artifact.IProcessingStepDescriptor; |
| import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; |
| import org.eclipse.equinox.p2.repository.artifact.spi.ProcessingStepDescriptor; |
| import org.eclipse.equinox.p2.repository.spi.PGPPublicKeyService; |
| import org.eclipse.equinox.p2.tests.TestAgentProvider; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| |
| public class PGPSignatureVerifierTest { |
| |
| @Rule |
| public TestAgentProvider agentProvider = new TestAgentProvider(); |
| |
| @Before |
| public void initialize() { |
| try { |
| PGPPublicKeyService keyService = agentProvider.getService(PGPPublicKeyService.class); |
| if (keyService instanceof DefaultPGPPublicKeyService) { |
| DefaultPGPPublicKeyService defaultPGPPublicKeyService = (DefaultPGPPublicKeyService) keyService; |
| defaultPGPPublicKeyService.setKeyServers(Set.of()); |
| defaultPGPPublicKeyService.setGPG(false); |
| } |
| } catch (ProvisionException e) { |
| //$FALL-THROUGH$ |
| } |
| } |
| |
| // @formatter:off |
| /* |
| * About test keys: * Install the public&private keys locally * then generate |
| * signatures with eg `gpg -u signer2@fakeuser.eclipse.org -a --output |
| * signed_by_signer_2 --detach-sig testArtifact` |
| */ |
| // @formatter:on |
| |
| private IArtifactDescriptor createArtifact(String signaturesResourcePath, String publicKeyResourcePath) |
| throws IOException, URISyntaxException { |
| ArtifactDescriptor res = new ArtifactDescriptor( |
| new ArtifactKey("whatever", "whatever", Version.parseVersion("1.0.0"))); |
| res.setProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME, read(signaturesResourcePath)); |
| res.setProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME, read(publicKeyResourcePath)); |
| return res; |
| } |
| |
| private static class ArtifactOutputStream extends ByteArrayOutputStream implements IAdaptable { |
| IArtifactDescriptor descriptor = new ArtifactDescriptor( |
| new ArtifactKey("whatever", "whatever", Version.parseVersion("1.0.0"))); |
| |
| public IArtifactDescriptor getDescriptor() { |
| return descriptor; |
| } |
| |
| @Override |
| public <T> T getAdapter(Class<T> adapter) { |
| if (adapter.isInstance(descriptor)) { |
| return adapter.cast(descriptor); |
| } |
| return null; |
| } |
| } |
| |
| private String read(String resource) throws IOException, URISyntaxException { |
| return Files.readString(new File(FileLocator.toFileURL(getClass().getResource(resource)).toURI()).toPath()); |
| } |
| |
| @Test |
| public void testOK() throws Exception { |
| IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); |
| IArtifactDescriptor artifact = createArtifact("signed_by_signer_1", "public_signer1.pgp"); |
| @SuppressWarnings("resource") |
| PGPSignatureVerifier verifier = new PGPSignatureVerifier(); |
| verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); |
| ArtifactOutputStream artifactOutputStream = new ArtifactOutputStream(); |
| verifier.link(artifactOutputStream, new NullProgressMonitor()); |
| Assert.assertTrue(verifier.getStatus().toString(), verifier.getStatus().isOK()); |
| try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { |
| bytes.transferTo(verifier); |
| } |
| Assert.assertTrue(verifier.getStatus().isOK()); |
| verifier.close(); |
| Assert.assertTrue(verifier.getStatus().isOK()); |
| |
| IArtifactDescriptor descriptor = artifactOutputStream.getDescriptor(); |
| Assert.assertNotNull("Signatures should be present", |
| descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); |
| Assert.assertNotNull("Keys should be present", |
| descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); |
| } |
| |
| @Test |
| public void testNoPublicKeyFound() throws Exception { |
| IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); |
| IArtifactDescriptor artifact = createArtifact("signed_by_signer_1", "public_signer2.pgp"); |
| try (PGPSignatureVerifier verifier = new PGPSignatureVerifier()) { |
| verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); |
| ArtifactOutputStream artifactOutputStream = new ArtifactOutputStream(); |
| verifier.link(artifactOutputStream, new NullProgressMonitor()); |
| Assert.assertTrue(verifier.getStatus().toString(), verifier.getStatus().isOK()); |
| try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { |
| bytes.transferTo(verifier); |
| } |
| Assert.assertTrue(verifier.getStatus().isOK()); |
| verifier.close(); |
| Assert.assertTrue(verifier.getStatus().isOK()); |
| |
| IArtifactDescriptor descriptor = artifactOutputStream.getDescriptor(); |
| Assert.assertNull("No signatures should be present", |
| descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNATURES_PROPERTY_NAME)); |
| Assert.assertNull("No keys should be present", |
| descriptor.getProperty(PGPSignatureVerifier.PGP_SIGNER_KEYS_PROPERTY_NAME)); |
| } |
| } |
| |
| @Test |
| public void testTamperedSignature() throws Exception { |
| IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); |
| IArtifactDescriptor artifact = createArtifact("signed_by_signer_1_tampered", "public_signer1.pgp"); |
| try (PGPSignatureVerifier verifier = new PGPSignatureVerifier()) { |
| verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); |
| // signature has random modification, making it invalid by itself |
| Assert.assertFalse(verifier.getStatus().isOK()); |
| } |
| } |
| |
| @Test |
| public void testSignatureForAnotherArtifact() throws Exception { |
| IProcessingStepDescriptor processingStepDescriptor = new ProcessingStepDescriptor(null, null, false); |
| IArtifactDescriptor artifact = createArtifact("signed_by_signer_1_otherArtifact", "public_signer1.pgp"); |
| @SuppressWarnings("resource") |
| PGPSignatureVerifier verifier = new PGPSignatureVerifier(); |
| verifier.initialize(agentProvider.getAgent(), processingStepDescriptor, artifact); |
| Assert.assertTrue(verifier.getStatus().isOK()); |
| try (InputStream bytes = getClass().getResourceAsStream("testArtifact")) { |
| bytes.transferTo(verifier); |
| } |
| Assert.assertTrue(verifier.getStatus().isOK()); |
| verifier.close(); |
| IStatus status = verifier.getStatus(); |
| assertEquals(IStatus.ERROR, status.getSeverity()); |
| assertTrue(status.getMessage().matches(".*signature.*invalid.*")); |
| } |
| } |