001package com.bitbucket.thinbus.srp6.js; 002 003import static com.nimbusds.srp6.BigIntegerUtils.fromHex; 004import static com.nimbusds.srp6.BigIntegerUtils.toHex; 005 006import java.io.Serializable; 007import java.math.BigInteger; 008 009import com.nimbusds.srp6.SRP6CryptoParams; 010import com.nimbusds.srp6.SRP6Exception; 011import com.nimbusds.srp6.SRP6Routines; 012import com.nimbusds.srp6.SRP6ServerSession; 013import com.nimbusds.srp6.SRP6ServerSession.State; 014 015abstract public class SRP6JavascriptServerSession implements Serializable { 016 017 /** 018 * Serializable class version number 019 */ 020 private static final long serialVersionUID = -5998252135527603869L; 021 022 /** 023 * Increments this SRP-6a authentication session to {@link State#STEP_1}. 024 * 025 * @param username 026 * The identity 'I' of the authenticating user. Must not be 027 * {@code null} or empty. 028 * @param salt 029 * The password salt 's'. Must not be {@code null}. 030 * @param v 031 * The password verifier 'v'. Must not be {@code null}. 032 * 033 * @return The server public value 'B' as hex encoded number. 034 * 035 * @throws IllegalStateException 036 * If the mehod is invoked in a state other than 037 * {@link State#INIT}. 038 */ 039 public String step1(final String username, final String salt, final String v) { 040 BigInteger B = session.step1(username, fromHex(salt), fromHex(v)); 041 return toHex(B); 042 } 043 044 /** 045 * Increments this SRP-6a authentication session to {@link State#STEP_2}. 046 * 047 * @param A 048 * The client public value. Must not be {@code null}. 049 * @param M1 050 * The client evidence message. Must not be {@code null}. 051 * 052 * @return The server evidence message 'M2' has hex encoded number with 053 * leading zero padding to match the 256bit hash length. 054 * 055 * @throws SRP6Exception 056 * If the client public value 'A' is invalid or the user 057 * credentials are invalid. 058 * 059 * @throws IllegalStateException 060 * If the mehod is invoked in a state other than 061 * {@link State#STEP_1}. 062 */ 063 public String step2(final String A, final String M1) throws Exception { 064 BigInteger M2 = session.step2(fromHex(A), fromHex(M1)); 065 String M2str = toHex(M2); 066 M2str = HexHashedRoutines.leadingZerosPad(M2str, HASH_HEX_LENGTH); 067 return M2str; 068 } 069 070 /** 071 * Returns the underlying session state as a String for JavaScript testing. 072 * 073 * @return The current state. 074 */ 075 public String getState() { 076 return session.getState().name(); 077 } 078 079 /** 080 * Gets the identity 'I' of the authenticating user. 081 * 082 * @return The user identity 'I', null if undefined. 083 */ 084 public String getUserID() { 085 return session.getUserID(); 086 } 087 088 /** 089 * The crypto parameters for the SRP-6a protocol. These must be agreed 090 * between client and server before authentication and consist of a large 091 * safe prime 'N', a corresponding generator 'g' and a hash function 092 * algorithm 'H'. You can generate your own with openssl using 093 * {@link OpenSSLCryptoConfig} 094 * 095 */ 096 protected final SRP6CryptoParams config; 097 098 /** 099 * The underlying Nimbus session which will be configure for JavaScript 100 * interactions 101 */ 102 protected final SRP6ServerSession session; 103 104 /** 105 * Constructs a JavaScript compatible server session which configures an 106 * underlying Nimbus SRP6ServerSession. 107 * 108 * @param srp6CryptoParams 109 * cryptographic constants which must match those being used by 110 * the client. 111 */ 112 public SRP6JavascriptServerSession(SRP6CryptoParams srp6CryptoParams) { 113 this.config = srp6CryptoParams; 114 session = new SRP6ServerSession(config); 115 session.setHashedKeysRoutine(new HexHashedURoutine()); 116 session.setClientEvidenceRoutine(new HexHashedClientEvidenceRoutine()); 117 session.setServerEvidenceRoutine(new HexHashedServerEvidenceRoutine()); 118 } 119 120 /** 121 * k is actually fixed and done with hash padding routine which uses 122 * java.net.BigInteger byte array constructor so this is a convenience 123 * method to get at the Java generated value to use in the configuration of 124 * the Javascript 125 * 126 * @return 'k' calculated as H( N, g ) 127 */ 128 public String k() { 129 return toHex(SRP6Routines.computeK(config.getMessageDigestInstance(), config.N, config.g)); 130 } 131 132 /** 133 * Turn a radix10 string into a java.net.BigInteger 134 * 135 * @param base10 136 * the radix10 string 137 * @return the BigInteger representation of the number 138 */ 139 public static BigInteger fromDecimal(String base10) { 140 return new BigInteger(base10, 10); 141 } 142 143 /** 144 * This must match the expected character length of the specified algorithm 145 */ 146 public static int HASH_HEX_LENGTH; 147 148 /** 149 * Outputs the configuration in the way which can be used to configure 150 * JavaScript. 151 * 152 * Note that 'k' is fixed but uses the byte array constructor of BigInteger 153 * which is not available in JavaScript to you must set it as configuration. 154 * 155 * @return Parameters required by JavaScript client. 156 */ 157 @Override 158 public String toString() { 159 StringBuilder builder = new StringBuilder(); 160 builder.append(String.format("g: %s\n", config.g.toString(10))); 161 builder.append(String.format("N: %s\n", config.N.toString(10))); 162 builder.append(String.format("k: %s\n", k())); 163 return builder.toString(); 164 } 165 166 /** 167 * Gets the password salt 's'. 168 * 169 * @return The salt 's' if available, else {@code null}. 170 */ 171 public String getSalt() { 172 return toHex(session.getSalt()); 173 } 174 175 /** 176 * Gets the public server value 'B'. 177 * 178 * @return The public server value 'B' if available, else {@code null}. 179 */ 180 public String getPublicServerValue() { 181 return toHex(session.getPublicServerValue()); 182 } 183 184 /** 185 * Gets the server evidence message 'M2'. 186 * 187 * @return The server evidence message 'M2' if available, else {@code null}. 188 */ 189 public String getServerEvidenceMessage() { 190 return toHex(session.getServerEvidenceMessage()); 191 } 192 193 /** 194 * Gets the shared session key 'S' or its hash H(S). 195 * 196 * @param doHash 197 * If {@code true} the hash H(S) of the session key will be 198 * returned instead of the raw value. 199 * 200 * @return The shared session key 'S' or its hash H(S). {@code null} will be 201 * returned if authentication failed or the method is invoked in a 202 * session state when the session key 'S' has not been computed yet. 203 */ 204 public String getSessionKey(boolean doHash) { 205 String S = toHex(session.getSessionKey(false)); 206 if (doHash) { 207 String K = HexHashedRoutines.toHexString(this.config 208 .getMessageDigestInstance().digest( 209 S.getBytes(HexHashedRoutines.utf8))); 210 return K; 211 } else { 212 return S; 213 } 214 } 215}