hare

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

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:
Mcrypto/salsa/+test.ha | 26+++++++++++++++++---------
Mcrypto/salsa/README | 10+++++-----
Mcrypto/salsa/salsa20.ha | 59++++++++++++++++++++++++++++-------------------------------
Mscripts/gen-stdlib | 7++++---
Mstdlib.mk | 4++--
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 \