commit 1912d0c56d46faf0e717ff1484055e1a2f843e1b
parent 65068a8ace6fc738bbce148ee0c5062f4f7c3ec5
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Mon, 9 May 2022 16:15:24 +0200
crypto::salsa: convert to a cipher::xorstream
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
5 files changed, 56 insertions(+), 50 deletions(-)
diff --git a/crypto/salsa/+test.ha b/crypto/salsa/+test.ha
@@ -1,8 +1,10 @@
// License: MPL-2.0
// (c) 2021 Armin Preiml <apreiml@strohwolke.at>
+use bufio;
use bytes;
use crypto::cipher;
use types;
+use io;
@test fn qr() void = {
let s: [4]u32 = [0xe7e8c006, 0xc4f9417d, 0x6479b4b2, 0x68c67137];
@@ -55,12 +57,14 @@ use types;
];
let result: [116]u8 = [0...];
+ let cipherbuf = bufio::fixed(result, io::mode::WRITE);
let c = salsa20();
- xsalsa20_init(&c, &key, &nonce);
- defer cipher::finish(&c);
+ defer io::close(&c)!;
- cipher::stream_xor(&c, result, msg);
+ xsalsa20_init(&c, &cipherbuf, &key, &nonce);
+
+ io::writeall(&c, msg)!;
assert(bytes::equal(cipher, result));
};
@@ -106,13 +110,15 @@ use types;
];
let result: [116]u8 = [0...];
+ let cipherbuf = bufio::fixed(result, io::mode::WRITE);
let c = salsa20();
- xsalsa20_init(&c, &key, &nonce);
+ defer io::close(&c)!;
+
+ xsalsa20_init(&c, &cipherbuf, &key, &nonce);
setctr(&c, types::U32_MAX);
- defer cipher::finish(&c);
- cipher::stream_xor(&c, result, msg);
+ io::writeall(&c, msg)!;
assert(bytes::equal(cipher, result));
};
@@ -159,12 +165,14 @@ use types;
let result: [116]u8 = [0...];
+ let cipherbuf = bufio::fixed(result, io::mode::WRITE);
let c = salsa20();
- xsalsa20_init(&c, &key, &nonce);
+ defer io::close(&c)!;
+
+ xsalsa20_init(&c, &cipherbuf, &key, &nonce);
setctr(&c, types::U64_MAX);
- defer cipher::finish(&c);
- cipher::stream_xor(&c, result, msg);
+ io::writeall(&c, msg)!;
assert(bytes::equal(cipher, result));
};
diff --git a/crypto/salsa/README b/crypto/salsa/README
@@ -2,11 +2,11 @@ crypto::salsa provides an implementation of the Salsa20 and XSalsa20 stream
ciphers, per "Salsa20 specification" by Daniel J. Bernstein.
Use [[salsa20]] to create a stream and either [[xsalsa20_init]] or
-[[salsa20_init]] to set key and nonce of the appropriate size, [[NONCESIZE]] for
-salsa20 or [[XNONCESIZE]] for XSalsa20. After calling the appropriate init
-function, [[crypto::cipher::stream_xor]] may be used to encrypt blocks. The
-stream must be finished with [[crypto::cipher::finish]] to wipe sensitive data
-from memory.
+[[salsa20_init]] to set handle, key and nonce of the appropriate size,
+[[NONCESIZE]] for salsa20 or [[XNONCESIZE]] for XSalsa20. After calling the
+appropriate init function, [[io::write]] may be used to encrypt blocks to the
+handle or [[io::read]] to decrypt blocks from the handle. The stream must be
+closed with [[io::close]] to wipe sensitive data from memory.
Writing blocks of length [[BLOCKSIZE]] is not required. However, seeking the key
stream with [[setctr]] only operates in units of [[BLOCKSIZE]].
diff --git a/crypto/salsa/salsa20.ha b/crypto/salsa/salsa20.ha
@@ -4,6 +4,7 @@ use bytes;
use crypto::cipher;
use crypto::math::{rotl32, xor};
use endian;
+use io;
// Size of a Salsa key, in bytes.
export def KEYSIZE: size = 32;
@@ -22,7 +23,7 @@ export def BLOCKSIZE: size = 64;
const magic: [4]u32 = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];
export type stream = struct {
- cipher::stream,
+ cipher::xorstream,
state: [16]u32,
xorbuf: [BLOCKSIZE]u8,
xorused: size,
@@ -30,11 +31,12 @@ export type stream = struct {
};
// Create a Salsa20 or XSalsa20 stream. Must be initialized with either
-// [[salsa20_init]] or [[xsalsa20_init]], and must be finished with
-// [[crypto::cipher::finish]] afterwards to wipe sensitive data from memory.
+// [[salsa20_init]] or [[xsalsa20_init]], and must be closed with [[io::close]]
+// after use to wipe sensitive data from memory.
export fn salsa20() stream = {
return stream {
- xor = &stream_xor,
+ keybuf = &keybuf,
+ advance = &advance,
finish = &finish,
xorused = BLOCKSIZE,
rounds = ROUNDS,
@@ -69,18 +71,22 @@ fn init(
// Initialize a Salsa20 stream.
export fn salsa20_init(
s: *stream,
+ h: io::handle,
key: *[KEYSIZE]u8,
nonce: *[NONCESIZE]u8,
) void = {
let counter: [8]u8 = [0...];
init(&s.state, key, nonce, &counter);
s.xorused = BLOCKSIZE;
+
+ cipher::xorstream_init(s, h);
};
// Initialize an XSalsa20 stream. XSalsa20 differs from Salsa20 via the use of a
// larger nonce parameter.
export fn xsalsa20_init(
s: *stream,
+ h: io::handle,
key: *[KEYSIZE]u8,
nonce: *[XNONCESIZE]u8
) void = {
@@ -98,7 +104,7 @@ export fn xsalsa20_init(
endian::leputu32(dkey[24..28], state[8]);
endian::leputu32(dkey[28..], state[9]);
- salsa20_init(s, &dkey, nonce[16..]: *[NONCESIZE]u8);
+ salsa20_init(s, h, &dkey, nonce[16..]: *[NONCESIZE]u8);
bytes::zero((state[..]: *[*]u8)[..64]);
bytes::zero(dkey);
@@ -112,33 +118,24 @@ export fn setctr(s: *stream, counter: u64) void = {
s.xorused = BLOCKSIZE;
};
-fn stream_xor(cs: *cipher::stream, dest: []u8, src: []u8) void = {
- assert(endian::host == &endian::little, "TODO big-endian");
- let s = cs: *stream;
-
- for (len(dest) > 0) {
- if (s.xorused >= BLOCKSIZE) {
- block((s.xorbuf[..]: *[*]u32)[..16], &s.state, s.rounds);
- s.state[8] += 1;
- if (s.state[8] == 0) {
- s.state[9] += 1;
- };
- s.xorused = 0;
- };
-
- const max = if (len(dest) > (BLOCKSIZE - s.xorused)) {
- yield BLOCKSIZE - s.xorused;
- } else {
- yield len(dest);
+fn keybuf(s: *cipher::xorstream) []u8 = {
+ let s = s: *stream;
+ if (s.xorused >= BLOCKSIZE) {
+ block((s.xorbuf[..]: *[*]u32)[..16], &s.state, s.rounds);
+ s.state[8] += 1;
+ if (s.state[8] == 0) {
+ s.state[9] += 1;
};
+ s.xorused = 0;
+ };
- xor(dest[..max], src[..max],
- s.xorbuf[s.xorused..s.xorused + max]);
- s.xorused += max;
+ return s.xorbuf[s.xorused..];
+};
- dest = dest[max..];
- src = src[max..];
- };
+fn advance(s: *cipher::xorstream, n: size) void = {
+ let s = s: *stream;
+ assert(n <= len(s.xorbuf));
+ s.xorused += n;
};
fn block(dest: []u32, state: *[16]u32, rounds: size) void = {
@@ -174,8 +171,8 @@ fn qr(a: *u32, b: *u32, c: *u32, d: *u32) void = {
*a ^= rotl32(*d + *c, 18);
};
-fn finish(cs: *cipher::stream) void = {
- let s = cs: *stream;
+fn finish(s: *cipher::xorstream) void = {
+ let s = s: *stream;
bytes::zero((s.state[..]: *[*]u8)[..len(s.state) * size(u32)]);
bytes::zero(s.xorbuf);
};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -401,11 +401,12 @@ crypto_salsa() {
if [ $testing -eq 0 ]
then
gen_srcs crypto::salsa salsa20.ha
- gen_ssa crypto::salsa bytes crypto::cipher crypto::math endian
+ gen_ssa crypto::salsa bytes crypto::cipher crypto::math endian \
+ io
else
gen_srcs crypto::salsa salsa20.ha +test.ha
- gen_ssa crypto::salsa bytes crypto::cipher crypto::math endian \
- types
+ gen_ssa crypto::salsa bytes bufio crypto::cipher crypto::math \
+ endian types io
fi
}
diff --git a/stdlib.mk b/stdlib.mk
@@ -916,7 +916,7 @@ $(HARECACHE)/crypto/poly1305/crypto_poly1305-any.ssa: $(stdlib_crypto_poly1305_a
stdlib_crypto_salsa_any_srcs = \
$(STDLIB)/crypto/salsa/salsa20.ha
-$(HARECACHE)/crypto/salsa/crypto_salsa-any.ssa: $(stdlib_crypto_salsa_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_cipher_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_endian_$(PLATFORM))
+$(HARECACHE)/crypto/salsa/crypto_salsa-any.ssa: $(stdlib_crypto_salsa_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_cipher_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_endian_$(PLATFORM)) $(stdlib_io_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/crypto/salsa
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::salsa \
@@ -3010,7 +3010,7 @@ testlib_crypto_salsa_any_srcs = \
$(STDLIB)/crypto/salsa/salsa20.ha \
$(STDLIB)/crypto/salsa/+test.ha
-$(TESTCACHE)/crypto/salsa/crypto_salsa-any.ssa: $(testlib_crypto_salsa_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_types_$(PLATFORM))
+$(TESTCACHE)/crypto/salsa/crypto_salsa-any.ssa: $(testlib_crypto_salsa_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_types_$(PLATFORM)) $(testlib_io_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto/salsa
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::salsa \