Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | 2x 2x 43x 1x 6x 5x 76x 76x 76x 76x 12x 12x 12x 11x 1x 1x 1x 1x 1x 12x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 25x 25x 25x 25x 193x 17x 17x 20x 20x 1x 1x 1x 1x 1x 1x | import {JsonPackMpint} from '../JsonPackMpint';
import type {IWriter, IWriterGrowable} from '@jsonjoy.com/buffers/lib';
import type {BinaryJsonEncoder} from '../types';
/**
* SSH 2.0 binary encoder for SSH protocol data types.
* Implements SSH binary encoding according to RFC 4251.
*
* Key SSH encoding principles:
* - Multi-byte quantities are transmitted in big-endian byte order (network byte order)
* - Strings are length-prefixed with uint32
* - No padding is used (unlike XDR)
*/
export class SshEncoder implements BinaryJsonEncoder {
constructor(public readonly writer: IWriter & IWriterGrowable) {}
public encode(value: unknown): Uint8Array {
const writer = this.writer;
writer.reset();
this.writeAny(value);
return writer.flush();
}
/**
* Called when the encoder encounters a value that it does not know how to encode.
*/
public writeUnknown(value: unknown): void {
throw new Error('SSH encoder does not support unknown types');
}
public writeAny(value: unknown): void {
switch (typeof value) {
case 'boolean':
return this.writeBoolean(value);
case 'number':
return this.writeNumber(value);
case 'string':
return this.writeStr(value);
case 'object': {
Iif (value === null) return this.writeNull();
const construct = value.constructor;
switch (construct) {
case Uint8Array:
return this.writeBin(value as Uint8Array);
case Array:
return this.writeNameList(value as string[]);
case JsonPackMpint:
return this.writeMpint(value as JsonPackMpint);
default:
return this.writeUnknown(value);
}
}
case 'bigint':
return this.writeUint64(value);
case 'undefined':
return this.writeNull();
default:
return this.writeUnknown(value);
}
}
/**
* SSH doesn't have a null type, but we provide it for interface compatibility.
*/
public writeNull(): void {
throw new Error('SSH protocol does not have a null type');
}
/**
* Writes an SSH boolean value as a single byte.
* The value 0 represents FALSE, and the value 1 represents TRUE.
*/
public writeBoolean(bool: boolean): void {
this.writer.u8(bool ? 1 : 0);
}
/**
* Writes an SSH byte value (8-bit).
*/
public writeByte(byte: number): void {
this.writer.u8(byte & 0xff);
}
/**
* Writes an SSH uint32 value in big-endian format.
*/
public writeUint32(uint: number): void {
const writer = this.writer;
writer.ensureCapacity(4);
writer.view.setUint32(writer.x, Math.trunc(uint) >>> 0, false); // big-endian
writer.move(4);
}
/**
* Writes an SSH uint64 value in big-endian format.
*/
public writeUint64(uint: number | bigint): void {
const writer = this.writer;
writer.ensureCapacity(8);
if (typeof uint === 'bigint') {
writer.view.setBigUint64(writer.x, uint, false); // big-endian
} else {
const truncated = Math.trunc(Math.abs(uint));
const high = Math.floor(truncated / 0x100000000);
const low = truncated >>> 0;
writer.view.setUint32(writer.x, high, false); // high 32 bits
writer.view.setUint32(writer.x + 4, low, false); // low 32 bits
}
writer.move(8);
}
/**
* Writes an SSH string as binary data (Uint8Array).
* Format: uint32 length + data bytes (no padding).
*/
public writeBinStr(data: Uint8Array): void {
this.writeUint32(data.length);
this.writer.buf(data, data.length);
}
/**
* Writes an SSH string with UTF-8 encoding.
* Format: uint32 length + UTF-8 bytes (no padding).
*/
public writeStr(str: string): void {
const writer = this.writer;
const maxSize = str.length * 4; // Max UTF-8 bytes for string
writer.ensureCapacity(4 + maxSize);
// Reserve space for length
const lengthOffset = writer.x;
writer.x += 4;
// Write the string and get actual byte count
const bytesWritten = writer.utf8(str);
// Go back to encode the actual length
const endPos = writer.x;
writer.x = lengthOffset;
this.writeUint32(bytesWritten);
writer.x = endPos;
}
/**
* Writes an SSH string with ASCII encoding.
* Format: uint32 length + ASCII bytes (no padding).
*/
public writeAsciiStr(str: string): void {
const writer = this.writer;
writer.ensureCapacity(4 + str.length);
this.writeUint32(str.length);
for (let i = 0; i < str.length; i++) {
writer.u8(str.charCodeAt(i) & 0x7f); // ASCII only
}
}
/**
* Writes an SSH mpint (multiple precision integer).
* Format: uint32 length + data bytes in two's complement format, MSB first.
*/
public writeMpint(mpint: JsonPackMpint): void {
this.writeUint32(mpint.data.length);
this.writer.buf(mpint.data, mpint.data.length);
}
/**
* Writes an SSH name-list.
* Format: uint32 length + comma-separated names.
*/
public writeNameList(names: string[]): void {
const nameListStr = names.join(',');
this.writeAsciiStr(nameListStr);
}
// BinaryJsonEncoder interface methods
/**
* Generic number writing - writes as uint32 by default
*/
public writeNumber(num: number): void {
if (Number.isInteger(num)) {
if (num >= 0 && num <= 0xffffffff) {
this.writeUint32(num);
} else {
this.writeUint64(num);
}
} else {
throw new Error('SSH protocol does not support floating point numbers');
}
}
/**
* Writes an integer value as uint32
*/
public writeInteger(int: number): void {
this.writeUint32(int);
}
/**
* Writes an unsigned integer value as uint32
*/
public writeUInteger(uint: number): void {
this.writeUint32(uint);
}
/**
* Writes a float value - SSH doesn't support floats
*/
public writeFloat(float: number): void {
throw new Error('SSH protocol does not support floating point numbers');
}
/**
* Writes binary data as SSH string
*/
public writeBin(buf: Uint8Array): void {
this.writeBinStr(buf);
}
/**
* Writes arrays - not supported in base SSH protocol
*/
public writeArr(arr: unknown[]): void {
throw new Error('SSH protocol does not have a generic array type. Use writeNameList for name-list type.');
}
/**
* Writes objects - not supported in base SSH protocol
*/
public writeObj(obj: Record<string, unknown>): void {
throw new Error('SSH protocol does not have an object type');
}
}
|