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