ECB/CBC Detection Oracle
This commit is contained in:
parent
3e03aedb9e
commit
f9f27c772e
2 changed files with 92 additions and 0 deletions
|
@ -38,6 +38,23 @@ pub fn detect(allocator: std.mem.Allocator, input: []const u8) !bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
pub const Mode = enum { ECB, CBC };
|
||||
|
||||
/// Detect whether an oracle encrypts with ECB or CBC
|
||||
pub fn detect_ecb_cbc(allocator: std.mem.Allocator, oracle: anytype) !Mode {
|
||||
// all we need to do is creating long enough input to guarantee 2
|
||||
// equal input blocks which will encrypt to 2 equal cipher blocks
|
||||
// in ECB but not in CBC. A length of 3x the block size should be
|
||||
// sufficient in face of any padding.
|
||||
const probe = [_]u8{0x00} ** (16 * 3);
|
||||
const result = try oracle.oracleFn(allocator, &probe);
|
||||
defer allocator.free(result);
|
||||
|
||||
const ecb = try detect(allocator, result);
|
||||
|
||||
return if (ecb) Mode.ECB else Mode.CBC;
|
||||
}
|
||||
|
||||
test "detect_no" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
|
|
75
src/main.zig
75
src/main.zig
|
@ -55,6 +55,9 @@ pub fn main() !void {
|
|||
if (std.mem.eql(u8, args[1], "s2c10")) {
|
||||
try s2c10(allocator, stdout);
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, args[1], "s2c11")) {
|
||||
try s2c11(allocator, stdout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,3 +307,75 @@ fn s2c10(allocator: std.mem.Allocator, stdout: anytype) !void {
|
|||
|
||||
try stdout.print("{s}", .{result});
|
||||
}
|
||||
|
||||
fn s2c11(allocator: std.mem.Allocator, stdout: anytype) !void {
|
||||
const Oracle = struct {
|
||||
mode: aes.Mode,
|
||||
alloc: std.mem.Allocator,
|
||||
padded_input: []u8 = undefined,
|
||||
|
||||
fn init(alloc: std.mem.Allocator, mode: aes.Mode) @This() {
|
||||
return @This(){ .alloc = alloc, .mode = mode };
|
||||
}
|
||||
|
||||
pub fn oracleFn(self: *@This(), alloc: std.mem.Allocator, input: []const u8) ![]u8 {
|
||||
var rng = std.crypto.random;
|
||||
const padding_start = rng.intRangeAtMost(u32, 0, 16);
|
||||
const padding_end = 16 - padding_start;
|
||||
|
||||
self.padded_input = try self.alloc.alloc(u8, (padding_start + input.len + padding_end));
|
||||
std.crypto.random.bytes(self.padded_input[0..padding_start]);
|
||||
std.crypto.random.bytes(self.padded_input[(padding_start + input.len)..]);
|
||||
|
||||
const key = try self.alloc.alloc(u8, 16);
|
||||
defer self.alloc.free(key);
|
||||
std.crypto.random.bytes(key);
|
||||
|
||||
const iv = try self.alloc.alloc(u8, 16);
|
||||
defer self.alloc.free(iv);
|
||||
std.crypto.random.bytes(iv);
|
||||
|
||||
var cipher = switch (self.mode) {
|
||||
.ECB => aes.AES.init_ecb(self.alloc, key),
|
||||
.CBC => aes.AES.init_cbc(self.alloc, key, iv),
|
||||
};
|
||||
defer cipher.deinit();
|
||||
|
||||
return try cipher.encrypt(alloc, input);
|
||||
}
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
self.alloc.free(self.padded_input);
|
||||
}
|
||||
};
|
||||
|
||||
for (0..1000) |_| {
|
||||
const mode_rnd = std.crypto.random.intRangeAtMost(u1, 0, 1);
|
||||
const mode = switch (mode_rnd) {
|
||||
0 => aes.Mode.ECB,
|
||||
1 => aes.Mode.CBC,
|
||||
};
|
||||
|
||||
var oracle = Oracle.init(allocator, mode);
|
||||
defer oracle.deinit();
|
||||
|
||||
const result = try aes_crack.detect_ecb_cbc(allocator, &oracle);
|
||||
|
||||
if (mode == aes.Mode.CBC and result == aes_crack.Mode.CBC) {
|
||||
try stdout.print("SUCCESS: ", .{});
|
||||
}
|
||||
if (mode == aes.Mode.ECB and result == aes_crack.Mode.ECB) {
|
||||
try stdout.print("SUCCESS: ", .{});
|
||||
}
|
||||
if (mode == aes.Mode.CBC and result == aes_crack.Mode.ECB) {
|
||||
try stdout.print("FAIL: ", .{});
|
||||
@panic("Detection failed");
|
||||
}
|
||||
if (mode == aes.Mode.ECB and result == aes_crack.Mode.CBC) {
|
||||
try stdout.print("FAIL: ", .{});
|
||||
@panic("Detection failed");
|
||||
}
|
||||
|
||||
try stdout.print("Mode: {}, Detected: {}\n", .{ mode, result });
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue