commit 65068a8ace6fc738bbce148ee0c5062f4f7c3ec5
parent 30e96588966a2e71264de8fd1db3dfae949bfce8
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Mon, 9 May 2022 16:15:23 +0200
crypto::chacha20: convert to cipher::xorstream
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
6 files changed, 97 insertions(+), 75 deletions(-)
diff --git a/crypto/authenc.ha b/crypto/authenc.ha
@@ -2,6 +2,7 @@
// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
// (c) 2022 Drew DeVault <sir@cmpwn.com>
use bytes;
+use bufio;
use crypto::chacha;
use crypto::cipher;
use crypto::poly1305;
@@ -9,6 +10,7 @@ use crypto::mac;
use crypto::math;
use endian;
use errors;
+use io;
// A secret session key.
export type sessionkey = [32]u8;
@@ -79,17 +81,21 @@ export fn encrypt(
additional: []u8...,
) box = {
let s = chacha::chacha20();
- chacha::xchacha20_init(&s, key, nonce);
+ defer io::close(&s)!;
let otk: poly1305::key = [0...];
defer bytes::zero(otk);
- cipher::stream_xor(&s, otk[..], otk[..]);
- chacha::setctr(&s, 1);
+ let otkbuf = bufio::fixed(otk, io::mode::WRITE);
+ chacha::xchacha20_init(&s, &otkbuf, key, nonce);
+ io::writeall(&s, otk[..])!;
let ciphertext = plaintext;
- cipher::stream_xor(&s, ciphertext, plaintext);
- cipher::finish(&s);
+ let cipherbuf = bufio::fixed(ciphertext, io::mode::WRITE);
+
+ chacha::xchacha20_init(&s, &cipherbuf, key, nonce);
+ chacha::setctr(&s, 1);
+ io::writeall(&s, plaintext)!;
let m: mac = [0...];
writemac(&m, &otk, ciphertext, additional...);
@@ -158,15 +164,17 @@ export fn decrypt(
additional: []u8...
) ([]u8 | errors::invalid) = {
let s = chacha::chacha20();
- chacha::xchacha20_init(&s, key, &box.1);
+ defer io::close(&s)!;
let otk: poly1305::key = [0...];
defer bytes::zero(otk);
- cipher::stream_xor(&s, otk[..], otk[..]);
- chacha::setctr(&s, 1);
+ let otkbuf = bufio::fixed(otk, io::mode::WRITE);
+ chacha::xchacha20_init(&s, &otkbuf, key, &box.1);
+ io::writeall(&s, otk)!;
let ciphertext = box.2;
+
let m: mac = [0...];
writemac(&m, &otk, ciphertext, additional...);
@@ -176,8 +184,12 @@ export fn decrypt(
};
let plaintext = ciphertext;
- cipher::stream_xor(&s, plaintext, ciphertext);
- cipher::finish(&s);
+ let cipherbuf = bufio::fixed(ciphertext, io::mode::READ);
+
+ chacha::xchacha20_init(&s, &cipherbuf, key, &box.1);
+ chacha::setctr(&s, 1);
+
+ io::readall(&s, plaintext)!;
return plaintext;
};
diff --git a/crypto/chacha/+test.ha b/crypto/chacha/+test.ha
@@ -1,7 +1,9 @@
// License: MPL-2.0
// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
+use bufio;
use bytes;
use crypto::cipher;
+use io;
// test vector taken from rfc8439
@test fn chacha20() void = {
@@ -46,20 +48,26 @@ use crypto::cipher;
let result: [114]u8 = [0...];
+ let cipherbuf = bufio::fixed(result, io::mode::WRITE);
+
let c = chacha20();
- chacha20_init(&c, &key, &nonce);
+ defer io::close(&c)!;
+
+ chacha20_init(&c, &cipherbuf, &key, &nonce);
setctr(&c, 1);
- defer cipher::finish(&c);
- cipher::stream_xor(&c, result, msg);
+ const n = io::writeall(&c, msg)!;
+ assert(n == len(msg));
assert(bytes::equal(cipher, result));
result = [0...];
- chacha20_init(&c, &key, &nonce);
+ cipherbuf = bufio::fixed(result, io::mode::WRITE);
+
+ chacha20_init(&c, &cipherbuf, &key, &nonce);
setctr(&c, 1);
- cipher::stream_xor(&c, result[..10], msg[..10]);
- cipher::stream_xor(&c, result[10..63], msg[10..63]);
- cipher::stream_xor(&c, result[63..], msg[63..]);
+ io::write(&c, msg[..10])!;
+ io::write(&c, msg[10..63])!;
+ io::writeall(&c, msg[63..])!;
assert(bytes::equal(cipher, result));
};
@@ -147,32 +155,36 @@ const xcipher: [_]u8 = [
@test fn xchacha20() void = {
let result: [304]u8 = [0...];
+ let cipherbuf = bufio::fixed(result, io::mode::WRITE);
let c = chacha20();
- xchacha20_init(&c, &xkey, &xnonce);
- defer cipher::finish(&c);
+ defer io::close(&c)!;
+
+ xchacha20_init(&c, &cipherbuf, &xkey, &xnonce);
setctr(&c, 1);
- cipher::stream_xor(&c, result, xmsg);
+ io::writeall(&c, xmsg)!;
assert(bytes::equal(xcipher, result));
};
@test fn skipblocks() void = {
let result: [18]u8 = [0...];
+ let cipherbuf = bufio::fixed(result, io::mode::WRITE);
let c = chacha20();
- xchacha20_init(&c, &xkey, &xnonce);
- defer cipher::finish(&c);
+ defer io::close(&c)!;
+
+ xchacha20_init(&c, &cipherbuf, &xkey, &xnonce);
// just encrypt a few bytes of each block, to check if setctr works
setctr(&c, 1);
- cipher::stream_xor(&c, result[..5], xmsg[..5]);
+ io::writeall(&c, xmsg[..5])!;
setctr(&c, 2);
- cipher::stream_xor(&c, result[5..11], xmsg[64..70]);
+ io::writeall(&c, xmsg[64..70])!;
setctr(&c, 3);
- cipher::stream_xor(&c, result[11..], xmsg[128..135]);
+ io::writeall(&c, xmsg[128..135])!;
assert(bytes::equal(result[..5], xcipher[..5]));
assert(bytes::equal(result[5..11], xcipher[64..70]));
diff --git a/crypto/chacha/README b/crypto/chacha/README
@@ -1,12 +1,12 @@
crypto::chacha provides an implementation of the Chacha20 and XChacha20 stream
ciphers.
-Use [[chacha20]] to create a stream and either [[chacha20_init]] or
-[[xchacha20_init]] to set key and nonce of the appropriate size, [[NONCESIZE]]
-for chacha20 or [[XNONCESIZE]] for XChacha20. 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.
+Use [[chacha20]] to create a [[cipher::xorstream]] and either [[chacha20_init]]
+or [[xchacha20_init]] to set the handle, key and nonce of the appropriate size,
+[[NONCESIZE]] for chacha20 or [[XNONCESIZE]] for XChacha20. 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/chacha/chacha20.ha b/crypto/chacha/chacha20.ha
@@ -4,6 +4,7 @@ use bytes;
use crypto::cipher;
use crypto::math::{rotl32, xor};
use endian;
+use io;
// Size of a Chacha key, in bytes.
export def KEYSIZE: size = 32;
@@ -20,11 +21,9 @@ export def BLOCKSIZE: size = 64;
def ROUNDS: size = 20;
const magic: [4]u32 = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];
-// Create a Chacha20 or XChacha20 stream. Needs to be initialized with either
-// [[chacha20_init]] or [[xchacha20_init]]. It must be finished with
-// [[crypto::cipher::finish]].
+// A ChaCha20 or XChaCha20 [[cipher::xorstream]] depending on intialisation.
export type stream = struct {
- cipher::stream,
+ cipher::xorstream,
state: [16]u32,
xorbuf: [BLOCKSIZE]u8,
xorused: size,
@@ -32,12 +31,12 @@ export type stream = struct {
};
// Creates a ChaCha20 or XChaCha20 stream cipher. Must be initialized with
-// [[chacha20_init]] or [[xchacha20_init]] prior to use, and must be finished
-// with [[crypto::cipher::finish]] afterwards to wipe sensitive data from
-// memory.
+// [[chacha20_init]] or [[xchacha20_init]] prior to use, and must be closed
+// with [[io::close]] after use to wipe sensitive data from memory.
export fn chacha20() stream = {
return stream {
- xor = &stream_xor,
+ keybuf = &keybuf,
+ advance = &advance,
finish = &finish,
xorused = BLOCKSIZE,
rounds = ROUNDS,
@@ -48,9 +47,12 @@ export fn chacha20() stream = {
// Initialize a Chacha20 stream.
export fn chacha20_init(
s: *stream,
+ h: io::handle,
key: *[KEYSIZE]u8,
nonce: *[NONCESIZE]u8
) void = {
+ cipher::xorstream_init(s, h);
+
s.state[0] = magic[0];
s.state[1] = magic[1];
s.state[2] = magic[2];
@@ -73,6 +75,7 @@ export fn chacha20_init(
// Initialise a XChacha20 stream.
export fn xchacha20_init(
s: *stream,
+ h: io::handle,
key: *[KEYSIZE]u8,
nonce: *[XNONCESIZE]u8
) void = {
@@ -104,7 +107,7 @@ export fn xchacha20_init(
let dnonce: [NONCESIZE]u8 = [0...];
dnonce[4..] = nonce[16..];
- chacha20_init(s, &dkey, &dnonce);
+ chacha20_init(s, h, &dkey, &dnonce);
bytes::zero((state: []u8: *[*]u8)[..BLOCKSIZE]);
bytes::zero(dkey);
@@ -120,32 +123,25 @@ export fn setctr(s: *stream, counter: u32) void = {
s.xorused = BLOCKSIZE;
};
-fn stream_xor(c: *cipher::stream, dest: []u8, src: []u8) void = {
- let s = c: *stream;
+fn keybuf(s: *cipher::xorstream) []u8 = {
+ let s = s: *stream;
- for (len(dest) > 0) {
- if (s.xorused >= BLOCKSIZE) {
- block((s.xorbuf: []u8: *[*]u32)[..16], &s.state,
- s.rounds);
- // TODO on big endian systems s.xorbuf values need to
- // be converted from little endian.
- s.state[12] += 1;
- s.xorused = 0;
- };
-
- const max = if (len(dest) > (BLOCKSIZE - s.xorused)) {
- yield BLOCKSIZE - s.xorused;
- } else {
- yield len(dest);
- };
-
- xor(dest[..max], src[..max],
- s.xorbuf[s.xorused..s.xorused + max]);
- s.xorused += max;
-
- dest = dest[max..];
- src = src[max..];
+ if (s.xorused >= BLOCKSIZE) {
+ block((s.xorbuf: []u8: *[*]u32)[..16], &s.state,
+ s.rounds);
+ // TODO on big endian systems s.xorbuf values need to
+ // be converted from little endian.
+ s.state[12] += 1;
+ s.xorused = 0;
};
+
+ return s.xorbuf[s.xorused..];
+};
+
+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 = {
@@ -193,7 +189,7 @@ fn qr(a: *u32, b: *u32, c: *u32, d: *u32) void = {
*b = rotl32(*b, 7);
};
-fn finish(c: *cipher::stream) void = {
+fn finish(c: *cipher::xorstream) void = {
let s = c: *stream;
bytes::zero((s.state[..]: *[*]u8)[..BLOCKSIZE]);
bytes::zero(s.xorbuf);
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -212,17 +212,17 @@ crypto() {
gen_srcs crypto \
authenc.ha \
keyderiv.ha
- gen_ssa crypto bytes crypto::argon2 crypto::chacha \
+ gen_ssa crypto bufio bytes crypto::argon2 crypto::chacha \
crypto::cihper crypto::poly1305 crypto::mac \
- crypto::math endian errors
+ crypto::math endian errors io
else
gen_srcs crypto \
authenc.ha \
keyderiv.ha \
+test/authenc.ha
- gen_ssa crypto bytes crypto::argon2 crypto::chacha \
+ gen_ssa crypto bytes bufio crypto::argon2 crypto::chacha \
crypto::cihper crypto::poly1305 crypto::mac \
- crypto::math endian errors
+ crypto::math endian errors io
fi
}
@@ -322,10 +322,12 @@ crypto_chacha() {
if [ $testing -eq 0 ]
then
gen_srcs crypto::chacha chacha20.ha
- gen_ssa crypto::chacha bytes crypto::cipher crypto::math endian
+ gen_ssa crypto::chacha bytes crypto::cipher crypto::math \
+ endian io
else
gen_srcs crypto::chacha chacha20.ha +test.ha
- gen_ssa crypto::chacha bytes crypto::cipher crypto::math endian
+ gen_ssa crypto::chacha bytes crypto::cipher crypto::math \
+ endian io
fi
}
diff --git a/stdlib.mk b/stdlib.mk
@@ -756,7 +756,7 @@ stdlib_crypto_any_srcs = \
$(STDLIB)/crypto/authenc.ha \
$(STDLIB)/crypto/keyderiv.ha
-$(HARECACHE)/crypto/crypto-any.ssa: $(stdlib_crypto_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_argon2_$(PLATFORM)) $(stdlib_crypto_chacha_$(PLATFORM)) $(stdlib_crypto_cihper_$(PLATFORM)) $(stdlib_crypto_poly1305_$(PLATFORM)) $(stdlib_crypto_mac_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_endian_$(PLATFORM)) $(stdlib_errors_$(PLATFORM))
+$(HARECACHE)/crypto/crypto-any.ssa: $(stdlib_crypto_any_srcs) $(stdlib_rt) $(stdlib_bufio_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_argon2_$(PLATFORM)) $(stdlib_crypto_chacha_$(PLATFORM)) $(stdlib_crypto_cihper_$(PLATFORM)) $(stdlib_crypto_poly1305_$(PLATFORM)) $(stdlib_crypto_mac_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_endian_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_io_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/crypto
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto \
@@ -828,7 +828,7 @@ $(HARECACHE)/crypto/blowfish/crypto_blowfish-any.ssa: $(stdlib_crypto_blowfish_a
stdlib_crypto_chacha_any_srcs = \
$(STDLIB)/crypto/chacha/chacha20.ha
-$(HARECACHE)/crypto/chacha/crypto_chacha-any.ssa: $(stdlib_crypto_chacha_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_cipher_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) $(stdlib_endian_$(PLATFORM))
+$(HARECACHE)/crypto/chacha/crypto_chacha-any.ssa: $(stdlib_crypto_chacha_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/chacha
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::chacha \
@@ -2837,7 +2837,7 @@ testlib_crypto_any_srcs = \
$(STDLIB)/crypto/keyderiv.ha \
$(STDLIB)/crypto/+test/authenc.ha
-$(TESTCACHE)/crypto/crypto-any.ssa: $(testlib_crypto_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_argon2_$(PLATFORM)) $(testlib_crypto_chacha_$(PLATFORM)) $(testlib_crypto_cihper_$(PLATFORM)) $(testlib_crypto_poly1305_$(PLATFORM)) $(testlib_crypto_mac_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_errors_$(PLATFORM))
+$(TESTCACHE)/crypto/crypto-any.ssa: $(testlib_crypto_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_crypto_argon2_$(PLATFORM)) $(testlib_crypto_chacha_$(PLATFORM)) $(testlib_crypto_cihper_$(PLATFORM)) $(testlib_crypto_poly1305_$(PLATFORM)) $(testlib_crypto_mac_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_io_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto \
@@ -2919,7 +2919,7 @@ testlib_crypto_chacha_any_srcs = \
$(STDLIB)/crypto/chacha/chacha20.ha \
$(STDLIB)/crypto/chacha/+test.ha
-$(TESTCACHE)/crypto/chacha/crypto_chacha-any.ssa: $(testlib_crypto_chacha_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM))
+$(TESTCACHE)/crypto/chacha/crypto_chacha-any.ssa: $(testlib_crypto_chacha_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_io_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto/chacha
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::chacha \