hare

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

stream.ha (2465B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use crypto::math::{xor};
      5 use io;
      6 
      7 // An abstract interface for implementing streams that encrypt or decrypt by
      8 // producing a key stream that is xored with the data. The stream field should
      9 // be set to &[[xorstream_vtable]].
     10 //
     11 // After initializing the xorstream can be written to with [[io::write]] to
     12 // encrypt data and write it to the handle 'h'. For decrpytion 'h' will provide
     13 // the ciphertext and the plaintext can be read from the xorstream with
     14 // [[io::read]].
     15 export type xorstream = struct {
     16 	stream: io::stream,
     17 	h: io::handle,
     18 
     19 	// Returns usable part of the current key buffer. The buffer must be
     20 	// modifiable by the callee.
     21 	keybuf: *fn(s: *xorstream) []u8,
     22 
     23 	// Advances the start index of the keybuffer by 'n' bytes.
     24 	advance: *fn(s: *xorstream, n: size) void,
     25 
     26 	// Erases all sensitive data from memory.
     27 	finish: *fn(s: *xorstream) void,
     28 };
     29 
     30 export const xorstream_vtable: io::vtable = io::vtable {
     31 	writer = &xor_writer,
     32 	reader = &xor_reader,
     33 	closer = &xor_closer,
     34 	...
     35 };
     36 
     37 fn xor_writer(s: *io::stream, in: const []u8) (size | io::error) = {
     38 	let s = s: *xorstream;
     39 
     40 	let keybuf = s.keybuf(s);
     41 
     42 	const max = if (len(in) > len(keybuf)) {
     43 		yield len(keybuf);
     44 	} else {
     45 		yield len(in);
     46 	};
     47 	if (max == 0) {
     48 		return 0z;
     49 	};
     50 
     51 	keybuf = keybuf[..max];
     52 
     53 	// Modify the keybuf to store the cipher for writing, so that we won't
     54 	// need additional space.
     55 	xor(keybuf, keybuf, in[..max]);
     56 
     57 	match (io::write(s.h, keybuf)) {
     58 	case let n: size =>
     59 		if (n < max) {
     60 			// revert the unused part of the keybuf to allow retry
     61 			xor(keybuf[n..], keybuf[n..], in[n..max]);
     62 		};
     63 		s.advance(s, n);
     64 		return n;
     65 	case let e: io::error =>
     66 		// revert the keybuf to allow retry
     67 		xor(keybuf, keybuf, in[..max]);
     68 		return e;
     69 	};
     70 };
     71 
     72 fn xor_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
     73 	let s = s: *xorstream;
     74 
     75 	if (len(buf) == 0) {
     76 		return 0z;
     77 	};
     78 
     79 	match (io::read(s.h, buf)) {
     80 	case io::EOF =>
     81 		return io::EOF;
     82 	case let e: io::error =>
     83 		return e;
     84 	case let n: size =>
     85 		for (let z = 0z; z < n) {
     86 			let keybuf = s.keybuf(s);
     87 
     88 			const max = if (n - z > len(keybuf)) {
     89 				yield len(keybuf);
     90 			} else {
     91 				yield n - z;
     92 			};
     93 
     94 			xor(buf[..max], buf[..max], keybuf[..max]);
     95 			buf = buf[max..];
     96 			s.advance(s, max);
     97 			z += max;
     98 		};
     99 		return n;
    100 	};
    101 };
    102 
    103 fn xor_closer(s: *io::stream) (void | io::error) = {
    104 	let s = s: *xorstream;
    105 	s.finish(s);
    106 };