2 * Copyright (c) 2023 PANTHEON.tech s.r.o. 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.ssh;
10 import com.google.common.collect.ImmutableList;
11 import com.google.common.collect.ImmutableMap;
12 import com.google.common.collect.Lists;
13 import com.google.common.collect.Maps;
14 import java.util.List;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.opendaylight.netconf.shaded.sshd.client.ClientBuilder;
18 import org.opendaylight.netconf.shaded.sshd.common.BaseBuilder;
19 import org.opendaylight.netconf.shaded.sshd.common.NamedFactory;
20 import org.opendaylight.netconf.shaded.sshd.common.cipher.BuiltinCiphers;
21 import org.opendaylight.netconf.shaded.sshd.common.cipher.Cipher;
22 import org.opendaylight.netconf.shaded.sshd.common.kex.BuiltinDHFactories;
23 import org.opendaylight.netconf.shaded.sshd.common.kex.KeyExchangeFactory;
24 import org.opendaylight.netconf.shaded.sshd.common.mac.BuiltinMacs;
25 import org.opendaylight.netconf.shaded.sshd.common.mac.Mac;
26 import org.opendaylight.netconf.shaded.sshd.common.signature.BuiltinSignatures;
27 import org.opendaylight.netconf.shaded.sshd.common.signature.Signature;
28 import org.opendaylight.netconf.shaded.sshd.server.ServerBuilder;
29 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.EcdsaSha2Nistp256;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.EcdsaSha2Nistp384;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.EcdsaSha2Nistp521;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.PublicKeyAlgBase;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.RsaSha2256;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.RsaSha2512;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.SshDss;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.SshEd25519;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh._public.key.algs.rev220616.SshRsa;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.AeadAes128Gcm;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.AeadAes256Gcm;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Aes128Cbc;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Aes128Ctr;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Aes192Cbc;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Aes192Ctr;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Aes256Cbc;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Aes256Ctr;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Arcfour128;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.Arcfour256;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.BlowfishCbc;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.EncryptionAlgBase;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.None;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.encryption.algs.rev220616.TripleDesCbc;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.Curve25519Sha256;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.Curve448Sha512;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup14Sha1;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup14Sha256;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup15Sha512;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup16Sha512;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup17Sha512;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup18Sha512;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroup1Sha1;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroupExchangeSha1;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.DiffieHellmanGroupExchangeSha256;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.EcdhSha2Nistp256;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.EcdhSha2Nistp384;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.EcdhSha2Nistp521;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.key.exchange.algs.rev220616.KeyExchangeAlgBase;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.HmacMd5;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.HmacMd596;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.HmacSha1;
71 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.HmacSha196;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.HmacSha2256;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.HmacSha2512;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.ssh.mac.algs.rev220616.MacAlgBase;
75 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev221212.transport.params.grouping.Encryption;
76 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev221212.transport.params.grouping.HostKey;
77 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev221212.transport.params.grouping.KeyExchange;
79 final class TransportUtils {
80 private static final Map<EncryptionAlgBase, NamedFactory<Cipher>> CIPHERS =
81 ImmutableMap.<EncryptionAlgBase, NamedFactory<Cipher>>builder()
82 .put(AeadAes128Gcm.VALUE, BuiltinCiphers.aes128gcm)
83 .put(AeadAes256Gcm.VALUE, BuiltinCiphers.aes256cbc)
84 .put(Aes128Cbc.VALUE, BuiltinCiphers.aes128cbc)
85 .put(Aes128Ctr.VALUE, BuiltinCiphers.aes128ctr)
86 .put(Aes192Cbc.VALUE, BuiltinCiphers.aes192cbc)
87 .put(Aes192Ctr.VALUE, BuiltinCiphers.aes192ctr)
88 .put(Aes256Cbc.VALUE, BuiltinCiphers.aes256cbc)
89 .put(Aes256Ctr.VALUE, BuiltinCiphers.aes256ctr)
90 .put(Arcfour128.VALUE, BuiltinCiphers.arcfour128)
91 .put(Arcfour256.VALUE, BuiltinCiphers.arcfour256)
92 .put(BlowfishCbc.VALUE, BuiltinCiphers.blowfishcbc)
93 .put(TripleDesCbc.VALUE, BuiltinCiphers.tripledescbc)
94 .put(None.VALUE, BuiltinCiphers.none)
96 private static final List<NamedFactory<Cipher>> DEFAULT_CIPHERS =
97 ImmutableList.<NamedFactory<Cipher>>builder().addAll(BaseBuilder.DEFAULT_CIPHERS_PREFERENCE).build();
99 private static final Map<KeyExchangeAlgBase, KeyExchangeFactory> CLIENT_KEXS;
100 private static final Map<KeyExchangeAlgBase, KeyExchangeFactory> SERVER_KEXS;
101 private static final List<KeyExchangeFactory> DEFAULT_CLIENT_KEXS;
102 private static final List<KeyExchangeFactory> DEFAULT_SERVER_KEXS;
105 final var factories = Maps.filterValues(ImmutableMap.<KeyExchangeAlgBase, BuiltinDHFactories>builder()
106 .put(Curve25519Sha256.VALUE, BuiltinDHFactories.curve25519)
107 .put(Curve448Sha512.VALUE, BuiltinDHFactories.curve448)
108 .put(DiffieHellmanGroup1Sha1.VALUE, BuiltinDHFactories.dhg1)
109 .put(DiffieHellmanGroup14Sha1.VALUE, BuiltinDHFactories.dhg14)
110 .put(DiffieHellmanGroup14Sha256.VALUE, BuiltinDHFactories.dhg14_256)
111 .put(DiffieHellmanGroup15Sha512.VALUE, BuiltinDHFactories.dhg15_512)
112 .put(DiffieHellmanGroup16Sha512.VALUE, BuiltinDHFactories.dhg16_512)
113 .put(DiffieHellmanGroup17Sha512.VALUE, BuiltinDHFactories.dhg17_512)
114 .put(DiffieHellmanGroup18Sha512.VALUE, BuiltinDHFactories.dhg18_512)
115 .put(DiffieHellmanGroupExchangeSha1.VALUE, BuiltinDHFactories.dhgex)
116 .put(DiffieHellmanGroupExchangeSha256.VALUE, BuiltinDHFactories.dhgex256)
118 .put(EcdhSha21284010045311.VALUE, null)
119 .put(EcdhSha213132016.VALUE, null)
120 .put(EcdhSha21313201.VALUE, null)
121 .put(EcdhSha213132026.VALUE, null)
122 .put(EcdhSha213132027.VALUE, null)
123 .put(EcdhSha213132033.VALUE, null)
124 .put(EcdhSha213132036.VALUE, null)
125 .put(EcdhSha213132037.VALUE, null)
126 .put(EcdhSha213132038.VALUE, null)
128 .put(EcdhSha2Nistp256.VALUE, BuiltinDHFactories.ecdhp256)
129 .put(EcdhSha2Nistp384.VALUE, BuiltinDHFactories.ecdhp384)
130 .put(EcdhSha2Nistp521.VALUE, BuiltinDHFactories.ecdhp521)
132 .put(EcmqvSha2.VALUE, null)
133 .put(ExtInfoC.VALUE, null)
134 .put(ExtInfoS.VALUE, null)
136 TODO: provide solution for remaining (commented out) KEX algorithms missing in BuiltinDHFactories
138 .build(), BuiltinDHFactories::isSupported);
140 CLIENT_KEXS = ImmutableMap.copyOf(Maps.transformValues(factories, ClientBuilder.DH2KEX::apply));
141 SERVER_KEXS = ImmutableMap.copyOf(Maps.transformValues(factories, ServerBuilder.DH2KEX::apply));
142 DEFAULT_CLIENT_KEXS =
143 ImmutableList.copyOf(Lists.transform(BaseBuilder.DEFAULT_KEX_PREFERENCE, ClientBuilder.DH2KEX::apply));
144 DEFAULT_SERVER_KEXS =
145 ImmutableList.copyOf(Lists.transform(BaseBuilder.DEFAULT_KEX_PREFERENCE, ServerBuilder.DH2KEX::apply));
148 private static final Map<MacAlgBase, NamedFactory<Mac>> MACS =
149 ImmutableMap.<MacAlgBase, NamedFactory<Mac>>builder()
150 .put(HmacMd5.VALUE, BuiltinMacs.hmacmd5)
151 .put(HmacMd596.VALUE, BuiltinMacs.hmacmd596)
152 .put(HmacSha1.VALUE, BuiltinMacs.hmacsha1)
153 .put(HmacSha196.VALUE, BuiltinMacs.hmacsha196)
154 .put(HmacSha2256.VALUE, BuiltinMacs.hmacsha256)
155 .put(HmacSha2512.VALUE, BuiltinMacs.hmacsha512)
160 openssh ETM extensions
161 TODO provide solution for remaining (commented out) macs missing in BuiltinMacs
164 private static final List<NamedFactory<Mac>> DEFAULT_MACS =
165 ImmutableList.<NamedFactory<Mac>>builder().addAll(BaseBuilder.DEFAULT_MAC_PREFERENCE).build();
167 static final Map<PublicKeyAlgBase, NamedFactory<Signature>> SIGNATURES =
168 ImmutableMap.<PublicKeyAlgBase, NamedFactory<Signature>>builder()
169 .put(EcdsaSha2Nistp256.VALUE, BuiltinSignatures.nistp256)
170 .put(EcdsaSha2Nistp384.VALUE, BuiltinSignatures.nistp384)
171 .put(EcdsaSha2Nistp521.VALUE, BuiltinSignatures.nistp521)
172 .put(RsaSha2512.VALUE, BuiltinSignatures.rsaSHA512)
173 // .put(PgpSignDss.VALUE, null)
174 // .put(PgpSignRsa.VALUE, null)
175 .put(RsaSha2256.VALUE, BuiltinSignatures.rsaSHA256)
176 // .put(SpkiSignRsa.VALUE, null)
177 // .put(SpkiSignDss.VALUE, null)
178 .put(SshDss.VALUE, BuiltinSignatures.dsa)
179 // .put(SshEd448.VALUE, null)
180 .put(SshEd25519.VALUE, BuiltinSignatures.ed25519)
181 .put(SshRsa.VALUE, BuiltinSignatures.rsa)
183 .put(X509v3EcdsaSha2Nistp256.VALUE, null)
184 .put(X509v3EcdsaSha2Nistp384.VALUE, null)
185 .put(X509v3EcdsaSha2Nistp521.VALUE, null)
186 .put(X509v3Rsa2048Sha256.VALUE, null)
187 .put(X509v3SshDss.VALUE, null)
188 .put(X509v3SshRsa.VALUE, null)
189 .put(Null.VALUE, null)
190 TODO provide solution for remaining (commented out) signatures missing in BuiltinSignatures
194 static final List<NamedFactory<Signature>> DEFAULT_SIGNATURES =
195 ImmutableList.<NamedFactory<Signature>>builder().addAll(BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE).build();
197 private TransportUtils() {
201 public static List<NamedFactory<Cipher>> getCipherFactories(final @Nullable Encryption encryption)
202 throws UnsupportedConfigurationException {
203 if (encryption != null) {
204 final var encAlg = encryption.getEncryptionAlg();
205 if (encAlg != null && !encAlg.isEmpty()) {
206 return mapValues(CIPHERS, encAlg, "Unsupported Encryption algorithm %s");
209 return DEFAULT_CIPHERS;
212 public static List<NamedFactory<Signature>> getSignatureFactories(@Nullable final HostKey hostKey)
213 throws UnsupportedConfigurationException {
214 if (hostKey != null) {
215 final var hostKeyAlg = hostKey.getHostKeyAlg();
216 if (hostKeyAlg != null && hostKeyAlg.isEmpty()) {
217 return mapValues(SIGNATURES, hostKeyAlg, "Unsupported Host Key algorithm %s");
220 return DEFAULT_SIGNATURES;
223 public static List<KeyExchangeFactory> getClientKexFactories(final KeyExchange keyExchange)
224 throws UnsupportedConfigurationException {
225 return getKexFactories(keyExchange, CLIENT_KEXS, DEFAULT_CLIENT_KEXS);
228 public static List<KeyExchangeFactory> getServerKexFactories(final KeyExchange keyExchange)
229 throws UnsupportedConfigurationException {
230 return getKexFactories(keyExchange, SERVER_KEXS, DEFAULT_SERVER_KEXS);
233 private static List<KeyExchangeFactory> getKexFactories(final KeyExchange keyExchange,
234 final Map<KeyExchangeAlgBase, KeyExchangeFactory> map,
235 final List<KeyExchangeFactory> defaultResult) throws UnsupportedConfigurationException {
236 if (keyExchange != null) {
237 final var kexAlg = keyExchange.getKeyExchangeAlg();
238 if (kexAlg != null && !kexAlg.isEmpty()) {
239 return mapValues(map, kexAlg, "Unsupported Key Exchange algorithm %s");
242 return defaultResult;
245 public static List<NamedFactory<Mac>> getMacFactories(
246 final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.common.rev221212
247 .transport.params.grouping.Mac mac) throws UnsupportedConfigurationException {
249 final var macAlg = mac.getMacAlg();
250 if (macAlg != null && !macAlg.isEmpty()) {
251 return mapValues(MACS, macAlg, "Unsupported MAC algorithm %s");
257 private static <K, V> List<V> mapValues(final Map<K, V> map, final List<K> values, final String errorTemplate)
258 throws UnsupportedConfigurationException {
259 final var builder = ImmutableList.<V>builderWithExpectedSize(values.size());
260 for (K value : values) {
261 final V mapped = map.get(value);
262 if (mapped == null) {
263 throw new UnsupportedOperationException(String.format(errorTemplate, value));
267 return builder.build();