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 if (!allowSlop)
116 return NonceStatus.BAD;
117
118
119 int idxSent = sent.indexOf('-');
120 int idxRecv = received.indexOf('-');
121 if (idxSent == -1 || idxRecv == -1)
122 return NonceStatus.BAD;
123
124 long signedStamp;
125 long advertisedStamp;
126 try {
127 signedStamp = Long.parseLong(received.substring(0, idxRecv));
128 advertisedStamp = Long.parseLong(sent.substring(0, idxSent));
129 } catch (Exception e) {
130 return NonceStatus.BAD;
131 }
132
133
134 String expect = createNonce(db, signedStamp);
135
136 if (!expect.equals(received))
137 return NonceStatus.BAD;
138
139 long nonceStampSlop = Math.abs(advertisedStamp - signedStamp);
140
141 if (nonceStampSlop <= slop) {
142 return NonceStatus.OK;
143 } else {
144 return NonceStatus.SLOP;
145 }
146 }
147
148 private static final String HEX = "0123456789ABCDEF";
149
150 private static String toHex(byte[] bytes) {
151 StringBuilder builder = new StringBuilder(2 * bytes.length);
152 for (byte b : bytes) {
153 builder.append(HEX.charAt((b & 0xF0) >> 4));
154 builder.append(HEX.charAt(b & 0xF));
155 }
156 return builder.toString();
157 }
158 }