ChannelBindingMechanism.java

  1package eu.siacs.conversations.crypto.sasl;
  2
  3import org.bouncycastle.jcajce.provider.digest.SHA256;
  4import org.conscrypt.Conscrypt;
  5
  6import java.security.MessageDigest;
  7import java.security.NoSuchAlgorithmException;
  8import java.security.cert.Certificate;
  9import java.security.cert.CertificateEncodingException;
 10import java.security.cert.X509Certificate;
 11
 12import javax.net.ssl.SSLException;
 13import javax.net.ssl.SSLPeerUnverifiedException;
 14import javax.net.ssl.SSLSession;
 15import javax.net.ssl.SSLSocket;
 16
 17public interface ChannelBindingMechanism {
 18
 19    String EXPORTER_LABEL = "EXPORTER-Channel-Binding";
 20
 21    ChannelBinding getChannelBinding();
 22
 23    static byte[] getChannelBindingData(final SSLSocket sslSocket, final ChannelBinding channelBinding)
 24            throws SaslMechanism.AuthenticationException {
 25        if (sslSocket == null) {
 26            throw new SaslMechanism.AuthenticationException("Channel binding attempt on non secure socket");
 27        }
 28        if (channelBinding == ChannelBinding.TLS_EXPORTER) {
 29            final byte[] keyingMaterial;
 30            try {
 31                keyingMaterial =
 32                        Conscrypt.exportKeyingMaterial(sslSocket, EXPORTER_LABEL, new byte[0], 32);
 33            } catch (final SSLException e) {
 34                throw new SaslMechanism.AuthenticationException("Could not export keying material");
 35            }
 36            if (keyingMaterial == null) {
 37                throw new SaslMechanism.AuthenticationException(
 38                        "Could not export keying material. Socket not ready");
 39            }
 40            return keyingMaterial;
 41        } else if (channelBinding == ChannelBinding.TLS_UNIQUE) {
 42            final byte[] unique = Conscrypt.getTlsUnique(sslSocket);
 43            if (unique == null) {
 44                throw new SaslMechanism.AuthenticationException(
 45                        "Could not retrieve tls unique. Socket not ready");
 46            }
 47            return unique;
 48        } else if (channelBinding == ChannelBinding.TLS_SERVER_END_POINT) {
 49            return getServerEndPointChannelBinding(sslSocket.getSession());
 50        } else {
 51            throw new SaslMechanism.AuthenticationException(
 52                    String.format("%s is not a valid channel binding", channelBinding));
 53        }
 54    }
 55
 56    static byte[] getServerEndPointChannelBinding(final SSLSession session)
 57            throws SaslMechanism.AuthenticationException {
 58        final Certificate[] certificates;
 59        try {
 60            certificates = session.getPeerCertificates();
 61        } catch (final SSLPeerUnverifiedException e) {
 62            throw new SaslMechanism.AuthenticationException("Could not verify peer certificates");
 63        }
 64        if (certificates == null || certificates.length == 0) {
 65            throw new SaslMechanism.AuthenticationException("Could not retrieve peer certificate");
 66        }
 67        final X509Certificate certificate;
 68        if (certificates[0] instanceof X509Certificate) {
 69            certificate = (X509Certificate) certificates[0];
 70        } else {
 71            throw new SaslMechanism.AuthenticationException("Certificate was not X509");
 72        }
 73        final String algorithm = certificate.getSigAlgName();
 74        final int withIndex = algorithm.indexOf("with");
 75        if (withIndex <= 0) {
 76            throw new SaslMechanism.AuthenticationException("Unable to parse SigAlgName");
 77        }
 78        final String hashAlgorithm = algorithm.substring(0, withIndex);
 79        final MessageDigest messageDigest;
 80        // https://www.rfc-editor.org/rfc/rfc5929#section-4.1
 81        if ("MD5".equalsIgnoreCase(hashAlgorithm) || "SHA1".equalsIgnoreCase(hashAlgorithm)) {
 82            messageDigest = new SHA256.Digest();
 83        } else {
 84            try {
 85                messageDigest = MessageDigest.getInstance(hashAlgorithm);
 86            } catch (final NoSuchAlgorithmException e) {
 87                throw new SaslMechanism.AuthenticationException(
 88                        "Could not instantiate message digest for " + hashAlgorithm);
 89            }
 90        }
 91        final byte[] encodedCertificate;
 92        try {
 93            encodedCertificate = certificate.getEncoded();
 94        } catch (final CertificateEncodingException e) {
 95            throw new SaslMechanism.AuthenticationException("Could not encode certificate");
 96        }
 97        messageDigest.update(encodedCertificate);
 98        return messageDigest.digest();
 99    }
100
101    static int getPriority(final SaslMechanism mechanism) {
102        if (mechanism instanceof ChannelBindingMechanism) {
103            final ChannelBindingMechanism channelBindingMechanism = (ChannelBindingMechanism) mechanism;
104            return ChannelBinding.priority(channelBindingMechanism.getChannelBinding());
105        } else {
106            return 0;
107        }
108    }
109}