1: 
  37: 
  38: 
  39: package ;
  40: 
  41: import ;
  42: import ;
  43: import ;
  44: import ;
  45: import ;
  46: import ;
  47: import ;
  48: import ;
  49: import ;
  50: import ;
  51: import ;
  52: import ;
  53: 
  54: import ;
  55: import ;
  56: import ;
  57: import ;
  58: import ;
  59: import ;
  60: import ;
  61: import ;
  62: import ;
  63: import ;
  64: 
  65: 
  68: public final class PasswordAuthenticatedEntry
  69:     extends MaskableEnvelopeEntry
  70:     implements PasswordProtectedEntry, Registry
  71: {
  72:   private static final Logger log = Logger.getLogger(PasswordAuthenticatedEntry.class.getName());
  73:   public static final int TYPE = 3;
  74: 
  75:   public PasswordAuthenticatedEntry(String mac, int maclen,
  76:                                     Properties properties)
  77:   {
  78:     super(TYPE, properties);
  79:     if (mac == null || mac.length() == 0)
  80:       throw new IllegalArgumentException("no MAC specified");
  81:     this.properties.put("mac", mac);
  82:     this.properties.put("maclen", String.valueOf(maclen));
  83:     setMasked(false);
  84:   }
  85: 
  86:   private PasswordAuthenticatedEntry()
  87:   {
  88:     super(TYPE);
  89:     setMasked(true);
  90:   }
  91: 
  92:   public static PasswordAuthenticatedEntry decode(DataInputStream in,
  93:                                                   char[] password)
  94:       throws IOException
  95:   {
  96:     PasswordAuthenticatedEntry entry = new PasswordAuthenticatedEntry();
  97:     entry.properties.decode(in);
  98:     IMac mac = entry.getMac(password);
  99:     int len = in.readInt() - mac.macSize();
 100:     MeteredInputStream min = new MeteredInputStream(in, len);
 101:     MacInputStream macin = new MacInputStream(min, mac);
 102:     DataInputStream in2 = new DataInputStream(macin);
 103:     entry.setMasked(false);
 104:     entry.decodeEnvelope(in2);
 105:     byte[] macValue = new byte[mac.macSize()];
 106:     in.readFully(macValue);
 107:     if (! Arrays.equals(macValue, mac.digest()))
 108:       throw new MalformedKeyringException("MAC verification failed");
 109:     return entry;
 110:   }
 111: 
 112:   public static PasswordAuthenticatedEntry decode(DataInputStream in)
 113:       throws IOException
 114:   {
 115:     PasswordAuthenticatedEntry entry = new PasswordAuthenticatedEntry();
 116:     entry.defaultDecode(in);
 117:     if (! entry.properties.containsKey("mac"))
 118:       throw new MalformedKeyringException("no MAC");
 119:     if (! entry.properties.containsKey("maclen"))
 120:       throw new MalformedKeyringException("no MAC length");
 121:     if (! entry.properties.containsKey("salt"))
 122:       throw new MalformedKeyringException("no salt");
 123:     return entry;
 124:   }
 125: 
 126:   public void verify(char[] password)
 127:   {
 128:     if (Configuration.DEBUG)
 129:       log.entering(this.getClass().getName(), "verify");
 130:     if (isMasked() && payload != null)
 131:       {
 132:         if (Configuration.DEBUG)
 133:           log.fine("payload to verify: " + Util.dumpString(payload));
 134:         long tt = -System.currentTimeMillis();
 135:         IMac m = null;
 136:         try
 137:           {
 138:             m = getMac(password);
 139:           }
 140:         catch (Exception x)
 141:           {
 142:             throw new IllegalArgumentException(x.toString(), x);
 143:           }
 144:         int limit = payload.length - m.macSize();
 145:         m.update(payload, 0, limit);
 146:         byte[] macValue = new byte[m.macSize()];
 147:         System.arraycopy(payload, payload.length - macValue.length, macValue,
 148:                          0, macValue.length);
 149:         if (! Arrays.equals(macValue, m.digest()))
 150:           throw new IllegalArgumentException("MAC verification failed");
 151:         setMasked(false);
 152:         ByteArrayInputStream bais;
 153:         try
 154:           {
 155:             bais = new ByteArrayInputStream(payload, 0, limit);
 156:             DataInputStream in = new DataInputStream(bais);
 157:             decodeEnvelope(in);
 158:           }
 159:         catch (IOException ioe)
 160:           {
 161:             throw new IllegalArgumentException("malformed keyring fragment");
 162:           }
 163:         tt += System.currentTimeMillis();
 164:         if (Configuration.DEBUG)
 165:           log.fine("Verified in " + tt + "ms.");
 166:       }
 167:     else if (Configuration.DEBUG)
 168:       log.fine("Skip verification; "
 169:                + (isMasked() ? "null payload" : "unmasked"));
 170:     if (Configuration.DEBUG)
 171:       log.exiting(this.getClass().getName(), "verify");
 172:   }
 173: 
 174:   public void authenticate(char[] password) throws IOException
 175:   {
 176:     if (Configuration.DEBUG)
 177:       log.entering(this.getClass().getName(), "authenticate");
 178:     long tt = -System.currentTimeMillis();
 179:     long t1 = -System.currentTimeMillis();
 180:     if (isMasked())
 181:       throw new IllegalStateException("entry is masked");
 182:     byte[] salt = new byte[8];
 183:     PRNG.getInstance().nextBytes(salt);
 184:     t1 += System.currentTimeMillis();
 185:     if (Configuration.DEBUG)
 186:       log.fine("-- Generated salt in " + t1 + "ms.");
 187:     properties.put("salt", Util.toString(salt));
 188:     IMac m = getMac(password);
 189:     ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
 190:     MacOutputStream macout = new MacOutputStream(bout, m);
 191:     DataOutputStream out2 = new DataOutputStream(macout);
 192:     for (Iterator it = entries.iterator(); it.hasNext();)
 193:       {
 194:         Entry entry = (Entry) it.next();
 195:         if (Configuration.DEBUG)
 196:           log.fine("-- About to authenticate one " + entry);
 197:         t1 = -System.currentTimeMillis();
 198:         entry.encode(out2);
 199:         t1 += System.currentTimeMillis();
 200:         if (Configuration.DEBUG)
 201:           log.fine("-- Authenticated an Entry in " + t1 + "ms.");
 202:       }
 203:     bout.write(m.digest());
 204:     payload = bout.toByteArray();
 205:     if (Configuration.DEBUG)
 206:       log.fine("authenticated payload: " + Util.dumpString(payload));
 207:     setMasked(true);
 208:     tt += System.currentTimeMillis();
 209:     if (Configuration.DEBUG)
 210:       {
 211:         log.fine("Authenticated in " + tt + "ms.");
 212:         log.exiting(this.getClass().getName(), "authenticate");
 213:       }
 214:   }
 215: 
 216:   public void encode(DataOutputStream out, char[] password) throws IOException
 217:   {
 218:     authenticate(password);
 219:     encode(out);
 220:   }
 221: 
 222:   protected void encodePayload(DataOutputStream out) throws IOException
 223:   {
 224:     if (payload == null)
 225:       {
 226:         log.fine("Null payload: " + this);
 227:         throw new IllegalStateException("mac not computed");
 228:       }
 229:   }
 230: 
 231:   private IMac getMac(char[] password) throws MalformedKeyringException
 232:   {
 233:     if (Configuration.DEBUG)
 234:       log.entering(this.getClass().getName(), "getMac");
 235:     String saltString = properties.get("salt");
 236:     if (saltString == null)
 237:       throw new MalformedKeyringException("no salt");
 238:     byte[] salt = Util.toBytesFromString(saltString);
 239:     String macAlgorithm = properties.get("mac");
 240:     IMac mac = MacFactory.getInstance(macAlgorithm);
 241:     if (mac == null)
 242:       throw new MalformedKeyringException("no such mac: " + macAlgorithm);
 243:     String macLenString = properties.get("maclen");
 244:     if (macLenString == null)
 245:       throw new MalformedKeyringException("no MAC length");
 246:     int maclen;
 247:     try
 248:       {
 249:         maclen = Integer.parseInt(macLenString);
 250:       }
 251:     catch (NumberFormatException nfe)
 252:       {
 253:         throw new MalformedKeyringException("bad MAC length");
 254:       }
 255:     HashMap pbAttr = new HashMap();
 256:     pbAttr.put(IPBE.PASSWORD, password);
 257:     pbAttr.put(IPBE.SALT, salt);
 258:     pbAttr.put(IPBE.ITERATION_COUNT, ITERATION_COUNT);
 259:     IRandom kdf = PRNGFactory.getInstance("PBKDF2-HMAC-SHA");
 260:     kdf.init(pbAttr);
 261:     int keylen = mac.macSize();
 262:     byte[] dk = new byte[keylen];
 263:     try
 264:       {
 265:         kdf.nextBytes(dk, 0, keylen);
 266:       }
 267:     catch (LimitReachedException shouldNotHappen)
 268:       {
 269:         throw new Error(shouldNotHappen.toString());
 270:       }
 271:     HashMap macAttr = new HashMap();
 272:     macAttr.put(IMac.MAC_KEY_MATERIAL, dk);
 273:     macAttr.put(IMac.TRUNCATED_SIZE, Integer.valueOf(maclen));
 274:     try
 275:       {
 276:         mac.init(macAttr);
 277:       }
 278:     catch (InvalidKeyException shouldNotHappen)
 279:       {
 280:         throw new Error(shouldNotHappen.toString());
 281:       }
 282:     if (Configuration.DEBUG)
 283:       log.exiting(this.getClass().getName(), "getMac");
 284:     return mac;
 285:   }
 286: }