hare

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

commit fd6a3ec9675a5d98759f41d3c1d17f3e4752134f
parent 2094ec8e94b34a8c3011bc2a60821f74423226b5
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue, 23 Nov 2021 11:54:57 +0100

crypto::cipher, crypto::aes: improve docs

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mcrypto/README | 5+++--
Acrypto/aes/README | 16++++++++++++++++
Mcrypto/aes/aes_ct64.ha | 20++++++++++----------
Mcrypto/cipher/README | 17++++++++++++++++-
Acrypto/cipher/block.ha | 24++++++++++++++++++++++++
Mcrypto/cipher/cbc.ha | 42++++++++++++++++++++++++------------------
Dcrypto/cipher/cipher.ha | 23-----------------------
Mcrypto/cipher/ctr.ha | 27+++++++++++++++------------
Mscripts/gen-stdlib | 8++++----
Mstdlib.mk | 16++++++++--------
10 files changed, 120 insertions(+), 78 deletions(-)

diff --git a/crypto/README b/crypto/README @@ -1,3 +1,4 @@ -crypto provides implementations of various cryptography-related standards. +The crypto module provides implementations of various cryptography-related +standards. -Hare's cryptography implementation has not been audited. +Be advised that Hare's cryptography implementations have not been audited. diff --git a/crypto/aes/README b/crypto/aes/README @@ -0,0 +1,16 @@ +The crypto::aes module provides an implementation of the Advanced Encryption +Standard per the [[crypto::cipher::block]] interface. Several implementations of +AES are provided which are optimized for different scenarios. To choose the most +appropriate one for your system, use [[new]]. + +When combined with a block cipher mode from [[crypto::cipher]], suitable buffer +lengths for static allocation are provided as constants such as [[BLOCKSIZE]], +[[CTR_BUFSIZE]], and [[CBC_BUFSIZE]]. + +This is a low-level module which implements cryptographic primitives. Direct use +of cryptographic primitives is not recommended for non-experts, as incorrect use +of these primitives can easily lead to the introduction of security +vulnerabilities. Non-experts are advised to use the high-level operations +available in the top-level [[crypto]] module. + +Be advised that Hare's cryptography implementations have not been audited. diff --git a/crypto/aes/aes_ct64.ha b/crypto/aes/aes_ct64.ha @@ -22,15 +22,15 @@ // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. - use crypto::cipher; use crypto::math; use endian; -export def BLOCKSIZE: size = 16; - def CT64_NPARALLEL: size = 4; +// The block size used by the AES algorithm. +export def BLOCKSIZE: size = 16; + // Size of the buffer used for [[crypto::cipher::ctr]]. export def CTR_BUFSIZE: size = BLOCKSIZE * (CT64_NPARALLEL + 1); @@ -44,10 +44,11 @@ export type ct64_block = struct { sk_exp: [120]u64, }; -// Creates a constant time, 64-bit optimised aes [[crypto::cipher::block]]. +// Returns an AES [[crypto::cipher::block]] cipher implementation optimized for +// constant time operation on 64-bit systems. export fn ct64(key: []u8) ct64_block = { let state = ct64_block { - sz = BLOCKSIZE, + blocksz = BLOCKSIZE, nparallel = CT64_NPARALLEL, encrypt = &aes_ct64_encrypt, decrypt = &aes_ct64_decrypt, @@ -66,10 +67,10 @@ export fn ct64(key: []u8) ct64_block = { fn aes_ct64_encrypt(block: *cipher::block, dest: []u8, src: []u8) void = { let b = block: *ct64_block; - assert(len(src) % b.sz == 0 && (len(src) / b.sz) <= b.nparallel, + assert(len(src) % b.blocksz == 0 && (len(src) / b.blocksz) <= b.nparallel, "invalid block size"); - let nblocks = len(src) / b.sz; + let nblocks = len(src) / b.blocksz; let q: [8]u64 = [0...]; let w: [16]u32 = [0...]; @@ -94,11 +95,10 @@ fn aes_ct64_encrypt(block: *cipher::block, dest: []u8, src: []u8) void = { fn aes_ct64_decrypt(block: *cipher::block, dest: []u8, src: []u8) void = { let b = block: *ct64_block; - assert(len(src) % b.sz == 0 && (len(src) / b.sz) <= b.nparallel, + assert(len(src) % b.blocksz == 0 && (len(src) / b.blocksz) <= b.nparallel, "invalid block size"); - let nblocks = len(src) / b.sz; - + const nblocks = len(src) / b.blocksz; let q: [8]u64 = [0...]; let w: [16]u32 = [0...]; diff --git a/crypto/cipher/README b/crypto/cipher/README @@ -1 +1,16 @@ -Block encryption modes +The crypto::cipher module provides block cipher encryption modes. The [[block]] +type provides an abstraction over a block cipher algorithm, and functions like +[[cbc_decryptor]] create encryptors or decryptors for specific block encryption +modes. + +Block ciphers in Hare rely upon caller-provided buffer allocations, and do not +allocate memory at runtime. Consult the documentation for the underlying +algorithm, e.g. [[crypto::aes]], for the appropriate buffer sizes to use. + +This is a low-level module which implements cryptographic primitives. Direct use +of cryptographic primitives is not recommended for non-experts, as incorrect use +of these primitives can easily lead to the introduction of security +vulnerabilities. Non-experts are advised to use the high-level operations +available in the top-level [[crypto]] module. + +Be advised that Hare's cryptography implementations have not been audited. diff --git a/crypto/cipher/block.ha b/crypto/cipher/block.ha @@ -0,0 +1,24 @@ +// An abstract interface for implementing block ciphers. +export type block = struct { + blocksz: size, + + nparallel: size, + + encrypt: *fn(b: *block, dest: []u8, src: []u8) void, + decrypt: *fn(b: *block, dest: []u8, src: []u8) void, +}; + +// Returns the block size in bytes for a [[block]] cipher. +export fn blocksz(b: *block) size = b.blocksz; + +// Returns the number of blocks that can be processed at once for a [[block]] +// cipher. +export fn nparallel(b: *block) size = b.nparallel; + +// Encrypt up to [[nparallel]] blocks from 'src' and writes to 'dest'. +export fn encrypt(b: *block, dest: []u8, src: []u8) void = + b.encrypt(b, dest, src); + +// Decrypt up to [[nparallel]] blocks from 'src' and writes to 'dest'. +export fn decrypt(b: *block, dest: []u8, src: []u8) void = + b.decrypt(b, dest, src); diff --git a/crypto/cipher/cbc.ha b/crypto/cipher/cbc.ha @@ -7,16 +7,20 @@ export type cbc_mode = struct { carrybuf: []u8, }; -// Creates a cipher block chaining mode encryptor. The encryptor will keep its -// state, so data can be encrypted using multiple cbc_encrypt calls. The size -// of the buffer depends on the block cipher. Use [[crypto::aes::CBC_BUFSIZE]] -// for the stdlib AES implementation. +// Creates a cipher block chaining (CBC) mode encryptor. +// +// The user must supply an initialization vector (IV) equal in length to the +// block size of the underlying [[block]] cipher, and a temporary state buffer +// whose size is equal to the block size times two. The module providing the +// underlying block cipher usually provides constants which define the lengths +// of these buffers for static allocation. export fn cbc_encryptor(b: *block, iv: []u8, buf: []u8) cbc_mode = { - assert(len(iv) == sz(b), "len(iv) must be the same as the block size"); - assert(len(buf) == sz(b) * 2, + assert(len(iv) == blocksz(b), + "len(iv) must be the same as the block size"); + assert(len(buf) == blocksz(b) * 2, "buffer needs to be two times of the block size"); - let bsz = sz(b); + const bsz = blocksz(b); let carry = buf[0..bsz]; carry[..] = iv[..]; @@ -28,16 +32,20 @@ export fn cbc_encryptor(b: *block, iv: []u8, buf: []u8) cbc_mode = { }; }; -// Creates a cipher block chaining mode decryptor. The decryptor will keep its -// state, so data can be encrypted using multiple cbc_decrypt calls. The size -// of the buffer depends on the block cipher. Use [[crypto::aes::CBC_BUFSIZE]] -// for the stdlib AES implementation. +// Creates a cipher block chaining (CBC) mode decryptor. +// +// The user must supply an initialization vector (IV) equal in length to the +// block size of the underlying [[block]] cipher, and a temporary state buffer +// whose size is equal to the block size times two. The module providing the +// underlying block cipher usually provides constants which define the lengths +// of these buffers for static allocation. export fn cbc_decryptor(b: *block, iv: []u8, buf: []u8) cbc_mode = { - assert(len(iv) == sz(b), "len(iv) must be the same as block length sz"); - assert(len(buf) == sz(b) * 2, + assert(len(iv) == blocksz(b), + "len(iv) must be the same as block length sz"); + assert(len(buf) == blocksz(b) * 2, "buffer needs to be two times of the block size"); - let bsz = sz(b); + const bsz = blocksz(b); let carry = buf[0..bsz]; carry[..] = iv[..]; @@ -53,8 +61,7 @@ export fn cbc_decryptor(b: *block, iv: []u8, buf: []u8) cbc_mode = { // In place encryption only works if dest and src point exactly at the // same range. export fn cbc_encrypt(c: *cbc_mode, dest: []u8, src: []u8) void = { - let sz = sz(c.b); - + const sz = blocksz(c.b); assert(c.encrypt); assert(len(dest) % sz == 0 && len(src) == len(dest), "size of dest and src needs to match and be a multiple of block size"); @@ -71,8 +78,7 @@ export fn cbc_encrypt(c: *cbc_mode, dest: []u8, src: []u8) void = { // In place decryption only works if dest and src point exactly at the // same range. export fn cbc_decrypt(c: *cbc_mode, dest: []u8, src: []u8) void = { - let sz = sz(c.b); - + const sz = blocksz(c.b); assert(!c.encrypt); assert(len(dest) % sz == 0 && len(src) == len(dest), "size of dest and src needs to match and be a multiple of block size"); diff --git a/crypto/cipher/cipher.ha b/crypto/cipher/cipher.ha @@ -1,23 +0,0 @@ -// An abstract interface for implementing block ciphers. -export type block = struct { - sz: size, - - nparallel: size, - - encrypt: *fn(b: *block, dest: []u8, src: []u8) void, - decrypt: *fn(b: *block, dest: []u8, src: []u8) void, -}; - - // Returns the blocksize in bytes -export fn sz(b: *block) size = b.sz; - -// The number of blocks that can be processed at once at encrypt or decrypt -export fn nparallel(b: *block) size = b.nparallel; - -// Encrypt up to nparallel blocks of size sz of src data and write it to dest -export fn encrypt(b: *block, dest: []u8, src: []u8) void = - b.encrypt(b, dest, src); - -// Decrypt up to nparallel blocks of size sz of src data and write it to dest -export fn decrypt(b: *block, dest: []u8, src: []u8) void = - b.decrypt(b, dest, src); diff --git a/crypto/cipher/ctr.ha b/crypto/cipher/ctr.ha @@ -8,15 +8,18 @@ export type ctr_stream = struct { xorused: size, }; -// Creates a ctr [[stream]] on top of given block cipher. The caller must -// provide the initiation vector and a buffer for the state. The size of -// the buffer depends on the block cipher. Use [[crypto::aes::CTR_BUFSIZE]] -// for the stdlib AES implementation. +// Creates a counter mode stream (CTR). +// +// The user must supply an initialization vector (IV) equal in length to the +// block size of the underlying [[block]] cipher, and a temporary state buffer +// whose size is equal to the block size times two. The module providing the +// underlying block cipher usually provides constants which define the lengths +// of these buffers for static allocation. export fn ctr(b: *block, iv: []u8, buf: []u8) ctr_stream = { - assert(len(iv) == sz(b), "iv is of invalid block size"); - assert(len(buf) >= sz(b) * 2, "buf must be at least 2 * blocksize"); + assert(len(iv) == blocksz(b), "iv is of invalid block size"); + assert(len(buf) >= blocksz(b) * 2, "buf must be at least 2 * blocksize"); - let bsz = sz(b); + const bsz = blocksz(b); // one buf block is used for the counter let counter = buf[0..bsz]; @@ -30,10 +33,10 @@ export fn ctr(b: *block, iv: []u8, buf: []u8) ctr_stream = { counter[..] = iv[..]; // cap the buffer to a multiple of bsz. - let maxxorbufsz = sz(b) * nparallel(b); + let maxxorbufsz = blocksz(b) * nparallel(b); let xorbufsz = len(xorbuf); if (xorbufsz < maxxorbufsz) { - xorbufsz = xorbufsz - xorbufsz % sz(b); + xorbufsz = xorbufsz - xorbufsz % blocksz(b); } else { xorbufsz = maxxorbufsz; }; @@ -50,8 +53,8 @@ export fn ctr(b: *block, iv: []u8, buf: []u8) ctr_stream = { fn ctr_stream_xor(s: *stream, dest: []u8, src: []u8) void = { let ctr = s: *ctr_stream; - let bsz = sz(ctr.b); - let nparallel = nparallel(ctr.b); + const bsz = blocksz(ctr.b); + const nparallel = nparallel(ctr.b); let i = 0z; for (true) { @@ -68,7 +71,7 @@ fn ctr_stream_xor(s: *stream, dest: []u8, src: []u8) void = { }; fn fill_xorbuf(ctr: *ctr_stream) void = { - let bsz = sz(ctr.b); + const bsz = blocksz(ctr.b); for (let i = 0z; i < len(ctr.xorbuf) / bsz; i += 1) { ctr.xorbuf[i * bsz..(i * bsz + bsz)] = ctr.counter[0..bsz]; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -224,11 +224,11 @@ crypto_blake2b() { crypto_cipher() { gen_srcs crypto::cipher \ - cipher.ha \ - stream.ha \ + block.ha \ cbc.ha \ - ctr.ha - gen_ssa crypto::cipher bytes crypto::math + ctr.ha \ + stream.ha + gen_ssa crypto::cipher crypto::math } crypto_hmac() { diff --git a/stdlib.mk b/stdlib.mk @@ -678,12 +678,12 @@ $(HARECACHE)/crypto/blake2b/crypto_blake2b-any.ssa: $(stdlib_crypto_blake2b_any_ # crypto::cipher (+any) stdlib_crypto_cipher_any_srcs= \ - $(STDLIB)/crypto/cipher/cipher.ha \ - $(STDLIB)/crypto/cipher/stream.ha \ + $(STDLIB)/crypto/cipher/block.ha \ $(STDLIB)/crypto/cipher/cbc.ha \ - $(STDLIB)/crypto/cipher/ctr.ha + $(STDLIB)/crypto/cipher/ctr.ha \ + $(STDLIB)/crypto/cipher/stream.ha -$(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM)) +$(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_srcs) $(stdlib_rt) $(stdlib_crypto_math_$(PLATFORM)) @printf 'HAREC \t$@\n' @mkdir -p $(HARECACHE)/crypto/cipher @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::cipher \ @@ -2402,12 +2402,12 @@ $(TESTCACHE)/crypto/blake2b/crypto_blake2b-any.ssa: $(testlib_crypto_blake2b_any # crypto::cipher (+any) testlib_crypto_cipher_any_srcs= \ - $(STDLIB)/crypto/cipher/cipher.ha \ - $(STDLIB)/crypto/cipher/stream.ha \ + $(STDLIB)/crypto/cipher/block.ha \ $(STDLIB)/crypto/cipher/cbc.ha \ - $(STDLIB)/crypto/cipher/ctr.ha + $(STDLIB)/crypto/cipher/ctr.ha \ + $(STDLIB)/crypto/cipher/stream.ha -$(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) +$(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_srcs) $(testlib_rt) $(testlib_crypto_math_$(PLATFORM)) @printf 'HAREC \t$@\n' @mkdir -p $(TESTCACHE)/crypto/cipher @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::cipher \