2 * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.transport.tls;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.netconf.transport.tls.ConfigUtils.setAsymmetricKey;
12 import static org.opendaylight.netconf.transport.tls.ConfigUtils.setEndEntityCertificateWithKey;
13 import static org.opendaylight.netconf.transport.tls.ConfigUtils.setX509Certificates;
14 import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.buildKeyManagerFactory;
15 import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.buildTrustManagerFactory;
16 import static org.opendaylight.netconf.transport.tls.KeyStoreUtils.newKeyStore;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableMap;
20 import io.netty.handler.ssl.SslContext;
21 import io.netty.handler.ssl.SslContextBuilder;
22 import java.security.KeyStore;
23 import java.util.List;
25 import javax.net.ssl.KeyManagerFactory;
26 import javax.net.ssl.SSLException;
27 import javax.net.ssl.TrustManagerFactory;
28 import org.eclipse.jdt.annotation.NonNull;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.opendaylight.netconf.transport.api.AbstractOverlayTransportStack;
31 import org.opendaylight.netconf.transport.api.TransportChannel;
32 import org.opendaylight.netconf.transport.api.TransportChannelListener;
33 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.CipherSuiteAlgBase;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes128CcmSha256;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes128GcmSha256;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsAes256GcmSha384;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsChacha20Poly1305Sha256;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes128Ccm;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes128GcmSha256;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes256Ccm;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithAes256GcmSha384;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDhePskWithChacha20Poly1305Sha256;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes128Ccm;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes128GcmSha256;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes256Ccm;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithAes256GcmSha384;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsDheRsaWithChacha20Poly1305Sha256;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithAes128GcmSha256;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithAes256GcmSha384;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheEcdsaWithChacha20Poly1305Sha256;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes128CcmSha256;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes128GcmSha256;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithAes256GcmSha384;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdhePskWithChacha20Poly1305Sha256;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithAes128GcmSha256;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithAes256GcmSha384;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.tls.cipher.suite.algs.rev220616.TlsEcdheRsaWithChacha20Poly1305Sha256;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.keystore.rev230417.InlineOrKeystoreAsymmetricKeyGrouping;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.keystore.rev230417.InlineOrKeystoreEndEntityCertWithKeyGrouping;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.common.rev230417.HelloParamsGrouping;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.common.rev230417.TlsVersionBase;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.truststore.rev230417.InlineOrTruststoreCertsGrouping;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.truststore.rev230417.InlineOrTruststorePublicKeysGrouping;
67 * Base class for TLS TransportStacks.
69 public abstract sealed class TLSTransportStack extends AbstractOverlayTransportStack<TLSTransportChannel>
70 permits TLSClient, TLSServer {
72 private static final ImmutableMap<CipherSuiteAlgBase, String> CIPHER_SUITES =
73 ImmutableMap.<CipherSuiteAlgBase, String>builder()
74 .put(TlsAes128CcmSha256.VALUE, "TLS_AES_128_CCM_SHA256")
75 .put(TlsAes128GcmSha256.VALUE, "TLS_AES_128_GCM_SHA256")
76 .put(TlsAes256GcmSha384.VALUE, "TLS_AES_256_GCM_SHA384")
77 .put(TlsChacha20Poly1305Sha256.VALUE, "TLS_CHACHA20_POLY1305_SHA256")
78 .put(TlsDhePskWithAes128Ccm.VALUE, "TLS_DHE_PSK_WITH_AES_128_CCM")
79 .put(TlsDhePskWithAes128GcmSha256.VALUE, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256")
80 .put(TlsDhePskWithAes256Ccm.VALUE, "TLS_DHE_PSK_WITH_AES_256_CCM")
81 .put(TlsDhePskWithAes256GcmSha384.VALUE, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384")
82 .put(TlsDhePskWithChacha20Poly1305Sha256.VALUE, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256")
83 .put(TlsDheRsaWithAes128Ccm.VALUE, "TLS_DHE_RSA_WITH_AES_128_CCM")
84 .put(TlsDheRsaWithAes128GcmSha256.VALUE, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256")
85 .put(TlsDheRsaWithAes256Ccm.VALUE, "TLS_DHE_RSA_WITH_AES_256_CCM")
86 .put(TlsDheRsaWithAes256GcmSha384.VALUE, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")
87 .put(TlsDheRsaWithChacha20Poly1305Sha256.VALUE, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
88 .put(TlsEcdheEcdsaWithAes128GcmSha256.VALUE, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
89 .put(TlsEcdheEcdsaWithAes256GcmSha384.VALUE, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
90 .put(TlsEcdheEcdsaWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
91 .put(TlsEcdhePskWithAes128CcmSha256.VALUE, "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256")
92 .put(TlsEcdhePskWithAes128GcmSha256.VALUE, "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256")
93 .put(TlsEcdhePskWithAes256GcmSha384.VALUE, "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384")
94 .put(TlsEcdhePskWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256")
95 .put(TlsEcdheRsaWithAes128GcmSha256.VALUE, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
96 .put(TlsEcdheRsaWithAes256GcmSha384.VALUE, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
97 .put(TlsEcdheRsaWithChacha20Poly1305Sha256.VALUE, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
100 private final SslHandlerFactory factory;
102 TLSTransportStack(final TransportChannelListener listener, final SslContext sslContext) {
103 this(listener, channel -> sslContext.newHandler(channel.alloc()));
106 TLSTransportStack(final TransportChannelListener listener, final SslHandlerFactory factory) {
108 this.factory = requireNonNull(factory);
112 protected final void onUnderlayChannelEstablished(final TransportChannel underlayChannel) {
113 final var channel = underlayChannel.channel();
114 final var sslHandler = factory.createSslHandler(channel);
116 channel.pipeline().addLast(sslHandler);
117 sslHandler.handshakeFuture().addListener(future -> {
118 final var cause = future.cause();
120 notifyTransportChannelFailed(cause);
123 addTransportChannel(new TLSTransportChannel(underlayChannel));
128 static KeyManagerFactory newKeyManager(
129 final @NonNull InlineOrKeystoreEndEntityCertWithKeyGrouping endEntityCert
130 ) throws UnsupportedConfigurationException {
131 final var keyStore = newKeyStore();
132 setEndEntityCertificateWithKey(keyStore, endEntityCert);
133 return buildKeyManagerFactory(keyStore);
136 static KeyManagerFactory newKeyManager(final @NonNull InlineOrKeystoreAsymmetricKeyGrouping rawPrivateKey)
137 throws UnsupportedConfigurationException {
138 final var keyStore = newKeyStore();
139 setAsymmetricKey(keyStore, rawPrivateKey);
140 return buildKeyManagerFactory(keyStore);
143 // FIXME: should be TrustManagerBuilder
144 protected static @Nullable TrustManagerFactory newTrustManager(
145 final @Nullable InlineOrTruststoreCertsGrouping caCerts,
146 final @Nullable InlineOrTruststoreCertsGrouping eeCerts,
147 final @Nullable InlineOrTruststorePublicKeysGrouping publicKeys) throws UnsupportedConfigurationException {
149 if (publicKeys != null) {
150 // FIXME: implement this and advertize server-auth-raw-public-key from IetfTlsClientFeatureProvider
151 throw new UnsupportedConfigurationException("Public key authentication not implemented");
153 if (caCerts != null || eeCerts != null) {
154 // X.509 certificates
155 final KeyStore keyStore = newKeyStore();
156 setX509Certificates(keyStore, caCerts, eeCerts);
157 return buildTrustManagerFactory(keyStore);
162 static SslContext buildSslContext(final SslContextBuilder builder, final HelloParamsGrouping helloParams)
163 throws UnsupportedConfigurationException {
164 if (helloParams != null) {
165 final var tlsVersions = helloParams.getTlsVersions();
166 if (tlsVersions != null) {
167 final var versions = tlsVersions.getTlsVersion();
168 if (versions != null && !versions.isEmpty()) {
169 builder.protocols(createTlsStrings(versions));
172 final var cipherSuites = helloParams.getCipherSuites();
173 if (cipherSuites != null) {
174 final var ciphers = cipherSuites.getCipherSuite();
175 if (ciphers != null && !ciphers.isEmpty()) {
176 builder.ciphers(createCipherStrings(ciphers));
181 return builder.build();
182 } catch (SSLException e) {
183 throw new UnsupportedConfigurationException("Cannot instantiate TLS context", e);
187 private static String[] createTlsStrings(final Set<TlsVersionBase> versions)
188 throws UnsupportedConfigurationException {
189 // FIXME: cache these
190 final var ret = new String[versions.size()];
192 for (var version : versions) {
193 final var str = IetfTlsCommonFeatureProvider.algorithmNameOf(version);
195 throw new UnsupportedConfigurationException("Unhandled TLS version " + version);
202 private static ImmutableList<String> createCipherStrings(final List<CipherSuiteAlgBase> ciphers)
203 throws UnsupportedConfigurationException {
204 // FIXME: cache these
205 final var builder = ImmutableList.<String>builderWithExpectedSize(ciphers.size());
206 for (var cipher : ciphers) {
207 final var str = CIPHER_SUITES.get(cipher);
209 throw new UnsupportedConfigurationException("Unhandled cipher suite " + cipher);
213 return builder.build();