hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Mcrypto/authenc.ha | 32++++++++++++++++++++++----------
Mcrypto/chacha/+test.ha | 42+++++++++++++++++++++++++++---------------
Mcrypto/chacha/README | 12++++++------
Mcrypto/chacha/chacha20.ha | 64++++++++++++++++++++++++++++++----------------------------------
Mscripts/gen-stdlib | 14++++++++------
Mstdlib.mk | 8++++----
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 \