diff --git a/build.zig b/build.zig index cac537c..3576b66 100644 --- a/build.zig +++ b/build.zig @@ -49,6 +49,7 @@ pub fn build(b: *std.Build) void { ctx.addTestModule(root_module); ctx.addTestFile("src/packet.zig"); + ctx.addTestFile("src/identity.zig"); } diff --git a/src/crypto.zig b/src/crypto.zig index 5027da2..679c46d 100644 --- a/src/crypto.zig +++ b/src/crypto.zig @@ -54,7 +54,6 @@ pub fn resolveEngine(comptime provider: PartialEngine) Engine { .sha512 = provider.sha512 orelse SHA512.DefaultImplementation, .x25519 = provider.x25519 orelse X25519.DefaultImplementation, }; - } diff --git a/src/crypto/aes.zig b/src/crypto/aes.zig index 853f39f..df886b2 100644 --- a/src/crypto/aes.zig +++ b/src/crypto/aes.zig @@ -10,6 +10,10 @@ pub const CBC256_Implementation = struct { // TODO pub const CBC265_DefaultImplementation = struct { + + // Zig stdlib seems to provide no support for AES CBC265 + // It may need some custom implementation or another library + pub fn encrypt(key: *const [32]u8, iv: *const [16]u8, data: *const []u8) !void { _ = key; _ = iv; diff --git a/src/crypto/ed25519.zig b/src/crypto/ed25519.zig index 2da4bc1..e58c993 100644 --- a/src/crypto/ed25519.zig +++ b/src/crypto/ed25519.zig @@ -9,21 +9,34 @@ pub const Implementation = struct { }; // TODO -pub const defaultImplementation = struct { +pub fn defaultImplementation() type { - pub fn generateKeys(privkey_buffer: *[32]u8) !void { - _ = privkey_buffer; - } - - pub fn sign(key: *const [32]u8, data: *const []const u8, signature_out: *const [64]u8) !void { - _ = key; - _ = data; - _ = signature_out; - } + const Ed25519 = @import("std").crypto.sign.Ed25519; + _ = Ed25519; - pub fn verify(signature: *const [64]u8, data: *const []const u8) !void { - _ = signature; - _ = data; - } + // Again, some Ed25519 functions seem to rely on the io module that is not necessarily available on embedded systems + // It may need some custom implementation or other library + + return struct { + + pub fn generateKeys(pubkey_buffer: *[32]u8, privkey_buffer: *[32]u8) !void { + _ = pubkey_buffer; + _ = privkey_buffer; + return error.NotImplemented; + + } -}; + pub fn sign(key: *const [32]u8, data: *const []const u8, signature_out: *const [64]u8) !void { + _ = key; + _ = data; + _ = signature_out; + return error.NotImplemented; + } + + pub fn verify(signature: *const [64]u8, data: *const []const u8) !void { + _ = signature; + _ = data; + return error.NotImplemented; + } + }; +} diff --git a/src/crypto/hkdf.zig b/src/crypto/hkdf.zig index 5344700..f1e9458 100644 --- a/src/crypto/hkdf.zig +++ b/src/crypto/hkdf.zig @@ -4,4 +4,6 @@ pub const Implementation = struct { pub const defaultImplementation = struct { + const Hkdf = @import("std").crypto.kdf.hkdf.Hkdf; + }; diff --git a/src/crypto/hmac.zig b/src/crypto/hmac.zig index 5344700..cb096e8 100644 --- a/src/crypto/hmac.zig +++ b/src/crypto/hmac.zig @@ -4,4 +4,12 @@ pub const Implementation = struct { pub const defaultImplementation = struct { + const Hmac = @import("std").crypto.auth.hmac.Hmac; + + pub fn create(out: []u8, msg: []const u8, key: []const u8) !void { + _ = out; + _ = msg; + _ = key; + return error.NotImplemented; + } }; diff --git a/src/crypto/sha256.zig b/src/crypto/sha256.zig index 5344700..70478e0 100644 --- a/src/crypto/sha256.zig +++ b/src/crypto/sha256.zig @@ -1,7 +1,18 @@ pub const Implementation = struct { + hash: *const fn (buffer: []const u8, out: []u8) void, }; pub const defaultImplementation = struct { + const Sha256 = @import("std").crypto.hash.sha2.Sha256; + + const HASH_LEN = 32; // In bytes + + pub fn hash(buffer: []const u8, out: []u8) !void { + if (out.len < HASH_LEN) { + return error.BufferTooShort; + } + Sha256.hash(buffer, out, .{}); + } }; diff --git a/src/crypto/sha512.zig b/src/crypto/sha512.zig index 5344700..a69a88d 100644 --- a/src/crypto/sha512.zig +++ b/src/crypto/sha512.zig @@ -1,7 +1,18 @@ pub const Implementation = struct { + hash: *const fn (buffer: []const u8, out: []u8) void, }; pub const defaultImplementation = struct { + const Sha512 = @import("std").crypto.sha2.Sha512; + + const HASH_LEN = 64; // In bytes + + pub fn hash(buffer: []const u8, out: []u8) !void { + if (out.len < HASH_LEN) { + return error.BufferTooShort; + } + Sha512.hash(buffer, out, .{}); + } }; diff --git a/src/crypto/x25519.zig b/src/crypto/x25519.zig index 5344700..f3f6607 100644 --- a/src/crypto/x25519.zig +++ b/src/crypto/x25519.zig @@ -3,5 +3,9 @@ pub const Implementation = struct { }; pub const defaultImplementation = struct { - + + const X25519 = @import("std").std.crypto.dh.X25519; + + // One problem, it uses the zig's io module, which is not supported yet as it depends on the target OS + // It may need a custom implementation or some other library }; diff --git a/src/destination.zig b/src/destination.zig index 77ea87a..e62749c 100644 --- a/src/destination.zig +++ b/src/destination.zig @@ -5,13 +5,14 @@ // pub const Type = enum(u2) { - single = 0x00, - group = 0x01, - plain = 0x02, - link = 0x03, + single = 0b0, + group = 0b01, + plain = 0b10, + link = 0b11, }; -pub const ProofStrategy = enum(u2) { +// From the reference implementation, seems weird +pub const ProofStrategy = enum(u8) { prove_none = 0x21, prove_app = 0x22, prove_all = 0x23, @@ -28,13 +29,31 @@ pub const Direction = enum(u2) { out = 0x12, }; -///////////////// Functions +///////////////// Enums // -//////// Helper functions -// +const Destination = struct { + + name: []const u8, + aspects: [][]const u8, + direction: Direction, + type: Type, -//////// Public functions + //////// Public functions + // + + // TODO + pub fn hash(self: *Destination, destination_buffer: [128]u8) !void { + _ = self; + _ = destination_buffer; + } + + // TODO + pub fn announce() !void {} +}; + + +///////////////// Helper functions // ///////////////// Tests diff --git a/src/identity.zig b/src/identity.zig new file mode 100644 index 0000000..dee9100 --- /dev/null +++ b/src/identity.zig @@ -0,0 +1,48 @@ +///////////////// Imports +// + +const CryptoEngine = @import("crypto.zig").Engine; + +///////////////// Constants +// + +///////////////// Enums +// + +///////////////// Structs +// + +// TODO +pub fn Identity(comptime crypto_engine: CryptoEngine) type { + _ = crypto_engine; + + return struct { + + //////// Fields + // + publicKey: []const u8, + privateKey: []const u8, + signature: []const u8, + hash: []const u8, + + //////// Functions + // + pub fn generate() !Identity { + return .{ + // .privateKey = crypto_engine.ed25519.generateKey(...), + // .signature = crypto_engine.x25519. + }; + } + }; +} + + +///////////////// Helper functions +// + +///////////////// Tests +// + +const std = @import("std"); + +const expect = std.testing.expect; diff --git a/src/packet.zig b/src/packet.zig index a38294e..7b465c4 100644 --- a/src/packet.zig +++ b/src/packet.zig @@ -1,13 +1,8 @@ -///////////////// Module +///////////////// Imports // -const Self = @This(); - -header: PacketHeader, -address1: [ADDRESS_SIZE]u8, -address2: [ADDRESS_SIZE]u8, -context: PacketContext, -data: []u8, +const CryptoEngine = @import("crypto.zig").Engine; +const Destination = @import("destination.zig"); ///////////////// Constants @@ -37,13 +32,6 @@ pub const PropagationType = enum(u1) { transport = 1 }; -pub const DestinationType = enum(u2) { - single = 0b0, - group = 0b01, - plain = 0b10, - link = 0b11, -}; - pub const PacketType = enum(u2) { data = 0b0, announce = 0b01, @@ -83,15 +71,89 @@ pub const PacketHeader = packed struct { header: HeaderType, context: ContextFlag, propagation: PropagationType, - destination: DestinationType, + destination: Destination.Type, packet: PacketType, hops: u8, }; -///////////////// Functions + +///////////////// Structs // -//////// Helper functions +const Packet = struct { + + const Self = @This(); + + ///////////////// Fields + // + + header: PacketHeader, + address1: [ADDRESS_SIZE]u8, + address2: [ADDRESS_SIZE]u8, + context: PacketContext, + data: []u8, + + + ///////////////// Public functions + // + + pub fn serialize(packet: *const Self, output_buffer: []u8) !usize { + const has_two_addresses = packet.header.header == HeaderType.type2; + + const target_size = getPacketSize(packet.data.len, has_two_addresses); + + if (output_buffer.len < target_size) { + return error.BufferTooShort; + } + + // Write buffer + var offset: usize = 0; + + @memcpy(output_buffer[offset .. offset + @sizeOf(PacketHeader)], asBytes(&packet.header)); + offset += @sizeOf(PacketHeader); + + @memcpy(output_buffer[offset .. offset + ADDRESS_SIZE], asBytes(&packet.address1)); + offset += ADDRESS_SIZE; + if (has_two_addresses) { + @memcpy(output_buffer[offset .. offset + ADDRESS_SIZE], asBytes(&packet.address2)); + offset += ADDRESS_SIZE; + } + + @memcpy(output_buffer[offset .. offset + @sizeOf(PacketContext)], asBytes(&packet.context)); + offset += @sizeOf(PacketContext); + + @memcpy(output_buffer[offset .. offset + packet.data.len], packet.data); + offset += packet.data.len; + + if (target_size != offset) { + return error.InternalError; + } + + return offset; + } + + // Reads the message buffer and builds the corresponding packet struct inside dest_packet + pub fn deserialize(packet: *Self, message_buffer: []u8) !void { + var offset: usize = 0; + + offset = try copyToPacket(&packet.header, message_buffer, offset, @sizeOf(PacketHeader)); + offset = try copyToPacket(&packet.address1, message_buffer, offset, ADDRESS_SIZE); + const has_two_addresses = packet.header.header == HeaderType.type2; + if (has_two_addresses) { + offset = try copyToPacket(&packet.address2, message_buffer, offset, ADDRESS_SIZE); + } + offset = try copyToPacket(&packet.context, message_buffer, offset, @sizeOf(PacketContext)); + offset = try copyToPacket(packet.data, message_buffer, offset, message_buffer.len - offset); + } + +}; + +// pub fn Packet(comptime crypto_engine: CryptoEngine) type { +// return struct {}; +// } + + +///////////////// Helper functions // // Copies serialized data (as a byte array) into any structure type @@ -116,58 +178,6 @@ fn getPacketSize(data_size: usize, has_two_addresses: bool) usize { return res; } -//////// Public functions -// - -pub fn serialize(packet: *const Self, output_buffer: []u8) !usize { - const has_two_addresses = packet.header.header == HeaderType.type2; - - const target_size = getPacketSize(packet.data.len, has_two_addresses); - - if (output_buffer.len < target_size) { - return error.BufferTooShort; - } - - // Write buffer - var offset: usize = 0; - - @memcpy(output_buffer[offset .. offset + @sizeOf(PacketHeader)], asBytes(&packet.header)); - offset += @sizeOf(PacketHeader); - - @memcpy(output_buffer[offset .. offset + ADDRESS_SIZE], asBytes(&packet.address1)); - offset += ADDRESS_SIZE; - if (has_two_addresses) { - @memcpy(output_buffer[offset .. offset + ADDRESS_SIZE], asBytes(&packet.address2)); - offset += ADDRESS_SIZE; - } - - @memcpy(output_buffer[offset .. offset + @sizeOf(PacketContext)], asBytes(&packet.context)); - offset += @sizeOf(PacketContext); - - @memcpy(output_buffer[offset .. offset + packet.data.len], packet.data); - offset += packet.data.len; - - if (target_size != offset) { - return error.InternalError; - } - - return offset; -} - -// Reads the message buffer and builds the corresponding packet struct inside dest_packet -pub fn deserialize(packet: *Self, message_buffer: []u8) !void { - var offset: usize = 0; - - offset = try copyToPacket(&packet.header, message_buffer, offset, @sizeOf(PacketHeader)); - offset = try copyToPacket(&packet.address1, message_buffer, offset, ADDRESS_SIZE); - const has_two_addresses = packet.header.header == HeaderType.type2; - if (has_two_addresses) { - offset = try copyToPacket(&packet.address2, message_buffer, offset, ADDRESS_SIZE); - } - offset = try copyToPacket(&packet.context, message_buffer, offset, @sizeOf(PacketContext)); - offset = try copyToPacket(packet.data, message_buffer, offset, message_buffer.len - offset); -} - ///////////////// Tests // @@ -181,8 +191,8 @@ const asBytes = std.mem.asBytes; // // Serializes the given packet and makes sure the output is correct -fn testPacketSerialization(packet: *const Self) !void { - const buf_size = comptime @sizeOf(Self) + MAX_DATA_SIZE; +fn testPacketSerialization(packet: *const Packet) !void { + const buf_size = comptime @sizeOf(Packet) + MAX_DATA_SIZE; var buf: [buf_size]u8 = undefined; const res: usize = try packet.serialize(&buf); const has_second_address = packet.header.header == HeaderType.type2; @@ -220,7 +230,7 @@ fn headersEql(h1: *const PacketHeader, h2: *const PacketHeader) bool { and h1.hops == h2.hops; } -fn packetsEql(p1: *const Self, p2: *const Self) bool { +fn packetsEql(p1: *const Packet, p2: *const Packet) bool { return headersEql(&p1.header, &p2.header) and memeql(u8, &p1.address1, &p2.address1) and memeql(u8, &p1.address2, &p2.address2) @@ -242,7 +252,7 @@ test "Basic serialization: header type 1, max data size" { .header = HeaderType.type1, .context = ContextFlag.set, .propagation = PropagationType.transport, - .destination = DestinationType.single, + .destination = Destination.Type.single, .packet = PacketType.announce, .hops = 0, }; @@ -250,7 +260,7 @@ test "Basic serialization: header type 1, max data size" { const data_size = MAX_DATA_SIZE; var data: [data_size]u8 = undefined; - const packet: Self = .{ + const packet: Packet = .{ .header = header, .address1 = undefined, .address2 = undefined, @@ -258,7 +268,7 @@ test "Basic serialization: header type 1, max data size" { .data = &data, }; - try packet.testPacketSerialization(); + try testPacketSerialization(&packet); } test "Basic serialization: header type 2, max data size" { @@ -267,7 +277,7 @@ test "Basic serialization: header type 2, max data size" { .header = HeaderType.type2, .context = ContextFlag.set, .propagation = PropagationType.transport, - .destination = DestinationType.single, + .destination = Destination.Type.single, .packet = PacketType.announce, .hops = 0, }; @@ -275,7 +285,7 @@ test "Basic serialization: header type 2, max data size" { const data_size = MAX_DATA_SIZE; var data: [data_size]u8 = undefined; - const packet: Self = .{ + const packet: Packet = .{ .header = header, .address1 = undefined, .address2 = undefined, @@ -283,7 +293,7 @@ test "Basic serialization: header type 2, max data size" { .data = &data, }; - try packet.testPacketSerialization(); + try testPacketSerialization(&packet); } test "Basic serialization: header type 1, medium data size" { @@ -292,7 +302,7 @@ test "Basic serialization: header type 1, medium data size" { .header = HeaderType.type1, .context = ContextFlag.set, .propagation = PropagationType.transport, - .destination = DestinationType.single, + .destination = Destination.Type.single, .packet = PacketType.announce, .hops = 0, }; @@ -300,7 +310,7 @@ test "Basic serialization: header type 1, medium data size" { const data_size = MAX_DATA_SIZE / 2 + 3; var data: [data_size]u8 = undefined; - const packet: Self = .{ + const packet: Packet = .{ .header = header, .address1 = undefined, .address2 = undefined, @@ -308,7 +318,7 @@ test "Basic serialization: header type 1, medium data size" { .data = &data, }; - try packet.testPacketSerialization(); + try testPacketSerialization(&packet); } test "Basic serialization: header type 2, medium data size" { @@ -317,7 +327,7 @@ test "Basic serialization: header type 2, medium data size" { .header = HeaderType.type2, .context = ContextFlag.set, .propagation = PropagationType.transport, - .destination = DestinationType.single, + .destination = Destination.Type.single, .packet = PacketType.announce, .hops = 0, }; @@ -325,7 +335,7 @@ test "Basic serialization: header type 2, medium data size" { const data_size = MAX_DATA_SIZE / 2 + 3; var data: [data_size]u8 = undefined; - const packet: Self = .{ + const packet: Packet = .{ .header = header, .address1 = undefined, .address2 = undefined, @@ -333,7 +343,7 @@ test "Basic serialization: header type 2, medium data size" { .data = &data, }; - try packet.testPacketSerialization(); + try testPacketSerialization(&packet); } test "Serialize / Deserialize Packet: Header type2, Medium data size" { @@ -342,7 +352,7 @@ test "Serialize / Deserialize Packet: Header type2, Medium data size" { .header = HeaderType.type2, .context = ContextFlag.set, .propagation = PropagationType.transport, - .destination = DestinationType.single, + .destination = Destination.Type.single, .packet = PacketType.announce, .hops = 0, }; @@ -350,7 +360,7 @@ test "Serialize / Deserialize Packet: Header type2, Medium data size" { const data_size = MAX_DATA_SIZE / 2 + 3; var data: [data_size]u8 = undefined; - const packet: Self = .{ + const packet: Packet = .{ .header = header, .address1 = undefined, .address2 = undefined, @@ -360,7 +370,7 @@ test "Serialize / Deserialize Packet: Header type2, Medium data size" { const buf_size = comptime @sizeOf(PacketHeader) + 2*ADDRESS_SIZE + @sizeOf(PacketContext) + data_size; var buf: [buf_size]u8 = undefined; - var res_packet: Self = undefined; + var res_packet: Packet = undefined; var res_data: [data_size]u8 = undefined; res_packet.data = &res_data; _ = try packet.serialize(&buf); diff --git a/src/root.zig b/src/root.zig index 9ab6784..0dc2d60 100644 --- a/src/root.zig +++ b/src/root.zig @@ -4,10 +4,10 @@ const crypto = @import("crypto.zig"); // You can pass your own cryptographic functions to take advantage of your // platform or use the library's software implementations for compatibility // Use its return value to access the library modules -pub fn RnsZero(comptime cryptoProvider: crypto.PartialEngine) type { - const cryptoEngine = crypto.resolveEngine(cryptoProvider); - _ = cryptoEngine; +pub fn RnsZero(comptime crypto_provider: crypto.PartialEngine) type { + const crypto_engine = crypto.resolveEngine(crypto_provider); return struct { pub const packet = @import("packet.zig"); + pub const identity = @import("identity.zig").Identity(crypto_engine); }; }