Created
August 11, 2018 13:49
-
-
Save shawnl/8ebb7bab14658cfa4b38b203b9ec618e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on public domain Supercop by Daniel J. Bernstein | |
const mem = @import("../mem.zig"); | |
const endian = @import("../endian.zig"); | |
const builtin = @import("builtin"); | |
const QuarterRound = struct { | |
a: usize, | |
b: usize, | |
c: usize, | |
d: usize, | |
}; | |
fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound { | |
return QuarterRound{ | |
.a = a, | |
.b = b, | |
.c = c, | |
.d = d, | |
}; | |
} | |
fn rotate(a: u32, b: u5) u32 { | |
return ((a << b) | | |
(a >> @intCast(u5, (32 - @intCast(u6, b)))) | |
); | |
} | |
fn salsa20_wordtobyte(input: [16]u32) [64]u8 { | |
var x: [16]u32 = undefined; | |
var out: [64]u8 = undefined; | |
for (x) |_, i| | |
x[i] = input[i]; | |
const rounds = comptime []QuarterRound{ | |
Rp( 0, 4, 8,12), | |
Rp( 1, 5, 9,13), | |
Rp( 2, 6,10,14), | |
Rp( 3, 7,11,15), | |
Rp( 0, 5,10,15), | |
Rp( 1, 6,11,12), | |
Rp( 2, 7, 8,13), | |
Rp( 3, 4, 9,14), | |
}; | |
comptime var j: usize = 20; | |
inline while (j > 0) : (j -=2) { | |
for (rounds) |r| { | |
x[r.a] +%= x[r.b]; x[r.d] = rotate(x[r.d] ^ x[r.a], 16); | |
x[r.c] +%= x[r.d]; x[r.b] = rotate(x[r.b] ^ x[r.c], 12); | |
x[r.a] +%= x[r.b]; x[r.d] = rotate(x[r.d] ^ x[r.a], 8); | |
x[r.c] +%= x[r.d]; x[r.b] = rotate(x[r.b] ^ x[r.c], 7); | |
} | |
} | |
for (x) |_, i| | |
x[i] +%= input[i]; | |
for (x) |_, i| | |
mem.writeInt(out[4 * i .. 4 * i + 4], x[i], builtin.Endian.Little); | |
return out; | |
} | |
pub fn chaCha20(in: []const u8, key: [32]u8, nonce: [8]u8, out: []u8) void { | |
var ctx: [16]u32 = undefined; | |
var remaining: usize = undefined; | |
var cursor: usize = 0; | |
if (in.len > out.*.len) { | |
remaining = out.len; | |
} else | |
remaining = in.len; | |
comptime const c = "expand 32-byte k"; | |
comptime const constant_le = []u32{ | |
comptime mem.readIntLE(u32, c[0..4]), | |
comptime mem.readIntLE(u32, c[4..8]), | |
comptime mem.readIntLE(u32, c[8..12]), | |
comptime mem.readIntLE(u32, c[12..16]), | |
}; | |
for (constant_le) |_, i| | |
ctx[i] = constant_le[i]; | |
ctx[4] = mem.readIntLE(u32, key[0..4]); | |
ctx[5] = mem.readIntLE(u32, key[4..8]); | |
ctx[6] = mem.readIntLE(u32, key[8..12]); | |
ctx[7] = mem.readIntLE(u32, key[12..16]); | |
ctx[8] = mem.readIntLE(u32, key[16..20]); | |
ctx[9] = mem.readIntLE(u32, key[20..24]); | |
ctx[10] = mem.readIntLE(u32, key[24..28]); | |
ctx[11] = mem.readIntLE(u32, key[28..32]); | |
ctx[12] = 0; | |
ctx[13] = 0; | |
ctx[14] = mem.readIntLE(u32, nonce[0..4]); | |
ctx[15] = mem.readIntLE(u32, nonce[4..8]); | |
while (true) { | |
var buf = salsa20_wordtobyte(ctx); | |
var count: u64 = undefined; | |
if (remaining < 64) { | |
var i: usize = 0; | |
while (i < remaining) : (i += 1) | |
out.*[cursor + i] = in[cursor + i] ^ buf[i]; | |
return; | |
} | |
comptime var i: usize = 0; | |
inline while (i < 64) : (i += 1) | |
out.*[cursor + i] = in[cursor + i] ^ buf[i]; | |
cursor += 64; | |
remaining -= 64; | |
count = @intCast(u64, ctx[12]) | @intCast(u64, ctx[13]) << 32; | |
count += 1; | |
ctx[12] = @intCast(u32, count & @maxValue(u32)); | |
ctx[13] = @intCast(u32, count >> 32); | |
} | |
} | |
// https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 | |
test "crypto.chacha20 test vector 1" { | |
const assert = @import("std").debug.assert; | |
const expected_result = []u8{ | |
0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, | |
0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, | |
0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, | |
0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, | |
0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, | |
0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, | |
0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, | |
0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, | |
}; | |
const input = []u8{ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
}; | |
var result: [64]u8 = undefined; | |
const key = []u8{ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
}; | |
const nonce = []u8{0, 0, 0, 0, 0, 0, 0, 0}; | |
chaCha20(input[0..], key, nonce, result[0..]); | |
assert(mem.compare(u8, expected_result, result) == mem.Compare.Equal); | |
} | |
test "crypto.chacha20 test vector 2" { | |
const assert = @import("std").debug.assert; | |
const expected_result = []u8{ | |
0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96, | |
0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96, | |
0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60, | |
0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41, | |
0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2, | |
0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c, | |
0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, | |
0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, | |
}; | |
const input = []u8{ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
}; | |
var result: [64]u8 = undefined; | |
const key = []u8{ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 1, | |
}; | |
const nonce = []u8{0, 0, 0, 0, 0, 0, 0, 0}; | |
chaCha20(input[0..], key, nonce, result[0..]); | |
assert(mem.compare(u8, expected_result, result) == mem.Compare.Equal); | |
} | |
test "crypto.chacha20 test vector 3" { | |
const assert = @import("std").debug.assert; | |
const expected_result = []u8{ | |
0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5, | |
0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a, | |
0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13, | |
0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31, | |
0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45, | |
0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b, | |
0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, | |
0x44, 0x5f, 0x41, 0xe3, | |
}; | |
const input = []u8{ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
}; | |
var result: [60]u8 = undefined; | |
const key = []u8{ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
}; | |
const nonce = []u8{0, 0, 0, 0, 0, 0, 0, 1}; | |
chaCha20(input[0..], key, nonce, result[0..]); | |
assert(mem.compare(u8, expected_result, result) == mem.Compare.Equal); | |
} | |
test "crypto.chacha20 test vector 4" { | |
const assert = @import("std").debug.assert; | |
const expected_result = []u8{ | |
0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb, | |
0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80, | |
0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac, | |
0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32, | |
0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c, | |
0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54, | |
0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, | |
0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, | |
}; | |
const input = []u8{ | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
}; | |
var result: [64]u8 = undefined; | |
const key = []u8{ | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, | |
}; | |
const nonce = []u8{1, 0, 0, 0, 0, 0, 0, 0}; | |
chaCha20(input[0..], key, nonce, result[0..]); | |
assert(mem.compare(u8, expected_result, result) == mem.Compare.Equal); | |
} | |
test "crypto.chacha20 test vector 5" { | |
const assert = @import("std").debug.assert; | |
const expected_result = []u8{ | |
0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69, | |
0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75, | |
0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93, | |
0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1, | |
0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41, | |
0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69, | |
0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1, | |
0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a, | |
0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94, | |
0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66, | |
0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58, | |
0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd, | |
0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56, | |
0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e, | |
0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e, | |
0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7, | |
0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15, | |
0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3, | |
0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a, | |
0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25, | |
0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5, | |
0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69, | |
0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4, | |
0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7, | |
0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79, | |
0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a, | |
0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a, | |
0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2, | |
0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a, | |
0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09, | |
0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, | |
0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, | |
}; | |
const input = []u8{ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
}; | |
var result: [256]u8 = undefined; | |
const key = []u8{ | |
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | |
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | |
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, | |
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, | |
}; | |
const nonce = []u8{ | |
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | |
}; | |
chaCha20(input[0..], key, nonce, result[0..]); | |
assert(mem.compare(u8, expected_result, result) == mem.Compare.Equal); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment