Implement DER encoding for BOOLEAN, INTEGER and UTF8String in Client
This commit is contained in:
parent
31712d5efa
commit
4b22bd726f
@ -11,3 +11,4 @@ add_project_arguments('-w', language: 'c')
|
|||||||
gtk_dep = dependency('gtk4')
|
gtk_dep = dependency('gtk4')
|
||||||
|
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
subdir('tests')
|
||||||
|
90
client/src/der.vala
Normal file
90
client/src/der.vala
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
namespace StudySystemClient.Der {
|
||||||
|
public abstract class Datum {
|
||||||
|
internal uint8 type;
|
||||||
|
internal uint8[] content;
|
||||||
|
|
||||||
|
public uint8[] encode() {
|
||||||
|
var buffer = new ByteArray();
|
||||||
|
|
||||||
|
buffer.append({type});
|
||||||
|
if (content.length >= 0x80) {
|
||||||
|
var length_bytes = encode_length(content.length);
|
||||||
|
buffer.append({0x80 | (uint8)length_bytes.length});
|
||||||
|
buffer.append(length_bytes);
|
||||||
|
} else {
|
||||||
|
buffer.append({(uint8)content.length});
|
||||||
|
}
|
||||||
|
buffer.append(content);
|
||||||
|
|
||||||
|
return buffer.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint8[] encode_length(uint length) {
|
||||||
|
var buffer = new ByteArray();
|
||||||
|
int shift = 0;
|
||||||
|
while (length >> (shift + 8) != 0)
|
||||||
|
shift += 8;
|
||||||
|
for (; shift >= 0; shift -= 8)
|
||||||
|
buffer.append({(uint8)(length >> shift)});
|
||||||
|
return buffer.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Boolean : Datum {
|
||||||
|
internal const uint8 TYPE = 0x01;
|
||||||
|
|
||||||
|
public Boolean(bool val) {
|
||||||
|
type = TYPE;
|
||||||
|
content = new uint8[] { val ? 0xff : 0x00 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Integer : Datum {
|
||||||
|
internal const uint8 TYPE = 0x02;
|
||||||
|
|
||||||
|
public Integer(int64 val) {
|
||||||
|
type = TYPE;
|
||||||
|
content = encode_int64(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint64 twos_complement(uint64 x) {
|
||||||
|
return ~x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int min_bits(bool negative, uint64 x) {
|
||||||
|
int n = 0;
|
||||||
|
if (negative) {
|
||||||
|
while ((x >> (n + 8) & 0xff) != 0xff)
|
||||||
|
n += 8;
|
||||||
|
} else {
|
||||||
|
while (x >> (n + 8) > 0)
|
||||||
|
n += 8;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint8[] encode_int64(int64 val) {
|
||||||
|
var negative = val < 0;
|
||||||
|
var uval = negative ? twos_complement(val.abs()) : val;
|
||||||
|
var shift = min_bits(negative, uval);
|
||||||
|
|
||||||
|
var buffer = new ByteArray();
|
||||||
|
for (; shift >= 0; shift -= 8)
|
||||||
|
buffer.append({(uint8)(uval >> shift)});
|
||||||
|
|
||||||
|
if (!negative && (buffer.data[0] & 0x80) != 0)
|
||||||
|
buffer.prepend({0x00});
|
||||||
|
|
||||||
|
return buffer.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Utf8String : Datum {
|
||||||
|
internal const uint8 TYPE = 0x0c;
|
||||||
|
|
||||||
|
public Utf8String(string val) {
|
||||||
|
type = TYPE;
|
||||||
|
content = val.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ lib = library(
|
|||||||
'study-system-client',
|
'study-system-client',
|
||||||
sources: files(
|
sources: files(
|
||||||
'connection.vala',
|
'connection.vala',
|
||||||
|
'der.vala',
|
||||||
'main_window.vala',
|
'main_window.vala',
|
||||||
),
|
),
|
||||||
dependencies: [gtk_dep],
|
dependencies: [gtk_dep],
|
||||||
|
95
client/tests/der_tests.vala
Normal file
95
client/tests/der_tests.vala
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using StudySystemClient.Der;
|
||||||
|
|
||||||
|
static bool bytes_equal(uint8[] expected, uint8[] actual)
|
||||||
|
{
|
||||||
|
if (expected.length != actual.length)
|
||||||
|
return false;
|
||||||
|
return Memory.cmp(expected, actual, expected.length) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string bytes_to_string(uint8[] bytes)
|
||||||
|
{
|
||||||
|
var s = "";
|
||||||
|
foreach (var byte in bytes)
|
||||||
|
s += "%02x".printf(byte);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_encode(Datum datum, uint8[] expected)
|
||||||
|
{
|
||||||
|
var bytes = datum.encode();
|
||||||
|
if (!bytes_equal(expected, bytes)) {
|
||||||
|
Test.message("Encoding is incorrect: expected %s got %s",
|
||||||
|
bytes_to_string(expected), bytes_to_string(bytes));
|
||||||
|
Test.fail();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(string[] args) {
|
||||||
|
Test.init(ref args);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encoding
|
||||||
|
*/
|
||||||
|
|
||||||
|
Test.add_func("/encode/boolean/true", () => {
|
||||||
|
test_encode(new Boolean(true), {0x01, 0x01, 0xff});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/boolean/false", () => {
|
||||||
|
test_encode(new Boolean(false), {0x01, 0x01, 0x00});
|
||||||
|
});
|
||||||
|
|
||||||
|
Test.add_func("/encode/integer/small/0", () => {
|
||||||
|
test_encode(new Integer(0), {0x02, 0x01, 0x00});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/small/5", () => {
|
||||||
|
test_encode(new Integer(5), {0x02, 0x01, 0x05});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/small/42", () => {
|
||||||
|
test_encode(new Integer(42), {0x02, 0x01, 0x2a});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/large/1337", () => {
|
||||||
|
test_encode(new Integer(1337), {0x02, 0x02, 0x05, 0x39});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/sign/128", () => {
|
||||||
|
test_encode(new Integer(128), {0x02, 0x02, 0x00, 0x80});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/sign/0xbeef", () => {
|
||||||
|
test_encode(new Integer(0xbeef), {0x02, 0x03, 0x00, 0xbe, 0xef});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/sign/-128", () => {
|
||||||
|
test_encode(new Integer(-128), {0x02, 0x01, 0x80});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/integer/sign/-1337", () => {
|
||||||
|
test_encode(new Integer(-1337), {0x02, 0x02, 0xfa, 0xc7});
|
||||||
|
});
|
||||||
|
|
||||||
|
Test.add_func("/encode/utf8string/short/foo", () => {
|
||||||
|
test_encode(new Utf8String("foo"),
|
||||||
|
{0x0c, 0x03, 0x66, 0x6f, 0x6f});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/utf8string/short/bar", () => {
|
||||||
|
test_encode(new Utf8String("bar"),
|
||||||
|
{0x0c, 0x03, 0x62, 0x61, 0x72});
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/utf8string/long/x300", () => {
|
||||||
|
var expected = new uint8[304];
|
||||||
|
expected[0] = 0x0c;
|
||||||
|
expected[1] = 0x82;
|
||||||
|
expected[2] = 0x01;
|
||||||
|
expected[3] = 0x2c;
|
||||||
|
Memory.set(expected[4:], 0x78, 300);
|
||||||
|
test_encode(new Utf8String(string.nfill(300, 'x')), expected);
|
||||||
|
});
|
||||||
|
Test.add_func("/encode/utf8string/long/x128", () => {
|
||||||
|
var expected = new uint8[131];
|
||||||
|
expected[0] = 0x0c;
|
||||||
|
expected[1] = 0x81;
|
||||||
|
expected[2] = 0x80;
|
||||||
|
Memory.set(expected[3:], 0x78, 128);
|
||||||
|
test_encode(new Utf8String(string.nfill(128, 'x')), expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
Test.run();
|
||||||
|
}
|
8
client/tests/meson.build
Normal file
8
client/tests/meson.build
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
test(
|
||||||
|
'DER tests',
|
||||||
|
executable(
|
||||||
|
'der_tests',
|
||||||
|
'der_tests.vala',
|
||||||
|
dependencies: [lib_dep, gtk_dep]
|
||||||
|
)
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user