hare

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

ctr.ha (3179B)


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