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:
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 \