1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.transport;
44
45 import java.io.UnsupportedEncodingException;
46 import java.security.InvalidKeyException;
47 import java.security.NoSuchAlgorithmException;
48
49 import javax.crypto.Mac;
50 import javax.crypto.spec.SecretKeySpec;
51
52 import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
53 import org.eclipse.jgit.lib.Repository;
54 import org.eclipse.jgit.transport.NonceGenerator;
55 import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
56
57
58
59
60
61
62 public class HMACSHA1NonceGenerator implements NonceGenerator {
63
64 private Mac mac;
65
66
67
68
69
70 public HMACSHA1NonceGenerator(String seed) throws IllegalStateException {
71 try {
72 byte[] keyBytes = seed.getBytes("ISO-8859-1");
73 SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
74 mac = Mac.getInstance("HmacSHA1");
75 mac.init(signingKey);
76 } catch (InvalidKeyException e) {
77 throw new IllegalStateException(e);
78 } catch (NoSuchAlgorithmException e) {
79 throw new IllegalStateException(e);
80 } catch (UnsupportedEncodingException e) {
81 throw new IllegalStateException(e);
82 }
83 }
84
85 public synchronized String createNonce(Repository repo, long timestamp)
86 throws IllegalStateException {
87 String path;
88 if (repo instanceof DfsRepository)
89 path = ((DfsRepository) repo).getDescription().getRepositoryName();
90 else if (repo.getDirectory() != null)
91 path = repo.getDirectory().getPath();
92 else
93 throw new IllegalStateException();
94
95 String input = path + ":" + String.valueOf(timestamp);
96 byte[] rawHmac;
97 try {
98 rawHmac = mac.doFinal(input.getBytes("UTF-8"));
99 } catch (UnsupportedEncodingException e) {
100 throw new IllegalStateException(e);
101 }
102 return Long.toString(timestamp) + "-" + toHex(rawHmac);
103 }
104
105 @Override
106 public NonceStatus verify(String received, String sent,
107 Repository db, boolean allowSlop, int slop) {
108 if (received.isEmpty()) {
109 return NonceStatus.MISSING;
110 } else if (sent.isEmpty()) {
111 return NonceStatus.UNSOLICITED;
112 } else if (received.equals(sent)) {
113 return NonceStatus.OK;
114 }
115
116 if (!allowSlop) {
117 return NonceStatus.BAD;
118 }
119
120
121 int idxSent = sent.indexOf('-');
122 int idxRecv = received.indexOf('-');
123 if (idxSent == -1 || idxRecv == -1) {
124 return NonceStatus.BAD;
125 }
126
127 String signedStampStr = received.substring(0, idxRecv);
128 String advertisedStampStr = sent.substring(0, idxSent);
129 long signedStamp;
130 long advertisedStamp;
131 try {
132 signedStamp = Long.parseLong(signedStampStr);
133 advertisedStamp = Long.parseLong(advertisedStampStr);
134 } catch (IllegalArgumentException e) {
135 return NonceStatus.BAD;
136 }
137
138
139 String expect = createNonce(db, signedStamp);
140
141 if (!expect.equals(received)) {
142 return NonceStatus.BAD;
143 }
144
145 long nonceStampSlop = Math.abs(advertisedStamp - signedStamp);
146
147 if (nonceStampSlop <= slop) {
148 return NonceStatus.OK;
149 } else {
150 return NonceStatus.SLOP;
151 }
152 }
153
154 private static final String HEX = "0123456789ABCDEF";
155
156 private static String toHex(byte[] bytes) {
157 StringBuilder builder = new StringBuilder(2 * bytes.length);
158 for (byte b : bytes) {
159 builder.append(HEX.charAt((b & 0xF0) >> 4));
160 builder.append(HEX.charAt(b & 0xF));
161 }
162 return builder.toString();
163 }
164 }