diff --git a/decoder/build.zig b/decoder/build.zig index 55c149c..98462d1 100644 --- a/decoder/build.zig +++ b/decoder/build.zig @@ -177,7 +177,7 @@ const Secrets = struct { subscription_key: [32]u8, public_key: [32]u8, channel_ids: [8]u16, - num_channel_ids: u3, + num_channel_ids: u4, flash_at_rest_key: [32]u8, metadata_key: [32]u8, }; @@ -204,7 +204,7 @@ fn getSecrets(allocator: std.mem.Allocator) !Secrets { defer secrets_unstructured.deinit(); const seeds = secrets_unstructured.value.object.get("seeds").?; var channel_ids: [8]u16 = undefined; - var num_channel_ids: u3 = 0; + var num_channel_ids: u4 = 0; for (seeds.object.keys(), 0..) |key, i| { const channel_id = try std.fmt.parseInt(u16, key, 0); channel_ids[i] = channel_id; diff --git a/decoder/subscribe.zig b/decoder/subscribe.zig index acf701b..3b986c3 100644 --- a/decoder/subscribe.zig +++ b/decoder/subscribe.zig @@ -2,11 +2,16 @@ const std = @import("std"); const root = @import("root"); const lib = @import("lib"); const secrets = @import("secrets"); +const ed25519 = @import("ed25519"); const flash = @import("flash.zig"); const messaging = @import("messaging.zig"); -pub const max_message_size = @sizeOf(SubscribeHeader) + @sizeOf(lib.Subscription.Bytes); +pub const max_message_size = @sizeOf(SignatureHeader) + @sizeOf(SubscribeHeader) + @sizeOf(lib.Subscription.Bytes); + +const SignatureHeader = extern struct { + signature: [64]u8 align(1), +}; const SubscribeHeader = extern struct { start: u64 align(1), @@ -15,17 +20,24 @@ const SubscribeHeader = extern struct { }; pub fn execute(body: []u8) !void { - std.crypto.stream.salsa.Salsa20.xor(body, body, 0, secrets.subscription_key, std.mem.zeroes([8]u8)); + const signature_header: *const SignatureHeader = @ptrCast(body.ptr); + const message_body = body[@sizeOf(SignatureHeader)..]; + const valid = ed25519.ed25519_verify(&signature_header.signature, message_body.ptr, message_body.len, &secrets.public_key); + if (valid == 0) { + messaging.sendDebug("Invalid signature", .{}); + return error.InvalidSignature; + } - const header: *const SubscribeHeader = @ptrCast(body.ptr); - const channel_index = try root.getChannelIndex(header.channel_id); + std.crypto.stream.salsa.Salsa20.xor(message_body, message_body, 0, secrets.subscription_key, std.mem.zeroes([8]u8)); + const header: *const SubscribeHeader = @ptrCast(message_body.ptr); + const channel_index = try root.getChannelIndex(header.channel_id); if (root.subscriptions[channel_index]) |*subscription| subscription.deinit(); root.subscriptions[channel_index] = lib.Subscription.init( header.channel_id, header.start, header.end, - body[@sizeOf(SubscribeHeader)..], + message_body[@sizeOf(SubscribeHeader)..], ); try flash.saveSubscriptions(@truncate(channel_index)); diff --git a/design/ectf25_design/gen_secrets.py b/design/ectf25_design/gen_secrets.py index 38ff01c..28fff1b 100644 --- a/design/ectf25_design/gen_secrets.py +++ b/design/ectf25_design/gen_secrets.py @@ -33,7 +33,6 @@ def gen_secrets(channels: list[int]) -> bytes: """ keypair = eddsa.import_private_key(os.urandom(32)) - signer = eddsa.new(keypair, "rfc8032") secrets = { "seeds": {str(channel): os.urandom(24).hex() for channel in channels}, diff --git a/design/ectf25_design/gen_subscription.py b/design/ectf25_design/gen_subscription.py index 56e5284..76d855d 100644 --- a/design/ectf25_design/gen_subscription.py +++ b/design/ectf25_design/gen_subscription.py @@ -18,6 +18,8 @@ from loguru import logger from blake3 import blake3 from Crypto.Cipher import Salsa20 +from Crypto.Signature import eddsa +from Crypto.PublicKey import ECC HASH_TREE_HEIGHT = 64 LEFT_SALT = b"L" @@ -50,10 +52,12 @@ def gen_subscription( secrets = json.loads(secrets) seed = bytes.fromhex(secrets["seeds"][str(channel)]) + subscription_salt = bytes.fromhex(secrets["subscription_salt"]) + signer = eddsa.new(ECC.import_key(secrets["private_key"]), "rfc8032") + subscription_key = blake3(f"{subscription_salt.hex()}{device_id:08x}".encode()).digest() roots = get_roots(start, end) - hashes = [] for root in roots: curr = seed @@ -64,12 +68,11 @@ def gen_subscription( curr = hash(curr + RIGHT_SALT) hashes.append(curr) - subscription_salt = bytes.fromhex(secrets["subscription_salt"]) - key = blake3(f"{subscription_salt.hex()}{device_id:08x}".encode()).digest() - - # Pack the subscription. This will be sent to the decoder with ectf25.tv.subscribe - return Salsa20.new(key=key, nonce=bytes(0 for i in range(8))).encrypt(struct.pack(" int: if x == 0: