]> source.dussan.org Git - jgit.git/blob
3924d685968bf423fa4c6fc93cde7d20f1d44dbf
[jgit.git] /
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 java.security.NoSuchAlgorithmException;
13 import java.text.MessageFormat;
14
15 import javax.crypto.Cipher;
16 import javax.crypto.SecretKey;
17 import javax.crypto.spec.IvParameterSpec;
18 import javax.crypto.spec.SecretKeySpec;
19
20 import org.bouncycastle.openpgp.PGPException;
21 import org.bouncycastle.openpgp.PGPUtil;
22 import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
23 import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
24 import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
25 import org.bouncycastle.util.Arrays;
26 import org.eclipse.jgit.gpg.bc.internal.BCText;
27
28 /**
29  * A {@link PBEProtectionRemoverFactory} using AES/OCB/NoPadding for decryption.
30  * It accepts an AAD in the factory's constructor, so the factory can be used to
31  * create a {@link PBESecretKeyDecryptor} only for a particular input.
32  * <p>
33  * For JGit's needs, this is sufficient, but for a general upstream
34  * implementation that limitation might not be acceptable.
35  * </p>
36  */
37 class OCBPBEProtectionRemoverFactory
38                 implements PBEProtectionRemoverFactory {
39
40         private final PGPDigestCalculatorProvider calculatorProvider;
41
42         private final char[] passphrase;
43
44         private final byte[] aad;
45
46         /**
47          * Creates a new factory instance with the given parameters.
48          * <p>
49          * Because the AAD is given at factory level, the {@link PBESecretKeyDecryptor}s
50          * created by the factory can be used to decrypt only a particular input
51          * matching this AAD.
52          * </p>
53          *
54          * @param passphrase         to use for secret key derivation
55          * @param calculatorProvider for computing digests
56          * @param aad                for the OCB decryption
57          */
58         OCBPBEProtectionRemoverFactory(char[] passphrase,
59                         PGPDigestCalculatorProvider calculatorProvider, byte[] aad) {
60                 this.calculatorProvider = calculatorProvider;
61                 this.passphrase = passphrase;
62                 this.aad = aad;
63         }
64
65         @Override
66         public PBESecretKeyDecryptor createDecryptor(String protection)
67                         throws PGPException {
68                 return new PBESecretKeyDecryptor(passphrase, calculatorProvider) {
69
70                         @Override
71                         public byte[] recoverKeyData(int encAlgorithm, byte[] key,
72                                         byte[] iv, byte[] encrypted, int encryptedOffset,
73                                         int encryptedLength) throws PGPException {
74                                 String algorithmName = PGPUtil
75                                                 .getSymmetricCipherName(encAlgorithm);
76                                 byte[] decrypted = null;
77                                 try {
78                                         // errorprone: "Dynamically constructed transformation
79                                         // strings are also flagged, as they may conceal an instance
80                                         // of ECB mode."
81                                         @SuppressWarnings("InsecureCryptoUsage")
82                                         Cipher c = Cipher
83                                                         .getInstance(algorithmName + "/OCB/NoPadding"); //$NON-NLS-1$
84                                         SecretKey secretKey = new SecretKeySpec(key, algorithmName);
85                                         c.init(Cipher.DECRYPT_MODE, secretKey,
86                                                         new IvParameterSpec(iv));
87                                         c.updateAAD(aad);
88                                         decrypted = new byte[c.getOutputSize(encryptedLength)];
89                                         int decryptedLength = c.update(encrypted, encryptedOffset,
90                                                         encryptedLength, decrypted);
91                                         // doFinal() for OCB will check the MAC and throw an
92                                         // exception if it doesn't match
93                                         decryptedLength += c.doFinal(decrypted, decryptedLength);
94                                         if (decryptedLength != decrypted.length) {
95                                                 throw new PGPException(MessageFormat.format(
96                                                                 BCText.get().cryptWrongDecryptedLength,
97                                                                 Integer.valueOf(decryptedLength),
98                                                                 Integer.valueOf(decrypted.length)));
99                                         }
100                                         byte[] result = decrypted;
101                                         decrypted = null; // Don't clear in finally
102                                         return result;
103                                 } catch (NoClassDefFoundError e) {
104                                         String msg = MessageFormat.format(
105                                                         BCText.get().gpgNoSuchAlgorithm,
106                                                         algorithmName + "/OCB"); //$NON-NLS-1$
107                                         throw new PGPException(msg,
108                                                         new NoSuchAlgorithmException(msg, e));
109                                 } catch (PGPException e) {
110                                         throw e;
111                                 } catch (Exception e) {
112                                         throw new PGPException(
113                                                         MessageFormat.format(BCText.get().cryptCipherError,
114                                                                         e.getLocalizedMessage()),
115                                                         e);
116                                 } finally {
117                                         if (decrypted != null) {
118                                                 // Prevent halfway decrypted data leaking.
119                                                 Arrays.fill(decrypted, (byte) 0);
120                                         }
121                                 }
122                         }
123                 };
124         }
125 }