support channel binding with tls-exporter

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java      | 19 
src/main/java/eu/siacs/conversations/crypto/sasl/ScramMechanism.java     |  3 
src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java | 23 
src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1Plus.java      |  2 
4 files changed, 32 insertions(+), 15 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java 🔗

@@ -18,6 +18,14 @@ public abstract class SaslMechanism {
         this.account = account;
     }
 
+    public static String namespace(final Version version) {
+        if (version == Version.SASL) {
+            return Namespace.SASL;
+        } else {
+            return Namespace.SASL_2;
+        }
+    }
+
     /**
      * The priority is used to pin the authentication mechanism. If authentication fails, it MAY be
      * retried with another mechanism of the same priority, but MUST NOT be tried with a mechanism
@@ -97,6 +105,9 @@ public abstract class SaslMechanism {
                 final Collection<String> mechanisms, final Collection<ChannelBinding> bindings) {
             if (mechanisms.contains(External.MECHANISM) && account.getPrivateKeyAlias() != null) {
                 return new External(account);
+            } else if (mechanisms.contains(ScramSha1Plus.MECHANISM)
+                    && bindings.contains(ChannelBinding.TLS_EXPORTER)) {
+                return new ScramSha1Plus(account, ChannelBinding.TLS_EXPORTER);
             } else if (mechanisms.contains(ScramSha512.MECHANISM)) {
                 return new ScramSha512(account);
             } else if (mechanisms.contains(ScramSha256.MECHANISM)) {
@@ -115,12 +126,4 @@ public abstract class SaslMechanism {
             }
         }
     }
-
-    public static String namespace(final Version version) {
-        if (version == Version.SASL) {
-            return Namespace.SASL;
-        } else {
-            return Namespace.SASL_2;
-        }
-    }
 }

src/main/java/eu/siacs/conversations/crypto/sasl/ScramMechanism.java 🔗

@@ -258,7 +258,8 @@ abstract class ScramMechanism extends SaslMechanism {
         }
     }
 
-    protected byte[] getChannelBindingData(final SSLSocket sslSocket) throws AuthenticationException {
+    protected byte[] getChannelBindingData(final SSLSocket sslSocket)
+            throws AuthenticationException {
         if (this.channelBinding == ChannelBinding.NONE) {
             return new byte[0];
         }

src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java 🔗

@@ -1,22 +1,35 @@
 package eu.siacs.conversations.crypto.sasl;
 
+import org.conscrypt.Conscrypt;
+
+import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSocket;
 
 import eu.siacs.conversations.entities.Account;
 
 abstract class ScramPlusMechanism extends ScramMechanism {
+
+    private static final String EXPORTER_LABEL = "EXPORTER-Channel-Binding";
+
     ScramPlusMechanism(Account account, ChannelBinding channelBinding) {
         super(account, channelBinding);
     }
 
     @Override
-    protected byte[] getChannelBindingData(final SSLSocket sslSocket) throws AuthenticationException {
-        if (this.channelBinding == ChannelBinding.NONE) {
-            throw new AuthenticationException(String.format("%s is not a valid channel binding", ChannelBinding.NONE));
-        }
+    protected byte[] getChannelBindingData(final SSLSocket sslSocket)
+            throws AuthenticationException {
         if (sslSocket == null) {
             throw new AuthenticationException("Channel binding attempt on non secure socket");
         }
-        throw new AssertionError("not yet implemented");
+        if (this.channelBinding == ChannelBinding.TLS_EXPORTER) {
+            try {
+                return Conscrypt.exportKeyingMaterial(sslSocket, EXPORTER_LABEL, new byte[0], 32);
+            } catch (final SSLException e) {
+                throw new AuthenticationException("Could not export keying material");
+            }
+        } else {
+            throw new AuthenticationException(
+                    String.format("%s is not a valid channel binding", ChannelBinding.NONE));
+        }
     }
 }