| ... | @@ -14,297 +14,293 @@ const AuthenticationError = crypto.errors.AuthenticationError; | ... | @@ -14,297 +14,293 @@ const AuthenticationError = crypto.errors.AuthenticationError; |
| 14 | const IdentityElementError = crypto.errors.IdentityElementError; | 14 | const IdentityElementError = crypto.errors.IdentityElementError; |
| 15 | const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; | 15 | const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; |
| 16 | | 16 | |
| 17 | const Salsa20VecImpl = struct { | 17 | /// The Salsa cipher with 20 rounds. |
| 18 | const Lane = @Vector(4, u32); | 18 | pub const Salsa20 = Salsa(20); |
| 19 | const Half = @Vector(2, u32); | 19 | |
| 20 | const BlockVec = [4]Lane; | 20 | /// The XSalsa cipher with 20 rounds. |
| 21 | | 21 | pub const XSalsa20 = XSalsa(20); |
| 22 | fn initContext(key: [8]u32, d: [4]u32) BlockVec { | 22 | |
| 23 | const c = "expand 32-byte k"; | 23 | fn SalsaVecImpl(comptime rounds: comptime_int) type { |
| 24 | const constant_le = comptime [4]u32{ | 24 | return struct { |
| 25 | mem.readIntLittle(u32, c[0..4]), | 25 | const Lane = @Vector(4, u32); |
| 26 | mem.readIntLittle(u32, c[4..8]), | 26 | const Half = @Vector(2, u32); |
| 27 | mem.readIntLittle(u32, c[8..12]), | 27 | const BlockVec = [4]Lane; |
| 28 | mem.readIntLittle(u32, c[12..16]), | 28 | |
| 29 | }; | 29 | fn initContext(key: [8]u32, d: [4]u32) BlockVec { |
| 30 | return BlockVec{ | 30 | const c = "expand 32-byte k"; |
| 31 | Lane{ key[0], key[1], key[2], key[3] }, | 31 | const constant_le = comptime [4]u32{ |
| 32 | Lane{ key[4], key[5], key[6], key[7] }, | 32 | mem.readIntLittle(u32, c[0..4]), |
| 33 | Lane{ constant_le[0], constant_le[1], constant_le[2], constant_le[3] }, | 33 | mem.readIntLittle(u32, c[4..8]), |
| 34 | Lane{ d[0], d[1], d[2], d[3] }, | 34 | mem.readIntLittle(u32, c[8..12]), |
| 35 | }; | 35 | mem.readIntLittle(u32, c[12..16]), |
| 36 | } | 36 | }; |
| 37 | | 37 | return BlockVec{ |
| 38 | inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void { | 38 | Lane{ key[0], key[1], key[2], key[3] }, |
| 39 | const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] }; | 39 | Lane{ key[4], key[5], key[6], key[7] }, |
| 40 | const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] }; | 40 | Lane{ constant_le[0], constant_le[1], constant_le[2], constant_le[3] }, |
| 41 | const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] }; | 41 | Lane{ d[0], d[1], d[2], d[3] }, |
| 42 | const k0k1 = Half{ input[0][0], input[0][1] }; | 42 | }; |
| 43 | const k2k3 = Half{ input[0][2], input[0][3] }; | | |
| 44 | const k4k5 = Half{ input[1][0], input[1][1] }; | | |
| 45 | const k6k7 = Half{ input[1][2], input[1][3] }; | | |
| 46 | const n0k0 = Half{ n3n0[1], k0k1[0] }; | | |
| 47 | const k0n0 = Half{ n0k0[1], n0k0[0] }; | | |
| 48 | const k4k5k0n0 = Lane{ k4k5[0], k4k5[1], k0n0[0], k0n0[1] }; | | |
| 49 | const k1k6 = Half{ k0k1[1], k6k7[0] }; | | |
| 50 | const k6k1 = Half{ k1k6[1], k1k6[0] }; | | |
| 51 | const n1n2k6k1 = Lane{ n1n2[0], n1n2[1], k6k1[0], k6k1[1] }; | | |
| 52 | const k7n3 = Half{ k6k7[1], n3n0[0] }; | | |
| 53 | const n3k7 = Half{ k7n3[1], k7n3[0] }; | | |
| 54 | const k2k3n3k7 = Lane{ k2k3[0], k2k3[1], n3k7[0], n3k7[1] }; | | |
| 55 | | | |
| 56 | var diag0 = input[2]; | | |
| 57 | var diag1 = @shuffle(u32, k4k5k0n0, undefined, [_]i32{ 1, 2, 3, 0 }); | | |
| 58 | var diag2 = @shuffle(u32, n1n2k6k1, undefined, [_]i32{ 1, 2, 3, 0 }); | | |
| 59 | var diag3 = @shuffle(u32, k2k3n3k7, undefined, [_]i32{ 1, 2, 3, 0 }); | | |
| 60 | | | |
| 61 | const start0 = diag0; | | |
| 62 | const start1 = diag1; | | |
| 63 | const start2 = diag2; | | |
| 64 | const start3 = diag3; | | |
| 65 | | | |
| 66 | var i: usize = 0; | | |
| 67 | while (i < 20) : (i += 2) { | | |
| 68 | var a0 = diag1 +% diag0; | | |
| 69 | diag3 ^= math.rotl(Lane, a0, 7); | | |
| 70 | var a1 = diag0 +% diag3; | | |
| 71 | diag2 ^= math.rotl(Lane, a1, 9); | | |
| 72 | var a2 = diag3 +% diag2; | | |
| 73 | diag1 ^= math.rotl(Lane, a2, 13); | | |
| 74 | var a3 = diag2 +% diag1; | | |
| 75 | diag0 ^= math.rotl(Lane, a3, 18); | | |
| 76 | | | |
| 77 | var diag3_shift = @shuffle(u32, diag3, undefined, [_]i32{ 3, 0, 1, 2 }); | | |
| 78 | var diag2_shift = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 }); | | |
| 79 | var diag1_shift = @shuffle(u32, diag1, undefined, [_]i32{ 1, 2, 3, 0 }); | | |
| 80 | diag3 = diag3_shift; | | |
| 81 | diag2 = diag2_shift; | | |
| 82 | diag1 = diag1_shift; | | |
| 83 | | | |
| 84 | a0 = diag3 +% diag0; | | |
| 85 | diag1 ^= math.rotl(Lane, a0, 7); | | |
| 86 | a1 = diag0 +% diag1; | | |
| 87 | diag2 ^= math.rotl(Lane, a1, 9); | | |
| 88 | a2 = diag1 +% diag2; | | |
| 89 | diag3 ^= math.rotl(Lane, a2, 13); | | |
| 90 | a3 = diag2 +% diag3; | | |
| 91 | diag0 ^= math.rotl(Lane, a3, 18); | | |
| 92 | | | |
| 93 | diag1_shift = @shuffle(u32, diag1, undefined, [_]i32{ 3, 0, 1, 2 }); | | |
| 94 | diag2_shift = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 }); | | |
| 95 | diag3_shift = @shuffle(u32, diag3, undefined, [_]i32{ 1, 2, 3, 0 }); | | |
| 96 | diag1 = diag1_shift; | | |
| 97 | diag2 = diag2_shift; | | |
| 98 | diag3 = diag3_shift; | | |
| 99 | } | 43 | } |
| 100 | | 44 | |
| 101 | if (feedback) { | 45 | inline fn salsaCore(x: *BlockVec, input: BlockVec, comptime feedback: bool) void { |
| 102 | diag0 +%= start0; | 46 | const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] }; |
| 103 | diag1 +%= start1; | 47 | const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] }; |
| 104 | diag2 +%= start2; | 48 | const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] }; |
| 105 | diag3 +%= start3; | 49 | const k0k1 = Half{ input[0][0], input[0][1] }; |
| 106 | } | 50 | const k2k3 = Half{ input[0][2], input[0][3] }; |
| | 51 | const k4k5 = Half{ input[1][0], input[1][1] }; |
| | 52 | const k6k7 = Half{ input[1][2], input[1][3] }; |
| | 53 | const n0k0 = Half{ n3n0[1], k0k1[0] }; |
| | 54 | const k0n0 = Half{ n0k0[1], n0k0[0] }; |
| | 55 | const k4k5k0n0 = Lane{ k4k5[0], k4k5[1], k0n0[0], k0n0[1] }; |
| | 56 | const k1k6 = Half{ k0k1[1], k6k7[0] }; |
| | 57 | const k6k1 = Half{ k1k6[1], k1k6[0] }; |
| | 58 | const n1n2k6k1 = Lane{ n1n2[0], n1n2[1], k6k1[0], k6k1[1] }; |
| | 59 | const k7n3 = Half{ k6k7[1], n3n0[0] }; |
| | 60 | const n3k7 = Half{ k7n3[1], k7n3[0] }; |
| | 61 | const k2k3n3k7 = Lane{ k2k3[0], k2k3[1], n3k7[0], n3k7[1] }; |
| | 62 | |
| | 63 | var diag0 = input[2]; |
| | 64 | var diag1 = @shuffle(u32, k4k5k0n0, undefined, [_]i32{ 1, 2, 3, 0 }); |
| | 65 | var diag2 = @shuffle(u32, n1n2k6k1, undefined, [_]i32{ 1, 2, 3, 0 }); |
| | 66 | var diag3 = @shuffle(u32, k2k3n3k7, undefined, [_]i32{ 1, 2, 3, 0 }); |
| | 67 | |
| | 68 | const start0 = diag0; |
| | 69 | const start1 = diag1; |
| | 70 | const start2 = diag2; |
| | 71 | const start3 = diag3; |
| | 72 | |
| | 73 | var i: usize = 0; |
| | 74 | while (i < rounds) : (i += 2) { |
| | 75 | diag3 ^= math.rotl(Lane, diag1 +% diag0, 7); |
| | 76 | diag2 ^= math.rotl(Lane, diag0 +% diag3, 9); |
| | 77 | diag1 ^= math.rotl(Lane, diag3 +% diag2, 13); |
| | 78 | diag0 ^= math.rotl(Lane, diag2 +% diag1, 18); |
| | 79 | |
| | 80 | diag3 = @shuffle(u32, diag3, undefined, [_]i32{ 3, 0, 1, 2 }); |
| | 81 | diag2 = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 }); |
| | 82 | diag1 = @shuffle(u32, diag1, undefined, [_]i32{ 1, 2, 3, 0 }); |
| | 83 | |
| | 84 | diag1 ^= math.rotl(Lane, diag3 +% diag0, 7); |
| | 85 | diag2 ^= math.rotl(Lane, diag0 +% diag1, 9); |
| | 86 | diag3 ^= math.rotl(Lane, diag1 +% diag2, 13); |
| | 87 | diag0 ^= math.rotl(Lane, diag2 +% diag3, 18); |
| | 88 | |
| | 89 | diag1 = @shuffle(u32, diag1, undefined, [_]i32{ 3, 0, 1, 2 }); |
| | 90 | diag2 = @shuffle(u32, diag2, undefined, [_]i32{ 2, 3, 0, 1 }); |
| | 91 | diag3 = @shuffle(u32, diag3, undefined, [_]i32{ 1, 2, 3, 0 }); |
| | 92 | } |
| 107 | | 93 | |
| 108 | const x0x1x10x11 = Lane{ diag0[0], diag1[1], diag0[2], diag1[3] }; | 94 | if (feedback) { |
| 109 | const x12x13x6x7 = Lane{ diag1[0], diag2[1], diag1[2], diag2[3] }; | 95 | diag0 +%= start0; |
| 110 | const x8x9x2x3 = Lane{ diag2[0], diag3[1], diag2[2], diag3[3] }; | 96 | diag1 +%= start1; |
| 111 | const x4x5x14x15 = Lane{ diag3[0], diag0[1], diag3[2], diag0[3] }; | 97 | diag2 +%= start2; |
| | 98 | diag3 +%= start3; |
| | 99 | } |
| 112 | | 100 | |
| 113 | x[0] = Lane{ x0x1x10x11[0], x0x1x10x11[1], x8x9x2x3[2], x8x9x2x3[3] }; | 101 | const x0x1x10x11 = Lane{ diag0[0], diag1[1], diag0[2], diag1[3] }; |
| 114 | x[1] = Lane{ x4x5x14x15[0], x4x5x14x15[1], x12x13x6x7[2], x12x13x6x7[3] }; | 102 | const x12x13x6x7 = Lane{ diag1[0], diag2[1], diag1[2], diag2[3] }; |
| 115 | x[2] = Lane{ x8x9x2x3[0], x8x9x2x3[1], x0x1x10x11[2], x0x1x10x11[3] }; | 103 | const x8x9x2x3 = Lane{ diag2[0], diag3[1], diag2[2], diag3[3] }; |
| 116 | x[3] = Lane{ x12x13x6x7[0], x12x13x6x7[1], x4x5x14x15[2], x4x5x14x15[3] }; | 104 | const x4x5x14x15 = Lane{ diag3[0], diag0[1], diag3[2], diag0[3] }; |
| 117 | } | | |
| 118 | | 105 | |
| 119 | fn hashToBytes(out: *[64]u8, x: BlockVec) void { | 106 | x[0] = Lane{ x0x1x10x11[0], x0x1x10x11[1], x8x9x2x3[2], x8x9x2x3[3] }; |
| 120 | var i: usize = 0; | 107 | x[1] = Lane{ x4x5x14x15[0], x4x5x14x15[1], x12x13x6x7[2], x12x13x6x7[3] }; |
| 121 | while (i < 4) : (i += 1) { | 108 | x[2] = Lane{ x8x9x2x3[0], x8x9x2x3[1], x0x1x10x11[2], x0x1x10x11[3] }; |
| 122 | mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]); | 109 | x[3] = Lane{ x12x13x6x7[0], x12x13x6x7[1], x4x5x14x15[2], x4x5x14x15[3] }; |
| 123 | mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]); | | |
| 124 | mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]); | | |
| 125 | mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]); | | |
| 126 | } | 110 | } |
| 127 | } | | |
| 128 | | 111 | |
| 129 | fn salsa20Xor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void { | 112 | fn hashToBytes(out: *[64]u8, x: BlockVec) void { |
| 130 | var ctx = initContext(key, d); | 113 | var i: usize = 0; |
| 131 | var x: BlockVec = undefined; | 114 | while (i < 4) : (i += 1) { |
| 132 | var buf: [64]u8 = undefined; | 115 | mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]); |
| 133 | var i: usize = 0; | 116 | mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i][1]); |
| 134 | while (i + 64 <= in.len) : (i += 64) { | 117 | mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i][2]); |
| 135 | salsa20Core(x[0..], ctx, true); | 118 | mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i][3]); |
| 136 | hashToBytes(buf[0..], x); | | |
| 137 | var xout = out[i..]; | | |
| 138 | const xin = in[i..]; | | |
| 139 | var j: usize = 0; | | |
| 140 | while (j < 64) : (j += 1) { | | |
| 141 | xout[j] = xin[j]; | | |
| 142 | } | 119 | } |
| 143 | j = 0; | 120 | } |
| 144 | while (j < 64) : (j += 1) { | 121 | |
| 145 | xout[j] ^= buf[j]; | 122 | fn salsaXor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void { |
| | 123 | var ctx = initContext(key, d); |
| | 124 | var x: BlockVec = undefined; |
| | 125 | var buf: [64]u8 = undefined; |
| | 126 | var i: usize = 0; |
| | 127 | while (i + 64 <= in.len) : (i += 64) { |
| | 128 | salsaCore(x[0..], ctx, true); |
| | 129 | hashToBytes(buf[0..], x); |
| | 130 | var xout = out[i..]; |
| | 131 | const xin = in[i..]; |
| | 132 | var j: usize = 0; |
| | 133 | while (j < 64) : (j += 1) { |
| | 134 | xout[j] = xin[j]; |
| | 135 | } |
| | 136 | j = 0; |
| | 137 | while (j < 64) : (j += 1) { |
| | 138 | xout[j] ^= buf[j]; |
| | 139 | } |
| | 140 | ctx[3][2] +%= 1; |
| | 141 | if (ctx[3][2] == 0) { |
| | 142 | ctx[3][3] += 1; |
| | 143 | } |
| 146 | } | 144 | } |
| 147 | ctx[3][2] +%= 1; | 145 | if (i < in.len) { |
| 148 | if (ctx[3][2] == 0) { | 146 | salsaCore(x[0..], ctx, true); |
| 149 | ctx[3][3] += 1; | 147 | hashToBytes(buf[0..], x); |
| | 148 | |
| | 149 | var xout = out[i..]; |
| | 150 | const xin = in[i..]; |
| | 151 | var j: usize = 0; |
| | 152 | while (j < in.len % 64) : (j += 1) { |
| | 153 | xout[j] = xin[j] ^ buf[j]; |
| | 154 | } |
| 150 | } | 155 | } |
| 151 | } | 156 | } |
| 152 | if (i < in.len) { | | |
| 153 | salsa20Core(x[0..], ctx, true); | | |
| 154 | hashToBytes(buf[0..], x); | | |
| 155 | | 157 | |
| 156 | var xout = out[i..]; | 158 | fn hsalsa(input: [16]u8, key: [32]u8) [32]u8 { |
| 157 | const xin = in[i..]; | 159 | var c: [4]u32 = undefined; |
| 158 | var j: usize = 0; | 160 | for (c) |_, i| { |
| 159 | while (j < in.len % 64) : (j += 1) { | 161 | c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]); |
| 160 | xout[j] = xin[j] ^ buf[j]; | | |
| 161 | } | 162 | } |
| | 163 | const ctx = initContext(keyToWords(key), c); |
| | 164 | var x: BlockVec = undefined; |
| | 165 | salsaCore(x[0..], ctx, false); |
| | 166 | var out: [32]u8 = undefined; |
| | 167 | mem.writeIntLittle(u32, out[0..4], x[0][0]); |
| | 168 | mem.writeIntLittle(u32, out[4..8], x[1][1]); |
| | 169 | mem.writeIntLittle(u32, out[8..12], x[2][2]); |
| | 170 | mem.writeIntLittle(u32, out[12..16], x[3][3]); |
| | 171 | mem.writeIntLittle(u32, out[16..20], x[1][2]); |
| | 172 | mem.writeIntLittle(u32, out[20..24], x[1][3]); |
| | 173 | mem.writeIntLittle(u32, out[24..28], x[2][0]); |
| | 174 | mem.writeIntLittle(u32, out[28..32], x[2][1]); |
| | 175 | return out; |
| 162 | } | 176 | } |
| 163 | } | 177 | }; |
| | 178 | } |
| 164 | | 179 | |
| 165 | fn hsalsa20(input: [16]u8, key: [32]u8) [32]u8 { | 180 | fn SalsaNonVecImpl(comptime rounds: comptime_int) type { |
| 166 | var c: [4]u32 = undefined; | 181 | return struct { |
| 167 | for (c) |_, i| { | 182 | const BlockVec = [16]u32; |
| 168 | c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]); | 183 | |
| | 184 | fn initContext(key: [8]u32, d: [4]u32) BlockVec { |
| | 185 | const c = "expand 32-byte k"; |
| | 186 | const constant_le = comptime [4]u32{ |
| | 187 | mem.readIntLittle(u32, c[0..4]), |
| | 188 | mem.readIntLittle(u32, c[4..8]), |
| | 189 | mem.readIntLittle(u32, c[8..12]), |
| | 190 | mem.readIntLittle(u32, c[12..16]), |
| | 191 | }; |
| | 192 | return BlockVec{ |
| | 193 | constant_le[0], key[0], key[1], key[2], |
| | 194 | key[3], constant_le[1], d[0], d[1], |
| | 195 | d[2], d[3], constant_le[2], key[4], |
| | 196 | key[5], key[6], key[7], constant_le[3], |
| | 197 | }; |
| 169 | } | 198 | } |
| 170 | const ctx = initContext(keyToWords(key), c); | | |
| 171 | var x: BlockVec = undefined; | | |
| 172 | salsa20Core(x[0..], ctx, false); | | |
| 173 | var out: [32]u8 = undefined; | | |
| 174 | mem.writeIntLittle(u32, out[0..4], x[0][0]); | | |
| 175 | mem.writeIntLittle(u32, out[4..8], x[1][1]); | | |
| 176 | mem.writeIntLittle(u32, out[8..12], x[2][2]); | | |
| 177 | mem.writeIntLittle(u32, out[12..16], x[3][3]); | | |
| 178 | mem.writeIntLittle(u32, out[16..20], x[1][2]); | | |
| 179 | mem.writeIntLittle(u32, out[20..24], x[1][3]); | | |
| 180 | mem.writeIntLittle(u32, out[24..28], x[2][0]); | | |
| 181 | mem.writeIntLittle(u32, out[28..32], x[2][1]); | | |
| 182 | return out; | | |
| 183 | } | | |
| 184 | }; | | |
| 185 | | 199 | |
| 186 | const Salsa20NonVecImpl = struct { | 200 | const QuarterRound = struct { |
| 187 | const BlockVec = [16]u32; | 201 | a: usize, |
| 188 | | 202 | b: usize, |
| 189 | fn initContext(key: [8]u32, d: [4]u32) BlockVec { | 203 | c: usize, |
| 190 | const c = "expand 32-byte k"; | 204 | d: u6, |
| 191 | const constant_le = comptime [4]u32{ | | |
| 192 | mem.readIntLittle(u32, c[0..4]), | | |
| 193 | mem.readIntLittle(u32, c[4..8]), | | |
| 194 | mem.readIntLittle(u32, c[8..12]), | | |
| 195 | mem.readIntLittle(u32, c[12..16]), | | |
| 196 | }; | | |
| 197 | return BlockVec{ | | |
| 198 | constant_le[0], key[0], key[1], key[2], | | |
| 199 | key[3], constant_le[1], d[0], d[1], | | |
| 200 | d[2], d[3], constant_le[2], key[4], | | |
| 201 | key[5], key[6], key[7], constant_le[3], | | |
| 202 | }; | 205 | }; |
| 203 | } | | |
| 204 | | 206 | |
| 205 | const QuarterRound = struct { | 207 | inline fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound { |
| 206 | a: usize, | 208 | return QuarterRound{ |
| 207 | b: usize, | 209 | .a = a, |
| 208 | c: usize, | 210 | .b = b, |
| 209 | d: u6, | 211 | .c = c, |
| 210 | }; | 212 | .d = d, |
| 211 | | 213 | }; |
| 212 | inline fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound { | 214 | } |
| 213 | return QuarterRound{ | | |
| 214 | .a = a, | | |
| 215 | .b = b, | | |
| 216 | .c = c, | | |
| 217 | .d = d, | | |
| 218 | }; | | |
| 219 | } | | |
| 220 | | 215 | |
| 221 | inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void { | 216 | inline fn salsaCore(x: *BlockVec, input: BlockVec, comptime feedback: bool) void { |
| 222 | const arx_steps = comptime [_]QuarterRound{ | 217 | const arx_steps = comptime [_]QuarterRound{ |
| 223 | Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18), | 218 | Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18), |
| 224 | Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18), | 219 | Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18), |
| 225 | Rp(14, 10, 6, 7), Rp(2, 14, 10, 9), Rp(6, 2, 14, 13), Rp(10, 6, 2, 18), | 220 | Rp(14, 10, 6, 7), Rp(2, 14, 10, 9), Rp(6, 2, 14, 13), Rp(10, 6, 2, 18), |
| 226 | Rp(3, 15, 11, 7), Rp(7, 3, 15, 9), Rp(11, 7, 3, 13), Rp(15, 11, 7, 18), | 221 | Rp(3, 15, 11, 7), Rp(7, 3, 15, 9), Rp(11, 7, 3, 13), Rp(15, 11, 7, 18), |
| 227 | Rp(1, 0, 3, 7), Rp(2, 1, 0, 9), Rp(3, 2, 1, 13), Rp(0, 3, 2, 18), | 222 | Rp(1, 0, 3, 7), Rp(2, 1, 0, 9), Rp(3, 2, 1, 13), Rp(0, 3, 2, 18), |
| 228 | Rp(6, 5, 4, 7), Rp(7, 6, 5, 9), Rp(4, 7, 6, 13), Rp(5, 4, 7, 18), | 223 | Rp(6, 5, 4, 7), Rp(7, 6, 5, 9), Rp(4, 7, 6, 13), Rp(5, 4, 7, 18), |
| 229 | Rp(11, 10, 9, 7), Rp(8, 11, 10, 9), Rp(9, 8, 11, 13), Rp(10, 9, 8, 18), | 224 | Rp(11, 10, 9, 7), Rp(8, 11, 10, 9), Rp(9, 8, 11, 13), Rp(10, 9, 8, 18), |
| 230 | Rp(12, 15, 14, 7), Rp(13, 12, 15, 9), Rp(14, 13, 12, 13), Rp(15, 14, 13, 18), | 225 | Rp(12, 15, 14, 7), Rp(13, 12, 15, 9), Rp(14, 13, 12, 13), Rp(15, 14, 13, 18), |
| 231 | }; | 226 | }; |
| 232 | x.* = input; | 227 | x.* = input; |
| 233 | var j: usize = 0; | 228 | var j: usize = 0; |
| 234 | while (j < 20) : (j += 2) { | 229 | while (j < rounds) : (j += 2) { |
| 235 | inline for (arx_steps) |r| { | 230 | inline for (arx_steps) |r| { |
| 236 | x[r.a] ^= math.rotl(u32, x[r.b] +% x[r.c], r.d); | 231 | x[r.a] ^= math.rotl(u32, x[r.b] +% x[r.c], r.d); |
| | 232 | } |
| 237 | } | 233 | } |
| 238 | } | 234 | if (feedback) { |
| 239 | if (feedback) { | 235 | j = 0; |
| 240 | j = 0; | 236 | while (j < 16) : (j += 1) { |
| 241 | while (j < 16) : (j += 1) { | 237 | x[j] +%= input[j]; |
| 242 | x[j] +%= input[j]; | 238 | } |
| 243 | } | 239 | } |
| 244 | } | 240 | } |
| 245 | } | | |
| 246 | | 241 | |
| 247 | fn hashToBytes(out: *[64]u8, x: BlockVec) void { | 242 | fn hashToBytes(out: *[64]u8, x: BlockVec) void { |
| 248 | for (x) |w, i| { | 243 | for (x) |w, i| { |
| 249 | mem.writeIntLittle(u32, out[i * 4 ..][0..4], w); | 244 | mem.writeIntLittle(u32, out[i * 4 ..][0..4], w); |
| | 245 | } |
| 250 | } | 246 | } |
| 251 | } | | |
| 252 | | 247 | |
| 253 | fn salsa20Xor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void { | 248 | fn salsaXor(out: []u8, in: []const u8, key: [8]u32, d: [4]u32) void { |
| 254 | var ctx = initContext(key, d); | 249 | var ctx = initContext(key, d); |
| 255 | var x: BlockVec = undefined; | 250 | var x: BlockVec = undefined; |
| 256 | var buf: [64]u8 = undefined; | 251 | var buf: [64]u8 = undefined; |
| 257 | var i: usize = 0; | 252 | var i: usize = 0; |
| 258 | while (i + 64 <= in.len) : (i += 64) { | 253 | while (i + 64 <= in.len) : (i += 64) { |
| 259 | salsa20Core(x[0..], ctx, true); | 254 | salsaCore(x[0..], ctx, true); |
| 260 | hashToBytes(buf[0..], x); | 255 | hashToBytes(buf[0..], x); |
| 261 | var xout = out[i..]; | 256 | var xout = out[i..]; |
| 262 | const xin = in[i..]; | 257 | const xin = in[i..]; |
| 263 | var j: usize = 0; | 258 | var j: usize = 0; |
| 264 | while (j < 64) : (j += 1) { | 259 | while (j < 64) : (j += 1) { |
| 265 | xout[j] = xin[j]; | 260 | xout[j] = xin[j]; |
| | 261 | } |
| | 262 | j = 0; |
| | 263 | while (j < 64) : (j += 1) { |
| | 264 | xout[j] ^= buf[j]; |
| | 265 | } |
| | 266 | ctx[9] += @boolToInt(@addWithOverflow(u32, ctx[8], 1, &ctx[8])); |
| 266 | } | 267 | } |
| 267 | j = 0; | 268 | if (i < in.len) { |
| 268 | while (j < 64) : (j += 1) { | 269 | salsaCore(x[0..], ctx, true); |
| 269 | xout[j] ^= buf[j]; | 270 | hashToBytes(buf[0..], x); |
| | 271 | |
| | 272 | var xout = out[i..]; |
| | 273 | const xin = in[i..]; |
| | 274 | var j: usize = 0; |
| | 275 | while (j < in.len % 64) : (j += 1) { |
| | 276 | xout[j] = xin[j] ^ buf[j]; |
| | 277 | } |
| 270 | } | 278 | } |
| 271 | ctx[9] += @boolToInt(@addWithOverflow(u32, ctx[8], 1, &ctx[8])); | | |
| 272 | } | 279 | } |
| 273 | if (i < in.len) { | | |
| 274 | salsa20Core(x[0..], ctx, true); | | |
| 275 | hashToBytes(buf[0..], x); | | |
| 276 | | 280 | |
| 277 | var xout = out[i..]; | 281 | fn hsalsa(input: [16]u8, key: [32]u8) [32]u8 { |
| 278 | const xin = in[i..]; | 282 | var c: [4]u32 = undefined; |
| 279 | var j: usize = 0; | 283 | for (c) |_, i| { |
| 280 | while (j < in.len % 64) : (j += 1) { | 284 | c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]); |
| 281 | xout[j] = xin[j] ^ buf[j]; | | |
| 282 | } | 285 | } |
| | 286 | const ctx = initContext(keyToWords(key), c); |
| | 287 | var x: BlockVec = undefined; |
| | 288 | salsaCore(x[0..], ctx, false); |
| | 289 | var out: [32]u8 = undefined; |
| | 290 | mem.writeIntLittle(u32, out[0..4], x[0]); |
| | 291 | mem.writeIntLittle(u32, out[4..8], x[5]); |
| | 292 | mem.writeIntLittle(u32, out[8..12], x[10]); |
| | 293 | mem.writeIntLittle(u32, out[12..16], x[15]); |
| | 294 | mem.writeIntLittle(u32, out[16..20], x[6]); |
| | 295 | mem.writeIntLittle(u32, out[20..24], x[7]); |
| | 296 | mem.writeIntLittle(u32, out[24..28], x[8]); |
| | 297 | mem.writeIntLittle(u32, out[28..32], x[9]); |
| | 298 | return out; |
| 283 | } | 299 | } |
| 284 | } | 300 | }; |
| 285 | | 301 | } |
| 286 | fn hsalsa20(input: [16]u8, key: [32]u8) [32]u8 { | | |
| 287 | var c: [4]u32 = undefined; | | |
| 288 | for (c) |_, i| { | | |
| 289 | c[i] = mem.readIntLittle(u32, input[4 * i ..][0..4]); | | |
| 290 | } | | |
| 291 | const ctx = initContext(keyToWords(key), c); | | |
| 292 | var x: BlockVec = undefined; | | |
| 293 | salsa20Core(x[0..], ctx, false); | | |
| 294 | var out: [32]u8 = undefined; | | |
| 295 | mem.writeIntLittle(u32, out[0..4], x[0]); | | |
| 296 | mem.writeIntLittle(u32, out[4..8], x[5]); | | |
| 297 | mem.writeIntLittle(u32, out[8..12], x[10]); | | |
| 298 | mem.writeIntLittle(u32, out[12..16], x[15]); | | |
| 299 | mem.writeIntLittle(u32, out[16..20], x[6]); | | |
| 300 | mem.writeIntLittle(u32, out[20..24], x[7]); | | |
| 301 | mem.writeIntLittle(u32, out[24..28], x[8]); | | |
| 302 | mem.writeIntLittle(u32, out[28..32], x[9]); | | |
| 303 | return out; | | |
| 304 | } | | |
| 305 | }; | | |
| 306 | | 302 | |
| 307 | const Salsa20Impl = if (builtin.cpu.arch == .x86_64) Salsa20VecImpl else Salsa20NonVecImpl; | 303 | const SalsaImpl = if (builtin.cpu.arch == .x86_64) SalsaVecImpl else SalsaNonVecImpl; |
| 308 | | 304 | |
| 309 | fn keyToWords(key: [32]u8) [8]u32 { | 305 | fn keyToWords(key: [32]u8) [8]u32 { |
| 310 | var k: [8]u32 = undefined; | 306 | var k: [8]u32 = undefined; |
| ... | @@ -315,52 +311,56 @@ fn keyToWords(key: [32]u8) [8]u32 { | ... | @@ -315,52 +311,56 @@ fn keyToWords(key: [32]u8) [8]u32 { |
| 315 | return k; | 311 | return k; |
| 316 | } | 312 | } |
| 317 | | 313 | |
| 318 | fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [8]u8 } { | 314 | fn extend(comptime rounds: comptime_int, key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [8]u8 } { |
| 319 | return .{ | 315 | return .{ |
| 320 | .key = Salsa20Impl.hsalsa20(nonce[0..16].*, key), | 316 | .key = SalsaImpl(rounds).hsalsa(nonce[0..16].*, key), |
| 321 | .nonce = nonce[16..24].*, | 317 | .nonce = nonce[16..24].*, |
| 322 | }; | 318 | }; |
| 323 | } | 319 | } |
| 324 | | 320 | |
| 325 | /// The Salsa20 stream cipher. | 321 | /// The Salsa stream cipher. |
| 326 | pub const Salsa20 = struct { | 322 | pub fn Salsa(comptime rounds: comptime_int) type { |
| 327 | /// Nonce length in bytes. | 323 | return struct { |
| 328 | pub const nonce_length = 8; | 324 | /// Nonce length in bytes. |
| 329 | /// Key length in bytes. | 325 | pub const nonce_length = 8; |
| 330 | pub const key_length = 32; | 326 | /// Key length in bytes. |
| 331 | | 327 | pub const key_length = 32; |
| 332 | /// Add the output of the Salsa20 stream cipher to `in` and stores the result into `out`. | 328 | |
| 333 | /// WARNING: This function doesn't provide authenticated encryption. | 329 | /// Add the output of the Salsa stream cipher to `in` and stores the result into `out`. |
| 334 | /// Using the AEAD or one of the `box` versions is usually preferred. | 330 | /// WARNING: This function doesn't provide authenticated encryption. |
| 335 | pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void { | 331 | /// Using the AEAD or one of the `box` versions is usually preferred. |
| 336 | debug.assert(in.len == out.len); | 332 | pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void { |
| 337 | | 333 | debug.assert(in.len == out.len); |
| 338 | var d: [4]u32 = undefined; | 334 | |
| 339 | d[0] = mem.readIntLittle(u32, nonce[0..4]); | 335 | var d: [4]u32 = undefined; |
| 340 | d[1] = mem.readIntLittle(u32, nonce[4..8]); | 336 | d[0] = mem.readIntLittle(u32, nonce[0..4]); |
| 341 | d[2] = @truncate(u32, counter); | 337 | d[1] = mem.readIntLittle(u32, nonce[4..8]); |
| 342 | d[3] = @truncate(u32, counter >> 32); | 338 | d[2] = @truncate(u32, counter); |
| 343 | Salsa20Impl.salsa20Xor(out, in, keyToWords(key), d); | 339 | d[3] = @truncate(u32, counter >> 32); |
| 344 | } | 340 | SalsaImpl(rounds).salsaXor(out, in, keyToWords(key), d); |
| 345 | }; | 341 | } |
| | 342 | }; |
| | 343 | } |
| 346 | | 344 | |
| 347 | /// The XSalsa20 stream cipher. | 345 | /// The XSalsa stream cipher. |
| 348 | pub const XSalsa20 = struct { | 346 | pub fn XSalsa(comptime rounds: comptime_int) type { |
| 349 | /// Nonce length in bytes. | 347 | return struct { |
| 350 | pub const nonce_length = 24; | 348 | /// Nonce length in bytes. |
| 351 | /// Key length in bytes. | 349 | pub const nonce_length = 24; |
| 352 | pub const key_length = 32; | 350 | /// Key length in bytes. |
| 353 | | 351 | pub const key_length = 32; |
| 354 | /// Add the output of the XSalsa20 stream cipher to `in` and stores the result into `out`. | 352 | |
| 355 | /// WARNING: This function doesn't provide authenticated encryption. | 353 | /// Add the output of the XSalsa stream cipher to `in` and stores the result into `out`. |
| 356 | /// Using the AEAD or one of the `box` versions is usually preferred. | 354 | /// WARNING: This function doesn't provide authenticated encryption. |
| 357 | pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void { | 355 | /// Using the AEAD or one of the `box` versions is usually preferred. |
| 358 | const extended = extend(key, nonce); | 356 | pub fn xor(out: []u8, in: []const u8, counter: u64, key: [key_length]u8, nonce: [nonce_length]u8) void { |
| 359 | Salsa20.xor(out, in, counter, extended.key, extended.nonce); | 357 | const extended = extend(rounds, key, nonce); |
| 360 | } | 358 | Salsa(rounds).xor(out, in, counter, extended.key, extended.nonce); |
| 361 | }; | 359 | } |
| | 360 | }; |
| | 361 | } |
| 362 | | 362 | |
| 363 | /// The XSalsa20 stream cipher, combined with the Poly1305 MAC | 363 | /// The XSalsa stream cipher, combined with the Poly1305 MAC |
| 364 | pub const XSalsa20Poly1305 = struct { | 364 | pub const XSalsa20Poly1305 = struct { |
| 365 | /// Authentication tag length in bytes. | 365 | /// Authentication tag length in bytes. |
| 366 | pub const tag_length = Poly1305.mac_length; | 366 | pub const tag_length = Poly1305.mac_length; |
| ... | @@ -369,6 +369,8 @@ pub const XSalsa20Poly1305 = struct { | ... | @@ -369,6 +369,8 @@ pub const XSalsa20Poly1305 = struct { |
| 369 | /// Key length in bytes. | 369 | /// Key length in bytes. |
| 370 | pub const key_length = XSalsa20.key_length; | 370 | pub const key_length = XSalsa20.key_length; |
| 371 | | 371 | |
| | 372 | const rounds = 20; |
| | 373 | |
| 372 | /// c: ciphertext: output buffer should be of size m.len | 374 | /// c: ciphertext: output buffer should be of size m.len |
| 373 | /// tag: authentication tag: output MAC | 375 | /// tag: authentication tag: output MAC |
| 374 | /// m: message | 376 | /// m: message |
| ... | @@ -377,7 +379,7 @@ pub const XSalsa20Poly1305 = struct { | ... | @@ -377,7 +379,7 @@ pub const XSalsa20Poly1305 = struct { |
| 377 | /// k: private key | 379 | /// k: private key |
| 378 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { | 380 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { |
| 379 | debug.assert(c.len == m.len); | 381 | debug.assert(c.len == m.len); |
| 380 | const extended = extend(k, npub); | 382 | const extended = extend(rounds, k, npub); |
| 381 | var block0 = [_]u8{0} ** 64; | 383 | var block0 = [_]u8{0} ** 64; |
| 382 | const mlen0 = math.min(32, m.len); | 384 | const mlen0 = math.min(32, m.len); |
| 383 | mem.copy(u8, block0[32..][0..mlen0], m[0..mlen0]); | 385 | mem.copy(u8, block0[32..][0..mlen0], m[0..mlen0]); |
| ... | @@ -398,7 +400,7 @@ pub const XSalsa20Poly1305 = struct { | ... | @@ -398,7 +400,7 @@ pub const XSalsa20Poly1305 = struct { |
| 398 | /// k: private key | 400 | /// k: private key |
| 399 | pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { | 401 | pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { |
| 400 | debug.assert(c.len == m.len); | 402 | debug.assert(c.len == m.len); |
| 401 | const extended = extend(k, npub); | 403 | const extended = extend(rounds, k, npub); |
| 402 | var block0 = [_]u8{0} ** 64; | 404 | var block0 = [_]u8{0} ** 64; |
| 403 | const mlen0 = math.min(32, c.len); | 405 | const mlen0 = math.min(32, c.len); |
| 404 | mem.copy(u8, block0[32..][0..mlen0], c[0..mlen0]); | 406 | mem.copy(u8, block0[32..][0..mlen0], c[0..mlen0]); |
| ... | @@ -482,7 +484,7 @@ pub const Box = struct { | ... | @@ -482,7 +484,7 @@ pub const Box = struct { |
| 482 | pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 { | 484 | pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 { |
| 483 | const p = try X25519.scalarmult(secret_key, public_key); | 485 | const p = try X25519.scalarmult(secret_key, public_key); |
| 484 | const zero = [_]u8{0} ** 16; | 486 | const zero = [_]u8{0} ** 16; |
| 485 | return Salsa20Impl.hsalsa20(zero, p); | 487 | return SalsaImpl(20).hsalsa(zero, p); |
| 486 | } | 488 | } |
| 487 | | 489 | |
| 488 | /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`. | 490 | /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`. |