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