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: }