OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [javax/] [crypto/] [sasl/] [srp/] [PasswordFile.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* PasswordFile.java --
2
   Copyright (C) 2003, 2006 Free Software Foundation, Inc.
3
 
4
This file is a part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or (at
9
your option) any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19
USA
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version.  */
37
 
38
 
39
package gnu.javax.crypto.sasl.srp;
40
 
41
import gnu.java.lang.CPStringBuilder;
42
 
43
import gnu.java.security.Registry;
44
import gnu.java.security.util.Util;
45
import gnu.javax.crypto.key.srp6.SRPAlgorithm;
46
import gnu.javax.crypto.sasl.NoSuchUserException;
47
import gnu.javax.crypto.sasl.UserAlreadyExistsException;
48
 
49
import java.io.BufferedReader;
50
import java.io.File;
51
import java.io.FileInputStream;
52
import java.io.FileNotFoundException;
53
import java.io.FileOutputStream;
54
import java.io.IOException;
55
import java.io.InputStream;
56
import java.io.InputStreamReader;
57
import java.io.PrintWriter;
58
import java.io.UnsupportedEncodingException;
59
import java.math.BigInteger;
60
import java.util.HashMap;
61
import java.util.Iterator;
62
import java.util.NoSuchElementException;
63
import java.util.StringTokenizer;
64
 
65
/**
66
 * The implementation of SRP password files.
67
 * <p>
68
 * For SRP, there are three (3) files:
69
 * <ol>
70
 * <li>The password configuration file: tpasswd.conf. It contains the pairs
71
 * &lt;N,g> indexed by a number for each pair used for a user. By default, this
72
 * file's pathname is constructed from the base password file pathname by
73
 * prepending it with the ".conf" suffix.</li>
74
 * <li>The base password file: tpasswd. It contains the related password
75
 * entries for all the users with values computed using SRP's default message
76
 * digest algorithm: SHA-1 (with 160-bit output block size).</li>
77
 * <li>The extended password file: tpasswd2. Its name, by default, is
78
 * constructed by adding the suffix "2" to the fully qualified pathname of the
79
 * base password file. It contains, in addition to the same fields as the base
80
 * password file, albeit with a different verifier value, an extra field
81
 * identifying the message digest algorithm used to compute this (verifier)
82
 * value.</li>
83
 * </ol>
84
 * <p>
85
 * This implementation assumes the following message digest algorithm codes:
86
 * <ul>
87
 * <li>0: the default hash algorithm, which is SHA-1 (or its alias SHA-160).</li>
88
 * <li>1: MD5.</li>
89
 * <li>2: RIPEMD-128.</li>
90
 * <li>3: RIPEMD-160.</li>
91
 * <li>4: SHA-256.</li>
92
 * <li>5: SHA-384.</li>
93
 * <li>6: SHA-512.</li>
94
 * </ul>
95
 * <p>
96
 * <b>IMPORTANT:</b> This method computes the verifiers as described in
97
 * RFC-2945, which differs from the description given on the web page for SRP-6.
98
 * <p>
99
 * Reference:
100
 * <ol>
101
 * <li><a href="http://srp.stanford.edu/design.html">SRP Protocol Design</a><br>
102
 * Thomas J. Wu.</li>
103
 * </ol>
104
 */
105
public class PasswordFile
106
{
107
  // names of property keys used in this class
108
  private static final String USER_FIELD = "user";
109
  private static final String VERIFIERS_FIELD = "verifier";
110
  private static final String SALT_FIELD = "salt";
111
  private static final String CONFIG_FIELD = "config";
112
  private static String DEFAULT_FILE;
113
  static
114
    {
115
      DEFAULT_FILE = System.getProperty(SRPRegistry.PASSWORD_FILE,
116
                                        SRPRegistry.DEFAULT_PASSWORD_FILE);
117
    }
118
  /** The SRP algorithm instances used by this object. */
119
  private static final HashMap srps;
120
  static
121
    {
122
      final HashMap map = new HashMap(SRPRegistry.SRP_ALGORITHMS.length);
123
      // The first entry MUST exist. The others are optional.
124
      map.put("0", SRP.instance(SRPRegistry.SRP_ALGORITHMS[0]));
125
      for (int i = 1; i < SRPRegistry.SRP_ALGORITHMS.length; i++)
126
        {
127
          try
128
            {
129
              map.put(String.valueOf(i),
130
                      SRP.instance(SRPRegistry.SRP_ALGORITHMS[i]));
131
            }
132
          catch (Exception x)
133
            {
134
              System.err.println("Ignored: " + x);
135
              x.printStackTrace(System.err);
136
            }
137
        }
138
      srps = map;
139
    }
140
 
141
  private String confName, pwName, pw2Name;
142
  private File configFile, passwdFile, passwd2File;
143
  private long lastmodPasswdFile, lastmodPasswd2File;
144
  private HashMap entries = new HashMap();
145
  private HashMap configurations = new HashMap();
146
  // default N values to use when creating a new password.conf file
147
  private static final BigInteger[] Nsrp = new BigInteger[] {
148
      SRPAlgorithm.N_2048,
149
      SRPAlgorithm.N_1536,
150
      SRPAlgorithm.N_1280,
151
      SRPAlgorithm.N_1024,
152
      SRPAlgorithm.N_768,
153
      SRPAlgorithm.N_640,
154
      SRPAlgorithm.N_512 };
155
 
156
  public PasswordFile() throws IOException
157
  {
158
    this(DEFAULT_FILE);
159
  }
160
 
161
  public PasswordFile(final File pwFile) throws IOException
162
  {
163
    this(pwFile.getAbsolutePath());
164
  }
165
 
166
  public PasswordFile(final String pwName) throws IOException
167
  {
168
    this(pwName, pwName + "2", pwName + ".conf");
169
  }
170
 
171
  public PasswordFile(final String pwName, final String confName)
172
      throws IOException
173
  {
174
    this(pwName, pwName + "2", confName);
175
  }
176
 
177
  public PasswordFile(final String pwName, final String pw2Name,
178
                      final String confName) throws IOException
179
  {
180
    super();
181
 
182
    this.pwName = pwName;
183
    this.pw2Name = pw2Name;
184
    this.confName = confName;
185
 
186
    readOrCreateConf();
187
    update();
188
  }
189
 
190
  /**
191
   * Returns a string representing the decimal value of an integer identifying
192
   * the message digest algorithm to use for the SRP computations.
193
   *
194
   * @param mdName the canonical name of a message digest algorithm.
195
   * @return a string representing the decimal value of an ID for that
196
   *         algorithm.
197
   */
198
  private static final String nameToID(final String mdName)
199
  {
200
    if (Registry.SHA_HASH.equalsIgnoreCase(mdName)
201
        || Registry.SHA1_HASH.equalsIgnoreCase(mdName)
202
        || Registry.SHA160_HASH.equalsIgnoreCase(mdName))
203
      return "0";
204
    else if (Registry.MD5_HASH.equalsIgnoreCase(mdName))
205
      return "1";
206
    else if (Registry.RIPEMD128_HASH.equalsIgnoreCase(mdName))
207
      return "2";
208
    else if (Registry.RIPEMD160_HASH.equalsIgnoreCase(mdName))
209
      return "3";
210
    else if (Registry.SHA256_HASH.equalsIgnoreCase(mdName))
211
      return "4";
212
    else if (Registry.SHA384_HASH.equalsIgnoreCase(mdName))
213
      return "5";
214
    else if (Registry.SHA512_HASH.equalsIgnoreCase(mdName))
215
      return "6";
216
    return "0";
217
  }
218
 
219
  /**
220
   * Checks if the current configuration file contains the &lt;N, g> pair for
221
   * the designated <code>index</code>.
222
   *
223
   * @param index a string representing 1-digit identification of an &lt;N, g>
224
   *          pair used.
225
   * @return <code>true</code> if the designated <code>index</code> is that
226
   *         of a known &lt;N, g> pair, and <code>false</code> otherwise.
227
   * @throws IOException if an exception occurs during the process.
228
   * @see SRPRegistry#N_2048_BITS
229
   * @see SRPRegistry#N_1536_BITS
230
   * @see SRPRegistry#N_1280_BITS
231
   * @see SRPRegistry#N_1024_BITS
232
   * @see SRPRegistry#N_768_BITS
233
   * @see SRPRegistry#N_640_BITS
234
   * @see SRPRegistry#N_512_BITS
235
   */
236
  public synchronized boolean containsConfig(final String index)
237
      throws IOException
238
  {
239
    checkCurrent();
240
    return configurations.containsKey(index);
241
  }
242
 
243
  /**
244
   * Returns a pair of strings representing the pair of <code>N</code> and
245
   * <code>g</code> MPIs for the designated <code>index</code>.
246
   *
247
   * @param index a string representing 1-digit identification of an &lt;N, g>
248
   *          pair to look up.
249
   * @return a pair of strings, arranged in an array, where the first (at index
250
   *         position #0) is the repesentation of the MPI <code>N</code>, and
251
   *         the second (at index position #1) is the representation of the MPI
252
   *         <code>g</code>. If the <code>index</code> refers to an unknown
253
   *         pair, then an empty string array is returned.
254
   * @throws IOException if an exception occurs during the process.
255
   */
256
  public synchronized String[] lookupConfig(final String index)
257
      throws IOException
258
  {
259
    checkCurrent();
260
    String[] result = null;
261
    if (configurations.containsKey(index))
262
      result = (String[]) configurations.get(index);
263
    return result;
264
  }
265
 
266
  public synchronized boolean contains(final String user) throws IOException
267
  {
268
    checkCurrent();
269
    return entries.containsKey(user);
270
  }
271
 
272
  public synchronized void add(final String user, final String passwd,
273
                               final byte[] salt, final String index)
274
      throws IOException
275
  {
276
    checkCurrent();
277
    if (entries.containsKey(user))
278
      throw new UserAlreadyExistsException(user);
279
    final HashMap fields = new HashMap(4);
280
    fields.put(USER_FIELD, user); // 0
281
    fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index)); // 1
282
    fields.put(SALT_FIELD, Util.toBase64(salt)); // 2
283
    fields.put(CONFIG_FIELD, index); // 3
284
    entries.put(user, fields);
285
    savePasswd();
286
  }
287
 
288
  public synchronized void changePasswd(final String user, final String passwd)
289
      throws IOException
290
  {
291
    checkCurrent();
292
    if (! entries.containsKey(user))
293
      throw new NoSuchUserException(user);
294
    final HashMap fields = (HashMap) entries.get(user);
295
    final byte[] salt;
296
    try
297
      {
298
        salt = Util.fromBase64((String) fields.get(SALT_FIELD));
299
      }
300
    catch (NumberFormatException x)
301
      {
302
        throw new IOException("Password file corrupt");
303
      }
304
    final String index = (String) fields.get(CONFIG_FIELD);
305
    fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index));
306
    entries.put(user, fields);
307
    savePasswd();
308
  }
309
 
310
  public synchronized void savePasswd() throws IOException
311
  {
312
    final FileOutputStream f1 = new FileOutputStream(passwdFile);
313
    final FileOutputStream f2 = new FileOutputStream(passwd2File);
314
    PrintWriter pw1 = null;
315
    PrintWriter pw2 = null;
316
    try
317
      {
318
        pw1 = new PrintWriter(f1, true);
319
        pw2 = new PrintWriter(f2, true);
320
        this.writePasswd(pw1, pw2);
321
      }
322
    finally
323
      {
324
        if (pw1 != null)
325
          try
326
            {
327
              pw1.flush();
328
            }
329
          finally
330
            {
331
              pw1.close();
332
            }
333
        if (pw2 != null)
334
          try
335
            {
336
              pw2.flush();
337
            }
338
          finally
339
            {
340
              pw2.close();
341
            }
342
        try
343
          {
344
            f1.close();
345
          }
346
        catch (IOException ignored)
347
          {
348
          }
349
        try
350
          {
351
            f2.close();
352
          }
353
        catch (IOException ignored)
354
          {
355
          }
356
      }
357
    lastmodPasswdFile = passwdFile.lastModified();
358
    lastmodPasswd2File = passwd2File.lastModified();
359
  }
360
 
361
  /**
362
   * Returns the triplet: verifier, salt and configuration file index, of a
363
   * designated user, and a designated message digest algorithm name, as an
364
   * array of strings.
365
   *
366
   * @param user the username.
367
   * @param mdName the canonical name of the SRP's message digest algorithm.
368
   * @return a string array containing, in this order, the BASE-64 encodings of
369
   *         the verifier, the salt and the index in the password configuration
370
   *         file of the MPIs N and g of the designated user.
371
   */
372
  public synchronized String[] lookup(final String user, final String mdName)
373
      throws IOException
374
  {
375
    checkCurrent();
376
    if (! entries.containsKey(user))
377
      throw new NoSuchUserException(user);
378
    final HashMap fields = (HashMap) entries.get(user);
379
    final HashMap verifiers = (HashMap) fields.get(VERIFIERS_FIELD);
380
    final String salt = (String) fields.get(SALT_FIELD);
381
    final String index = (String) fields.get(CONFIG_FIELD);
382
    final String verifier = (String) verifiers.get(nameToID(mdName));
383
    return new String[] { verifier, salt, index };
384
  }
385
 
386
  private synchronized void readOrCreateConf() throws IOException
387
  {
388
    configurations.clear();
389
    final FileInputStream fis;
390
    configFile = new File(confName);
391
    try
392
      {
393
        fis = new FileInputStream(configFile);
394
        readConf(fis);
395
      }
396
    catch (FileNotFoundException x)
397
      { // create a default one
398
        final String g = Util.toBase64(Util.trim(new BigInteger("2")));
399
        String index, N;
400
        for (int i = 0; i < Nsrp.length; i++)
401
          {
402
            index = String.valueOf(i + 1);
403
            N = Util.toBase64(Util.trim(Nsrp[i]));
404
            configurations.put(index, new String[] { N, g });
405
          }
406
        FileOutputStream f0 = null;
407
        PrintWriter pw0 = null;
408
        try
409
          {
410
            f0 = new FileOutputStream(configFile);
411
            pw0 = new PrintWriter(f0, true);
412
            this.writeConf(pw0);
413
          }
414
        finally
415
          {
416
            if (pw0 != null)
417
              pw0.close();
418
            else if (f0 != null)
419
              f0.close();
420
          }
421
      }
422
  }
423
 
424
  private void readConf(final InputStream in) throws IOException
425
  {
426
    final BufferedReader din = new BufferedReader(new InputStreamReader(in));
427
    String line, index, N, g;
428
    StringTokenizer st;
429
    while ((line = din.readLine()) != null)
430
      {
431
        st = new StringTokenizer(line, ":");
432
        try
433
          {
434
            index = st.nextToken();
435
            N = st.nextToken();
436
            g = st.nextToken();
437
          }
438
        catch (NoSuchElementException x)
439
          {
440
            throw new IOException("SRP password configuration file corrupt");
441
          }
442
        configurations.put(index, new String[] { N, g });
443
      }
444
  }
445
 
446
  private void writeConf(final PrintWriter pw)
447
  {
448
    String ndx;
449
    String[] mpi;
450
    CPStringBuilder sb;
451
    for (Iterator it = configurations.keySet().iterator(); it.hasNext();)
452
      {
453
        ndx = (String) it.next();
454
        mpi = (String[]) configurations.get(ndx);
455
        sb = new CPStringBuilder(ndx)
456
            .append(":").append(mpi[0])
457
            .append(":").append(mpi[1]);
458
        pw.println(sb.toString());
459
      }
460
  }
461
 
462
  /**
463
   * Compute the new verifiers for the designated username and password.
464
   * <p>
465
   * <b>IMPORTANT:</b> This method computes the verifiers as described in
466
   * RFC-2945, which differs from the description given on the web page for
467
   * SRP-6.
468
   *
469
   * @param user the user's name.
470
   * @param s the user's salt.
471
   * @param password the user's password
472
   * @param index the index of the &lt;N, g> pair to use for this user.
473
   * @return a {@link java.util.Map} of user verifiers.
474
   * @throws UnsupportedEncodingException if the US-ASCII decoder is not
475
   *           available on this platform.
476
   */
477
  private HashMap newVerifiers(final String user, final byte[] s,
478
                               final String password, final String index)
479
      throws UnsupportedEncodingException
480
  {
481
    // to ensure inter-operability with non-java tools
482
    final String[] mpi = (String[]) configurations.get(index);
483
    final BigInteger N = new BigInteger(1, Util.fromBase64(mpi[0]));
484
    final BigInteger g = new BigInteger(1, Util.fromBase64(mpi[1]));
485
    final HashMap result = new HashMap(srps.size());
486
    BigInteger x, v;
487
    SRP srp;
488
    for (int i = 0; i < srps.size(); i++)
489
      {
490
        final String digestID = String.valueOf(i);
491
        srp = (SRP) srps.get(digestID);
492
        x = new BigInteger(1, srp.computeX(s, user, password));
493
        v = g.modPow(x, N);
494
        final String verifier = Util.toBase64(v.toByteArray());
495
        result.put(digestID, verifier);
496
      }
497
    return result;
498
  }
499
 
500
  private synchronized void update() throws IOException
501
  {
502
    entries.clear();
503
    FileInputStream fis;
504
    passwdFile = new File(pwName);
505
    lastmodPasswdFile = passwdFile.lastModified();
506
    try
507
      {
508
        fis = new FileInputStream(passwdFile);
509
        readPasswd(fis);
510
      }
511
    catch (FileNotFoundException ignored)
512
      {
513
      }
514
    passwd2File = new File(pw2Name);
515
    lastmodPasswd2File = passwd2File.lastModified();
516
    try
517
      {
518
        fis = new FileInputStream(passwd2File);
519
        readPasswd2(fis);
520
      }
521
    catch (FileNotFoundException ignored)
522
      {
523
      }
524
  }
525
 
526
  private void checkCurrent() throws IOException
527
  {
528
    if (passwdFile.lastModified() > lastmodPasswdFile
529
        || passwd2File.lastModified() > lastmodPasswd2File)
530
      update();
531
  }
532
 
533
  private void readPasswd(final InputStream in) throws IOException
534
  {
535
    final BufferedReader din = new BufferedReader(new InputStreamReader(in));
536
    String line, user, verifier, salt, index;
537
    StringTokenizer st;
538
    while ((line = din.readLine()) != null)
539
      {
540
        st = new StringTokenizer(line, ":");
541
        try
542
          {
543
            user = st.nextToken();
544
            verifier = st.nextToken();
545
            salt = st.nextToken();
546
            index = st.nextToken();
547
          }
548
        catch (NoSuchElementException x)
549
          {
550
            throw new IOException("SRP base password file corrupt");
551
          }
552
        final HashMap verifiers = new HashMap(6);
553
        verifiers.put("0", verifier);
554
        final HashMap fields = new HashMap(4);
555
        fields.put(USER_FIELD, user);
556
        fields.put(VERIFIERS_FIELD, verifiers);
557
        fields.put(SALT_FIELD, salt);
558
        fields.put(CONFIG_FIELD, index);
559
        entries.put(user, fields);
560
      }
561
  }
562
 
563
  private void readPasswd2(final InputStream in) throws IOException
564
  {
565
    final BufferedReader din = new BufferedReader(new InputStreamReader(in));
566
    String line, digestID, user, verifier;
567
    StringTokenizer st;
568
    HashMap fields, verifiers;
569
    while ((line = din.readLine()) != null)
570
      {
571
        st = new StringTokenizer(line, ":");
572
        try
573
          {
574
            digestID = st.nextToken();
575
            user = st.nextToken();
576
            verifier = st.nextToken();
577
          }
578
        catch (NoSuchElementException x)
579
          {
580
            throw new IOException("SRP extended password file corrupt");
581
          }
582
        fields = (HashMap) entries.get(user);
583
        if (fields != null)
584
          {
585
            verifiers = (HashMap) fields.get(VERIFIERS_FIELD);
586
            verifiers.put(digestID, verifier);
587
          }
588
      }
589
  }
590
 
591
  private void writePasswd(final PrintWriter pw1, final PrintWriter pw2)
592
      throws IOException
593
  {
594
    String user, digestID;
595
    HashMap fields, verifiers;
596
    CPStringBuilder sb1, sb2;
597
    Iterator j;
598
    final Iterator i = entries.keySet().iterator();
599
    while (i.hasNext())
600
      {
601
        user = (String) i.next();
602
        fields = (HashMap) entries.get(user);
603
        if (! user.equals(fields.get(USER_FIELD)))
604
          throw new IOException("Inconsistent SRP password data");
605
        verifiers = (HashMap) fields.get(VERIFIERS_FIELD);
606
        sb1 = new CPStringBuilder(user)
607
            .append(":").append((String) verifiers.get("0"))
608
            .append(":").append((String) fields.get(SALT_FIELD))
609
            .append(":").append((String) fields.get(CONFIG_FIELD));
610
        pw1.println(sb1.toString());
611
        // write extended information
612
        j = verifiers.keySet().iterator();
613
        while (j.hasNext())
614
          {
615
            digestID = (String) j.next();
616
            if (! "0".equals(digestID))
617
              {
618
                // #0 is the default digest, already present in tpasswd!
619
                sb2 = new CPStringBuilder(digestID)
620
                    .append(":").append(user)
621
                    .append(":").append((String) verifiers.get(digestID));
622
                pw2.println(sb2.toString());
623
              }
624
          }
625
      }
626
  }
627
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.