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