public SocketSshConnection(ILogger logger, SequencePool sequencePool, Socket socket) : base(sequencePool) { _logger = logger; _socket = socket; _receiveBuffer = sequencePool.RentSequence(); _sendBuffer = sequencePool.RentSequence(); _decoder = new PacketDecoder(SequencePool); _encoder = new PacketEncoder(); }
private byte[] CalculateExchangeHash(SequencePool sequencePool, SshConnectionInfo connectionInfo, Packet clientKexInitMsg, Packet serverKexInitMsg, ReadOnlySequence <byte> public_host_key, ECPoint q_c, ECPoint q_s, BigInteger sharedSecret) { /* * string V_C, client's identification string (CR and LF excluded) * string V_S, server's identification string (CR and LF excluded) * string I_C, payload of the client's SSH_MSG_KEXINIT * string I_S, payload of the server's SSH_MSG_KEXINIT * string K_S, server's public host key * string Q_C, client's ephemeral public key octet string * string Q_S, server's ephemeral public key octet string * mpint K, shared secret */ using Sequence sequence = sequencePool.RentSequence(); var writer = new SequenceWriter(sequence); writer.WriteString(connectionInfo.ClientIdentificationString !); writer.WriteString(connectionInfo.ServerIdentificationString !); writer.WriteString(clientKexInitMsg.Payload); writer.WriteString(serverKexInitMsg.Payload); writer.WriteString(public_host_key); writer.WriteString(q_c); writer.WriteString(q_s); writer.WriteMPInt(sharedSecret); using IncrementalHash hash = IncrementalHash.CreateHash(_hashAlgorithmName); foreach (var segment in sequence.AsReadOnlySequence()) { hash.AppendData(segment.Span); } return(hash.GetHashAndReset()); }
private byte[] Hash(SequencePool sequencePool, BigInteger sharedSecret, byte[] exchangeHash, byte c, byte[] sessionId, int hashLength) { // https://tools.ietf.org/html/rfc4253#section-7.2 byte[] hashRv = new byte[hashLength]; int hashOffset = 0; // TODO: handle 'If the key length needed is longer than the output of the HASH' // HASH(K || H || c || session_id) using Sequence sequence = sequencePool.RentSequence(); var writer = new SequenceWriter(sequence); writer.WriteMPInt(sharedSecret); writer.Write(exchangeHash); writer.WriteByte(c); writer.Write(sessionId); using IncrementalHash hash = IncrementalHash.CreateHash(_hashAlgorithmName); foreach (var segment in sequence.AsReadOnlySequence()) { hash.AppendData(segment.Span); } byte[] K1 = hash.GetHashAndReset(); Append(hashRv, K1, ref hashOffset); while (hashOffset != hashRv.Length) { // TODO: handle 'If the key length needed is longer than the output of the HASH' // K3 = HASH(K || H || K1 || K2) throw new NotSupportedException(); } return(hashRv);
private static Packet CreatePublicKeyRequestMessage(SequencePool sequencePool, string userName, byte[] sessionId, PrivateKey privateKey) { /* * byte SSH_MSG_USERAUTH_REQUEST * string user name * string service name * string "publickey" * boolean TRUE * string public key algorithm name * string public key to be used for authentication * string signature */ using var packet = sequencePool.RentPacket(); var writer = packet.GetWriter(); writer.WriteByte(MessageNumber.SSH_MSG_USERAUTH_REQUEST); writer.WriteString(userName); writer.WriteString("ssh-connection"); writer.WriteString("publickey"); writer.WriteBoolean(true); writer.WriteString(privateKey.Format); privateKey.AppendPublicKey(ref writer); { /* * string session identifier * byte SSH_MSG_USERAUTH_REQUEST * string user name * string service name * string "publickey" * boolean TRUE * string public key algorithm name * string public key to be used for authentication */ using var signatureData = sequencePool.RentSequence(); var signatureWriter = new SequenceWriter(signatureData); signatureWriter.WriteString(sessionId); signatureWriter.WriteByte(MessageNumber.SSH_MSG_USERAUTH_REQUEST); signatureWriter.WriteString(userName); signatureWriter.WriteString("ssh-connection"); signatureWriter.WriteString("publickey"); signatureWriter.WriteBoolean(true); signatureWriter.WriteString(privateKey.Format); privateKey.AppendPublicKey(ref signatureWriter); privateKey.AppendSignature(ref writer, signatureData.AsReadOnlySequence()); } return(packet.Move()); }
public bool TryDecodePacket(Sequence receiveBuffer, int maxLength, out Packet packet) { // Binary Packet Protocol: https://tools.ietf.org/html/rfc4253#section-6. /* * uint32 packet_length * byte padding_length * byte[n1] payload; n1 = packet_length - padding_length - 1 * byte[n2] random padding; n2 = padding_length * byte[m] mac (Message Authentication Code - MAC); m = mac_length */ if (_decodedPacket == null) { _decodedPacket = _sequencePool.RentSequence(); } // We can't decode past the packet, because the mac is not encrypted. // We need to know the packet length to know how much we can decrypt. while (_decodedPacket.Length < 4 && receiveBuffer.Length >= _decode.BlockSize) { _decode.Transform(receiveBuffer.AsReadOnlySequence().Slice(0, _decode.BlockSize), _decodedPacket); receiveBuffer.Remove(_decode.BlockSize); } var decodedReader = new SequenceReader(_decodedPacket); if (decodedReader.Length >= 4) { // Read the packet length. uint packet_length = decodedReader.ReadUInt32(); if (packet_length > maxLength) { ThrowHelper.ThrowProtocolPacketTooLong(); } // Decode the entire packet. uint concatenated_length = 4 + packet_length; // TODO: verify contatenated_length is a multiple of the cipher block size or 8, whichever is larger. long remaining = concatenated_length - decodedReader.Length; if (remaining > 0 && receiveBuffer.Length >= remaining) { _decode.Transform(receiveBuffer.AsReadOnlySequence().Slice(0, remaining), _decodedPacket); receiveBuffer.Remove(remaining); remaining = 0; } if (remaining == 0) { if (_decodedPacket.Length != concatenated_length) { ThrowHelper.ThrowInvalidOperation("Complete packet expected."); } // TODO: verify mac receiveBuffer.Remove(_mac.HashSize); packet = new Packet(_decodedPacket); _decodedPacket = null; return(true); } } packet = new Packet(null); return(false); }
public static Packet RentPacket(this SequencePool sequencePool) { Sequence sequence = sequencePool.RentSequence(); return(new Packet(sequence)); }