hare

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

commit 203c0c02076c2ef203b2e3850b06a0abb2354ae2
parent 22314c3157a69b08b71852cb928b9c4f8f98322f
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Sat,  7 May 2022 14:41:16 +0200

crypto::cipher: add an abstract xorstream

that will provide reader, writer and closer functions for streams
implementing that interface.

Signed-off-by: Armin Preiml <apreiml@strohwolke.at>

Diffstat:
Mcrypto/cipher/stream.ha | 115++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 1 deletion(-)

diff --git a/crypto/cipher/stream.ha b/crypto/cipher/stream.ha @@ -1,5 +1,7 @@ // License: MPL-2.0 -// (c) 2021 Armin Preiml <apreiml@strohwolke.at> +// (c) 2022 Armin Preiml <apreiml@strohwolke.at> +use io; +use crypto::math::{xor}; // An abstract interface for implementing cipher streams export type stream = struct { @@ -14,3 +16,114 @@ export fn stream_xor(s: *stream, dest: []u8, src: []u8) void = { assert(len(dest) == len(src), "stream_xor: slices must have the same length"); s.xor(s, dest, src); }; + + +// An abstract interface for implementing streams that encrypt or decrypt by +// producing a key stream that is xored with the data. The implementing stream +// must call [[xorstream_init]] after creation so that [[io::reader]], +// [[io::writer]] and [[io::closer]] functions will be registered. +// +// After initializing the xorstream can be written to with [[io::write]] to +// encrypt data and write it to the handle 'h'. For decrpytion 'h' will provide +// the ciphertext and the plaintext can be read from the xorstream with +// [[io::read]]. +export type xorstream = struct { + stream: io::stream, + h: io::handle, + + // Returns usable part of the current key buffer. The buffer must be + // modifiable by the callee. + keybuf: *fn(s: *xorstream) []u8, + + // Advances the start index of the keybuffer by 'n' bytes. + advance: *fn(s: *xorstream, n: size) void, + + // Erases all sensitive data from memory. + finish: *fn(s: *xorstream) void, +}; + +// Initialises a xorstream with the handle 'h' and sets [[io::reader]], +// [[io::writer]] and [[io::closer]] functions. +export fn xorstream_init(xs: *xorstream, h: io::handle) void = { + xs.h = h; + xs.stream = &xorstream_vtable; +}; + +const xorstream_vtable: io::vtable = io::vtable { + writer = &xor_writer, + reader = &xor_reader, + closer = &xor_closer, + ... +}; + +fn xor_writer(s: *io::stream, in: const []u8) (size | io::error) = { + let s = s: *xorstream; + + let keybuf = s.keybuf(s); + + const max = if (len(in) > len(keybuf)) { + yield len(keybuf); + } else { + yield len(in); + }; + if (max == 0) { + return 0z; + }; + + keybuf = keybuf[..max]; + + // Modify the keybuf to store the cipher for writing, so that we won't + // need additional space. + xor(keybuf, keybuf, in[..max]); + + match (io::write(s.h, keybuf)) { + case let n: size => + if (n < max) { + // revert the unused part of the keybuf to allow retry + xor(keybuf[n..], keybuf[n..], in[n..max]); + }; + s.advance(s, n); + return n; + case let e: io::error => + // revert the keybuf to allow retry + xor(keybuf, keybuf, in[..max]); + return e; + }; +}; + +fn xor_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = { + let s = s: *xorstream; + + if (len(buf) == 0) { + return 0z; + }; + + match (io::read(s.h, buf)) { + case io::EOF => + return io::EOF; + case let e: io::error => + return e; + case let n: size => + for (let z = 0z; z < n) { + let keybuf = s.keybuf(s); + + const max = if (n - z > len(keybuf)) { + yield len(keybuf); + } else { + yield n - z; + }; + + xor(buf[..max], buf[..max], keybuf[..max]); + buf = buf[max..]; + s.advance(s, max); + z += max; + }; + return n; + }; +}; + +fn xor_closer(s: *io::stream) (void | io::error) = { + let s = s: *xorstream; + s.finish(s); +}; +