aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/pixart/messenger/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/pixart/messenger/crypto')
-rw-r--r--src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java123
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java102
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java4
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java26
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java1
-rw-r--r--src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java120
6 files changed, 199 insertions, 177 deletions
diff --git a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
index 3f3c358a7..80a6086aa 100644
--- a/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
+++ b/src/main/java/de/pixart/messenger/crypto/XmppDomainVerifier.java
@@ -32,6 +32,70 @@ public class XmppDomainVerifier implements DomainHostnameVerifier {
private static final String SRV_NAME = "1.3.6.1.5.5.7.8.7";
private static final String XMPP_ADDR = "1.3.6.1.5.5.7.8.5";
+ private static List<String> getCommonNames(X509Certificate certificate) {
+ List<String> domains = new ArrayList<>();
+ try {
+ X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
+ RDN[] rdns = x500name.getRDNs(BCStyle.CN);
+ for (int i = 0; i < rdns.length; ++i) {
+ domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
+ }
+ return domains;
+ } catch (CertificateEncodingException e) {
+ return domains;
+ }
+ }
+
+ private static Pair<String, String> parseOtherName(byte[] otherName) {
+ try {
+ ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
+ if (asn1Primitive instanceof DERTaggedObject) {
+ ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
+ if (inner instanceof DLSequence) {
+ DLSequence sequence = (DLSequence) inner;
+ if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
+ String oid = sequence.getObjectAt(0).toString();
+ ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
+ if (value instanceof DERUTF8String) {
+ return new Pair<>(oid, ((DERUTF8String) value).getString());
+ } else if (value instanceof DERIA5String) {
+ return new Pair<>(oid, ((DERIA5String) value).getString());
+ }
+ }
+ }
+ }
+ return null;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static boolean matchDomain(String needle, List<String> haystack) {
+ for (String entry : haystack) {
+ if (entry.startsWith("*.")) {
+ int offset = 0;
+ while (offset < needle.length()) {
+ int i = needle.indexOf('.', offset);
+ if (i < 0) {
+ break;
+ }
+ Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
+ if (needle.substring(i).equals(entry.substring(1))) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ offset = i + 1;
+ }
+ } else {
+ if (entry.equals(needle)) {
+ Log.d(LOGTAG, "domain " + needle + " matched " + entry);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public boolean verify(String domain, String hostname, SSLSession sslSession) {
try {
@@ -92,63 +156,6 @@ public class XmppDomainVerifier implements DomainHostnameVerifier {
}
}
- private static List<String> getCommonNames(X509Certificate certificate) {
- List<String> domains = new ArrayList<>();
- try {
- X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
- RDN[] rdns = x500name.getRDNs(BCStyle.CN);
- for (int i = 0; i < rdns.length; ++i) {
- domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue()));
- }
- return domains;
- } catch (CertificateEncodingException e) {
- return domains;
- }
- }
-
- private static Pair<String, String> parseOtherName(byte[] otherName) {
- try {
- ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName);
- if (asn1Primitive instanceof DERTaggedObject) {
- ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject();
- if (inner instanceof DLSequence) {
- DLSequence sequence = (DLSequence) inner;
- if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) {
- String oid = sequence.getObjectAt(0).toString();
- ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject();
- if (value instanceof DERUTF8String) {
- return new Pair<>(oid, ((DERUTF8String) value).getString());
- } else if (value instanceof DERIA5String) {
- return new Pair<>(oid, ((DERIA5String) value).getString());
- }
- }
- }
- }
- return null;
- } catch (IOException e) {
- return null;
- }
- }
-
- private static boolean matchDomain(String needle, List<String> haystack) {
- for (String entry : haystack) {
- if (entry.startsWith("*.")) {
- int i = needle.indexOf('.');
- Log.d(LOGTAG, "comparing " + needle.substring(i) + " and " + entry.substring(1));
- if (i != -1 && needle.substring(i).equals(entry.substring(1))) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- } else {
- if (entry.equals(needle)) {
- Log.d(LOGTAG, "domain " + needle + " matched " + entry);
- return true;
- }
- }
- }
- return false;
- }
-
private boolean isSelfSigned(X509Certificate certificate) {
try {
certificate.verify(certificate.getPublicKey());
@@ -162,4 +169,4 @@ public class XmppDomainVerifier implements DomainHostnameVerifier {
public boolean verify(String domain, SSLSession sslSession) {
return verify(domain, null, sslSession);
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
index 02062eba1..cc9d85008 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/AxolotlService.java
@@ -483,9 +483,18 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
publishOwnDeviceId(deviceIds);
}
}
+ final Set<Integer> oldSet = this.deviceIds.get(jid);
+ final boolean changed = oldSet == null || oldSet.hashCode() != hash;
this.deviceIds.put(jid, deviceIds);
- mXmppConnectionService.updateConversationUi(); //update the lock icon
- mXmppConnectionService.keyStatusUpdated(null);
+ if (changed) {
+ mXmppConnectionService.updateConversationUi(); //update the lock icon
+ mXmppConnectionService.keyStatusUpdated(null);
+ if (me) {
+ mXmppConnectionService.updateAccountUi();
+ }
+ } else {
+ Log.d(Config.LOGTAG, "skipped device list update because it hasn't changed");
+ }
}
public void wipeOtherPepDevices() {
@@ -516,6 +525,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids.");
} else {
+ //TODO consider calling registerDevices only after item-not-found to account for broken PEPs
Element item = mXmppConnectionService.getIqParser().getItem(packet);
Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retrieved own device list: " + deviceIds);
@@ -540,7 +550,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": own device " + session.getFingerprint() + " was active " + hours + " hours ago");
}
- }
+ } //TODO print last activation diff
}
}
return devices;
@@ -1012,28 +1022,33 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
if (packet != null) {
mXmppConnectionService.sendIqPacket(account, packet, (account, response) -> {
- synchronized (fetchDeviceIdsMap) {
- List<OnDeviceIdsFetched> callbacks = fetchDeviceIdsMap.remove(jid);
- if (response.getType() == IqPacket.TYPE.RESULT) {
- fetchDeviceListStatus.put(jid, true);
- Element item = mXmppConnectionService.getIqParser().getItem(response);
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
- registerDevices(jid, deviceIds);
- if (callbacks != null) {
- for (OnDeviceIdsFetched c : callbacks) {
- c.fetched(jid, deviceIds);
- }
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ fetchDeviceListStatus.put(jid, true);
+ Element item = mXmppConnectionService.getIqParser().getItem(response);
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ registerDevices(jid, deviceIds);
+ final List<OnDeviceIdsFetched> callbacks;
+ synchronized (fetchDeviceIdsMap) {
+ callbacks = fetchDeviceIdsMap.remove(jid);
+ }
+ if (callbacks != null) {
+ for (OnDeviceIdsFetched c : callbacks) {
+ c.fetched(jid, deviceIds);
}
+ }
+ } else {
+ if (response.getType() == IqPacket.TYPE.TIMEOUT) {
+ fetchDeviceListStatus.remove(jid);
} else {
- if (response.getType() == IqPacket.TYPE.TIMEOUT) {
- fetchDeviceListStatus.remove(jid);
- } else {
- fetchDeviceListStatus.put(jid, false);
- }
- if (callbacks != null) {
- for (OnDeviceIdsFetched c : callbacks) {
- c.fetched(jid, null);
- }
+ fetchDeviceListStatus.put(jid, false);
+ }
+ final List<OnDeviceIdsFetched> callbacks;
+ synchronized (fetchDeviceIdsMap) {
+ callbacks = fetchDeviceIdsMap.remove(jid);
+ }
+ if (callbacks != null) {
+ for (OnDeviceIdsFetched c : callbacks) {
+ c.fetched(jid, null);
}
}
}
@@ -1148,8 +1163,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
Set<SignalProtocolAddress> addresses = new HashSet<>();
for (Jid jid : getCryptoTargets(conversation)) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + jid);
- if (deviceIds.get(jid) != null) {
- for (Integer foreignId : this.deviceIds.get(jid)) {
+ final Set<Integer> ids = deviceIds.get(jid);
+ if (ids != null && !ids.isEmpty()) {
+ for (Integer foreignId : ids) {
SignalProtocolAddress address = new SignalProtocolAddress(jid.toString(), foreignId);
if (sessions.get(address) == null) {
IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
@@ -1172,22 +1188,21 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
}
}
- if (deviceIds.get(account.getJid().asBareJid()) != null) {
- for (Integer ownId : this.deviceIds.get(account.getJid().asBareJid())) {
- SignalProtocolAddress address = new SignalProtocolAddress(account.getJid().asBareJid().toString(), ownId);
- if (sessions.get(address) == null) {
- IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
- if (identityKey != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
- XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
- sessions.put(address, session);
+ Set<Integer> ownIds = this.deviceIds.get(account.getJid().asBareJid());
+ for (Integer ownId : (ownIds != null ? ownIds : new HashSet<Integer>())) {
+ SignalProtocolAddress address = new SignalProtocolAddress(account.getJid().asBareJid().toString(), ownId);
+ if (sessions.get(address) == null) {
+ IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
+ if (identityKey != null) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache...");
+ XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey);
+ sessions.put(address, session);
+ } else {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().asBareJid() + ":" + ownId);
+ if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
+ addresses.add(address);
} else {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().asBareJid() + ":" + ownId);
- if (fetchStatusMap.get(address) != FetchStatus.ERROR) {
- addresses.add(address);
- } else {
- Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
- }
+ Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken");
}
}
}
@@ -1206,12 +1221,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": createSessionsIfNeeded() - jids with empty device list: " + jidsWithEmptyDeviceList);
if (jidsWithEmptyDeviceList.size() > 0) {
- fetchDeviceIds(jidsWithEmptyDeviceList, new OnMultipleDeviceIdFetched() {
- @Override
- public void fetched() {
- createSessionsIfNeededActual(conversation);
- }
- });
+ fetchDeviceIds(jidsWithEmptyDeviceList, () -> createSessionsIfNeededActual(conversation));
return true;
} else {
return createSessionsIfNeededActual(conversation);
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
index d47df0a0b..cfe8bea4d 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/FingerprintStatus.java
@@ -78,6 +78,10 @@ public class FingerprintStatus implements Comparable<FingerprintStatus> {
return status;
}
+ public static FingerprintStatus createActive(Boolean trusted) {
+ return createActive(trusted != null && trusted);
+ }
+
public static FingerprintStatus createActive(boolean trusted) {
final FingerprintStatus status = new FingerprintStatus();
status.trust = trusted ? Trust.TRUSTED : Trust.UNTRUSTED;
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
index f9b2539c3..71fbd40db 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -27,12 +27,12 @@ import rocks.xmpp.addr.Jid;
public class XmppAxolotlMessage {
public static final String CONTAINERTAG = "encrypted";
- public static final String HEADER = "header";
- public static final String SOURCEID = "sid";
- public static final String KEYTAG = "key";
- public static final String REMOTEID = "rid";
- public static final String IVTAG = "iv";
- public static final String PAYLOAD = "payload";
+ private static final String HEADER = "header";
+ private static final String SOURCEID = "sid";
+ private static final String KEYTAG = "key";
+ private static final String REMOTEID = "rid";
+ private static final String IVTAG = "iv";
+ private static final String PAYLOAD = "payload";
private static final String KEYTYPE = "AES";
private static final String CIPHERMODE = "AES/GCM/NoPadding";
@@ -50,7 +50,7 @@ public class XmppAxolotlMessage {
private final String plaintext;
private final String fingerprint;
- public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
+ XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
this.plaintext = plaintext;
this.fingerprint = fingerprint;
}
@@ -70,7 +70,7 @@ public class XmppAxolotlMessage {
private final byte[] key;
private final byte[] iv;
- public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
+ XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) {
this.fingerprint = fingerprint;
this.key = key;
this.iv = iv;
@@ -140,7 +140,7 @@ public class XmppAxolotlMessage {
}
}
- public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
+ XmppAxolotlMessage(Jid from, int sourceDeviceId) {
this.from = from;
this.sourceDeviceId = sourceDeviceId;
this.keys = new SparseArray<>();
@@ -174,11 +174,11 @@ public class XmppAxolotlMessage {
return ciphertext != null;
}
- public void encrypt(String plaintext) throws CryptoFailedException {
+ void encrypt(String plaintext) throws CryptoFailedException {
try {
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
this.ciphertext = cipher.doFinal(Config.OMEMO_PADDING ? getPaddedBytes(plaintext) : plaintext.getBytes());
if (Config.PUT_AUTH_TAG_INTO_KEY && this.ciphertext != null) {
@@ -273,7 +273,7 @@ public class XmppAxolotlMessage {
return session.processReceiving(encryptedKey);
}
- public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
+ XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
return new XmppAxolotlKeyTransportMessage(session.getFingerprint(), unpackKey(session, sourceDeviceId), getIV());
}
@@ -294,7 +294,7 @@ public class XmppAxolotlMessage {
key = newKey;
}
- Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
index 09045f032..caa7f9c23 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlSession.java
@@ -110,6 +110,7 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> {
}
if (!status.isActive()) {
setTrust(status.toActive());
+ //TODO: also (re)add to device list?
}
} else {
throw new CryptoFailedException("not encrypting omemo message from fingerprint "+getFingerprint()+" because it was marked as compromised");
diff --git a/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java b/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java
index 046b68fda..e1a153ef6 100644
--- a/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java
+++ b/src/main/java/de/pixart/messenger/crypto/sasl/ScramMechanism.java
@@ -22,31 +22,19 @@ import de.pixart.messenger.xml.TagWriter;
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;
- }
- }
+ private static final LruCache<String, KeyPair> CACHE;
+ static HMac HMAC;
+ static Digest DIGEST;
static {
CACHE = new LruCache<String, KeyPair>(10) {
protected KeyPair create(final String k) {
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations,SASL-Mechanism".
// 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);
+ final String[] kparts = k.split(",", 5);
try {
final byte[] saltedPassword, serverKey, clientKey;
saltedPassword = hi(CryptoHelper.hexToString(kparts[1]).getBytes(),
@@ -62,9 +50,10 @@ abstract class ScramMechanism extends SaslMechanism {
};
}
- private static final LruCache<String, KeyPair> CACHE;
-
+ private final String clientNonce;
protected State state = State.INITIAL;
+ private String clientFirstMessageBare;
+ private byte[] serverSignature = null;
ScramMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
super(tagWriter, account, rng);
@@ -74,6 +63,41 @@ abstract class ScramMechanism extends SaslMechanism {
clientFirstMessageBare = "";
}
+ 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;
+ }
+
@Override
public String getClientFirstMessage() {
if (clientFirstMessageBare.isEmpty() && state == State.INITIAL) {
@@ -120,13 +144,13 @@ abstract class ScramMechanism extends SaslMechanism {
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.
- */
+ /*
+ * 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'");
}
}
@@ -147,12 +171,13 @@ abstract class ScramMechanism extends SaslMechanism {
final byte[] authMessage = (clientFirstMessageBare + ',' + new String(serverFirstMessage) + ','
+ clientFinalMessageWithoutProof).getBytes();
- // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations".
+ // Map keys are "bytesToHex(JID),bytesToHex(password),bytesToHex(salt),iterations,SASL-Mechanism".
final KeyPair keys = CACHE.get(
CryptoHelper.bytesToHex(account.getJid().asBareJid().toString().getBytes()) + ","
+ CryptoHelper.bytesToHex(account.getPassword().getBytes()) + ","
+ CryptoHelper.bytesToHex(salt.getBytes()) + ","
- + String.valueOf(iterationCount)
+ + String.valueOf(iterationCount) + ","
+ + getMechanism()
);
if (keys == null) {
throw new AuthenticationException("Invalid keys generated");
@@ -188,7 +213,7 @@ abstract class ScramMechanism extends SaslMechanism {
}
state = State.VALID_SERVER_RESPONSE;
return "";
- } catch(Exception e) {
+ } catch (Exception e) {
throw new AuthenticationException("Server final message does not match calculated final message");
}
default:
@@ -196,38 +221,13 @@ abstract class ScramMechanism extends SaslMechanism {
}
}
- 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;
- }
+ private static class KeyPair {
+ final byte[] clientKey;
+ final byte[] serverKey;
- /*
- * 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];
- }
+ KeyPair(final byte[] clientKey, final byte[] serverKey) {
+ this.clientKey = clientKey;
+ this.serverKey = serverKey;
}
- return out;
}
} \ No newline at end of file