001package com.nimbusds.srp6;
002
003
004import java.math.BigInteger;
005import java.security.MessageDigest;
006import java.security.SecureRandom;
007import java.util.HashMap;
008import java.util.Map;
009
010
011/**
012 * The base abstract class for client and server-side Secure Remote Password 
013 * (SRP-6a) authentication sessions.
014 *
015 * @author Vladimir Dzhuvinov
016 * @author John Kim
017 */
018public abstract class SRP6Session {
019        
020
021        /**
022         * The crypto configuration.
023         */
024        protected SRP6CryptoParams config;
025        
026        
027        /**
028         * Message digest (not thread-safe).
029         */
030        protected MessageDigest digest;
031        
032        
033        /**
034         * Source of randomness.
035         */
036        protected final SecureRandom random = new SecureRandom();
037        
038        
039        /**
040         * The SRP-6a authentication session timeout in seconds. If the 
041         * authenticating counterparty (server or client) fails to respond 
042         * within the specified time the session will be closed. Zero implies
043         * no timeout.
044         */
045        protected final int timeout;
046        
047        
048        /**
049         * The last activity timestamp, from System.currentTimeMillis().
050         */
051        protected long lastActivity;
052        
053        
054        /**
055         * The identity 'I' of the authenticating user.
056         */
057        protected String userID = null;
058        
059        
060        /**
061         * The password salt 's'.
062         */
063        protected BigInteger s = null;
064        
065        
066        /**
067         * The client public value 'A'.
068         */
069        protected BigInteger A = null;
070        
071        
072        /**
073         * The server public value 'B'.
074         */
075        protected BigInteger B = null;
076        
077        
078        /**
079         * The random scrambling parameter 'u'.
080         */
081        protected BigInteger u = null;
082        
083        
084        /**
085         * The multiplier 'k'.
086         */
087        protected BigInteger k = null;
088        
089        
090        /**
091         * The shared session key 'S'.
092         */
093        protected BigInteger S = null;
094        
095        
096        /**
097         * The client evidence message 'M1'.
098         */
099        protected BigInteger M1 = null;
100        
101        
102        /**
103         * The server evidence message 'M2'.
104         */
105        protected BigInteger M2 = null;
106        
107        
108        /**
109         * Custom routine for the client evidence message 'M1' computation.
110         */
111        protected ClientEvidenceRoutine clientEvidenceRoutine = null;
112        
113        
114        /**
115         * Custom routine for the server evidence message 'M2' computation.
116         */
117        protected ServerEvidenceRoutine serverEvidenceRoutine = null;
118
119
120        /**
121         * Custom routine for the hashed keys 'u' computation.
122         */
123        protected URoutine hashedKeysRoutine = null;
124
125
126        /**
127         * Optional storage of arbitrary session attributes.
128         */
129        private Map<String,Object> attributes = null;
130        
131        
132        /**
133         * Creates a new SRP-6a authentication session.
134         *
135         * @param timeout The SRP-6a authentication session timeout in seconds. 
136         *                If the authenticating counterparty (server or client) 
137         *                fails to respond within the specified time the
138         *                session will be closed. If zero timeouts are
139         *                disabled.
140         */
141        public SRP6Session(final int timeout) {
142        
143                if (timeout < 0)
144                        throw new IllegalArgumentException("The timeout must be zero (no timeout) or greater");
145                
146                this.timeout = timeout;
147        }
148        
149        
150        /**
151         * Creates a new SRP-6a authentication session, session timeouts are 
152         * disabled.
153         */
154        public SRP6Session() {
155        
156                this(0);
157        }
158        
159        
160        /**
161         * Updates the last activity timestamp.
162         */
163        protected void updateLastActivityTime() {
164        
165                lastActivity = System.currentTimeMillis();
166        }
167        
168        
169        /**
170         * Gets the last session activity timestamp, in milliseconds since 
171         * midnight, January 1, 1970 UTC (see System.currentTimeMillis()).
172         *
173         * @return The last activity timestamp.
174         */
175        public long getLastActivityTime() {
176        
177                return lastActivity;
178        }
179        
180        
181        /**
182         * Returns {@code true} if the session has timed out, based on the 
183         * timeout configuration and the last activity timestamp.
184         *
185         * @return {@code true} if the session has timed out, else 
186         *         {@code false}.
187         */
188        public boolean hasTimedOut() {
189        
190                if (timeout == 0)
191                        return false;
192        
193                final long now = System.currentTimeMillis();
194
195                return now > lastActivity + (timeout * 1000);
196        }
197        
198        
199        /**
200         * Gets the SRP-6a crypto parameters for this session.
201         *
202         * @return The SRP-6a crypto parameters, {@code null} if undefined.
203         */
204        public SRP6CryptoParams getCryptoParams() {
205        
206                return config;
207        }
208        
209        
210        /**
211         * Gets the identity 'I' of the authenticating user.
212         *
213         * @return The user identity 'I', {@code null} if undefined.
214         */
215        public String getUserID() {
216        
217                return userID;
218        }
219        
220        
221        /**
222         * Gets the SRP-6a authentication session timeout.
223         *
224         * @return The SRP-6a authentication session timeout, in seconds. Zero
225         *         implies to timeout.
226         */
227        public int getTimeout() {
228        
229                return timeout;
230        }
231        
232        
233        /**
234         * Sets a custom routine to compute the client evidence message 'M1'.
235         * Note that the custom routine must be set prior to 
236         * {@link SRP6ClientSession.State#STEP_2} or
237         * {@link SRP6ServerSession.State#STEP_2}.
238         *
239         * @param routine The client evidence message 'M1' routine or 
240         *                {@code null} to use the default 
241         *                {@link SRP6Routines#computeClientEvidence}.
242         */
243        public void setClientEvidenceRoutine(final ClientEvidenceRoutine routine) {
244        
245                clientEvidenceRoutine = routine;
246        }
247        
248        
249        /**
250         * Gets the custom routine to compute the client evidence message 'M1'.
251         *
252         * @return The routine instance or {@code null} if the default 
253         *         {@link SRP6Routines#computeClientEvidence} is used.
254         */
255        public ClientEvidenceRoutine getClientEvidenceRoutine() {
256        
257                return clientEvidenceRoutine;
258        }
259        
260        
261        /**
262         * Sets a custom routine to compute the server evidence message 'M2'.
263         * Note that the custom routine must be set prior to 
264         * {@link SRP6ClientSession.State#STEP_3} or
265         * {@link SRP6ServerSession.State#STEP_2}.
266         *
267         * @param routine The server evidence message 'M2' routine or 
268         *                {@code null} to use the default 
269         *                {@link SRP6Routines#computeServerEvidence}.
270         */
271        public void setServerEvidenceRoutine(final ServerEvidenceRoutine routine) {
272        
273                serverEvidenceRoutine = routine;
274        }
275        
276        
277        /**
278         * Gets the custom routine to compute the server evidence message 'M2'.
279         *
280         * @return The routine instance or {@code null} if the default 
281         *         {@link SRP6Routines#computeServerEvidence} is used.
282         */
283        public ServerEvidenceRoutine getServerEvidenceRoutine() {
284        
285                return serverEvidenceRoutine;
286        }
287
288
289        /**
290         * Gets the custom routine to compute hashed keys 'u' a 'H(A | B)'.
291         * 
292         * @return The routine instance or {@code null} if the default
293         *         {@link SRP6Routines#computeU} is to be used.
294         */
295        public URoutine getHashedKeysRoutine() {
296
297                return hashedKeysRoutine;
298        }
299
300
301        /**
302         * Sets a custom routine to compute hashed keys 'u' a 'H(A | B)'. Note
303         * that the custom routine must be set prior to
304         * {@link SRP6ServerSession.State#STEP_2}.
305         * 
306         * @param hashedKeysRoutine The hashed keys 'u' routine or {@code null}
307         *                          to use the default
308         *                          {@link SRP6Routines#computeU}.
309         */
310        public void setHashedKeysRoutine(final URoutine hashedKeysRoutine) {
311
312                this.hashedKeysRoutine = hashedKeysRoutine;
313        }
314
315
316        /**
317         * Gets the password salt 's'.
318         * 
319         * @return The salt 's' if available, else {@code null}.
320         */
321        public BigInteger getSalt() {
322        
323                return s;
324        }
325        
326        
327        /**
328         * Gets the public client value 'A'.
329         *
330         * @return The public client value 'A' if available, else {@code null}.
331         */
332        public BigInteger getPublicClientValue() {
333        
334                return A;
335        }
336        
337        
338        /**
339         * Gets the public server value 'B'.
340         *
341         * @return The public server value 'B' if available, else {@code null}.
342         */
343        public BigInteger getPublicServerValue() {
344        
345                return B;
346        }
347        
348        
349        /**
350         * Gets the client evidence message 'M1'.
351         *
352         * @return The client evidence message 'M1' if available, else
353         *         {@code null}.
354         */
355        public BigInteger getClientEvidenceMessage() {
356        
357                return M1;
358        }
359        
360        
361        /**
362         * Gets the server evidence message 'M2'.
363         *
364         * @return The server evidence message 'M2' if available, else
365         *         {@code null}.
366         */
367        public BigInteger getServerEvidenceMessage() {
368        
369                return M2;
370        }
371        
372        
373        /**
374         * Gets the shared session key 'S' or its hash H(S).
375         *
376         * @param doHash If {@code true} the hash H(S) of the session key will
377         *               be returned instead of the raw value.
378         *
379         * @return The shared session key 'S' or its hash H(S). {@code null} 
380         *         will be returned if authentication failed or the method is
381         *         invoked in a session state when the session key 'S' has not
382         *         been computed yet.
383         */
384        public BigInteger getSessionKey(final boolean doHash) {
385        
386                if (S == null)
387                        return null;
388        
389                if (doHash) {
390                        digest.reset();
391                        return new BigInteger(digest.digest(S.toByteArray()));
392                }
393                else {
394                        return S;
395                }
396        }
397        
398        
399        /**
400         * Sets a session attribute. This method can be used to store arbitrary
401         * objects with this session and retrieve them later with 
402         * {@link #getAttribute}.
403         *
404         * @param key   The attribute key. Must not be {@code null}.
405         * @param value The attribute value. May be {@code null}.
406         */
407        public void setAttribute(final String key, final Object value) {
408        
409                if (key == null)
410                        throw new IllegalArgumentException("The attribute key must not be null");
411                        
412                // create new attribute map on demand
413                if (attributes == null)
414                        attributes = new HashMap<>();
415                
416                attributes.put(key, value);
417        }
418        
419        
420        /**
421         * Gets a session attribute. This method can be used to retrieve
422         * arbitrary objects stored with this session with 
423         * {@link #setAttribute}.
424         *
425         * @param key The attribute key. Must not be {@code null}.
426         *
427         * @return The attribute value, {@code null} if none was found by the
428         *         specified key or its value is {@code null}.
429         */
430        public Object getAttribute(final String key) {
431        
432                if (key == null)
433                        throw new IllegalArgumentException("The attribute key must not be null");
434                
435                if (attributes == null)
436                        return null;
437                
438                return attributes.get(key);
439        }
440}