View Javadoc
1   /*
2    * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.gpg.bc.internal.keys;
11  
12  import static org.junit.Assert.assertEquals;
13  import static org.junit.Assert.assertFalse;
14  import static org.junit.Assert.assertNotNull;
15  import static org.junit.Assert.assertTrue;
16  
17  import java.io.BufferedInputStream;
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.security.Security;
21  import java.util.Iterator;
22  
23  import javax.crypto.Cipher;
24  
25  import org.bouncycastle.jce.provider.BouncyCastleProvider;
26  import org.bouncycastle.openpgp.PGPException;
27  import org.bouncycastle.openpgp.PGPPublicKey;
28  import org.bouncycastle.openpgp.PGPPublicKeyRing;
29  import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
30  import org.bouncycastle.openpgp.PGPSecretKey;
31  import org.bouncycastle.openpgp.PGPUtil;
32  import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
33  import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
34  import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
35  import org.junit.BeforeClass;
36  import org.junit.Test;
37  import org.junit.runner.RunWith;
38  import org.junit.runners.Parameterized;
39  import org.junit.runners.Parameterized.Parameter;
40  import org.junit.runners.Parameterized.Parameters;
41  
42  @RunWith(Parameterized.class)
43  public class SecretKeysTest {
44  
45  	@BeforeClass
46  	public static void ensureBC() {
47  		if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
48  			Security.addProvider(new BouncyCastleProvider());
49  		}
50  	}
51  
52  	private static volatile Boolean haveOCB;
53  
54  	private static boolean ocbAvailable() {
55  		Boolean haveIt = haveOCB;
56  		if (haveIt != null) {
57  			return haveIt.booleanValue();
58  		}
59  		try {
60  			Cipher c = Cipher.getInstance("AES/OCB/NoPadding"); //$NON-NLS-1$
61  			if (c == null) {
62  				haveOCB = Boolean.FALSE;
63  				return false;
64  			}
65  		} catch (NoClassDefFoundError | Exception e) {
66  			haveOCB = Boolean.FALSE;
67  			return false;
68  		}
69  		haveOCB = Boolean.TRUE;
70  		return true;
71  	}
72  
73  	private static class TestData {
74  
75  		final String name;
76  
77  		final boolean encrypted;
78  
79  		final boolean keyValue;
80  
81  		TestData(String name, boolean encrypted, boolean keyValue) {
82  			this.name = name;
83  			this.encrypted = encrypted;
84  			this.keyValue = keyValue;
85  		}
86  
87  		@Override
88  		public String toString() {
89  			return name;
90  		}
91  	}
92  
93  	@Parameters(name = "{0}")
94  	public static TestData[] initTestData() {
95  		return new TestData[] {
96  				new TestData("AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11", false, false),
97  				new TestData("2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A", false, true),
98  				new TestData("66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9", true, true),
99  				new TestData("F727FAB884DA3BD402B6E0F5472E108D21033124", true, true),
100 				new TestData("faked", false, true) };
101 	}
102 
103 	private static byte[] readTestKey(String filename) throws Exception {
104 		try (InputStream in = new BufferedInputStream(
105 				SecretKeysTest.class.getResourceAsStream(filename))) {
106 			return SecretKeys.keyFromNameValueFormat(in);
107 		}
108 	}
109 
110 	private static PGPPublicKey readAsc(InputStream in)
111 			throws IOException, PGPException {
112 		PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
113 			PGPUtil.getDecoderStream(in), new JcaKeyFingerprintCalculator());
114 
115 		Iterator<PGPPublicKeyRing> keyRings = pgpPub.getKeyRings();
116 		while (keyRings.hasNext()) {
117 			PGPPublicKeyRing keyRing = keyRings.next();
118 
119 			Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
120 			if (keys.hasNext()) {
121 				return keys.next();
122 			}
123 		}
124 		return null;
125 	}
126 
127 	// Injected by JUnit
128 	@Parameter
129 	public TestData data;
130 
131 	@Test
132 	public void testKeyRead() throws Exception {
133 		if (data.keyValue) {
134 			byte[] bytes = readTestKey(data.name + ".key");
135 			assertEquals('(', bytes[0]);
136 			assertEquals(')', bytes[bytes.length - 1]);
137 		}
138 		try (InputStream pubIn = this.getClass()
139 				.getResourceAsStream(data.name + ".asc")) {
140 			if (pubIn != null) {
141 				PGPPublicKey publicKey = readAsc(pubIn);
142 				// Do a full test trying to load the secret key.
143 				PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
144 						.build();
145 				try (InputStream in = new BufferedInputStream(this.getClass()
146 						.getResourceAsStream(data.name + ".key"))) {
147 					PGPSecretKey secretKey = SecretKeys.readSecretKey(in,
148 							calculatorProvider,
149 							data.encrypted ? () -> "nonsense".toCharArray()
150 									: null,
151 							publicKey);
152 					assertNotNull(secretKey);
153 				} catch (PGPException e) {
154 					// Currently we may not be able to load OCB-encrypted keys.
155 					assertTrue(e.getMessage().contains("OCB"));
156 					assertTrue(data.encrypted);
157 					assertFalse(ocbAvailable());
158 				}
159 			}
160 		}
161 	}
162 
163 }