Merge pull request #2233 from SamWhited/scram-sha-2
Add SCRAM-SHA-2 support
This commit is contained in:
		
						commit
						ac2eee8e81
					
				| @ -0,0 +1,228 @@ | ||||
| package eu.siacs.conversations.crypto.sasl; | ||||
| 
 | ||||
| import android.annotation.TargetApi; | ||||
| import android.os.Build; | ||||
| import android.util.Base64; | ||||
| import android.util.LruCache; | ||||
| 
 | ||||
| import org.bouncycastle.crypto.Digest; | ||||
| import org.bouncycastle.crypto.macs.HMac; | ||||
| import org.bouncycastle.crypto.params.KeyParameter; | ||||
| 
 | ||||
| import java.math.BigInteger; | ||||
| import java.nio.charset.Charset; | ||||
| import java.security.InvalidKeyException; | ||||
| import java.security.SecureRandom; | ||||
| 
 | ||||
| import eu.siacs.conversations.entities.Account; | ||||
| import eu.siacs.conversations.utils.CryptoHelper; | ||||
| import eu.siacs.conversations.xml.TagWriter; | ||||
| 
 | ||||
| @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) | ||||
| abstract class ScramMechanism extends SaslMechanism { | ||||
| 	// TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage. | ||||
| 	private final static String GS2_HEADER = "n,,"; | ||||
| 	private String clientFirstMessageBare; | ||||
| 	private final String clientNonce; | ||||
| 	private byte[] serverSignature = null; | ||||
| 	static HMac HMAC; | ||||
| 	static Digest DIGEST; | ||||
| 	private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes(); | ||||
| 	private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes(); | ||||
| 
 | ||||
| 	private static class KeyPair { | ||||
| 		final byte[] clientKey; | ||||
| 		final byte[] serverKey; | ||||
| 
 | ||||
| 		KeyPair(final byte[] clientKey, final byte[] serverKey) { | ||||
| 			this.clientKey = clientKey; | ||||
| 			this.serverKey = serverKey; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	static { | ||||
| 		CACHE = new LruCache<String, KeyPair>(10) { | ||||
| 			protected KeyPair create(final String k) { | ||||
| 				// Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations". | ||||
| 				// Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()' | ||||
| 				// is applied to prevent commas in the strings breaking things. | ||||
| 				final String[] kparts = k.split(",", 4); | ||||
| 				try { | ||||
| 					final byte[] saltedPassword, serverKey, clientKey; | ||||
| 					saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(), | ||||
| 							Base64.decode(CryptoHelper.hexToString(kparts[2]), Base64.DEFAULT), Integer.valueOf(kparts[3])); | ||||
| 					serverKey = hmac(saltedPassword, SERVER_KEY_BYTES); | ||||
| 					clientKey = hmac(saltedPassword, CLIENT_KEY_BYTES); | ||||
| 
 | ||||
| 					return new KeyPair(clientKey, serverKey); | ||||
| 				} catch (final InvalidKeyException | NumberFormatException e) { | ||||
| 					return null; | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	private static final LruCache<String, KeyPair> CACHE; | ||||
| 
 | ||||
| 	protected State state = State.INITIAL; | ||||
| 
 | ||||
| 	ScramMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) { | ||||
| 		super(tagWriter, account, rng); | ||||
| 
 | ||||
| 		// This nonce should be different for each authentication attempt. | ||||
| 		clientNonce = new BigInteger(100, this.rng).toString(32); | ||||
| 		clientFirstMessageBare = ""; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getClientFirstMessage() { | ||||
| 		if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) { | ||||
| 			clientFirstMessageBare = "n=" + CryptoHelper.saslEscape(CryptoHelper.saslPrep(account.getUsername())) + | ||||
| 				",r=" + this.clientNonce; | ||||
| 			state = State.AUTH_TEXT_SENT; | ||||
| 		} | ||||
| 		return Base64.encodeToString( | ||||
| 				(GS2_HEADER + clientFirstMessageBare).getBytes(Charset.defaultCharset()), | ||||
| 				Base64.NO_WRAP); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getResponse(final String challenge) throws AuthenticationException { | ||||
| 		switch (state) { | ||||
| 			case AUTH_TEXT_SENT: | ||||
| 				if (challenge == null) { | ||||
| 					throw new AuthenticationException("challenge can not be null"); | ||||
| 				} | ||||
| 				byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT); | ||||
| 				final Tokenizer tokenizer = new Tokenizer(serverFirstMessage); | ||||
| 				String nonce = ""; | ||||
| 				int iterationCount = -1; | ||||
| 				String salt = ""; | ||||
| 				for (final String token : tokenizer) { | ||||
| 					if (token.charAt(1) == '=') { | ||||
| 						switch (token.charAt(0)) { | ||||
| 							case 'i': | ||||
| 								try { | ||||
| 									iterationCount = Integer.parseInt(token.substring(2)); | ||||
| 								} catch (final NumberFormatException e) { | ||||
| 									throw new AuthenticationException(e); | ||||
| 								} | ||||
| 								break; | ||||
| 							case 's': | ||||
| 								salt = token.substring(2); | ||||
| 								break; | ||||
| 							case 'r': | ||||
| 								nonce = token.substring(2); | ||||
| 								break; | ||||
| 							case 'm': | ||||
| 								/* | ||||
| 								 * RFC 5802: | ||||
| 								 * m: This attribute is reserved for future extensibility.  In this | ||||
| 								 * version of SCRAM, its presence in a client or a server message | ||||
| 								 * MUST cause authentication failure when the attribute is parsed by | ||||
| 								 * the other end. | ||||
| 								 */ | ||||
| 								throw new AuthenticationException("Server sent reserved token: `m'"); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if (iterationCount < 0) { | ||||
| 					throw new AuthenticationException("Server did not send iteration count"); | ||||
| 				} | ||||
| 				if (nonce.isEmpty() || !nonce.startsWith(clientNonce)) { | ||||
| 					throw new AuthenticationException("Server nonce does not contain client nonce: " + nonce); | ||||
| 				} | ||||
| 				if (salt.isEmpty()) { | ||||
| 					throw new AuthenticationException("Server sent empty salt"); | ||||
| 				} | ||||
| 
 | ||||
| 				final String clientFinalMessageWithoutProof = "c=" + Base64.encodeToString( | ||||
| 						GS2_HEADER.getBytes(), Base64.NO_WRAP) + ",r=" + nonce; | ||||
| 				final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ',' | ||||
| 						+ clientFinalMessageWithoutProof).getBytes(); | ||||
| 
 | ||||
| 				// Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations". | ||||
| 				final KeyPair keys = CACHE.get( | ||||
| 						CryptoHelper.bytesToHex(account.getJid().toBareJid().toString().getBytes()) + "," | ||||
| 						+ CryptoHelper.bytesToHex(account.getPassword().getBytes()) + "," | ||||
| 						+ CryptoHelper.bytesToHex(salt.getBytes()) + "," | ||||
| 						+ String.valueOf(iterationCount) | ||||
| 						); | ||||
| 				if (keys == null) { | ||||
| 					throw new AuthenticationException("Invalid keys generated"); | ||||
| 				} | ||||
| 				final byte[] clientSignature; | ||||
| 				try { | ||||
| 					serverSignature = hmac(keys.serverKey, authMessage); | ||||
| 					final byte[] storedKey = digest(keys.clientKey); | ||||
| 
 | ||||
| 					clientSignature = hmac(storedKey, authMessage); | ||||
| 
 | ||||
| 				} catch (final InvalidKeyException e) { | ||||
| 					throw new AuthenticationException(e); | ||||
| 				} | ||||
| 
 | ||||
| 				final byte[] clientProof = new byte[keys.clientKey.length]; | ||||
| 
 | ||||
| 				for (int i = 0; i < clientProof.length; i++) { | ||||
| 					clientProof[i] = (byte) (keys.clientKey[i] ^ clientSignature[i]); | ||||
| 				} | ||||
| 
 | ||||
| 
 | ||||
| 				final String clientFinalMessage = clientFinalMessageWithoutProof + ",p=" + | ||||
| 					Base64.encodeToString(clientProof, Base64.NO_WRAP); | ||||
| 				state = State.RESPONSE_SENT; | ||||
| 				return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP); | ||||
| 			case RESPONSE_SENT: | ||||
| 				try { | ||||
| 					final String clientCalculatedServerFinalMessage = "v=" + | ||||
| 						Base64.encodeToString(serverSignature, Base64.NO_WRAP); | ||||
| 					if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) { | ||||
| 						throw new Exception(); | ||||
| 					} | ||||
| 					state = State.VALID_SERVER_RESPONSE; | ||||
| 					return ""; | ||||
| 				} catch(Exception e) { | ||||
| 					throw new AuthenticationException("Server final message does not match calculated final message"); | ||||
| 				} | ||||
| 			default: | ||||
| 				throw new InvalidStateException(state); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static synchronized byte[] hmac(final byte[] key, final byte[] input) | ||||
| 		throws InvalidKeyException { | ||||
| 		HMAC.init(new KeyParameter(key)); | ||||
| 		HMAC.update(input, 0, input.length); | ||||
| 		final byte[] out = new byte[HMAC.getMacSize()]; | ||||
| 		HMAC.doFinal(out, 0); | ||||
| 		return out; | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized byte[] digest(byte[] bytes) { | ||||
| 		DIGEST.reset(); | ||||
| 		DIGEST.update(bytes, 0, bytes.length); | ||||
| 		final byte[] out = new byte[DIGEST.getDigestSize()]; | ||||
| 		DIGEST.doFinal(out, 0); | ||||
| 		return out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the | ||||
| 	 * pseudorandom function (PRF) and with dkLen == output length of | ||||
| 	 * HMAC() == output length of H(). | ||||
| 	 */ | ||||
| 	private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations) | ||||
| 		throws InvalidKeyException { | ||||
| 		byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE)); | ||||
| 		byte[] out = u.clone(); | ||||
| 		for (int i = 1; i < iterations; i++) { | ||||
| 			u = hmac(key, u); | ||||
| 			for (int j = 0; j < u.length; j++) { | ||||
| 				out[j] ^= u[j]; | ||||
| 			} | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
| } | ||||
| @ -1,77 +1,21 @@ | ||||
| package eu.siacs.conversations.crypto.sasl; | ||||
| 
 | ||||
| import android.util.Base64; | ||||
| import android.util.LruCache; | ||||
| 
 | ||||
| import org.bouncycastle.crypto.Digest; | ||||
| import org.bouncycastle.crypto.digests.SHA1Digest; | ||||
| import org.bouncycastle.crypto.macs.HMac; | ||||
| import org.bouncycastle.crypto.params.KeyParameter; | ||||
| 
 | ||||
| import java.math.BigInteger; | ||||
| import java.nio.charset.Charset; | ||||
| import java.security.InvalidKeyException; | ||||
| import java.security.SecureRandom; | ||||
| 
 | ||||
| import eu.siacs.conversations.entities.Account; | ||||
| import eu.siacs.conversations.utils.CryptoHelper; | ||||
| import eu.siacs.conversations.xml.TagWriter; | ||||
| 
 | ||||
| public class ScramSha1 extends SaslMechanism { | ||||
| 	// TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage. | ||||
| 	final private static String GS2_HEADER = "n,,"; | ||||
| 	private String clientFirstMessageBare; | ||||
| 	final private String clientNonce; | ||||
| 	private byte[] serverSignature = null; | ||||
| 	private static HMac HMAC; | ||||
| 	private static Digest DIGEST; | ||||
| 	private static final byte[] CLIENT_KEY_BYTES = "Client Key".getBytes(); | ||||
| 	private static final byte[] SERVER_KEY_BYTES = "Server Key".getBytes(); | ||||
| 
 | ||||
| 	public static class KeyPair { | ||||
| 		final public byte[] clientKey; | ||||
| 		final public byte[] serverKey; | ||||
| 
 | ||||
| 		public KeyPair(final byte[] clientKey, final byte[] serverKey) { | ||||
| 			this.clientKey = clientKey; | ||||
| 			this.serverKey = serverKey; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static final LruCache<String, KeyPair> CACHE; | ||||
| 
 | ||||
| public class ScramSha1 extends ScramMechanism { | ||||
| 	static { | ||||
| 		DIGEST = new SHA1Digest(); | ||||
| 		HMAC = new HMac(new SHA1Digest()); | ||||
| 		CACHE = new LruCache<String, KeyPair>(10) { | ||||
| 			protected KeyPair create(final String k) { | ||||
| 				// Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations". | ||||
| 				// Changing any of these values forces a cache miss. `CryptoHelper.bytesToHex()' | ||||
| 				// is applied to prevent commas in the strings breaking things. | ||||
| 				final String[] kparts = k.split(",", 4); | ||||
| 				try { | ||||
| 					final byte[] saltedPassword, serverKey, clientKey; | ||||
| 					saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(), | ||||
| 							Base64.decode(CryptoHelper.hexToString(kparts[2]), Base64.DEFAULT), Integer.valueOf(kparts[3])); | ||||
| 					serverKey = hmac(saltedPassword, SERVER_KEY_BYTES); | ||||
| 					clientKey = hmac(saltedPassword, CLIENT_KEY_BYTES); | ||||
| 
 | ||||
| 					return new KeyPair(clientKey, serverKey); | ||||
| 				} catch (final InvalidKeyException | NumberFormatException e) { | ||||
| 					return null; | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	private State state = State.INITIAL; | ||||
| 
 | ||||
| 	public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) { | ||||
| 		super(tagWriter, account, rng); | ||||
| 
 | ||||
| 		// This nonce should be different for each authentication attempt. | ||||
| 		clientNonce = new BigInteger(100, this.rng).toString(32); | ||||
| 		clientFirstMessageBare = ""; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| @ -83,156 +27,4 @@ public class ScramSha1 extends SaslMechanism { | ||||
| 	public String getMechanism() { | ||||
| 		return "SCRAM-SHA-1"; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getClientFirstMessage() { | ||||
| 		if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) { | ||||
| 			clientFirstMessageBare = "n=" + CryptoHelper.saslEscape(CryptoHelper.saslPrep(account.getUsername())) + | ||||
| 				",r=" + this.clientNonce; | ||||
| 			state = State.AUTH_TEXT_SENT; | ||||
| 		} | ||||
| 		return Base64.encodeToString( | ||||
| 				(GS2_HEADER + clientFirstMessageBare).getBytes(Charset.defaultCharset()), | ||||
| 				Base64.NO_WRAP); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getResponse(final String challenge) throws AuthenticationException { | ||||
| 		switch (state) { | ||||
| 			case AUTH_TEXT_SENT: | ||||
| 				if (challenge == null) { | ||||
| 					throw new AuthenticationException("challenge can not be null"); | ||||
| 				} | ||||
| 				byte[] serverFirstMessage = Base64.decode(challenge, Base64.DEFAULT); | ||||
| 				final Tokenizer tokenizer = new Tokenizer(serverFirstMessage); | ||||
| 				String nonce = ""; | ||||
| 				int iterationCount = -1; | ||||
| 				String salt = ""; | ||||
| 				for (final String token : tokenizer) { | ||||
| 					if (token.charAt(1) == '=') { | ||||
| 						switch (token.charAt(0)) { | ||||
| 							case 'i': | ||||
| 								try { | ||||
| 									iterationCount = Integer.parseInt(token.substring(2)); | ||||
| 								} catch (final NumberFormatException e) { | ||||
| 									throw new AuthenticationException(e); | ||||
| 								} | ||||
| 								break; | ||||
| 							case 's': | ||||
| 								salt = token.substring(2); | ||||
| 								break; | ||||
| 							case 'r': | ||||
| 								nonce = token.substring(2); | ||||
| 								break; | ||||
| 							case 'm': | ||||
| 								/* | ||||
| 								 * RFC 5802: | ||||
| 								 * m: This attribute is reserved for future extensibility.  In this | ||||
| 								 * version of SCRAM, its presence in a client or a server message | ||||
| 								 * MUST cause authentication failure when the attribute is parsed by | ||||
| 								 * the other end. | ||||
| 								 */ | ||||
| 								throw new AuthenticationException("Server sent reserved token: `m'"); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				if (iterationCount < 0) { | ||||
| 					throw new AuthenticationException("Server did not send iteration count"); | ||||
| 				} | ||||
| 				if (nonce.isEmpty() || !nonce.startsWith(clientNonce)) { | ||||
| 					throw new AuthenticationException("Server nonce does not contain client nonce: " + nonce); | ||||
| 				} | ||||
| 				if (salt.isEmpty()) { | ||||
| 					throw new AuthenticationException("Server sent empty salt"); | ||||
| 				} | ||||
| 
 | ||||
| 				final String clientFinalMessageWithoutProof = "c=" + Base64.encodeToString( | ||||
| 						GS2_HEADER.getBytes(), Base64.NO_WRAP) + ",r=" + nonce; | ||||
| 				final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ',' | ||||
| 						+ clientFinalMessageWithoutProof).getBytes(); | ||||
| 
 | ||||
| 				// Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations". | ||||
| 				final KeyPair keys = CACHE.get( | ||||
| 						CryptoHelper.bytesToHex(account.getJid().toBareJid().toString().getBytes()) + "," | ||||
| 						+ CryptoHelper.bytesToHex(account.getPassword().getBytes()) + "," | ||||
| 						+ CryptoHelper.bytesToHex(salt.getBytes()) + "," | ||||
| 						+ String.valueOf(iterationCount) | ||||
| 						); | ||||
| 				if (keys == null) { | ||||
| 					throw new AuthenticationException("Invalid keys generated"); | ||||
| 				} | ||||
| 				final byte[] clientSignature; | ||||
| 				try { | ||||
| 					serverSignature = hmac(keys.serverKey, authMessage); | ||||
| 					final byte[] storedKey = digest(keys.clientKey); | ||||
| 
 | ||||
| 					clientSignature = hmac(storedKey, authMessage); | ||||
| 
 | ||||
| 				} catch (final InvalidKeyException e) { | ||||
| 					throw new AuthenticationException(e); | ||||
| 				} | ||||
| 
 | ||||
| 				final byte[] clientProof = new byte[keys.clientKey.length]; | ||||
| 
 | ||||
| 				for (int i = 0; i < clientProof.length; i++) { | ||||
| 					clientProof[i] = (byte) (keys.clientKey[i] ^ clientSignature[i]); | ||||
| 				} | ||||
| 
 | ||||
| 
 | ||||
| 				final String clientFinalMessage = clientFinalMessageWithoutProof + ",p=" + | ||||
| 					Base64.encodeToString(clientProof, Base64.NO_WRAP); | ||||
| 				state = State.RESPONSE_SENT; | ||||
| 				return Base64.encodeToString(clientFinalMessage.getBytes(), Base64.NO_WRAP); | ||||
| 			case RESPONSE_SENT: | ||||
| 				try { | ||||
| 					final String clientCalculatedServerFinalMessage = "v=" + | ||||
| 							Base64.encodeToString(serverSignature, Base64.NO_WRAP); | ||||
| 					if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) { | ||||
| 						throw new Exception(); | ||||
| 					}; | ||||
| 					state = State.VALID_SERVER_RESPONSE; | ||||
| 					return ""; | ||||
| 				} catch(Exception e) { | ||||
| 					throw new AuthenticationException("Server final message does not match calculated final message"); | ||||
| 				} | ||||
| 			default: | ||||
| 				throw new InvalidStateException(state); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized byte[] hmac(final byte[] key, final byte[] input) | ||||
| 		throws InvalidKeyException { | ||||
| 		HMAC.init(new KeyParameter(key)); | ||||
| 		HMAC.update(input, 0, input.length); | ||||
| 		final byte[] out = new byte[HMAC.getMacSize()]; | ||||
| 		HMAC.doFinal(out, 0); | ||||
| 		return out; | ||||
| 	} | ||||
| 
 | ||||
| 	public static synchronized byte[] digest(byte[] bytes) { | ||||
| 		DIGEST.reset(); | ||||
| 		DIGEST.update(bytes, 0, bytes.length); | ||||
| 		final byte[] out = new byte[DIGEST.getDigestSize()]; | ||||
| 		DIGEST.doFinal(out, 0); | ||||
| 		return out; | ||||
| 	} | ||||
| 
 | ||||
| 	/* | ||||
| 	 * Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the | ||||
| 	 * pseudorandom function (PRF) and with dkLen == output length of | ||||
| 	 * HMAC() == output length of H(). | ||||
| 	 */ | ||||
| 	private static synchronized byte[] hi(final byte[] key, final byte[] salt, final int iterations) | ||||
| 		throws InvalidKeyException { | ||||
| 		byte[] u = hmac(key, CryptoHelper.concatenateByteArrays(salt, CryptoHelper.ONE)); | ||||
| 		byte[] out = u.clone(); | ||||
| 		for (int i = 1; i < iterations; i++) { | ||||
| 			u = hmac(key, u); | ||||
| 			for (int j = 0; j < u.length; j++) { | ||||
| 				out[j] ^= u[j]; | ||||
| 			} | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,30 @@ | ||||
| package eu.siacs.conversations.crypto.sasl; | ||||
| 
 | ||||
| import org.bouncycastle.crypto.digests.SHA256Digest; | ||||
| import org.bouncycastle.crypto.macs.HMac; | ||||
| 
 | ||||
| import java.security.SecureRandom; | ||||
| 
 | ||||
| import eu.siacs.conversations.entities.Account; | ||||
| import eu.siacs.conversations.xml.TagWriter; | ||||
| 
 | ||||
| public class ScramSha256 extends ScramMechanism { | ||||
| 	static { | ||||
| 		DIGEST = new SHA256Digest(); | ||||
| 		HMAC = new HMac(new SHA256Digest()); | ||||
| 	} | ||||
| 
 | ||||
| 	public ScramSha256(final TagWriter tagWriter, final Account account, final SecureRandom rng) { | ||||
| 		super(tagWriter, account, rng); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public int getPriority() { | ||||
| 		return 25; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public String getMechanism() { | ||||
| 		return "SCRAM-SHA-256"; | ||||
| 	} | ||||
| } | ||||
| @ -13,8 +13,6 @@ import android.util.Log; | ||||
| import android.util.Pair; | ||||
| import android.util.SparseArray; | ||||
| 
 | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
| import org.xmlpull.v1.XmlPullParserException; | ||||
| 
 | ||||
| import java.io.ByteArrayInputStream; | ||||
| @ -26,8 +24,8 @@ import java.net.IDN; | ||||
| import java.net.InetAddress; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.net.Socket; | ||||
| import java.net.UnknownHostException; | ||||
| import java.net.URL; | ||||
| import java.net.UnknownHostException; | ||||
| import java.security.KeyManagementException; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.security.Principal; | ||||
| @ -61,6 +59,7 @@ import eu.siacs.conversations.crypto.sasl.External; | ||||
| import eu.siacs.conversations.crypto.sasl.Plain; | ||||
| import eu.siacs.conversations.crypto.sasl.SaslMechanism; | ||||
| import eu.siacs.conversations.crypto.sasl.ScramSha1; | ||||
| import eu.siacs.conversations.crypto.sasl.ScramSha256; | ||||
| import eu.siacs.conversations.entities.Account; | ||||
| import eu.siacs.conversations.entities.Message; | ||||
| import eu.siacs.conversations.entities.ServiceDiscoveryResult; | ||||
| @ -855,6 +854,8 @@ public class XmppConnection implements Runnable { | ||||
| 		auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); | ||||
| 		if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) { | ||||
| 			saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG()); | ||||
| 		} else if (mechanisms.contains("SCRAM-SHA-256")) { | ||||
| 			saslMechanism = new ScramSha256(tagWriter, account, mXmppConnectionService.getRNG()); | ||||
| 		} else if (mechanisms.contains("SCRAM-SHA-1")) { | ||||
| 			saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG()); | ||||
| 		} else if (mechanisms.contains("PLAIN")) { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch