ScramPlusMechanism.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
 17import eu.siacs.conversations.entities.Account;
 18
 19abstract class ScramPlusMechanism extends ScramMechanism {
 20
 21    private static final String EXPORTER_LABEL = "EXPORTER-Channel-Binding";
 22
 23    ScramPlusMechanism(Account account, ChannelBinding channelBinding) {
 24        super(account, channelBinding);
 25    }
 26
 27    @Override
 28    protected byte[] getChannelBindingData(final SSLSocket sslSocket)
 29            throws AuthenticationException {
 30        if (sslSocket == null) {
 31            throw new AuthenticationException("Channel binding attempt on non secure socket");
 32        }
 33        if (this.channelBinding == ChannelBinding.TLS_EXPORTER) {
 34            final byte[] keyingMaterial;
 35            try {
 36                keyingMaterial =
 37                        Conscrypt.exportKeyingMaterial(sslSocket, EXPORTER_LABEL, new byte[0], 32);
 38            } catch (final SSLException e) {
 39                throw new AuthenticationException("Could not export keying material");
 40            }
 41            if (keyingMaterial == null) {
 42                throw new AuthenticationException(
 43                        "Could not export keying material. Socket not ready");
 44            }
 45            return keyingMaterial;
 46        } else if (this.channelBinding == ChannelBinding.TLS_UNIQUE) {
 47            final byte[] unique = Conscrypt.getTlsUnique(sslSocket);
 48            if (unique == null) {
 49                throw new AuthenticationException(
 50                        "Could not retrieve tls unique. Socket not ready");
 51            }
 52            return unique;
 53        } else if (this.channelBinding == ChannelBinding.TLS_SERVER_END_POINT) {
 54            final byte[] endPoint = getServerEndPointChannelBinding(sslSocket.getSession());
 55            return endPoint;
 56        } else {
 57            throw new AuthenticationException(
 58                    String.format("%s is not a valid channel binding", channelBinding));
 59        }
 60    }
 61
 62    private byte[] getServerEndPointChannelBinding(final SSLSession session)
 63            throws AuthenticationException {
 64        final Certificate[] certificates;
 65        try {
 66            certificates = session.getPeerCertificates();
 67        } catch (final SSLPeerUnverifiedException e) {
 68            throw new AuthenticationException("Could not verify peer certificates");
 69        }
 70        if (certificates == null || certificates.length == 0) {
 71            throw new AuthenticationException("Could not retrieve peer certificate");
 72        }
 73        final X509Certificate certificate;
 74        if (certificates[0] instanceof X509Certificate) {
 75            certificate = (X509Certificate) certificates[0];
 76        } else {
 77            throw new AuthenticationException("Certificate was not X509");
 78        }
 79        final String algorithm = certificate.getSigAlgName();
 80        final int withIndex = algorithm.indexOf("with");
 81        if (withIndex <= 0) {
 82            throw new AuthenticationException("Unable to parse SigAlgName");
 83        }
 84        final String hashAlgorithm = algorithm.substring(0, withIndex);
 85        final MessageDigest messageDigest;
 86        // https://www.rfc-editor.org/rfc/rfc5929#section-4.1
 87        if ("MD5".equalsIgnoreCase(hashAlgorithm) || "SHA1".equalsIgnoreCase(hashAlgorithm)) {
 88            messageDigest = new SHA256.Digest();
 89        } else {
 90            try {
 91                messageDigest = MessageDigest.getInstance(hashAlgorithm);
 92            } catch (final NoSuchAlgorithmException e) {
 93                throw new AuthenticationException(
 94                        "Could not instantiate message digest for " + hashAlgorithm);
 95            }
 96        }
 97        final byte[] encodedCertificate;
 98        try {
 99            encodedCertificate = certificate.getEncoded();
100        } catch (final CertificateEncodingException e) {
101            throw new AuthenticationException("Could not encode certificate");
102        }
103        messageDigest.update(encodedCertificate);
104        return messageDigest.digest();
105    }
106}