CTR stream cipher mode

This commit is contained in:
Armin Friedl 2025-03-08 22:01:08 +01:00
parent c3b3b729ea
commit 733d2d8ef0
2 changed files with 76 additions and 1 deletions

View file

@ -6,12 +6,13 @@ const xor = @import("xor.zig");
const base64 = @import("base64.zig");
const padding = @import("padding.zig");
pub const Mode = enum { ECB, CBC };
pub const Mode = enum { ECB, CBC, CTR };
pub const AES = struct {
allocator: std.mem.Allocator,
key: []const u8,
iv: ?[]const u8 = null,
nonce: ?u64 = null,
nettle_ctx: nettle.struct_aes_ctx,
mode: Mode,
@ -59,6 +60,23 @@ pub const AES = struct {
return aes;
}
pub fn init_ctr(allocator: std.mem.Allocator, key: []const u8, nonce: u64) AES {
const owned_key = allocator.dupe(u8, key) catch {
@panic("Could not allocate key memory");
};
errdefer allocator.free(owned_key);
const aes = AES{
.allocator = allocator,
.key = owned_key,
.nettle_ctx = nettle.struct_aes_ctx{},
.mode = Mode.CTR,
.nonce = nonce,
};
return aes;
}
/// Encrypt buffer `buf`. Must be integer multiple of keysize.
/// Caller must free result.
pub fn encrypt(self: *@This(), allocator: std.mem.Allocator, buf: []const u8) ![]u8 {
@ -67,6 +85,7 @@ pub const AES = struct {
switch (self.mode) {
.ECB => return self.encrypt_ecb(allocator, buf),
.CBC => return self.encrypt_cbc(allocator, buf),
.CTR => return self.encrypt_ctr(allocator, buf),
}
}
@ -104,6 +123,41 @@ pub const AES = struct {
return enc;
}
fn encrypt_ctr(self: @This(), allocator: std.mem.Allocator, buf: []const u8) ![]u8 {
const enc = try allocator.alloc(u8, buf.len);
errdefer allocator.free(enc);
const nonce = self.nonce.?;
var counter: u64 = 0;
const block_size = self.key.len;
var block_idx: usize = 0;
const ctr_block = try allocator.alloc(u8, 16);
defer allocator.free(ctr_block);
const enc_buf = try allocator.alloc(u8, 16);
defer allocator.free(enc_buf);
var it = std.mem.window(u8, buf, block_size, block_size);
while (it.next()) |block| {
@memcpy(ctr_block[0..8], &@as([8]u8, @bitCast(nonce)));
@memcpy(ctr_block[8..], &@as([8]u8, @bitCast(counter)));
nettle.aes_encrypt(&self.nettle_ctx, block_size, enc_buf.ptr, ctr_block.ptr);
for (0..block.len) |i| {
enc[block_idx + i] = block[i] ^ enc_buf[i];
}
block_idx += block.len;
counter += 1;
}
return enc;
}
/// Decrypt buffer `buf`. Must be integer multiple of keysize.
/// Caller must free result.
pub fn decrypt(self: *@This(), allocator: std.mem.Allocator, buf: []const u8) ![]u8 {
@ -112,6 +166,7 @@ pub const AES = struct {
switch (self.mode) {
.ECB => return self.decrypt_ecb(allocator, buf),
.CBC => return self.decrypt_cbc(allocator, buf),
.CTR => return self.encrypt(allocator, buf),
}
}
@ -186,6 +241,7 @@ pub const ECB_CBC_Oracle = struct {
var cipher = switch (self.mode) {
.ECB => AES.init_ecb(self.alloc, key),
.CBC => AES.init_cbc(self.alloc, key, iv),
.CTR => @panic("No CTR Oracle available"),
};
defer cipher.deinit();

View file

@ -91,6 +91,10 @@ pub fn main() !void {
if (std.mem.eql(u8, args[1], "s3c17")) {
try s3c17(allocator, stdout);
}
if (std.mem.eql(u8, args[1], "s3c18")) {
try s3c18(allocator, stdout);
}
}
fn s1c1(allocator: std.mem.Allocator, stdout: anytype) !void {
@ -471,3 +475,18 @@ fn s3c17(allocator: std.mem.Allocator, stdout: anytype) !void {
try stdout.print("Cracked secret: \n\t{s}\nClear text: \n\t{s}\n", .{ secret_encoded, result });
}
fn s3c18(allocator: std.mem.Allocator, stdout: anytype) !void {
const secret = "L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ==";
const secret_decoded = try b64.decode(allocator, secret);
defer allocator.free(secret_decoded);
var cipher = aes.AES.init_ctr(allocator, "YELLOW SUBMARINE", 0);
defer cipher.deinit();
const clear_text = try cipher.decrypt(allocator, secret_decoded);
defer allocator.free(clear_text);
try stdout.print("Clear text:\n{s}\n", .{clear_text});
}