1   package com.atlassian.user.impl.osuser.security.password;
2   
3   import com.atlassian.user.security.password.Credential;
4   import com.atlassian.user.security.password.PasswordEncryptor;
5   import com.atlassian.user.util.Base64Encoder;
6   
7   import java.util.HashMap;
8   
9   
10  /**
11   * Password digester based on BouncyCastle's SHA1-512 implementation
12   *
13   * @author Victor Salaman (salaman@qoretech.com)
14   */
15  public class OSUPasswordEncryptor implements PasswordEncryptor
16  {
17      //~ Static fields/initializers /////////////////////////////////////////////
18  
19      private static final int DIGEST_LENGTH = 64;
20      static final long[] K = {
21          0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL,
22          0xe9b5dba58189dbbcL, 0x3956c25bf348b538L, 0x59f111f1b605d019L,
23          0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, 0xd807aa98a3030242L,
24          0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L,
25          0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L,
26          0xc19bf174cf692694L, 0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L,
27          0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, 0x2de92c6f592b0275L,
28          0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L,
29          0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL,
30          0xbf597fc7beef0ee4L, 0xc6e00bf33da88fc2L, 0xd5a79147930aa725L,
31          0x06ca6351e003826fL, 0x142929670a0e6e70L, 0x27b70a8546d22ffcL,
32          0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL,
33          0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L,
34          0x92722c851482353bL, 0xa2bfe8a14cf10364L, 0xa81a664bbc423001L,
35          0xc24b8b70d0f89791L, 0xc76c51a30654be30L, 0xd192e819d6ef5218L,
36          0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L,
37          0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L,
38          0x34b0bcb5e19b48a8L, 0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL,
39          0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, 0x748f82ee5defb2fcL,
40          0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL,
41          0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L,
42          0xc67178f2e372532bL, 0xca273eceea26619cL, 0xd186b8c721c0c207L,
43          0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, 0x06f067aa72176fbaL,
44          0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL,
45          0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL,
46          0x431d67c49c100d4cL, 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL,
47          0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L
48      };
49  
50      //~ Instance fields ////////////////////////////////////////////////////////
51  
52      protected long H1;
53      protected long H2;
54      protected long H3;
55      protected long H4;
56      protected long H5;
57      protected long H6;
58      protected long H7;
59      protected long H8;
60      private long[] W = new long[80];
61      private byte[] xBuf;
62      private int wOff;
63      private int xBufOff;
64      private long byteCount1;
65      private long byteCount2;
66  
67      //~ Constructors ///////////////////////////////////////////////////////////
68  
69      public OSUPasswordEncryptor()
70      {
71          xBuf = new byte[8];
72          xBufOff = 0;
73  
74          reset();
75      }
76  
77      protected OSUPasswordEncryptor(OSUPasswordEncryptor t)
78      {
79          xBuf = new byte[t.xBuf.length];
80          System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
81  
82          xBufOff = t.xBufOff;
83          byteCount1 = t.byteCount1;
84          byteCount2 = t.byteCount2;
85  
86          H1 = t.H1;
87          H2 = t.H2;
88          H3 = t.H3;
89          H4 = t.H4;
90          H5 = t.H5;
91          H6 = t.H6;
92          H7 = t.H7;
93          H8 = t.H8;
94  
95          System.arraycopy(t.W, 0, W, 0, t.W.length);
96          wOff = t.wOff;
97      }
98  
99      //~ Methods ////////////////////////////////////////////////////////////////
100 
101     public static byte[] digest(byte[] input)
102     {
103         OSUPasswordEncryptor digester = new OSUPasswordEncryptor();
104         byte[] output = new byte[digester.getDigestSize()];
105         digester.update(input, 0, input.length);
106         digester.doFinal(output, 0);
107 
108         return output;
109     }
110 
111     public int getDigestSize()
112     {
113         return DIGEST_LENGTH;
114     }
115 
116     public int doFinal(byte[] out, int outOff)
117     {
118         finish();
119 
120         unpackWord(H1, out, outOff);
121         unpackWord(H2, out, outOff + 8);
122         unpackWord(H3, out, outOff + 16);
123         unpackWord(H4, out, outOff + 24);
124         unpackWord(H5, out, outOff + 32);
125         unpackWord(H6, out, outOff + 40);
126         unpackWord(H7, out, outOff + 48);
127         unpackWord(H8, out, outOff + 56);
128 
129         reset();
130 
131         return DIGEST_LENGTH;
132     }
133 
134     public void finish()
135     {
136         adjustByteCounts();
137 
138         long lowBitLength = byteCount1 << 3;
139         long hiBitLength = byteCount2;
140 
141         //
142         // add the pad bytes.
143         //
144         update((byte) 128);
145 
146         while (xBufOff != 0)
147         {
148             update((byte) 0);
149         }
150 
151         processLength(lowBitLength, hiBitLength);
152 
153         processBlock();
154     }
155 
156     public void reset()
157     {
158         byteCount1 = 0;
159         byteCount2 = 0;
160 
161         xBufOff = 0;
162 
163         for (int i = 0; i < xBuf.length; i++)
164         {
165             xBuf[i] = 0;
166         }
167 
168         wOff = 0;
169 
170         for (int i = 0; i != W.length; i++)
171         {
172             W[i] = 0;
173         }
174 
175         H1 = 0x6a09e667f3bcc908L;
176         H2 = 0xbb67ae8584caa73bL;
177         H3 = 0x3c6ef372fe94f82bL;
178         H4 = 0xa54ff53a5f1d36f1L;
179         H5 = 0x510e527fade682d1L;
180         H6 = 0x9b05688c2b3e6c1fL;
181         H7 = 0x1f83d9abfb41bd6bL;
182         H8 = 0x5be0cd19137e2179L;
183     }
184 
185     public void update(byte in)
186     {
187         xBuf[xBufOff++] = in;
188 
189         if (xBufOff == xBuf.length)
190         {
191             processWord(xBuf, 0);
192             xBufOff = 0;
193         }
194 
195         byteCount1++;
196     }
197 
198     public void update(byte[] in, int inOff, int len)
199     {
200         //
201         // fill the current word
202         //
203         while ((xBufOff != 0) && (len > 0))
204         {
205             update(in[inOff]);
206 
207             inOff++;
208             len--;
209         }
210 
211         //
212         // process whole words.
213         //
214         while (len > xBuf.length)
215         {
216             processWord(in, inOff);
217 
218             inOff += xBuf.length;
219             len -= xBuf.length;
220             byteCount1 += xBuf.length;
221         }
222 
223         //
224         // load in the remainder.
225         //
226         while (len > 0)
227         {
228             update(in[inOff]);
229 
230             inOff++;
231             len--;
232         }
233     }
234 
235     protected void processBlock()
236     {
237         adjustByteCounts();
238 
239         //
240         // expand 16 word block into 80 word blocks.
241         //
242         for (int t = 16; t <= 79; t++)
243         {
244             W[t] = Sigma1(W[t - 2]) + W[t - 7] + Sigma0(W[t - 15]) + W[t - 16];
245         }
246 
247         //
248         // set up working variables.
249         //
250         long a = H1;
251         long b = H2;
252         long c = H3;
253         long d = H4;
254         long e = H5;
255         long f = H6;
256         long g = H7;
257         long h = H8;
258 
259         for (int t = 0; t <= 79; t++)
260         {
261             long T1;
262             long T2;
263 
264             T1 = h + Sum1(e) + Ch(e, f, g) + K[t] + W[t];
265             T2 = Sum0(a) + Maj(a, b, c);
266             h = g;
267             g = f;
268             f = e;
269             e = d + T1;
270             d = c;
271             c = b;
272             b = a;
273             a = T1 + T2;
274         }
275 
276         H1 += a;
277         H2 += b;
278         H3 += c;
279         H4 += d;
280         H5 += e;
281         H6 += f;
282         H7 += g;
283         H8 += h;
284 
285         //
286         // reset the offset and clean out the word buffer.
287         //
288         wOff = 0;
289 
290         for (int i = 0; i != W.length; i++)
291         {
292             W[i] = 0;
293         }
294     }
295 
296     protected void processLength(long lowW, long hiW)
297     {
298         if (wOff > 14)
299         {
300             processBlock();
301         }
302 
303         W[14] = hiW;
304         W[15] = lowW;
305     }
306 
307     protected void processWord(byte[] in, int inOff)
308     {
309         W[wOff++] = ((long) (in[inOff] & 0xff) << 56) | ((long) (in[inOff + 1] & 0xff) << 48)
310                 | ((long) (in[inOff + 2] & 0xff) << 40) | ((long) (in[inOff + 3] & 0xff) << 32)
311                 | ((long) (in[inOff + 4] & 0xff) << 24) | ((long) (in[inOff + 5] & 0xff) << 16)
312                 | ((long) (in[inOff + 6] & 0xff) << 8) | ((long) (in[inOff + 7] & 0xff));
313 
314         if (wOff == 16)
315         {
316             processBlock();
317         }
318     }
319 
320     protected void unpackWord(long word, byte[] out, int outOff)
321     {
322         out[outOff] = (byte) (word >>> 56);
323         out[outOff + 1] = (byte) (word >>> 48);
324         out[outOff + 2] = (byte) (word >>> 40);
325         out[outOff + 3] = (byte) (word >>> 32);
326         out[outOff + 4] = (byte) (word >>> 24);
327         out[outOff + 5] = (byte) (word >>> 16);
328         out[outOff + 6] = (byte) (word >>> 8);
329         out[outOff + 7] = (byte) word;
330     }
331 
332     private long Ch(long x, long y, long z)
333     {
334         return ((x & y) ^ ((~x) & z));
335     }
336 
337     private long Maj(long x, long y, long z)
338     {
339         return ((x & y) ^ (x & z) ^ (y & z));
340     }
341 
342     private long Sigma0(long x)
343     {
344         return rotateRight(x, 1) ^ rotateRight(x, 8) ^ (x >>> 7);
345     }
346 
347     private long Sigma1(long x)
348     {
349         return rotateRight(x, 19) ^ rotateRight(x, 61) ^ (x >>> 6);
350     }
351 
352     private long Sum0(long x)
353     {
354         return rotateRight(x, 28) ^ rotateRight(x, 34) ^ rotateRight(x, 39);
355     }
356 
357     private long Sum1(long x)
358     {
359         return rotateRight(x, 14) ^ rotateRight(x, 18) ^ rotateRight(x, 41);
360     }
361 
362     /**
363      * adjust the byte counts so that byteCount2 represents the
364      * upper long (less 3 bits) word of the byte count.
365      */
366     private void adjustByteCounts()
367     {
368         if (byteCount1 > 0x1fffffffffffffffL)
369         {
370             byteCount2 += (byteCount1 >>> 61);
371             byteCount1 &= 0x1fffffffffffffffL;
372         }
373     }
374 
375     private long rotateRight(long x, int n)
376     {
377         return (x >>> n) | (x << (64 - n));
378     }
379 
380     /**
381      * @return an encrypted version of the original {@link String} unencryptedPassword
382      */
383     public String encrypt(String unencryptedPassword)
384     {
385         byte[] digested = digest(unencryptedPassword.getBytes());
386         byte[]encoded = Base64Encoder.encode(digested);
387         return new String(encoded);
388     }
389 
390     /**
391      * @deprecated since 13 March 2007, use constructor instead.
392      * @see PasswordEncryptor#init(HashMap)
393      */
394     public void init(HashMap args)
395     {
396         //left blank as no dependencies need to be met.
397     }
398 
399     public String getEncryptedValue(Credential credential)
400     {
401         if (credential.isEncrypted())
402         {
403             return credential.getValue();
404         }
405         return encrypt(credential.getValue());
406     }
407 }
408