hare

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

ctr.ha (3161B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use bytes;
      5 use crypto::math::{xor};
      6 use io;
      7 
      8 // A counter mode (CTR) stream.
      9 export type ctr_stream = struct {
     10 	xorstream,
     11 	b: *block,
     12 	counter: []u8,
     13 	xorbuf: []u8,
     14 	xorused: size,
     15 };
     16 
     17 // Creates a counter mode (CTR) cipher stream which can be used for encryption
     18 // (by encrypting writes to the underlying handle) or decryption (or by
     19 // decrypting reads from the underlying handle), but not both.
     20 //
     21 // The user must supply an initialization vector (IV) equal in length to the
     22 // block size of the underlying [[block]] cipher, and a temporary state buffer
     23 // whose size is equal to the block size times two. The module providing the
     24 // underlying block cipher usually provides constants which define the lengths
     25 // of these buffers for static allocation.
     26 //
     27 // The user must call [[io::close]] when they are done using the stream to
     28 // securely erase secret information stored in the stream state. This will also
     29 // finish the underlying [[block]] cipher.
     30 export fn ctr(h: io::handle, b: *block, iv: []u8, buf: []u8) ctr_stream = {
     31 	assert(len(iv) == blocksz(b), "iv is of invalid block size");
     32 	assert(len(buf) >= blocksz(b) * 2, "buf must be at least 2 * blocksize");
     33 
     34 	const bsz = blocksz(b);
     35 
     36 	// one buf block is used for the counter
     37 	let counter = buf[0..bsz];
     38 
     39 	// the remaining space is used to store the key stream. It needs
     40 	// to be at least the size of one block and ideally the size of
     41 	// nparallel(b) times the block size. A bigger buffer than the latter
     42 	// option is of no use.
     43 	let xorbuf = buf[bsz..];
     44 
     45 	counter[..] = iv[..];
     46 
     47 	// cap the buffer to a multiple of bsz.
     48 	let maxxorbufsz = blocksz(b) * nparallel(b);
     49 	const xorbufsz = if (len(xorbuf) < maxxorbufsz) {
     50 		yield len(xorbuf) - len(xorbuf) % blocksz(b);
     51 	} else {
     52 		yield maxxorbufsz;
     53 	};
     54 
     55 	let s = ctr_stream {
     56 		stream = &xorstream_vtable,
     57 		h = h,
     58 		keybuf = &ctr_keybuf,
     59 		advance = &ctr_advance,
     60 		finish = &ctr_finish,
     61 
     62 		b = b,
     63 		counter = counter,
     64 		xorbuf = xorbuf[..xorbufsz],
     65 		// mark all as used to force fill xorbuf
     66 		xorused = xorbufsz,
     67 		...
     68 	};
     69 	return s;
     70 };
     71 
     72 fn fill_xorbuf(ctr: *ctr_stream) void = {
     73 	const bsz = blocksz(ctr.b);
     74 
     75 	// Write and increment the counter to each available block
     76 	for (let i = 0z; i < len(ctr.xorbuf) / bsz; i += 1) {
     77 		ctr.xorbuf[i * bsz..(i * bsz + bsz)] = ctr.counter[0..bsz];
     78 
     79 		for (let j = len(ctr.counter); j > 0; j -= 1) {
     80 			ctr.counter[j - 1] += 1;
     81 			if (ctr.counter[j - 1] != 0) {
     82 				break;
     83 			};
     84 		};
     85 	};
     86 
     87 	encrypt(ctr.b, ctr.xorbuf, ctr.xorbuf);
     88 	ctr.xorused = 0;
     89 };
     90 
     91 fn ctr_keybuf(s: *xorstream) []u8 = {
     92 	let ctr = s: *ctr_stream;
     93 	if (ctr.xorused >= len(ctr.xorbuf)) {
     94 		fill_xorbuf(ctr);
     95 	};
     96 	return ctr.xorbuf[ctr.xorused..];
     97 };
     98 
     99 fn ctr_advance(s: *xorstream, n: size) void = {
    100 	let ctr = s: *ctr_stream;
    101 
    102 	// fill_xorbuf could be smarter, to skip multiple blocks at once.
    103 	// It's of no use, since xorstream doesn't support skipping an arbritary
    104 	// number of blocks.
    105 	assert(n <= len(ctr.xorbuf));
    106 
    107 	ctr.xorused += n;
    108 };
    109 
    110 fn ctr_finish(s: *xorstream) void = {
    111 	let ctr = s: *ctr_stream;
    112 	bytes::zero(ctr.xorbuf);
    113 	finish(ctr.b);
    114 };