diff options
Diffstat (limited to 'src/main/java/de/pixart/messenger/crypto')
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 |