Implement DER decoding for BOOLEAN, INTEGER and UTF8String in Client
This commit is contained in:
@@ -1,4 +1,64 @@
|
||||
namespace StudySystemClient.Der {
|
||||
public errordomain DecodeError {
|
||||
INCOMPLETE,
|
||||
INVALID_CONTENT,
|
||||
UNKNOWN_TYPE,
|
||||
}
|
||||
|
||||
private const uint BASE_HEADER_SIZE = 2;
|
||||
|
||||
public static Datum decode(uint8[] bytes) throws DecodeError {
|
||||
if (bytes.length < BASE_HEADER_SIZE) {
|
||||
throw new DecodeError.INCOMPLETE(
|
||||
"Message is fewer than %u bytes", BASE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
uint header_size = 0;
|
||||
var length = decode_length(bytes, ref header_size);
|
||||
if (header_size + length > bytes.length) {
|
||||
throw new DecodeError.INCOMPLETE(
|
||||
"Length %u but only %u bytes available", length,
|
||||
bytes.length - header_size);
|
||||
}
|
||||
var content = bytes[header_size:header_size + length];
|
||||
return decode_datum(bytes[0], content);
|
||||
}
|
||||
|
||||
private static uint decode_length(uint8[] bytes, ref uint header_size)
|
||||
throws DecodeError {
|
||||
if ((bytes[1] & 0x80) != 0) {
|
||||
var length_size = bytes[1] & 0x7f;
|
||||
if (BASE_HEADER_SIZE + length_size > bytes.length) {
|
||||
throw new DecodeError.INCOMPLETE(
|
||||
"Length with size %u but only %u bytes available",
|
||||
length_size, bytes.length - BASE_HEADER_SIZE);
|
||||
}
|
||||
var length = 0;
|
||||
for (int i = 0; i < length_size; ++i)
|
||||
length = length << 8 | bytes[BASE_HEADER_SIZE + i];
|
||||
header_size = BASE_HEADER_SIZE + length_size;
|
||||
return length;
|
||||
} else {
|
||||
header_size = BASE_HEADER_SIZE;
|
||||
return bytes[1];
|
||||
}
|
||||
}
|
||||
|
||||
private static Datum decode_datum(uint8 type, uint8[] content)
|
||||
throws DecodeError {
|
||||
switch (type) {
|
||||
case Boolean.TYPE:
|
||||
return new Boolean.from_content(content);
|
||||
case Integer.TYPE:
|
||||
return new Integer.from_content(content);
|
||||
case Utf8String.TYPE:
|
||||
return new Utf8String.from_content(content);
|
||||
default:
|
||||
throw new DecodeError.UNKNOWN_TYPE("Unsupported type: %02x",
|
||||
type);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Datum {
|
||||
internal uint8 type;
|
||||
internal uint8[] content;
|
||||
@@ -33,20 +93,56 @@ namespace StudySystemClient.Der {
|
||||
public class Boolean : Datum {
|
||||
internal const uint8 TYPE = 0x01;
|
||||
|
||||
public bool value { get; private set; }
|
||||
|
||||
public Boolean(bool val) {
|
||||
type = TYPE;
|
||||
content = new uint8[] { val ? 0xff : 0x00 };
|
||||
value = val;
|
||||
}
|
||||
|
||||
internal Boolean.from_content(uint8[] bytes) throws DecodeError {
|
||||
type = TYPE;
|
||||
content = bytes;
|
||||
value = decode_bool(content);
|
||||
}
|
||||
|
||||
internal static bool decode_bool(uint8[] bytes)
|
||||
throws DecodeError {
|
||||
if (bytes.length != 1) {
|
||||
throw new DecodeError.INVALID_CONTENT(
|
||||
"Invalid length for boolean: %u", bytes.length);
|
||||
}
|
||||
switch (bytes[0]) {
|
||||
case 0xff:
|
||||
return true;
|
||||
case 0x00:
|
||||
return false;
|
||||
default:
|
||||
throw new DecodeError.INVALID_CONTENT(
|
||||
"Invalid value of boolean: %02x", bytes[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Integer : Datum {
|
||||
internal const uint8 TYPE = 0x02;
|
||||
private const uint8 MAX_BYTES = 8;
|
||||
|
||||
public int64 value { get; private set; }
|
||||
|
||||
public Integer(int64 val) {
|
||||
type = TYPE;
|
||||
content = encode_int64(val);
|
||||
value = val;
|
||||
}
|
||||
|
||||
internal Integer.from_content(uint8[] bytes) throws DecodeError {
|
||||
type = TYPE;
|
||||
content = bytes;
|
||||
value = decode_int64(content);
|
||||
}
|
||||
|
||||
private static uint64 twos_complement(uint64 x) {
|
||||
return ~x + 1;
|
||||
}
|
||||
@@ -77,14 +173,52 @@ namespace StudySystemClient.Der {
|
||||
|
||||
return buffer.data;
|
||||
}
|
||||
|
||||
private static int64 decode_int64(uint8[] bytes) throws DecodeError {
|
||||
if (bytes.length > MAX_BYTES) {
|
||||
throw new DecodeError.INVALID_CONTENT(
|
||||
"int64 too small for %u bytes", bytes.length);
|
||||
}
|
||||
var negative = (bytes[0] & 0x80) != 0;
|
||||
var val = decode_start_val(negative, bytes.length);
|
||||
foreach (var byte in bytes)
|
||||
val = val << 8 | byte;
|
||||
return negative ? -(int64)twos_complement(val) : (int64)val;
|
||||
}
|
||||
|
||||
private static uint64 decode_start_val(bool negative, uint length)
|
||||
{
|
||||
if (!negative)
|
||||
return 0;
|
||||
var val = 0;
|
||||
for (uint i = 0; i < MAX_BYTES - length; ++i)
|
||||
val = val << 8 | 0xff;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public class Utf8String : Datum {
|
||||
internal const uint8 TYPE = 0x0c;
|
||||
|
||||
public string value { get; private set; }
|
||||
|
||||
public Utf8String(string val) {
|
||||
type = TYPE;
|
||||
content = val.data;
|
||||
value = val;
|
||||
}
|
||||
|
||||
public Utf8String.from_content(uint8[] bytes) {
|
||||
type = TYPE;
|
||||
content = bytes;
|
||||
value = decode_string(bytes);
|
||||
}
|
||||
|
||||
public static string decode_string(uint8[] bytes) {
|
||||
var buffer = new uint8[bytes.length + 1];
|
||||
Memory.copy(buffer, bytes, bytes.length);
|
||||
buffer[bytes.length] = 0;
|
||||
return (string)buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user