Skip to content

Commit

Permalink
Support ETM (Encrypt-then-MAC) variants for HMAC
Browse files Browse the repository at this point in the history
  • Loading branch information
scott-xu committed Feb 12, 2024
1 parent 2d0e03b commit 12984cc
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 55 deletions.
58 changes: 34 additions & 24 deletions src/Renci.SshNet/Abstractions/CryptoAbstraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,75 +84,85 @@ public static System.Security.Cryptography.RIPEMD160 CreateRIPEMD160()
}
#endif // FEATURE_HASH_RIPEMD160

public static System.Security.Cryptography.HMACMD5 CreateHMACMD5(byte[] key)
public static HMAC CreateHMACMD5(byte[] key)
{
#pragma warning disable CA5351 // Do not use broken cryptographic algorithms
return new System.Security.Cryptography.HMACMD5(key);
return new HMAC(new System.Security.Cryptography.HMACMD5(key));
#pragma warning restore CA5351 // Do not use broken cryptographic algorithms
}

public static HMACMD5 CreateHMACMD5(byte[] key, int hashSize)
public static HMAC CreateHMACMD5(byte[] key, int hashSize)
{
#pragma warning disable CA5351 // Do not use broken cryptographic algorithms
return new HMACMD5(key, hashSize);
return new HMAC(new HMACMD5(key, hashSize));
#pragma warning restore CA5351 // Do not use broken cryptographic algorithms
}

public static System.Security.Cryptography.HMACSHA1 CreateHMACSHA1(byte[] key)
public static HMAC CreateHMACSHA1(byte[] key)
{
#pragma warning disable CA5350 // Do not use weak cryptographic algorithms
return new System.Security.Cryptography.HMACSHA1(key);
return new HMAC(new System.Security.Cryptography.HMACSHA1(key));
#pragma warning restore CA5350 // Do not use weak cryptographic algorithms
}

public static HMACSHA1 CreateHMACSHA1(byte[] key, int hashSize)
public static HMAC CreateHMACSHA1(byte[] key, int hashSize)
{
#pragma warning disable CA5350 // Do not use weak cryptographic algorithms
return new HMACSHA1(key, hashSize);
return new HMAC(new HMACSHA1(key, hashSize));
#pragma warning restore CA5350 // Do not use weak cryptographic algorithms
}

public static System.Security.Cryptography.HMACSHA256 CreateHMACSHA256(byte[] key)
public static HMAC CreateHMACSHA256(byte[] key)
{
return new System.Security.Cryptography.HMACSHA256(key);
return new HMAC(new System.Security.Cryptography.HMACSHA256(key));
}

public static HMACSHA256 CreateHMACSHA256(byte[] key, int hashSize)
public static HMAC CreateHMACSHA256(byte[] key, int hashSize)
{
return new HMACSHA256(key, hashSize);
return new HMAC(new HMACSHA256(key, hashSize));
}

public static System.Security.Cryptography.HMACSHA384 CreateHMACSHA384(byte[] key)
public static HMAC CreateHMACSHA256(byte[] key, bool etm)
{
return new System.Security.Cryptography.HMACSHA384(key);
return new HMAC(new System.Security.Cryptography.HMACSHA256(key), etm);
}

public static HMACSHA384 CreateHMACSHA384(byte[] key, int hashSize)
public static HMAC CreateHMACSHA384(byte[] key)
{
return new HMACSHA384(key, hashSize);
return new HMAC(new System.Security.Cryptography.HMACSHA384(key));
}

public static System.Security.Cryptography.HMACSHA512 CreateHMACSHA512(byte[] key)
public static HMAC CreateHMACSHA384(byte[] key, int hashSize)
{
return new System.Security.Cryptography.HMACSHA512(key);
return new HMAC(new HMACSHA384(key, hashSize));
}

public static HMACSHA512 CreateHMACSHA512(byte[] key, int hashSize)
public static HMAC CreateHMACSHA512(byte[] key)
{
return new HMACSHA512(key, hashSize);
return new HMAC(new System.Security.Cryptography.HMACSHA512(key));
}

public static HMAC CreateHMACSHA512(byte[] key, int hashSize)
{
return new HMAC(new HMACSHA512(key, hashSize));
}

public static HMAC CreateHMACSHA512(byte[] key, bool etm)
{
return new HMAC(new System.Security.Cryptography.HMACSHA512(key), etm);
}

#if FEATURE_HMAC_RIPEMD160
public static System.Security.Cryptography.HMACRIPEMD160 CreateHMACRIPEMD160(byte[] key)
public static HMAC CreateHMACRIPEMD160(byte[] key)
{
#pragma warning disable CA5350 // Do not use weak cryptographic algorithms
return new System.Security.Cryptography.HMACRIPEMD160(key);
return new HMAC(new System.Security.Cryptography.HMACRIPEMD160(key));
#pragma warning restore CA5350 // Do not use weak cryptographic algorithms
}
#else
public static global::SshNet.Security.Cryptography.HMACRIPEMD160 CreateHMACRIPEMD160(byte[] key)
public static HMAC CreateHMACRIPEMD160(byte[] key)
{
return new global::SshNet.Security.Cryptography.HMACRIPEMD160(key);
return new HMAC(new global::SshNet.Security.Cryptography.HMACRIPEMD160(key));
}
#endif // FEATURE_HMAC_RIPEMD160
}
Expand Down
4 changes: 4 additions & 0 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
#pragma warning disable IDE0200 // Remove unnecessary lambda expression; We want to prevent instantiating the HashAlgorithm objects.
HmacAlgorithms = new Dictionary<string, HashInfo>
{
/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
{ "hmac-sha2-256", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key)) },
{ "hmac-sha2-512", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key)) },
{ "hmac-sha2-512-96", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, 96)) },
Expand All @@ -386,6 +387,9 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
{ "hmac-sha1-96", new HashInfo(20*8, key => CryptoAbstraction.CreateHMACSHA1(key, 96)) },
{ "hmac-md5", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key)) },
{ "hmac-md5-96", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key, 96)) },
/* Encrypt-then-MAC variants */
{ "hmac-sha2-256-etm@openssh.com", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key, etm: true)) },
{ "hmac-sha2-512-etm@openssh.com", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, etm: true)) },
};
#pragma warning restore IDE0200 // Remove unnecessary lambda expression

Expand Down
13 changes: 9 additions & 4 deletions src/Renci.SshNet/HashInfo.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Security.Cryptography;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;

namespace Renci.SshNet
{
Expand All @@ -20,17 +20,22 @@ public class HashInfo
/// <summary>
/// Gets the cipher.
/// </summary>
public Func<byte[], HashAlgorithm> HashAlgorithm { get; private set; }
public Func<byte[], HMAC> HMAC { get; private set; }

/// <summary>
/// Gets a value indicating whether Encrypt-then-MAC or not.
/// </summary>
public bool ETM { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="HashInfo"/> class.
/// </summary>
/// <param name="keySize">Size of the key.</param>
/// <param name="hash">The hash algorithm to use for a given key.</param>
public HashInfo(int keySize, Func<byte[], HashAlgorithm> hash)
public HashInfo(int keySize, Func<byte[], HMAC> hash)
{
KeySize = keySize;
HashAlgorithm = key => hash(key.Take(KeySize / 8));
HMAC = key => hash(key.Take(KeySize / 8));
}
}
}
49 changes: 49 additions & 0 deletions src/Renci.SshNet/Security/Cryptography/HMAC.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Security.Cryptography;

namespace Renci.SshNet.Security.Cryptography
{
/// <summary>
/// Represents the info for Message Authentication Code (MAC).
/// </summary>
public sealed class HMAC : IDisposable
{
/// <summary>
/// Initializes a new instance of the <see cref="HMAC"/> class.
/// </summary>
/// <param name="hashAlgorithm">The hash algorithm.</param>
public HMAC(HashAlgorithm hashAlgorithm)
: this(hashAlgorithm, etm: false)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="HMAC"/> class.
/// </summary>
/// <param name="hashAlgorithm">The hash algorithm.</param>
/// <param name="etm"><see langword="true"/> to enable encrypt-then-MAC, <see langword="false"/> to use encrypt-and-MAC.</param>
public HMAC(
HashAlgorithm hashAlgorithm,
bool etm)
{
HashAlgorithm = hashAlgorithm;
ETM = etm;
}

/// <inheritdoc/>
public void Dispose()
{
HashAlgorithm?.Dispose();
}

/// <summary>
/// Gets the hash algorithem.
/// </summary>
public HashAlgorithm HashAlgorithm { get; private set; }

/// <summary>
/// Gets a value indicating whether enable encryption-to-mac or encryption-then-mac.
/// </summary>
public bool ETM { get; private set; }
}
}
5 changes: 2 additions & 3 deletions src/Renci.SshNet/Security/IKeyExchange.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Security.Cryptography;

using Renci.SshNet.Common;
using Renci.SshNet.Compression;
Expand Down Expand Up @@ -69,15 +68,15 @@ public interface IKeyExchange : IDisposable
/// <returns>
/// The server hash algorithm.
/// </returns>
HashAlgorithm CreateServerHash();
HMAC CreateServerHash();

/// <summary>
/// Creates the client-side hash algorithm to use.
/// </summary>
/// <returns>
/// The client hash algorithm.
/// </returns>
HashAlgorithm CreateClientHash();
HMAC CreateClientHash();

/// <summary>
/// Creates the compression algorithm to use to deflate data.
Expand Down
11 changes: 5 additions & 6 deletions src/Renci.SshNet/Security/KeyExchange.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;

using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
Expand Down Expand Up @@ -103,7 +102,7 @@ from a in message.MacAlgorithmsClientToServer
select a).FirstOrDefault();
if (string.IsNullOrEmpty(clientHmacAlgorithmName))
{
throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
throw new SshConnectionException("Client HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
}

session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName;
Expand Down Expand Up @@ -221,7 +220,7 @@ public Cipher CreateClientCipher()
/// <returns>
/// The server-side hash algorithm.
/// </returns>
public HashAlgorithm CreateServerHash()
public HMAC CreateServerHash()
{
// Resolve Session ID
var sessionId = Session.SessionId ?? ExchangeHash;
Expand All @@ -235,7 +234,7 @@ public HashAlgorithm CreateServerHash()
Session.ToHex(Session.SessionId),
Session.ConnectionInfo.CurrentServerHmacAlgorithm));

return _serverHashInfo.HashAlgorithm(serverKey);
return _serverHashInfo.HMAC(serverKey);
}

/// <summary>
Expand All @@ -244,7 +243,7 @@ public HashAlgorithm CreateServerHash()
/// <returns>
/// The client-side hash algorithm.
/// </returns>
public HashAlgorithm CreateClientHash()
public HMAC CreateClientHash()
{
// Resolve Session ID
var sessionId = Session.SessionId ?? ExchangeHash;
Expand All @@ -258,7 +257,7 @@ public HashAlgorithm CreateClientHash()
Session.ToHex(Session.SessionId),
Session.ConnectionInfo.CurrentClientHmacAlgorithm));

return _clientHashInfo.HashAlgorithm(clientKey);
return _clientHashInfo.HMAC(clientKey);
}

/// <summary>
Expand Down
Loading

0 comments on commit 12984cc

Please sign in to comment.