1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.transport;
11
12 import static java.nio.charset.StandardCharsets.ISO_8859_1;
13 import static java.nio.charset.StandardCharsets.UTF_8;
14
15 import java.security.InvalidKeyException;
16 import java.security.NoSuchAlgorithmException;
17
18 import javax.crypto.Mac;
19 import javax.crypto.spec.SecretKeySpec;
20
21 import org.eclipse.jgit.lib.Repository;
22 import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
23
24
25
26
27
28
29 public class HMACSHA1NonceGenerator implements NonceGenerator {
30
31 private Mac mac;
32
33
34
35
36
37
38
39
40 public HMACSHA1NonceGenerator(String seed) throws IllegalStateException {
41 try {
42 byte[] keyBytes = seed.getBytes(ISO_8859_1);
43 SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
44 mac = Mac.getInstance("HmacSHA1");
45 mac.init(signingKey);
46 } catch (InvalidKeyException | NoSuchAlgorithmException e) {
47 throw new IllegalStateException(e);
48 }
49 }
50
51
52 @Override
53 public synchronized String createNonce(Repository repo, long timestamp)
54 throws IllegalStateException {
55 String input = repo.getIdentifier() + ":" + String.valueOf(timestamp);
56 byte[] rawHmac = mac.doFinal(input.getBytes(UTF_8));
57 return Long.toString(timestamp) + "-" + toHex(rawHmac);
58 }
59
60
61 @Override
62 public NonceStatus verify(String received, String sent,
63 Repository db, boolean allowSlop, int slop) {
64 if (received.isEmpty()) {
65 return NonceStatus.MISSING;
66 } else if (sent.isEmpty()) {
67 return NonceStatus.UNSOLICITED;
68 } else if (received.equals(sent)) {
69 return NonceStatus.OK;
70 }
71
72 if (!allowSlop) {
73 return NonceStatus.BAD;
74 }
75
76
77 int idxSent = sent.indexOf('-');
78 int idxRecv = received.indexOf('-');
79 if (idxSent == -1 || idxRecv == -1) {
80 return NonceStatus.BAD;
81 }
82
83 String signedStampStr = received.substring(0, idxRecv);
84 String advertisedStampStr = sent.substring(0, idxSent);
85 long signedStamp;
86 long advertisedStamp;
87 try {
88 signedStamp = Long.parseLong(signedStampStr);
89 advertisedStamp = Long.parseLong(advertisedStampStr);
90 } catch (IllegalArgumentException e) {
91 return NonceStatus.BAD;
92 }
93
94
95 String expect = createNonce(db, signedStamp);
96
97 if (!expect.equals(received)) {
98 return NonceStatus.BAD;
99 }
100
101 long nonceStampSlop = Math.abs(advertisedStamp - signedStamp);
102
103 if (nonceStampSlop <= slop) {
104 return NonceStatus.OK;
105 }
106 return NonceStatus.SLOP;
107 }
108
109 private static final String HEX = "0123456789ABCDEF";
110
111 private static String toHex(byte[] bytes) {
112 StringBuilder builder = new StringBuilder(2 * bytes.length);
113 for (byte b : bytes) {
114 builder.append(HEX.charAt((b & 0xF0) >> 4));
115 builder.append(HEX.charAt(b & 0xF));
116 }
117 return builder.toString();
118 }
119 }