1 files changed, 150 insertions, 0 deletions
diff --git a/crypto/src/util/ssh/SSHBuffer.cs b/crypto/src/util/ssh/SSHBuffer.cs
new file mode 100644
index 000000000..8d3c3f977
--- /dev/null
+++ b/crypto/src/util/ssh/SSHBuffer.cs
@@ -0,0 +1,150 @@
+using System;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Utilities.SSH
+{
+ public class SSHBuffer
+ {
+ private readonly byte[] buffer;
+ private int pos = 0;
+
+ public SSHBuffer(byte[] magic, byte[] buffer)
+ {
+ this.buffer = buffer;
+ for (int i = 0; i != magic.Length; i++)
+ {
+ if (magic[i] != buffer[i])
+ {
+ throw new ArgumentException("magic-number incorrect");
+ }
+ }
+
+ pos += magic.Length;
+ }
+
+ public SSHBuffer(byte[] buffer)
+ {
+ this.buffer = buffer;
+ }
+
+ public int ReadU32()
+ {
+ if (pos > (buffer.Length - 4))
+ {
+ throw new ArgumentOutOfRangeException("4 bytes for U32 exceeds buffer.");
+ }
+
+ int i = (buffer[pos++] & 0xFF) << 24;
+ i |= (buffer[pos++] & 0xFF) << 16;
+ i |= (buffer[pos++] & 0xFF) << 8;
+ i |= (buffer[pos++] & 0xFF);
+
+ return i;
+ }
+
+ public String ReadString()
+ {
+ return Strings.FromByteArray(ReadBlock());
+ }
+
+ public byte[] ReadBlock()
+ {
+ int len = ReadU32();
+ if (len == 0)
+ {
+ return new byte[0];
+ }
+
+ if (pos > (buffer.Length - len))
+ {
+ throw new ArgumentException("not enough data for block");
+ }
+
+ int start = pos; pos += len;
+ return Arrays.CopyOfRange(buffer, start, pos);
+ }
+
+ public void SkipBlock()
+ {
+ int len = ReadU32();
+ if (pos > (buffer.Length - len))
+ {
+ throw new ArgumentException("not enough data for block");
+ }
+
+ pos += len;
+ }
+
+ public byte[] ReadPaddedBlock()
+ {
+ return ReadPaddedBlock(8);
+ }
+
+ public byte[] ReadPaddedBlock(int blockSize)
+ {
+ int len = ReadU32();
+ if (len == 0)
+ {
+ return new byte[0];
+ }
+
+ if (pos > (buffer.Length - len))
+ {
+ throw new ArgumentException("not enough data for block");
+ }
+
+ int align = len % blockSize;
+ if (0 != align)
+ {
+ throw new ArgumentException("missing padding");
+ }
+
+ int start = pos; pos += len;
+ int end = pos;
+
+ if (len > 0)
+ {
+ // TODO If encryption is supported, should be constant-time
+ int lastByte = buffer[pos - 1] & 0xFF;
+ if (0 < lastByte && lastByte < blockSize)
+ {
+ int padCount = lastByte;
+ end -= padCount;
+
+ for (int i = 1, padPos = end; i <= padCount; ++i, ++padPos)
+ {
+ if (i != (buffer[padPos] & 0xFF))
+ {
+ throw new ArgumentException("incorrect padding");
+ }
+ }
+ }
+ }
+
+ return Arrays.CopyOfRange(buffer, start, end);
+ }
+
+ public BigInteger ReadBigNumPositive()
+ {
+ int len = ReadU32();
+ if (pos + len > buffer.Length)
+ {
+ throw new ArgumentException("not enough data for big num");
+ }
+
+ int start = pos; pos += len;
+ byte[] d = Arrays.CopyOfRange(buffer, start, pos);
+ return new BigInteger(1, d);
+ }
+
+ public byte[] GetBuffer()
+ {
+ return Arrays.Clone(buffer);
+ }
+
+ public Boolean HasRemaining()
+ {
+ return pos < buffer.Length;
+ }
+ }
+}
|