Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into ssh_config
Browse files Browse the repository at this point in the history
  • Loading branch information
tmds committed Jul 31, 2024
2 parents 5ef2ed0 + 9b20e1a commit 19ab3d3
Show file tree
Hide file tree
Showing 20 changed files with 357 additions and 50 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ enum UnixFilePermissions // values match System.IO.UnixFileMode.
SetGroup,
SetUser,
}
static class UnixFilePemissionExtensions
static class UnixFilePermissionsExtensions
{
static UnixFilePermissions ToUnixFilePermissions(this System.IO.UnixFileMode mode);
static System.IO.UnixFileMode ToUnixFileMode(this UnixFilePermissions permissions);
Expand Down Expand Up @@ -419,12 +419,15 @@ This section lists the currently supported algorithms. If you would like support

Supported private key formats:
- RSA in `RSA PRIVATE KEY`
- RSA in `OPENSSH PRIVATE KEY` (`openssh-key-v1`)
- RSA, ECDSA in `OPENSSH PRIVATE KEY` (`openssh-key-v1`)

Supported private key encryption cyphers:
- none

Supported client key algorithms:
- ecdsa-sha2-nistp521
- ecdsa-sha2-nistp384
- ecdsa-sha2-nistp256
- rsa-sha2-512
- rsa-sha2-256

Expand Down
6 changes: 3 additions & 3 deletions src/Tmds.Ssh/AlgorithmNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ static class AlgorithmNames // TODO: rename to KnownNames
public static Name HMacSha2_256 => new Name(HMacSha2_256Bytes);

// Curve names.
private static readonly byte[] Nistp265Bytes = "nistp256"u8.ToArray();
public static Name Nistp265 => new Name(Nistp265Bytes);
private static readonly byte[] Nistp256Bytes = "nistp256"u8.ToArray();
public static Name Nistp256 => new Name(Nistp256Bytes);
private static readonly byte[] Nistp384Bytes = "nistp384"u8.ToArray();
public static Name Nistp384 => new Name(Nistp384Bytes);
private static readonly byte[] Nistp521Bytes = "nistp521"u8.ToArray();
Expand All @@ -67,5 +67,5 @@ static class AlgorithmNames // TODO: rename to KnownNames
public static Name Password => new Name(PasswordBytes);
private static readonly byte[] PublicKeyBytes = "publickey"u8.ToArray();
public static Name PublicKey => new Name(PublicKeyBytes);

}
1 change: 1 addition & 0 deletions src/Tmds.Ssh/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ static class Constants
public const int MaxNameLength = 128; // Arbitrary limit, may be increased.
public const int MaxECPointLength = 256 + 1; // Arbitrary limit, may be increased.
public const int MaxKeyLength = 1024; // Arbitrary limit, may be increased.
public const int MaxMPIntLength = 1024; // Arbitrary limit, may be increased.
public const int MaxBannerPackets = 1024; // Abitrary limit
}
76 changes: 76 additions & 0 deletions src/Tmds.Ssh/ECDsaPrivateKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// This file is part of Tmds.Ssh which is released under MIT.
// See file LICENSE for full license details.

using System;
using System.Buffers;
using System.Formats.Asn1;
using System.Numerics;
using System.Security.Cryptography;

namespace Tmds.Ssh;

sealed class ECDsaPrivateKey : PrivateKey
{
private readonly ECDsa _ecdsa;
private readonly Name _algorithm;
private readonly Name _curveName;
private readonly HashAlgorithmName _hashAlgorithm;

public ECDsaPrivateKey(ECDsa ecdsa, Name algorithm, Name curveName, HashAlgorithmName hashAlgorithm) :
base([algorithm])
{
_ecdsa = ecdsa ?? throw new ArgumentNullException(nameof(ecdsa));
_algorithm = algorithm;
_curveName = curveName;
_hashAlgorithm = hashAlgorithm;
}

public override void Dispose()
{
_ecdsa.Dispose();
}

public override void AppendPublicKey(ref SequenceWriter writer)
{
ECParameters parameters = _ecdsa.ExportParameters(includePrivateParameters: false);

using var innerData = writer.SequencePool.RentSequence();
var innerWriter = new SequenceWriter(innerData);
innerWriter.WriteString(_algorithm);
innerWriter.WriteString(_curveName);
innerWriter.WriteString(parameters.Q);

writer.WriteString(innerData.AsReadOnlySequence());
}

public override void AppendSignature(Name algorithm, ref SequenceWriter writer, ReadOnlySequence<byte> data)
{
if (algorithm != _algorithm)
{
ThrowHelper.ThrowProtocolUnexpectedValue();
return;
}

byte[] signature = _ecdsa.SignData(
data.IsSingleSegment ? data.FirstSpan : data.ToArray().AsSpan(),
_hashAlgorithm,
DSASignatureFormat.Rfc3279DerSequence);

AsnReader reader = new AsnReader(signature, AsnEncodingRules.DER);
AsnReader innerReader = reader.ReadSequence();
BigInteger r = innerReader.ReadInteger();
BigInteger s = innerReader.ReadInteger();

using var ecdsaSigData = writer.SequencePool.RentSequence();
var ecdsaSigWriter = new SequenceWriter(ecdsaSigData);
ecdsaSigWriter.WriteMPInt(r);
ecdsaSigWriter.WriteMPInt(s);

using var innerData = writer.SequencePool.RentSequence();
var innerWriter = new SequenceWriter(innerData);
innerWriter.WriteString(algorithm);
innerWriter.WriteString(ecdsaSigData.AsReadOnlySequence());

writer.WriteString(innerData.AsReadOnlySequence());
}
}
2 changes: 1 addition & 1 deletion src/Tmds.Ssh/ECDsaPublicKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static ECDsaPublicKey CreateFromSshKey(byte[] key)
var name = reader.ReadName();
if (name == AlgorithmNames.EcdsaSha2Nistp256)
{
reader.ReadName(AlgorithmNames.Nistp265);
reader.ReadName(AlgorithmNames.Nistp256);
ECPoint q = reader.ReadStringAsECPoint();
reader.ReadEnd();
return new ECDsaPublicKey(AlgorithmNames.EcdsaSha2Nistp256, ECCurve.NamedCurves.nistP256, q, HashAlgorithmName.SHA256);
Expand Down
16 changes: 8 additions & 8 deletions src/Tmds.Ssh/RsaPublicKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ namespace Tmds.Ssh;

class RsaPublicKey : PublicKey
{
private readonly BigInteger _e;
private readonly BigInteger _n;
private readonly byte[] _e;
private readonly byte[] _n;

public RsaPublicKey(BigInteger e, BigInteger n)
public RsaPublicKey(byte[] e, byte[] n)
{
_e = e;
_n = n;
}

public int KeySize => _n.GetByteCount(isUnsigned: true) * 8;
public int KeySize => _n.Length * 8;

public static RsaPublicKey CreateFromSshKey(byte[] key)
{
SequenceReader reader = new SequenceReader(key);
reader.ReadName(AlgorithmNames.SshRsa);
BigInteger e = reader.ReadMPInt();
BigInteger n = reader.ReadMPInt();
byte[] e = reader.ReadMPIntAsByteArray(isUnsigned: true);
byte[] n = reader.ReadMPIntAsByteArray(isUnsigned: true);
reader.ReadEnd();
return new RsaPublicKey(e, n);
}
Expand All @@ -55,8 +55,8 @@ internal override bool VerifySignature(IReadOnlyList<Name> allowedAlgorithms, Sp

var rsaParameters = new RSAParameters
{
Exponent = _e.ToByteArray(isUnsigned: true, isBigEndian: true),
Modulus = _n.ToByteArray(isUnsigned: true, isBigEndian: true)
Exponent = _e,
Modulus = _n
};
using var rsa = RSA.Create(rsaParameters);
int signatureLength = rsa.KeySize / 8;
Expand Down
56 changes: 56 additions & 0 deletions src/Tmds.Ssh/SequenceReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ static Name ReadName(ReadOnlySequence<byte> nameSequence)
public BigInteger ReadMPInt()
{
long length = ReadUInt32();

if (length > Constants.MaxMPIntLength)
{
ThrowHelper.ThrowProtocolMPIntTooLong();
}

if (length == 0)
{
return BigInteger.Zero;
Expand All @@ -320,6 +326,56 @@ public BigInteger ReadMPInt()
}
}

public byte[] ReadMPIntAsByteArray(bool isUnsigned, int minLength = -1)
{
minLength = Math.Max(1, minLength);

uint l = ReadUInt32();

if (Math.Max(l, minLength) > Constants.MaxMPIntLength)
{
ThrowHelper.ThrowProtocolMPIntTooLong();
}

int length = (int)l;

if (length == 0)
{
return new byte[minLength];
}

byte firstByte = ReadByte();

bool isNegative = firstByte >= 128;
if (isUnsigned && isNegative)
{
ThrowHelper.ThrowProtocolValueOutOfRange();
}

bool skipFirstByte = isUnsigned && firstByte == 0;

int arrayLength = Math.Max(minLength, length + (skipFirstByte ? - 1 : 0));
byte[] array = new byte[arrayLength];

length--;

array.AsSpan(0, arrayLength - length).Fill(isNegative ? (byte)0xff : (byte)0x00);

if (!skipFirstByte)
{
array[arrayLength - length - 1] = firstByte;
}

if (!_reader.TryCopyTo(array.AsSpan(arrayLength - length)))
{
ThrowHelper.ThrowProtocolUnexpectedEndOfPacket();
}

_reader.Advance(length);

return array;
}

public ECPoint ReadStringAsECPoint()
{
long length = ReadUInt32();
Expand Down
2 changes: 1 addition & 1 deletion src/Tmds.Ssh/SshClientSettings.Defaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ partial class SshClientSettings
internal readonly static List<Name> SupportedKeyExchangeAlgorithms = [ AlgorithmNames.EcdhSha2Nistp256, AlgorithmNames.EcdhSha2Nistp384, AlgorithmNames.EcdhSha2Nistp521 ];
internal readonly static List<Name> SupportedServerHostKeyAlgorithms = [ AlgorithmNames.EcdsaSha2Nistp521, AlgorithmNames.EcdsaSha2Nistp384, AlgorithmNames.EcdsaSha2Nistp256, AlgorithmNames.RsaSshSha2_512, AlgorithmNames.RsaSshSha2_256 ];
internal readonly static List<Name> SupportedEncryptionAlgorithms = [ AlgorithmNames.Aes256Gcm, AlgorithmNames.Aes128Gcm ];
internal readonly static List<Name> SupportedPublicKeyAcceptedAlgorithms = [ ..AlgorithmNames.SshRsaAlgorithms ];
internal readonly static List<Name> SupportedPublicKeyAcceptedAlgorithms = [ AlgorithmNames.EcdsaSha2Nistp521, AlgorithmNames.EcdsaSha2Nistp384, AlgorithmNames.EcdsaSha2Nistp256, AlgorithmNames.RsaSshSha2_512, AlgorithmNames.RsaSshSha2_256 ];
internal readonly static List<Name> SupportedMacAlgorithms = EmptyList;
internal readonly static List<Name> SupportedCompressionAlgorithms = [ AlgorithmNames.None ];
internal readonly static List<Name> DisableCompressionAlgorithms = [ AlgorithmNames.None ];
Expand Down
12 changes: 12 additions & 0 deletions src/Tmds.Ssh/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ public static void ThrowProtocolPacketLongerThanExpected()
throw new ProtocolException("The packet contains more data than expected.");
}

[DoesNotReturn]
public static void ThrowProtocolValueOutOfRange()
{
throw new ProtocolException("The value is out of the expected range.");
}

[DoesNotReturn]
public static void ThrowProtocolUnexpectedValue()
{
Expand Down Expand Up @@ -133,6 +139,12 @@ public static void ThrowProtocolECPointTooLong()
throw new ProtocolException("The elliptic curve point is too long.");
}

[DoesNotReturn]
public static void ThrowProtocolMPIntTooLong()
{
throw new ProtocolException("The mpint is too long.");
}

[DoesNotReturn]
public static void ThrowNotSupportedException(string message)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Tmds.Ssh/UnixFilePermissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public enum UnixFilePermissions : short
SetUser = 2048,
}

public static class UnixFilePemissionExtensions
public static class UnixFilePermissionsExtensions
{
#if NET7_0_OR_GREATER
public static UnixFilePermissions ToUnixFilePermissions(this UnixFileMode mode)
Expand Down
Loading

0 comments on commit 19ab3d3

Please sign in to comment.