1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.gpg.bc.internal.keys;
11
12 import java.math.BigInteger;
13 import java.nio.charset.StandardCharsets;
14 import java.text.MessageFormat;
15 import java.util.Arrays;
16
17 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
18 import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
19 import org.bouncycastle.asn1.x9.ECNamedCurveTable;
20 import org.bouncycastle.asn1.x9.X9ECParameters;
21 import org.bouncycastle.bcpg.DSAPublicBCPGKey;
22 import org.bouncycastle.bcpg.ECPublicBCPGKey;
23 import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
24 import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
25 import org.bouncycastle.bcpg.RSAPublicBCPGKey;
26 import org.bouncycastle.crypto.ec.CustomNamedCurves;
27 import org.bouncycastle.math.ec.ECAlgorithms;
28 import org.bouncycastle.math.field.FiniteField;
29 import org.bouncycastle.openpgp.PGPException;
30 import org.bouncycastle.openpgp.PGPPublicKey;
31 import org.bouncycastle.util.encoders.Hex;
32 import org.eclipse.jgit.annotations.NonNull;
33 import org.eclipse.jgit.gpg.bc.internal.BCText;
34 import org.eclipse.jgit.util.sha1.SHA1;
35
36
37
38
39
40
41
42
43
44
45 public final class KeyGrip {
46
47
48
49 private static String OID_OPENPGP_ED25519 = "1.3.6.1.4.1.11591.15.1";
50
51 private static String OID_RFC8410_CURVE25519 = "1.3.101.110";
52
53 private static String OID_RFC8410_ED25519 = "1.3.101.112";
54
55 private KeyGrip() {
56
57 }
58
59
60
61
62
63
64
65
66
67
68 @NonNull
69 public static byte[] getKeyGrip(PGPPublicKey publicKey)
70 throws PGPException {
71 SHA1 grip = SHA1.newInstance();
72 grip.setDetectCollision(false);
73
74 switch (publicKey.getAlgorithm()) {
75 case PublicKeyAlgorithmTags.RSA_GENERAL:
76 case PublicKeyAlgorithmTags.RSA_ENCRYPT:
77 case PublicKeyAlgorithmTags.RSA_SIGN:
78 BigInteger modulus = ((RSAPublicBCPGKey) publicKey
79 .getPublicKeyPacket().getKey()).getModulus();
80 hash(grip, modulus.toByteArray());
81 break;
82 case PublicKeyAlgorithmTags.DSA:
83 DSAPublicBCPGKey dsa = (DSAPublicBCPGKey) publicKey
84 .getPublicKeyPacket().getKey();
85 hash(grip, dsa.getP().toByteArray(), 'p', true);
86 hash(grip, dsa.getQ().toByteArray(), 'q', true);
87 hash(grip, dsa.getG().toByteArray(), 'g', true);
88 hash(grip, dsa.getY().toByteArray(), 'y', true);
89 break;
90 case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
91 case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
92 ElGamalPublicBCPGKey eg = (ElGamalPublicBCPGKey) publicKey
93 .getPublicKeyPacket().getKey();
94 hash(grip, eg.getP().toByteArray(), 'p', true);
95 hash(grip, eg.getG().toByteArray(), 'g', true);
96 hash(grip, eg.getY().toByteArray(), 'y', true);
97 break;
98 case PublicKeyAlgorithmTags.ECDH:
99 case PublicKeyAlgorithmTags.ECDSA:
100 case PublicKeyAlgorithmTags.EDDSA:
101 ECPublicBCPGKey ec = (ECPublicBCPGKey) publicKey
102 .getPublicKeyPacket().getKey();
103 ASN1ObjectIdentifier curveOID = ec.getCurveOID();
104
105 if (OID_OPENPGP_ED25519.equals(curveOID.getId())
106 || OID_RFC8410_ED25519.equals(curveOID.getId())) {
107 return hashEd25519(grip, ec.getEncodedPoint());
108 } else if (CryptlibObjectIdentifiers.curvey25519.equals(curveOID)
109 || OID_RFC8410_CURVE25519.equals(curveOID.getId())) {
110
111
112
113
114 return hashCurve25519(grip, ec.getEncodedPoint());
115 }
116 X9ECParameters params = getX9Parameters(curveOID);
117 if (params == null) {
118 throw new PGPException(MessageFormat
119 .format(BCText.get().unknownCurve, curveOID.getId()));
120 }
121
122 BigInteger q = ec.getEncodedPoint();
123 byte[] g = params.getG().getEncoded(false);
124 BigInteger a = params.getCurve().getA().toBigInteger();
125 BigInteger b = params.getCurve().getB().toBigInteger();
126 BigInteger n = params.getN();
127 BigInteger p = null;
128 FiniteField field = params.getCurve().getField();
129 if (ECAlgorithms.isFpField(field)) {
130 p = field.getCharacteristic();
131 }
132 if (p == null) {
133
134 throw new PGPException(MessageFormat.format(
135 BCText.get().unknownCurveParameters, curveOID.getId()));
136 }
137 hash(grip, p.toByteArray(), 'p', false);
138 hash(grip, a.toByteArray(), 'a', false);
139 hash(grip, b.toByteArray(), 'b', false);
140 hash(grip, g, 'g', false);
141 hash(grip, n.toByteArray(), 'n', false);
142 if (publicKey.getAlgorithm() == PublicKeyAlgorithmTags.EDDSA) {
143 hashQ25519(grip, q);
144 } else {
145 hash(grip, q.toByteArray(), 'q', false);
146 }
147 break;
148 default:
149 throw new PGPException(
150 MessageFormat.format(BCText.get().unknownKeyType,
151 Integer.toString(publicKey.getAlgorithm())));
152 }
153 return grip.digest();
154 }
155
156 private static void hash(SHA1 grip, byte[] data) {
157
158 int i = 0;
159 while (i < data.length && data[i] == 0) {
160 i++;
161 }
162 int length = data.length - i;
163 if (i < data.length) {
164 if ((data[i] & 0x80) != 0) {
165 grip.update((byte) 0);
166 }
167 grip.update(data, i, length);
168 }
169 }
170
171 private static void hash(SHA1 grip, byte[] data, char id, boolean zeroPad) {
172
173 int i = 0;
174 while (i < data.length && data[i] == 0) {
175 i++;
176 }
177 int length = data.length - i;
178 boolean addZero = false;
179 if (i < data.length && zeroPad && (data[i] & 0x80) != 0) {
180 addZero = true;
181 }
182
183 String prefix = "(1:" + id + (addZero ? length + 1 : length) + ':';
184 grip.update(prefix.getBytes(StandardCharsets.US_ASCII));
185
186 if (addZero) {
187 grip.update((byte) 0);
188 }
189 if (i < data.length) {
190 grip.update(data, i, length);
191 }
192 grip.update((byte) ')');
193 }
194
195 private static void hashQ25519(SHA1 grip, BigInteger q)
196 throws PGPException {
197 byte[] data = q.toByteArray();
198 switch (data[0]) {
199 case 0x04:
200 if (data.length != 65) {
201 throw new PGPException(MessageFormat.format(
202 BCText.get().corrupt25519Key, Hex.toHexString(data)));
203 }
204
205 throw new PGPException(MessageFormat.format(
206 BCText.get().uncompressed25519Key, Hex.toHexString(data)));
207 case 0x40:
208 if (data.length != 33) {
209 throw new PGPException(MessageFormat.format(
210 BCText.get().corrupt25519Key, Hex.toHexString(data)));
211 }
212
213 hash(grip, Arrays.copyOfRange(data, 1, data.length), 'q', false);
214 break;
215 default:
216 if (data.length != 32) {
217 throw new PGPException(MessageFormat.format(
218 BCText.get().corrupt25519Key, Hex.toHexString(data)));
219 }
220
221 hash(grip, data, 'q', false);
222 break;
223 }
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 @SuppressWarnings("nls")
241 static byte[] hashEd25519(SHA1 grip, BigInteger q) throws PGPException {
242
243
244 hash(grip, Hex.decodeStrict(
245 "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
246 'p', false);
247
248 hash(grip, new byte[] { 0x01 }, 'a', false);
249
250
251
252 hash(grip, Hex.decodeStrict(
253 "2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A"),
254 'b', false);
255
256
257
258
259
260
261 hash(grip, Hex.decodeStrict("04"
262 + "216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
263 + "6666666666666666666666666666666666666666666666666666666666666658"),
264 'g', false);
265
266 hash(grip, Hex.decodeStrict(
267 "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
268 'n', false);
269 hashQ25519(grip, q);
270 return grip.digest();
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 @SuppressWarnings("nls")
288 static byte[] hashCurve25519(SHA1 grip, BigInteger q) throws PGPException {
289 hash(grip, Hex.decodeStrict(
290 "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
291 'p', false);
292
293
294
295 hash(grip, new byte[] { 0x01, (byte) 0xDB, 0x41 }, 'a', false);
296 hash(grip, new byte[] { 0x01 }, 'b', false);
297
298
299
300
301
302 hash(grip, Hex.decodeStrict("04"
303 + "0000000000000000000000000000000000000000000000000000000000000009"
304 + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"),
305 'g', false);
306 hash(grip, Hex.decodeStrict(
307 "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
308 'n', false);
309 hashQ25519(grip, q);
310 return grip.digest();
311 }
312
313 private static X9ECParameters getX9Parameters(
314 ASN1ObjectIdentifier curveOID) {
315 X9ECParameters params = CustomNamedCurves.getByOID(curveOID);
316 if (params == null) {
317 params = ECNamedCurveTable.getByOID(curveOID);
318 }
319 return params;
320 }
321
322 }