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