Skip to content

Commit 12984cc

Browse files
committed
Support ETM (Encrypt-then-MAC) variants for HMAC
1 parent 2d0e03b commit 12984cc

File tree

10 files changed

+167
-55
lines changed

10 files changed

+167
-55
lines changed

src/Renci.SshNet/Abstractions/CryptoAbstraction.cs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -84,75 +84,85 @@ public static System.Security.Cryptography.RIPEMD160 CreateRIPEMD160()
8484
}
8585
#endif // FEATURE_HASH_RIPEMD160
8686

87-
public static System.Security.Cryptography.HMACMD5 CreateHMACMD5(byte[] key)
87+
public static HMAC CreateHMACMD5(byte[] key)
8888
{
8989
#pragma warning disable CA5351 // Do not use broken cryptographic algorithms
90-
return new System.Security.Cryptography.HMACMD5(key);
90+
return new HMAC(new System.Security.Cryptography.HMACMD5(key));
9191
#pragma warning restore CA5351 // Do not use broken cryptographic algorithms
9292
}
9393

94-
public static HMACMD5 CreateHMACMD5(byte[] key, int hashSize)
94+
public static HMAC CreateHMACMD5(byte[] key, int hashSize)
9595
{
9696
#pragma warning disable CA5351 // Do not use broken cryptographic algorithms
97-
return new HMACMD5(key, hashSize);
97+
return new HMAC(new HMACMD5(key, hashSize));
9898
#pragma warning restore CA5351 // Do not use broken cryptographic algorithms
9999
}
100100

101-
public static System.Security.Cryptography.HMACSHA1 CreateHMACSHA1(byte[] key)
101+
public static HMAC CreateHMACSHA1(byte[] key)
102102
{
103103
#pragma warning disable CA5350 // Do not use weak cryptographic algorithms
104-
return new System.Security.Cryptography.HMACSHA1(key);
104+
return new HMAC(new System.Security.Cryptography.HMACSHA1(key));
105105
#pragma warning restore CA5350 // Do not use weak cryptographic algorithms
106106
}
107107

108-
public static HMACSHA1 CreateHMACSHA1(byte[] key, int hashSize)
108+
public static HMAC CreateHMACSHA1(byte[] key, int hashSize)
109109
{
110110
#pragma warning disable CA5350 // Do not use weak cryptographic algorithms
111-
return new HMACSHA1(key, hashSize);
111+
return new HMAC(new HMACSHA1(key, hashSize));
112112
#pragma warning restore CA5350 // Do not use weak cryptographic algorithms
113113
}
114114

115-
public static System.Security.Cryptography.HMACSHA256 CreateHMACSHA256(byte[] key)
115+
public static HMAC CreateHMACSHA256(byte[] key)
116116
{
117-
return new System.Security.Cryptography.HMACSHA256(key);
117+
return new HMAC(new System.Security.Cryptography.HMACSHA256(key));
118118
}
119119

120-
public static HMACSHA256 CreateHMACSHA256(byte[] key, int hashSize)
120+
public static HMAC CreateHMACSHA256(byte[] key, int hashSize)
121121
{
122-
return new HMACSHA256(key, hashSize);
122+
return new HMAC(new HMACSHA256(key, hashSize));
123123
}
124124

125-
public static System.Security.Cryptography.HMACSHA384 CreateHMACSHA384(byte[] key)
125+
public static HMAC CreateHMACSHA256(byte[] key, bool etm)
126126
{
127-
return new System.Security.Cryptography.HMACSHA384(key);
127+
return new HMAC(new System.Security.Cryptography.HMACSHA256(key), etm);
128128
}
129129

130-
public static HMACSHA384 CreateHMACSHA384(byte[] key, int hashSize)
130+
public static HMAC CreateHMACSHA384(byte[] key)
131131
{
132-
return new HMACSHA384(key, hashSize);
132+
return new HMAC(new System.Security.Cryptography.HMACSHA384(key));
133133
}
134134

135-
public static System.Security.Cryptography.HMACSHA512 CreateHMACSHA512(byte[] key)
135+
public static HMAC CreateHMACSHA384(byte[] key, int hashSize)
136136
{
137-
return new System.Security.Cryptography.HMACSHA512(key);
137+
return new HMAC(new HMACSHA384(key, hashSize));
138138
}
139139

140-
public static HMACSHA512 CreateHMACSHA512(byte[] key, int hashSize)
140+
public static HMAC CreateHMACSHA512(byte[] key)
141141
{
142-
return new HMACSHA512(key, hashSize);
142+
return new HMAC(new System.Security.Cryptography.HMACSHA512(key));
143+
}
144+
145+
public static HMAC CreateHMACSHA512(byte[] key, int hashSize)
146+
{
147+
return new HMAC(new HMACSHA512(key, hashSize));
148+
}
149+
150+
public static HMAC CreateHMACSHA512(byte[] key, bool etm)
151+
{
152+
return new HMAC(new System.Security.Cryptography.HMACSHA512(key), etm);
143153
}
144154

145155
#if FEATURE_HMAC_RIPEMD160
146-
public static System.Security.Cryptography.HMACRIPEMD160 CreateHMACRIPEMD160(byte[] key)
156+
public static HMAC CreateHMACRIPEMD160(byte[] key)
147157
{
148158
#pragma warning disable CA5350 // Do not use weak cryptographic algorithms
149-
return new System.Security.Cryptography.HMACRIPEMD160(key);
159+
return new HMAC(new System.Security.Cryptography.HMACRIPEMD160(key));
150160
#pragma warning restore CA5350 // Do not use weak cryptographic algorithms
151161
}
152162
#else
153-
public static global::SshNet.Security.Cryptography.HMACRIPEMD160 CreateHMACRIPEMD160(byte[] key)
163+
public static HMAC CreateHMACRIPEMD160(byte[] key)
154164
{
155-
return new global::SshNet.Security.Cryptography.HMACRIPEMD160(key);
165+
return new HMAC(new global::SshNet.Security.Cryptography.HMACRIPEMD160(key));
156166
}
157167
#endif // FEATURE_HMAC_RIPEMD160
158168
}

src/Renci.SshNet/ConnectionInfo.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
376376
#pragma warning disable IDE0200 // Remove unnecessary lambda expression; We want to prevent instantiating the HashAlgorithm objects.
377377
HmacAlgorithms = new Dictionary<string, HashInfo>
378378
{
379+
/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
379380
{ "hmac-sha2-256", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key)) },
380381
{ "hmac-sha2-512", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key)) },
381382
{ "hmac-sha2-512-96", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, 96)) },
@@ -386,6 +387,9 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
386387
{ "hmac-sha1-96", new HashInfo(20*8, key => CryptoAbstraction.CreateHMACSHA1(key, 96)) },
387388
{ "hmac-md5", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key)) },
388389
{ "hmac-md5-96", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key, 96)) },
390+
/* Encrypt-then-MAC variants */
391+
{ "hmac-sha2-256-etm@openssh.com", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key, etm: true)) },
392+
{ "hmac-sha2-512-etm@openssh.com", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, etm: true)) },
389393
};
390394
#pragma warning restore IDE0200 // Remove unnecessary lambda expression
391395

src/Renci.SshNet/HashInfo.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
2-
using System.Security.Cryptography;
32
using Renci.SshNet.Common;
3+
using Renci.SshNet.Security.Cryptography;
44

55
namespace Renci.SshNet
66
{
@@ -20,17 +20,22 @@ public class HashInfo
2020
/// <summary>
2121
/// Gets the cipher.
2222
/// </summary>
23-
public Func<byte[], HashAlgorithm> HashAlgorithm { get; private set; }
23+
public Func<byte[], HMAC> HMAC { get; private set; }
24+
25+
/// <summary>
26+
/// Gets a value indicating whether Encrypt-then-MAC or not.
27+
/// </summary>
28+
public bool ETM { get; private set; }
2429

2530
/// <summary>
2631
/// Initializes a new instance of the <see cref="HashInfo"/> class.
2732
/// </summary>
2833
/// <param name="keySize">Size of the key.</param>
2934
/// <param name="hash">The hash algorithm to use for a given key.</param>
30-
public HashInfo(int keySize, Func<byte[], HashAlgorithm> hash)
35+
public HashInfo(int keySize, Func<byte[], HMAC> hash)
3136
{
3237
KeySize = keySize;
33-
HashAlgorithm = key => hash(key.Take(KeySize / 8));
38+
HMAC = key => hash(key.Take(KeySize / 8));
3439
}
3540
}
3641
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
4+
namespace Renci.SshNet.Security.Cryptography
5+
{
6+
/// <summary>
7+
/// Represents the info for Message Authentication Code (MAC).
8+
/// </summary>
9+
public sealed class HMAC : IDisposable
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of the <see cref="HMAC"/> class.
13+
/// </summary>
14+
/// <param name="hashAlgorithm">The hash algorithm.</param>
15+
public HMAC(HashAlgorithm hashAlgorithm)
16+
: this(hashAlgorithm, etm: false)
17+
{
18+
}
19+
20+
/// <summary>
21+
/// Initializes a new instance of the <see cref="HMAC"/> class.
22+
/// </summary>
23+
/// <param name="hashAlgorithm">The hash algorithm.</param>
24+
/// <param name="etm"><see langword="true"/> to enable encrypt-then-MAC, <see langword="false"/> to use encrypt-and-MAC.</param>
25+
public HMAC(
26+
HashAlgorithm hashAlgorithm,
27+
bool etm)
28+
{
29+
HashAlgorithm = hashAlgorithm;
30+
ETM = etm;
31+
}
32+
33+
/// <inheritdoc/>
34+
public void Dispose()
35+
{
36+
HashAlgorithm?.Dispose();
37+
}
38+
39+
/// <summary>
40+
/// Gets the hash algorithem.
41+
/// </summary>
42+
public HashAlgorithm HashAlgorithm { get; private set; }
43+
44+
/// <summary>
45+
/// Gets a value indicating whether enable encryption-to-mac or encryption-then-mac.
46+
/// </summary>
47+
public bool ETM { get; private set; }
48+
}
49+
}

src/Renci.SshNet/Security/IKeyExchange.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Security.Cryptography;
32

43
using Renci.SshNet.Common;
54
using Renci.SshNet.Compression;
@@ -69,15 +68,15 @@ public interface IKeyExchange : IDisposable
6968
/// <returns>
7069
/// The server hash algorithm.
7170
/// </returns>
72-
HashAlgorithm CreateServerHash();
71+
HMAC CreateServerHash();
7372

7473
/// <summary>
7574
/// Creates the client-side hash algorithm to use.
7675
/// </summary>
7776
/// <returns>
7877
/// The client hash algorithm.
7978
/// </returns>
80-
HashAlgorithm CreateClientHash();
79+
HMAC CreateClientHash();
8180

8281
/// <summary>
8382
/// Creates the compression algorithm to use to deflate data.

src/Renci.SshNet/Security/KeyExchange.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Security.Cryptography;
54

65
using Renci.SshNet.Abstractions;
76
using Renci.SshNet.Common;
@@ -103,7 +102,7 @@ from a in message.MacAlgorithmsClientToServer
103102
select a).FirstOrDefault();
104103
if (string.IsNullOrEmpty(clientHmacAlgorithmName))
105104
{
106-
throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
105+
throw new SshConnectionException("Client HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
107106
}
108107

109108
session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName;
@@ -221,7 +220,7 @@ public Cipher CreateClientCipher()
221220
/// <returns>
222221
/// The server-side hash algorithm.
223222
/// </returns>
224-
public HashAlgorithm CreateServerHash()
223+
public HMAC CreateServerHash()
225224
{
226225
// Resolve Session ID
227226
var sessionId = Session.SessionId ?? ExchangeHash;
@@ -235,7 +234,7 @@ public HashAlgorithm CreateServerHash()
235234
Session.ToHex(Session.SessionId),
236235
Session.ConnectionInfo.CurrentServerHmacAlgorithm));
237236

238-
return _serverHashInfo.HashAlgorithm(serverKey);
237+
return _serverHashInfo.HMAC(serverKey);
239238
}
240239

241240
/// <summary>
@@ -244,7 +243,7 @@ public HashAlgorithm CreateServerHash()
244243
/// <returns>
245244
/// The client-side hash algorithm.
246245
/// </returns>
247-
public HashAlgorithm CreateClientHash()
246+
public HMAC CreateClientHash()
248247
{
249248
// Resolve Session ID
250249
var sessionId = Session.SessionId ?? ExchangeHash;
@@ -258,7 +257,7 @@ public HashAlgorithm CreateClientHash()
258257
Session.ToHex(Session.SessionId),
259258
Session.ConnectionInfo.CurrentClientHmacAlgorithm));
260259

261-
return _clientHashInfo.HashAlgorithm(clientKey);
260+
return _clientHashInfo.HMAC(clientKey);
262261
}
263262

264263
/// <summary>

0 commit comments

Comments
 (0)