xref: /JGit/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java (revision bdc48aeac756cc0471618b06d793083e63109ee0)
1 /*
2  * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
3  * <p>
4  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5  * and associated documentation files (the "Software"), to deal in the Software without restriction,
6  *including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7  * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8  * subject to the following conditions:
9  * </p>
10  * <p>
11  * The above copyright notice and this permission notice shall be included in all copies or substantial
12  * portions of the Software.
13  * </p>
14  * <p>
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17  * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  * </p>
22  */
23 package org.eclipse.jgit.gpg.bc.internal.keys;
24 
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.math.BigInteger;
31 import java.util.Date;
32 
33 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
34 import org.bouncycastle.bcpg.DSAPublicBCPGKey;
35 import org.bouncycastle.bcpg.DSASecretBCPGKey;
36 import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
37 import org.bouncycastle.bcpg.ECPublicBCPGKey;
38 import org.bouncycastle.bcpg.ECSecretBCPGKey;
39 import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
40 import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
41 import org.bouncycastle.bcpg.HashAlgorithmTags;
42 import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
43 import org.bouncycastle.bcpg.PublicKeyPacket;
44 import org.bouncycastle.bcpg.RSAPublicBCPGKey;
45 import org.bouncycastle.bcpg.RSASecretBCPGKey;
46 import org.bouncycastle.bcpg.S2K;
47 import org.bouncycastle.bcpg.SecretKeyPacket;
48 import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
49 import org.bouncycastle.openpgp.PGPException;
50 import org.bouncycastle.openpgp.PGPPublicKey;
51 import org.bouncycastle.openpgp.PGPSecretKey;
52 import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
53 import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
54 import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
55 import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
56 import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
57 import org.bouncycastle.util.Arrays;
58 import org.bouncycastle.util.Strings;
59 
60 /**
61  * A parser for secret keys stored in s-expressions. Original BouncyCastle code
62  * modified by the JGit team to:
63  * <ul>
64  * <li>handle unencrypted DSA, EC, and ElGamal keys (upstream only handles
65  * unencrypted RSA), and</li>
66  * <li>handle secret keys using AES/OCB as encryption (those don't have a
67  * hash).</li>
68  * </ul>
69  */
70 @SuppressWarnings("nls")
71 public class SExprParser {
72 	private final PGPDigestCalculatorProvider digestProvider;
73 
74 	/**
75 	 * Base constructor.
76 	 *
77 	 * @param digestProvider
78 	 *            a provider for digest calculations. Used to confirm key
79 	 *            protection hashes.
80 	 */
SExprParser(PGPDigestCalculatorProvider digestProvider)81 	public SExprParser(PGPDigestCalculatorProvider digestProvider) {
82 		this.digestProvider = digestProvider;
83 	}
84 
85 	/**
86 	 * Parse a secret key from one of the GPG S expression keys associating it
87 	 * with the passed in public key.
88 	 *
89 	 * @param inputStream
90 	 *            to read from
91 	 * @param keyProtectionRemoverFactory
92 	 *            for decrypting encrypted keys
93 	 * @param pubKey
94 	 *            the private key should belong to
95 	 *
96 	 * @return a secret key object.
97 	 * @throws IOException
98 	 * @throws PGPException
99 	 */
parseSecretKey(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, PGPPublicKey pubKey)100 	public PGPSecretKey parseSecretKey(InputStream inputStream,
101 			PBEProtectionRemoverFactory keyProtectionRemoverFactory,
102 			PGPPublicKey pubKey) throws IOException, PGPException {
103 		SXprUtils.skipOpenParenthesis(inputStream);
104 
105 		String type;
106 
107 		type = SXprUtils.readString(inputStream, inputStream.read());
108 		if (type.equals("protected-private-key")
109 				|| type.equals("private-key")) {
110 			SXprUtils.skipOpenParenthesis(inputStream);
111 
112 			String keyType = SXprUtils.readString(inputStream,
113 					inputStream.read());
114 			if (keyType.equals("ecc")) {
115 				SXprUtils.skipOpenParenthesis(inputStream);
116 
117 				String curveID = SXprUtils.readString(inputStream,
118 						inputStream.read());
119 				String curveName = SXprUtils.readString(inputStream,
120 						inputStream.read());
121 
122 				SXprUtils.skipCloseParenthesis(inputStream);
123 
124 				byte[] qVal;
125 
126 				SXprUtils.skipOpenParenthesis(inputStream);
127 
128 				type = SXprUtils.readString(inputStream, inputStream.read());
129 				if (type.equals("q")) {
130 					qVal = SXprUtils.readBytes(inputStream, inputStream.read());
131 				} else {
132 					throw new PGPException("no q value found");
133 				}
134 
135 				SXprUtils.skipCloseParenthesis(inputStream);
136 
137 				BigInteger d = processECSecretKey(inputStream, curveID,
138 						curveName, qVal, keyProtectionRemoverFactory);
139 
140 				if (curveName.startsWith("NIST ")) {
141 					curveName = curveName.substring("NIST ".length());
142 				}
143 
144 				ECPublicBCPGKey basePubKey = new ECDSAPublicBCPGKey(
145 						ECNamedCurveTable.getOID(curveName),
146 						new BigInteger(1, qVal));
147 				ECPublicBCPGKey assocPubKey = (ECPublicBCPGKey) pubKey
148 						.getPublicKeyPacket().getKey();
149 				if (!basePubKey.getCurveOID().equals(assocPubKey.getCurveOID())
150 						|| !basePubKey.getEncodedPoint()
151 								.equals(assocPubKey.getEncodedPoint())) {
152 					throw new PGPException(
153 							"passed in public key does not match secret key");
154 				}
155 
156 				return new PGPSecretKey(
157 						new SecretKeyPacket(pubKey.getPublicKeyPacket(),
158 								SymmetricKeyAlgorithmTags.NULL, null, null,
159 								new ECSecretBCPGKey(d).getEncoded()),
160 						pubKey);
161 			} else if (keyType.equals("dsa")) {
162 				BigInteger p = readBigInteger("p", inputStream);
163 				BigInteger q = readBigInteger("q", inputStream);
164 				BigInteger g = readBigInteger("g", inputStream);
165 
166 				BigInteger y = readBigInteger("y", inputStream);
167 
168 				BigInteger x = processDSASecretKey(inputStream, p, q, g, y,
169 						keyProtectionRemoverFactory);
170 
171 				DSAPublicBCPGKey basePubKey = new DSAPublicBCPGKey(p, q, g, y);
172 				DSAPublicBCPGKey assocPubKey = (DSAPublicBCPGKey) pubKey
173 						.getPublicKeyPacket().getKey();
174 				if (!basePubKey.getP().equals(assocPubKey.getP())
175 						|| !basePubKey.getQ().equals(assocPubKey.getQ())
176 						|| !basePubKey.getG().equals(assocPubKey.getG())
177 						|| !basePubKey.getY().equals(assocPubKey.getY())) {
178 					throw new PGPException(
179 							"passed in public key does not match secret key");
180 				}
181 				return new PGPSecretKey(
182 						new SecretKeyPacket(pubKey.getPublicKeyPacket(),
183 								SymmetricKeyAlgorithmTags.NULL, null, null,
184 								new DSASecretBCPGKey(x).getEncoded()),
185 						pubKey);
186 			} else if (keyType.equals("elg")) {
187 				BigInteger p = readBigInteger("p", inputStream);
188 				BigInteger g = readBigInteger("g", inputStream);
189 
190 				BigInteger y = readBigInteger("y", inputStream);
191 
192 				BigInteger x = processElGamalSecretKey(inputStream, p, g, y,
193 						keyProtectionRemoverFactory);
194 
195 				ElGamalPublicBCPGKey basePubKey = new ElGamalPublicBCPGKey(p, g,
196 						y);
197 				ElGamalPublicBCPGKey assocPubKey = (ElGamalPublicBCPGKey) pubKey
198 						.getPublicKeyPacket().getKey();
199 				if (!basePubKey.getP().equals(assocPubKey.getP())
200 						|| !basePubKey.getG().equals(assocPubKey.getG())
201 						|| !basePubKey.getY().equals(assocPubKey.getY())) {
202 					throw new PGPException(
203 							"passed in public key does not match secret key");
204 				}
205 
206 				return new PGPSecretKey(
207 						new SecretKeyPacket(pubKey.getPublicKeyPacket(),
208 								SymmetricKeyAlgorithmTags.NULL, null, null,
209 								new ElGamalSecretBCPGKey(x).getEncoded()),
210 						pubKey);
211 			} else if (keyType.equals("rsa")) {
212 				BigInteger n = readBigInteger("n", inputStream);
213 				BigInteger e = readBigInteger("e", inputStream);
214 
215 				BigInteger[] values = processRSASecretKey(inputStream, n, e,
216 						keyProtectionRemoverFactory);
217 
218 				// TODO: type of RSA key?
219 				RSAPublicBCPGKey basePubKey = new RSAPublicBCPGKey(n, e);
220 				RSAPublicBCPGKey assocPubKey = (RSAPublicBCPGKey) pubKey
221 						.getPublicKeyPacket().getKey();
222 				if (!basePubKey.getModulus().equals(assocPubKey.getModulus())
223 						|| !basePubKey.getPublicExponent()
224 								.equals(assocPubKey.getPublicExponent())) {
225 					throw new PGPException(
226 							"passed in public key does not match secret key");
227 				}
228 
229 				return new PGPSecretKey(new SecretKeyPacket(
230 						pubKey.getPublicKeyPacket(),
231 						SymmetricKeyAlgorithmTags.NULL, null, null,
232 						new RSASecretBCPGKey(values[0], values[1], values[2])
233 								.getEncoded()),
234 						pubKey);
235 			} else {
236 				throw new PGPException("unknown key type: " + keyType);
237 			}
238 		}
239 
240 		throw new PGPException("unknown key type found");
241 	}
242 
243 	/**
244 	 * Parse a secret key from one of the GPG S expression keys.
245 	 *
246 	 * @param inputStream
247 	 *            to read from
248 	 * @param keyProtectionRemoverFactory
249 	 *            for decrypting encrypted keys
250 	 * @param fingerPrintCalculator
251 	 *            for calculating key fingerprints
252 	 *
253 	 * @return a secret key object.
254 	 * @throws IOException
255 	 * @throws PGPException
256 	 */
parseSecretKey(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, KeyFingerPrintCalculator fingerPrintCalculator)257 	public PGPSecretKey parseSecretKey(InputStream inputStream,
258 			PBEProtectionRemoverFactory keyProtectionRemoverFactory,
259 			KeyFingerPrintCalculator fingerPrintCalculator)
260 			throws IOException, PGPException {
261 		SXprUtils.skipOpenParenthesis(inputStream);
262 
263 		String type;
264 
265 		type = SXprUtils.readString(inputStream, inputStream.read());
266 		if (type.equals("protected-private-key")
267 				|| type.equals("private-key")) {
268 			SXprUtils.skipOpenParenthesis(inputStream);
269 
270 			String keyType = SXprUtils.readString(inputStream,
271 					inputStream.read());
272 			if (keyType.equals("ecc")) {
273 				SXprUtils.skipOpenParenthesis(inputStream);
274 
275 				String curveID = SXprUtils.readString(inputStream,
276 						inputStream.read());
277 				String curveName = SXprUtils.readString(inputStream,
278 						inputStream.read());
279 
280 				if (curveName.startsWith("NIST ")) {
281 					curveName = curveName.substring("NIST ".length());
282 				}
283 
284 				SXprUtils.skipCloseParenthesis(inputStream);
285 
286 				byte[] qVal;
287 
288 				SXprUtils.skipOpenParenthesis(inputStream);
289 
290 				type = SXprUtils.readString(inputStream, inputStream.read());
291 				if (type.equals("q")) {
292 					qVal = SXprUtils.readBytes(inputStream, inputStream.read());
293 				} else {
294 					throw new PGPException("no q value found");
295 				}
296 
297 				PublicKeyPacket pubPacket = new PublicKeyPacket(
298 						PublicKeyAlgorithmTags.ECDSA, new Date(),
299 						new ECDSAPublicBCPGKey(
300 								ECNamedCurveTable.getOID(curveName),
301 								new BigInteger(1, qVal)));
302 
303 				SXprUtils.skipCloseParenthesis(inputStream);
304 
305 				BigInteger d = processECSecretKey(inputStream, curveID,
306 						curveName, qVal, keyProtectionRemoverFactory);
307 
308 				return new PGPSecretKey(
309 						new SecretKeyPacket(pubPacket,
310 								SymmetricKeyAlgorithmTags.NULL, null, null,
311 								new ECSecretBCPGKey(d).getEncoded()),
312 						new PGPPublicKey(pubPacket, fingerPrintCalculator));
313 			} else if (keyType.equals("dsa")) {
314 				BigInteger p = readBigInteger("p", inputStream);
315 				BigInteger q = readBigInteger("q", inputStream);
316 				BigInteger g = readBigInteger("g", inputStream);
317 
318 				BigInteger y = readBigInteger("y", inputStream);
319 
320 				BigInteger x = processDSASecretKey(inputStream, p, q, g, y,
321 						keyProtectionRemoverFactory);
322 
323 				PublicKeyPacket pubPacket = new PublicKeyPacket(
324 						PublicKeyAlgorithmTags.DSA, new Date(),
325 						new DSAPublicBCPGKey(p, q, g, y));
326 
327 				return new PGPSecretKey(
328 						new SecretKeyPacket(pubPacket,
329 								SymmetricKeyAlgorithmTags.NULL, null, null,
330 								new DSASecretBCPGKey(x).getEncoded()),
331 						new PGPPublicKey(pubPacket, fingerPrintCalculator));
332 			} else if (keyType.equals("elg")) {
333 				BigInteger p = readBigInteger("p", inputStream);
334 				BigInteger g = readBigInteger("g", inputStream);
335 
336 				BigInteger y = readBigInteger("y", inputStream);
337 
338 				BigInteger x = processElGamalSecretKey(inputStream, p, g, y,
339 						keyProtectionRemoverFactory);
340 
341 				PublicKeyPacket pubPacket = new PublicKeyPacket(
342 						PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, new Date(),
343 						new ElGamalPublicBCPGKey(p, g, y));
344 
345 				return new PGPSecretKey(
346 						new SecretKeyPacket(pubPacket,
347 								SymmetricKeyAlgorithmTags.NULL, null, null,
348 								new ElGamalSecretBCPGKey(x).getEncoded()),
349 						new PGPPublicKey(pubPacket, fingerPrintCalculator));
350 			} else if (keyType.equals("rsa")) {
351 				BigInteger n = readBigInteger("n", inputStream);
352 				BigInteger e = readBigInteger("e", inputStream);
353 
354 				BigInteger[] values = processRSASecretKey(inputStream, n, e,
355 						keyProtectionRemoverFactory);
356 
357 				// TODO: type of RSA key?
358 				PublicKeyPacket pubPacket = new PublicKeyPacket(
359 						PublicKeyAlgorithmTags.RSA_GENERAL, new Date(),
360 						new RSAPublicBCPGKey(n, e));
361 
362 				return new PGPSecretKey(
363 						new SecretKeyPacket(pubPacket,
364 								SymmetricKeyAlgorithmTags.NULL, null, null,
365 								new RSASecretBCPGKey(values[0], values[1],
366 										values[2]).getEncoded()),
367 						new PGPPublicKey(pubPacket, fingerPrintCalculator));
368 			} else {
369 				throw new PGPException("unknown key type: " + keyType);
370 			}
371 		}
372 
373 		throw new PGPException("unknown key type found");
374 	}
375 
readBigInteger(String expectedType, InputStream inputStream)376 	private BigInteger readBigInteger(String expectedType,
377 			InputStream inputStream) throws IOException, PGPException {
378 		SXprUtils.skipOpenParenthesis(inputStream);
379 
380 		String type = SXprUtils.readString(inputStream, inputStream.read());
381 		if (!type.equals(expectedType)) {
382 			throw new PGPException(expectedType + " value expected");
383 		}
384 
385 		byte[] nBytes = SXprUtils.readBytes(inputStream, inputStream.read());
386 		BigInteger v = new BigInteger(1, nBytes);
387 
388 		SXprUtils.skipCloseParenthesis(inputStream);
389 
390 		return v;
391 	}
392 
extractData(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory)393 	private static byte[][] extractData(InputStream inputStream,
394 			PBEProtectionRemoverFactory keyProtectionRemoverFactory)
395 			throws PGPException, IOException {
396 		byte[] data;
397 		byte[] protectedAt = null;
398 
399 		SXprUtils.skipOpenParenthesis(inputStream);
400 
401 		String type = SXprUtils.readString(inputStream, inputStream.read());
402 		if (type.equals("protected")) {
403 			String protection = SXprUtils.readString(inputStream,
404 					inputStream.read());
405 
406 			SXprUtils.skipOpenParenthesis(inputStream);
407 
408 			S2K s2k = SXprUtils.parseS2K(inputStream);
409 
410 			byte[] iv = SXprUtils.readBytes(inputStream, inputStream.read());
411 
412 			SXprUtils.skipCloseParenthesis(inputStream);
413 
414 			byte[] secKeyData = SXprUtils.readBytes(inputStream,
415 					inputStream.read());
416 
417 			SXprUtils.skipCloseParenthesis(inputStream);
418 
419 			PBESecretKeyDecryptor keyDecryptor = keyProtectionRemoverFactory
420 					.createDecryptor(protection);
421 
422 			// TODO: recognise other algorithms
423 			byte[] key = keyDecryptor.makeKeyFromPassPhrase(
424 					SymmetricKeyAlgorithmTags.AES_128, s2k);
425 
426 			data = keyDecryptor.recoverKeyData(
427 					SymmetricKeyAlgorithmTags.AES_128, key, iv, secKeyData, 0,
428 					secKeyData.length);
429 
430 			// check if protected at is present
431 			if (inputStream.read() == '(') {
432 				ByteArrayOutputStream bOut = new ByteArrayOutputStream();
433 
434 				bOut.write('(');
435 				int ch;
436 				while ((ch = inputStream.read()) >= 0 && ch != ')') {
437 					bOut.write(ch);
438 				}
439 
440 				if (ch != ')') {
441 					throw new IOException("unexpected end to SExpr");
442 				}
443 
444 				bOut.write(')');
445 
446 				protectedAt = bOut.toByteArray();
447 			}
448 
449 			SXprUtils.skipCloseParenthesis(inputStream);
450 			SXprUtils.skipCloseParenthesis(inputStream);
451 		} else if (type.equals("d") || type.equals("x")) {
452 			// JGit modification: unencrypted DSA or ECC keys can have an "x"
453 			// here
454 			return null;
455 		} else {
456 			throw new PGPException("protected block not found");
457 		}
458 
459 		return new byte[][] { data, protectedAt };
460 	}
461 
processDSASecretKey(InputStream inputStream, BigInteger p, BigInteger q, BigInteger g, BigInteger y, PBEProtectionRemoverFactory keyProtectionRemoverFactory)462 	private BigInteger processDSASecretKey(InputStream inputStream,
463 			BigInteger p, BigInteger q, BigInteger g, BigInteger y,
464 			PBEProtectionRemoverFactory keyProtectionRemoverFactory)
465 			throws IOException, PGPException {
466 		String type;
467 		byte[][] basicData = extractData(inputStream,
468 				keyProtectionRemoverFactory);
469 
470 		// JGit modification: handle unencrypted DSA keys
471 		if (basicData == null) {
472 			byte[] nBytes = SXprUtils.readBytes(inputStream,
473 					inputStream.read());
474 			BigInteger x = new BigInteger(1, nBytes);
475 			SXprUtils.skipCloseParenthesis(inputStream);
476 			return x;
477 		}
478 
479 		byte[] keyData = basicData[0];
480 		byte[] protectedAt = basicData[1];
481 
482 		//
483 		// parse the secret key S-expr
484 		//
485 		InputStream keyIn = new ByteArrayInputStream(keyData);
486 
487 		SXprUtils.skipOpenParenthesis(keyIn);
488 		SXprUtils.skipOpenParenthesis(keyIn);
489 
490 		BigInteger x = readBigInteger("x", keyIn);
491 
492 		SXprUtils.skipCloseParenthesis(keyIn);
493 
494 		// JGit modification: OCB-encrypted keys don't have and don't need a
495 		// hash
496 		if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
497 			return x;
498 		}
499 
500 		SXprUtils.skipOpenParenthesis(keyIn);
501 		type = SXprUtils.readString(keyIn, keyIn.read());
502 
503 		if (!type.equals("hash")) {
504 			throw new PGPException("hash keyword expected");
505 		}
506 		type = SXprUtils.readString(keyIn, keyIn.read());
507 
508 		if (!type.equals("sha1")) {
509 			throw new PGPException("hash keyword expected");
510 		}
511 
512 		byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
513 
514 		SXprUtils.skipCloseParenthesis(keyIn);
515 
516 		if (digestProvider != null) {
517 			PGPDigestCalculator digestCalculator = digestProvider
518 					.get(HashAlgorithmTags.SHA1);
519 
520 			OutputStream dOut = digestCalculator.getOutputStream();
521 
522 			dOut.write(Strings.toByteArray("(3:dsa"));
523 			writeCanonical(dOut, "p", p);
524 			writeCanonical(dOut, "q", q);
525 			writeCanonical(dOut, "g", g);
526 			writeCanonical(dOut, "y", y);
527 			writeCanonical(dOut, "x", x);
528 
529 			// check protected-at
530 			if (protectedAt != null) {
531 				dOut.write(protectedAt);
532 			}
533 
534 			dOut.write(Strings.toByteArray(")"));
535 
536 			byte[] check = digestCalculator.getDigest();
537 			if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
538 				throw new PGPException(
539 						"checksum on protected data failed in SExpr");
540 			}
541 		}
542 
543 		return x;
544 	}
545 
processElGamalSecretKey(InputStream inputStream, BigInteger p, BigInteger g, BigInteger y, PBEProtectionRemoverFactory keyProtectionRemoverFactory)546 	private BigInteger processElGamalSecretKey(InputStream inputStream,
547 			BigInteger p, BigInteger g, BigInteger y,
548 			PBEProtectionRemoverFactory keyProtectionRemoverFactory)
549 			throws IOException, PGPException {
550 		String type;
551 		byte[][] basicData = extractData(inputStream,
552 				keyProtectionRemoverFactory);
553 
554 		// JGit modification: handle unencrypted EC keys
555 		if (basicData == null) {
556 			byte[] nBytes = SXprUtils.readBytes(inputStream,
557 					inputStream.read());
558 			BigInteger x = new BigInteger(1, nBytes);
559 			SXprUtils.skipCloseParenthesis(inputStream);
560 			return x;
561 		}
562 
563 		byte[] keyData = basicData[0];
564 		byte[] protectedAt = basicData[1];
565 
566 		//
567 		// parse the secret key S-expr
568 		//
569 		InputStream keyIn = new ByteArrayInputStream(keyData);
570 
571 		SXprUtils.skipOpenParenthesis(keyIn);
572 		SXprUtils.skipOpenParenthesis(keyIn);
573 
574 		BigInteger x = readBigInteger("x", keyIn);
575 
576 		SXprUtils.skipCloseParenthesis(keyIn);
577 
578 		// JGit modification: OCB-encrypted keys don't have and don't need a
579 		// hash
580 		if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
581 			return x;
582 		}
583 
584 		SXprUtils.skipOpenParenthesis(keyIn);
585 		type = SXprUtils.readString(keyIn, keyIn.read());
586 
587 		if (!type.equals("hash")) {
588 			throw new PGPException("hash keyword expected");
589 		}
590 		type = SXprUtils.readString(keyIn, keyIn.read());
591 
592 		if (!type.equals("sha1")) {
593 			throw new PGPException("hash keyword expected");
594 		}
595 
596 		byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
597 
598 		SXprUtils.skipCloseParenthesis(keyIn);
599 
600 		if (digestProvider != null) {
601 			PGPDigestCalculator digestCalculator = digestProvider
602 					.get(HashAlgorithmTags.SHA1);
603 
604 			OutputStream dOut = digestCalculator.getOutputStream();
605 
606 			dOut.write(Strings.toByteArray("(3:elg"));
607 			writeCanonical(dOut, "p", p);
608 			writeCanonical(dOut, "g", g);
609 			writeCanonical(dOut, "y", y);
610 			writeCanonical(dOut, "x", x);
611 
612 			// check protected-at
613 			if (protectedAt != null) {
614 				dOut.write(protectedAt);
615 			}
616 
617 			dOut.write(Strings.toByteArray(")"));
618 
619 			byte[] check = digestCalculator.getDigest();
620 			if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
621 				throw new PGPException(
622 						"checksum on protected data failed in SExpr");
623 			}
624 		}
625 
626 		return x;
627 	}
628 
processECSecretKey(InputStream inputStream, String curveID, String curveName, byte[] qVal, PBEProtectionRemoverFactory keyProtectionRemoverFactory)629 	private BigInteger processECSecretKey(InputStream inputStream,
630 			String curveID, String curveName, byte[] qVal,
631 			PBEProtectionRemoverFactory keyProtectionRemoverFactory)
632 			throws IOException, PGPException {
633 		String type;
634 
635 		byte[][] basicData = extractData(inputStream,
636 				keyProtectionRemoverFactory);
637 
638 		// JGit modification: handle unencrypted EC keys
639 		if (basicData == null) {
640 			byte[] nBytes = SXprUtils.readBytes(inputStream,
641 					inputStream.read());
642 			BigInteger d = new BigInteger(1, nBytes);
643 			SXprUtils.skipCloseParenthesis(inputStream);
644 			return d;
645 		}
646 
647 		byte[] keyData = basicData[0];
648 		byte[] protectedAt = basicData[1];
649 
650 		//
651 		// parse the secret key S-expr
652 		//
653 		InputStream keyIn = new ByteArrayInputStream(keyData);
654 
655 		SXprUtils.skipOpenParenthesis(keyIn);
656 		SXprUtils.skipOpenParenthesis(keyIn);
657 		BigInteger d = readBigInteger("d", keyIn);
658 		SXprUtils.skipCloseParenthesis(keyIn);
659 
660 		// JGit modification: OCB-encrypted keys don't have and don't need a
661 		// hash
662 		if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
663 			return d;
664 		}
665 
666 		SXprUtils.skipOpenParenthesis(keyIn);
667 
668 		type = SXprUtils.readString(keyIn, keyIn.read());
669 
670 		if (!type.equals("hash")) {
671 			throw new PGPException("hash keyword expected");
672 		}
673 		type = SXprUtils.readString(keyIn, keyIn.read());
674 
675 		if (!type.equals("sha1")) {
676 			throw new PGPException("hash keyword expected");
677 		}
678 
679 		byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
680 
681 		SXprUtils.skipCloseParenthesis(keyIn);
682 
683 		if (digestProvider != null) {
684 			PGPDigestCalculator digestCalculator = digestProvider
685 					.get(HashAlgorithmTags.SHA1);
686 
687 			OutputStream dOut = digestCalculator.getOutputStream();
688 
689 			dOut.write(Strings.toByteArray("(3:ecc"));
690 
691 			dOut.write(Strings.toByteArray("(" + curveID.length() + ":"
692 					+ curveID + curveName.length() + ":" + curveName + ")"));
693 
694 			writeCanonical(dOut, "q", qVal);
695 			writeCanonical(dOut, "d", d);
696 
697 			// check protected-at
698 			if (protectedAt != null) {
699 				dOut.write(protectedAt);
700 			}
701 
702 			dOut.write(Strings.toByteArray(")"));
703 
704 			byte[] check = digestCalculator.getDigest();
705 
706 			if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
707 				throw new PGPException(
708 						"checksum on protected data failed in SExpr");
709 			}
710 		}
711 
712 		return d;
713 	}
714 
processRSASecretKey(InputStream inputStream, BigInteger n, BigInteger e, PBEProtectionRemoverFactory keyProtectionRemoverFactory)715 	private BigInteger[] processRSASecretKey(InputStream inputStream,
716 			BigInteger n, BigInteger e,
717 			PBEProtectionRemoverFactory keyProtectionRemoverFactory)
718 			throws IOException, PGPException {
719 		String type;
720 		byte[][] basicData = extractData(inputStream,
721 				keyProtectionRemoverFactory);
722 
723 		byte[] keyData;
724 		byte[] protectedAt = null;
725 
726 		InputStream keyIn;
727 		BigInteger d;
728 
729 		if (basicData == null) {
730 			keyIn = inputStream;
731 			byte[] nBytes = SXprUtils.readBytes(inputStream,
732 					inputStream.read());
733 			d = new BigInteger(1, nBytes);
734 
735 			SXprUtils.skipCloseParenthesis(inputStream);
736 
737 		} else {
738 			keyData = basicData[0];
739 			protectedAt = basicData[1];
740 
741 			keyIn = new ByteArrayInputStream(keyData);
742 
743 			SXprUtils.skipOpenParenthesis(keyIn);
744 			SXprUtils.skipOpenParenthesis(keyIn);
745 			d = readBigInteger("d", keyIn);
746 		}
747 
748 		//
749 		// parse the secret key S-expr
750 		//
751 
752 		BigInteger p = readBigInteger("p", keyIn);
753 		BigInteger q = readBigInteger("q", keyIn);
754 		BigInteger u = readBigInteger("u", keyIn);
755 
756 		// JGit modification: OCB-encrypted keys don't have and don't need a
757 		// hash
758 		if (basicData == null
759 				|| keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
760 			return new BigInteger[] { d, p, q, u };
761 		}
762 
763 		SXprUtils.skipCloseParenthesis(keyIn);
764 
765 		SXprUtils.skipOpenParenthesis(keyIn);
766 		type = SXprUtils.readString(keyIn, keyIn.read());
767 
768 		if (!type.equals("hash")) {
769 			throw new PGPException("hash keyword expected");
770 		}
771 		type = SXprUtils.readString(keyIn, keyIn.read());
772 
773 		if (!type.equals("sha1")) {
774 			throw new PGPException("hash keyword expected");
775 		}
776 
777 		byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
778 
779 		SXprUtils.skipCloseParenthesis(keyIn);
780 
781 		if (digestProvider != null) {
782 			PGPDigestCalculator digestCalculator = digestProvider
783 					.get(HashAlgorithmTags.SHA1);
784 
785 			OutputStream dOut = digestCalculator.getOutputStream();
786 
787 			dOut.write(Strings.toByteArray("(3:rsa"));
788 
789 			writeCanonical(dOut, "n", n);
790 			writeCanonical(dOut, "e", e);
791 			writeCanonical(dOut, "d", d);
792 			writeCanonical(dOut, "p", p);
793 			writeCanonical(dOut, "q", q);
794 			writeCanonical(dOut, "u", u);
795 
796 			// check protected-at
797 			if (protectedAt != null) {
798 				dOut.write(protectedAt);
799 			}
800 
801 			dOut.write(Strings.toByteArray(")"));
802 
803 			byte[] check = digestCalculator.getDigest();
804 
805 			if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
806 				throw new PGPException(
807 						"checksum on protected data failed in SExpr");
808 			}
809 		}
810 
811 		return new BigInteger[] { d, p, q, u };
812 	}
813 
writeCanonical(OutputStream dOut, String label, BigInteger i)814 	private void writeCanonical(OutputStream dOut, String label, BigInteger i)
815 			throws IOException {
816 		writeCanonical(dOut, label, i.toByteArray());
817 	}
818 
writeCanonical(OutputStream dOut, String label, byte[] data)819 	private void writeCanonical(OutputStream dOut, String label, byte[] data)
820 			throws IOException {
821 		dOut.write(Strings.toByteArray(
822 				"(" + label.length() + ":" + label + data.length + ":"));
823 		dOut.write(data);
824 		dOut.write(Strings.toByteArray(")"));
825 	}
826 }
827