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 };