4c9c0faadf34a62bcddd7d372b3ef6531eef3895
[netconf.git] / transport / transport-ssh / src / main / java / org / opendaylight / netconf / transport / ssh / TransportUtils.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.transport.ssh;
9
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;
15 import java.util.Map;
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;
78
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)
95                     .build();
96     private static final List<NamedFactory<Cipher>> DEFAULT_CIPHERS =
97             ImmutableList.<NamedFactory<Cipher>>builder().addAll(BaseBuilder.DEFAULT_CIPHERS_PREFERENCE).build();
98
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;
103
104     static {
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)
117                 /*
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)
127                  */
128                 .put(EcdhSha2Nistp256.VALUE, BuiltinDHFactories.ecdhp256)
129                 .put(EcdhSha2Nistp384.VALUE, BuiltinDHFactories.ecdhp384)
130                 .put(EcdhSha2Nistp521.VALUE, BuiltinDHFactories.ecdhp521)
131                 /*
132                 .put(EcmqvSha2.VALUE, null)
133                 .put(ExtInfoC.VALUE, null)
134                 .put(ExtInfoS.VALUE, null)
135                  Gss*
136                  TODO: provide solution for remaining (commented out) KEX algorithms missing in BuiltinDHFactories
137                 */
138                 .build(), BuiltinDHFactories::isSupported);
139
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));
146     }
147
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)
156                     /*
157                      AeadAes128Gcm.VALUE
158                      AeadAes256Gcm.VALUE
159                      None.VALUE
160                      openssh ETM extensions
161                      TODO provide solution for remaining (commented out) macs missing in BuiltinMacs
162                       */
163                     .build();
164     private static final List<NamedFactory<Mac>> DEFAULT_MACS =
165             ImmutableList.<NamedFactory<Mac>>builder().addAll(BaseBuilder.DEFAULT_MAC_PREFERENCE).build();
166
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)
182                     /*
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
191                     */
192                     .build();
193
194     static final List<NamedFactory<Signature>> DEFAULT_SIGNATURES =
195             ImmutableList.<NamedFactory<Signature>>builder().addAll(BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE).build();
196
197     private TransportUtils() {
198         // utility class
199     }
200
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");
207             }
208         }
209         return DEFAULT_CIPHERS;
210     }
211
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");
218             }
219         }
220         return DEFAULT_SIGNATURES;
221     }
222
223     public static List<KeyExchangeFactory> getClientKexFactories(final KeyExchange keyExchange)
224             throws UnsupportedConfigurationException {
225         return getKexFactories(keyExchange, CLIENT_KEXS, DEFAULT_CLIENT_KEXS);
226     }
227
228     public static List<KeyExchangeFactory> getServerKexFactories(final KeyExchange keyExchange)
229             throws UnsupportedConfigurationException {
230         return getKexFactories(keyExchange, SERVER_KEXS, DEFAULT_SERVER_KEXS);
231     }
232
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");
240             }
241         }
242         return defaultResult;
243     }
244
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 {
248         if (mac != null) {
249             final var macAlg = mac.getMacAlg();
250             if (macAlg != null && !macAlg.isEmpty()) {
251                 return mapValues(MACS, macAlg, "Unsupported MAC algorithm %s");
252             }
253         }
254         return DEFAULT_MACS;
255     }
256
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));
264             }
265             builder.add(mapped);
266         }
267         return builder.build();
268     }
269 }