Skip to content

015: Implement hmac sha1

Shane DeSeranno edited this page Feb 15, 2017 · 3 revisions

One thing about programming, it's all about the patterns. So, follow previous pages, let's create a folder called MACAlgorithms and create an interface IMACAlgorithm that implements from IAlgorithm.

    public interface IMACAlgorithm : IAlgorithm
    {
        uint KeySize { get; }
        uint DigestLength { get; }
        void SetKey(byte[] key);
        byte[] ComputeHash(uint packetNumber, byte[] data);
    }

This has a KeySize and DigestLength property to help with the MAC hash creation. It also allows the client code to SetKey() and ComputeHash() for a given packet number and data. Now create a class HMACSHA1 that implements IMACAlgorithm:

    public class HMACSHA1 : IMACAlgorithm
    {
        public uint DigestLength
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public uint KeySize
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public string Name
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public byte[] ComputeHash(uint packetNumber, byte[] data)
        {
            throw new NotImplementedException();
        }

        public void SetKey(byte[] key)
        {
            throw new NotImplementedException();
        }
    }

All we need to do now is fill in the values for the hmac-sha1. The comments in the code link to relevant documents:

    public class HMACSHA1 : IMACAlgorithm
    {
        System.Security.Cryptography.HMACSHA1 m_HMAC = null;

        public uint KeySize
        {
            get
            {
                // https://tools.ietf.org/html/rfc4253#section-6.4
                // According to this, the KeySize is 20
                return 20;
            }
        }

        public uint DigestLength
        {
            get
            {
                // https://tools.ietf.org/html/rfc4253#section-6.4
                // According to this, the DigestLength is 20
                return 20;
            }
        }

        public string Name
        {
            get
            {
                return "hmac-sha1";
            }
        }

        public void SetKey(byte[] key)
        {
            m_HMAC = new System.Security.Cryptography.HMACSHA1(key);
        }

        public byte[] ComputeHash(uint packetNumber, byte[] data)
        {
            if (m_HMAC == null)
                throw new SSHServerException(DisconnectReason.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, "SetKey must be called before attempting to ComputeHash.");

            using (ByteWriter writer = new ByteWriter())
            {
                writer.WriteUInt32(packetNumber);
                writer.WriteRawBytes(data);
                return m_HMAC.ComputeHash(writer.ToByteArray());
            }
        }
    }

Now open Server and add the SupportedMACAlgorithms:

        public static IReadOnlyList<Type> SupportedMACAlgorithms { get; private set; } = new List<Type>()
        {
            typeof(HMACSHA1)
        };

And open Client and add the supported algorithms to the KEX packet:

    m_KexInitServerToClient.MacAlgorithmsClientToServer.AddRange(Server.GetNames(Server.SupportedMACAlgorithms));
    m_KexInitServerToClient.MacAlgorithmsServerToClient.AddRange(Server.GetNames(Server.SupportedMACAlgorithms));

Once again, if you run the server, the output is the same, but the OpenSSH results are amazing:

debug2: KEX algorithms: diffie-hellman-group14-sha1
debug2: host key algorithms: ssh-rsa
debug2: ciphers ctos: 3des-cbc
debug2: ciphers stoc: 3des-cbc
debug2: MACs ctos: hmac-sha1
debug2: MACs stoc: hmac-sha1
...
debug1: kex: algorithm: diffie-hellman-group14-sha1
debug1: kex: host key algorithm: ssh-rsa
Unable to negotiate with 127.0.0.1 port 22: no matching compression method found. Their offer:

It clearly sees our offer of hmac-sha1, but is sad because we haven't provided a compression! We are getting very close now to providing all of the pieces needed. The code at this point is tagged with Implement_hmac-sh1. In the next section, we'll implement no compression.