Example #1
0
        public async Task <KeyExchangeOutput> TryExchangeAsync(SshConnection connection, KeyExchangeInput input, ILogger logger, CancellationToken ct)
        {
            var sequencePool = connection.SequencePool;

            using ECDiffieHellman ecdh = ECDiffieHellman.Create(_ecCurve);

            // Send ECDH_INIT.
            using ECDiffieHellmanPublicKey myPublicKey = ecdh.PublicKey;
            ECPoint q_c = myPublicKey.ExportParameters().Q;
            {
                using var ecdhInitMsg = CreateEcdhInitMessage(sequencePool, q_c);
                await connection.SendPacketAsync(ecdhInitMsg, ct);
            }

            // Receive ECDH_REPLY.
            Packet exchangeInitMsg = input.ExchangeInitMsg;

            using Packet exchangeInitMsgDispose =
                      exchangeInitMsg.IsEmpty ? (exchangeInitMsg = await connection.ReceivePacketAsync(ct)) : default(Packet);
            var ecdhReply = ParceEcdhReply(exchangeInitMsg);

            // TODO: Verify received key is valid.
            // TODO: Verify host key belongs to server.

            // Compute shared secret.
            // TODO: what types of exceptions can we get when creating the public key?
            ECParameters parameters = new ECParameters
            {
                Curve = _ecCurve,
                Q     = ecdhReply.q_s
            };

            using ECDiffieHellman peerEcdh = ECDiffieHellman.Create(parameters);
            using ECDiffieHellmanPublicKey peerPublicKey = peerEcdh.PublicKey;
            BigInteger sharedSecret = DeriveSharedSecret(ecdh, peerPublicKey);

            var publicHostKey = PublicKey.Read(ecdhReply.public_host_key, input.HostKeyAlgorithms);

            // Generate exchange hash.
            byte[] exchangeHash = CalculateExchangeHash(sequencePool, input.ConnectionInfo, input.ClientKexInitMsg, input.ServerKexInitMsg, ecdhReply.public_host_key, q_c, ecdhReply.q_s, sharedSecret);

            // Verify the server's signature.
            if (!publicHostKey.VerifySignature(exchangeHash, ecdhReply.exchange_hash_signature))
            {
                throw new KeyExchangeFailedException("Signature does not match host key.");
            }

            byte[] sessionId        = input.ConnectionInfo.SessionId ?? exchangeHash;
            byte[] initialIVC2S     = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'A', sessionId, input.InitialIVC2SLength);
            byte[] initialIVS2C     = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'B', sessionId, input.InitialIVS2CLength);
            byte[] encryptionKeyC2S = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'C', sessionId, input.EncryptionKeyC2SLength);
            byte[] encryptionKeyS2C = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'D', sessionId, input.EncryptionKeyS2CLength);
            byte[] integrityKeyC2S  = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'E', sessionId, input.IntegrityKeyC2SLength);
            byte[] integrityKeyS2C  = Hash(sequencePool, sharedSecret, exchangeHash, (byte)'F', sessionId, input.IntegrityKeyS2CLength);

            return(new KeyExchangeOutput(exchangeHash,
                                         initialIVS2C, encryptionKeyS2C, integrityKeyS2C,
                                         initialIVC2S, encryptionKeyC2S, integrityKeyC2S));
        }
Example #2
0
        // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob
        // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export
        // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific"
        // from https://github.com/dotnet/runtime/issues/27276
        // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix
        internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey publicKey)
        {
            byte[] keyBlob = new byte[ECCPublicKeyBlob.Size];

            // Set magic number
            Array.Copy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4);
            // Set key size
            keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize;

            ECPoint ecPoint = publicKey.ExportParameters().Q;

            Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize,
                         $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}");
            // Copy x and y coordinates to key blob
            Array.Copy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize);
            Array.Copy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize);
            return(keyBlob);
        }
Example #3
0
        private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, System.Security.Cryptography.IncrementalHash?hash)
        {
            PublicKey otherPartyNSecPublicKey;

            if (otherPartyPublicKey is X25519PublicKey x25519PublicKey)
            {
                otherPartyNSecPublicKey = x25519PublicKey.publicKey;
            }
            else
            {
                var parameters = otherPartyPublicKey.ExportParameters();
                otherPartyNSecPublicKey = NSec.Cryptography.PublicKey.Import(KeyAgreementAlgorithm.X25519, parameters.Q.X, KeyBlobFormat.RawPublicKey);
            }

            Debug.Assert(this.privateKey != null);
            Debug.Assert(hash != null);

            using var sharedSecret = KeyAgreementAlgorithm.X25519.Agree(this.privateKey, otherPartyNSecPublicKey);

            // NSec doesn't offer a way to export the shared secret. Unfortunately it also doesn't provide
            // the correct key derivation function so we have to resort to using the private API.
            var memoryHandle = (SafeHandle?)typeof(SharedSecret).GetProperty("Handle", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetMethod?.Invoke(sharedSecret, null);

            Debug.Assert(memoryHandle != null);
            byte[] secretBytes = new byte[32];
            try
            {
                Marshal.Copy(memoryHandle.DangerousGetHandle(), secretBytes, 0, 32);
                hash.AppendData(secretBytes);
            }
            finally
            {
                CryptographicOperations.ZeroMemory(secretBytes);
            }

            return(null);
        }
Example #4
0
 public override ECParameters ExportParameters() => _wrapped.ExportParameters();