2
0
Эх сурвалжийг харах

core/crypto: Switch to using `ensure`

Yawning Angel 7 сар өмнө
parent
commit
2f301e46dc
36 өөрчлөгдсөн 188 нэмэгдсэн , 363 устгасан
  1. 1 3
      core/crypto/_aes/ct64/ct64_enc.odin
  2. 2 2
      core/crypto/_aes/ct64/ct64_keysched.odin
  3. 2 3
      core/crypto/_aes/ct64/ghash.odin
  4. 6 18
      core/crypto/_aes/ct64/helpers.odin
  5. 11 17
      core/crypto/_blake2/blake2.odin
  6. 11 14
      core/crypto/_chacha20/chacha20.odin
  7. 2 6
      core/crypto/_edwards25519/edwards25519.odin
  8. 2 6
      core/crypto/_edwards25519/edwards25519_scalar.odin
  9. 1 3
      core/crypto/_fiat/field_poly1305/field.odin
  10. 3 6
      core/crypto/_fiat/field_scalar25519/field.odin
  11. 15 18
      core/crypto/_sha3/sha3.odin
  12. 9 11
      core/crypto/_sha3/sp800_185.odin
  13. 3 9
      core/crypto/aead/low_level.odin
  14. 4 8
      core/crypto/aes/aes_ctr.odin
  15. 6 10
      core/crypto/aes/aes_ecb.odin
  16. 10 26
      core/crypto/aes/aes_gcm.odin
  17. 1 1
      core/crypto/aes/aes_gcm_hw_intel.odin
  18. 2 3
      core/crypto/blake2b/blake2b.odin
  19. 2 3
      core/crypto/blake2s/blake2s.odin
  20. 5 11
      core/crypto/chacha20/chacha20.odin
  21. 8 20
      core/crypto/chacha20poly1305/chacha20poly1305.odin
  22. 9 24
      core/crypto/deoxysii/deoxysii.odin
  23. 8 24
      core/crypto/ed25519/ed25519.odin
  24. 5 8
      core/crypto/hmac/hmac.odin
  25. 3 9
      core/crypto/kmac/kmac.odin
  26. 9 9
      core/crypto/legacy/keccak/keccak.odin
  27. 3 6
      core/crypto/legacy/md5/md5.odin
  28. 3 6
      core/crypto/legacy/sha1/sha1.odin
  29. 4 8
      core/crypto/poly1305/poly1305.odin
  30. 16 22
      core/crypto/ristretto255/ristretto255.odin
  31. 2 6
      core/crypto/ristretto255/ristretto255_scalar.odin
  32. 4 7
      core/crypto/sha2/sha2.odin
  33. 6 11
      core/crypto/siphash/siphash.odin
  34. 4 7
      core/crypto/sm3/sm3.odin
  35. 3 9
      core/crypto/x25519/x25519.odin
  36. 3 9
      core/crypto/x448/x448.odin

+ 1 - 3
core/crypto/_aes/ct64/ct64_enc.odin

@@ -23,9 +23,7 @@
 package aes_ct64
 package aes_ct64
 
 
 add_round_key :: proc "contextless" (q: ^[8]u64, sk: []u64) #no_bounds_check {
 add_round_key :: proc "contextless" (q: ^[8]u64, sk: []u64) #no_bounds_check {
-	if len(sk) < 8 {
-		panic_contextless("aes/ct64: invalid round key size")
-	}
+	ensure_contextless(len(sk) >= 8, "aes/ct64: invalid round key size")
 
 
 	q[0] ~= sk[0]
 	q[0] ~= sk[0]
 	q[1] ~= sk[1]
 	q[1] ~= sk[1]

+ 2 - 2
core/crypto/_aes/ct64/ct64_keysched.odin

@@ -41,7 +41,7 @@ sub_word :: proc "contextless" (x: u32) -> u32 {
 }
 }
 
 
 @(private, require_results)
 @(private, require_results)
-keysched :: proc(comp_skey: []u64, key: []byte) -> int {
+keysched :: proc "contextless" (comp_skey: []u64, key: []byte) -> int {
 	num_rounds, key_len := 0, len(key)
 	num_rounds, key_len := 0, len(key)
 	switch key_len {
 	switch key_len {
 	case _aes.KEY_SIZE_128:
 	case _aes.KEY_SIZE_128:
@@ -51,7 +51,7 @@ keysched :: proc(comp_skey: []u64, key: []byte) -> int {
 	case _aes.KEY_SIZE_256:
 	case _aes.KEY_SIZE_256:
 		num_rounds = _aes.ROUNDS_256
 		num_rounds = _aes.ROUNDS_256
 	case:
 	case:
-		panic("crypto/aes: invalid AES key size")
+		panic_contextless("crypto/aes: invalid AES key size")
 	}
 	}
 
 
 	skey: [60]u32 = ---
 	skey: [60]u32 = ---

+ 2 - 3
core/crypto/_aes/ct64/ghash.odin

@@ -63,9 +63,8 @@ rev64 :: proc "contextless" (x: u64) -> u64 {
 // Note: `dst` is both an input and an output, to support easy implementation
 // Note: `dst` is both an input and an output, to support easy implementation
 // of GCM.
 // of GCM.
 ghash :: proc "contextless" (dst, key, data: []byte) {
 ghash :: proc "contextless" (dst, key, data: []byte) {
-	if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
-		panic_contextless("aes/ghash: invalid dst or key size")
-	}
+	ensure_contextless(len(dst) == _aes.GHASH_BLOCK_SIZE)
+	ensure_contextless(len(key) == _aes.GHASH_BLOCK_SIZE)
 
 
 	buf := data
 	buf := data
 	l := len(buf)
 	l := len(buf)

+ 6 - 18
core/crypto/_aes/ct64/helpers.odin

@@ -31,41 +31,31 @@ and_interleaved :: #force_inline proc "contextless" (a0, a1, b0, b1: u64) -> (u6
 }
 }
 
 
 load_blockx1 :: proc "contextless" (q: ^[8]u64, src: []byte) {
 load_blockx1 :: proc "contextless" (q: ^[8]u64, src: []byte) {
-	if len(src) != _aes.BLOCK_SIZE {
-		panic_contextless("aes/ct64: invalid block size")
-	}
+	ensure_contextless(len(src) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
 
 
 	q[0], q[4] = #force_inline load_interleaved(src)
 	q[0], q[4] = #force_inline load_interleaved(src)
 	orthogonalize(q)
 	orthogonalize(q)
 }
 }
 
 
 store_blockx1 :: proc "contextless" (dst: []byte, q: ^[8]u64) {
 store_blockx1 :: proc "contextless" (dst: []byte, q: ^[8]u64) {
-	if len(dst) != _aes.BLOCK_SIZE {
-		panic_contextless("aes/ct64: invalid block size")
-	}
+	ensure_contextless(len(dst) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
 
 
 	orthogonalize(q)
 	orthogonalize(q)
 	#force_inline store_interleaved(dst, q[0], q[4])
 	#force_inline store_interleaved(dst, q[0], q[4])
 }
 }
 
 
 load_blocks :: proc "contextless" (q: ^[8]u64, src: [][]byte) {
 load_blocks :: proc "contextless" (q: ^[8]u64, src: [][]byte) {
-	if n := len(src); n > STRIDE || n == 0 {
-		panic_contextless("aes/ct64: invalid block(s) size")
-	}
+	ensure_contextless(len(src) == 0 || len(src) <= STRIDE, "aes/ct64: invalid block(s) size")
 
 
 	for s, i in src {
 	for s, i in src {
-		if len(s) != _aes.BLOCK_SIZE {
-			panic_contextless("aes/ct64: invalid block size")
-		}
+		ensure_contextless(len(s) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
 		q[i], q[i + 4] = #force_inline load_interleaved(s)
 		q[i], q[i + 4] = #force_inline load_interleaved(s)
 	}
 	}
 	orthogonalize(q)
 	orthogonalize(q)
 }
 }
 
 
 store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
 store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
-	if n := len(dst); n > STRIDE || n == 0 {
-		panic_contextless("aes/ct64: invalid block(s) size")
-	}
+	ensure_contextless(len(dst) == 0 || len(dst) <= STRIDE, "aes/ct64: invalid block(s) size")
 
 
 	orthogonalize(q)
 	orthogonalize(q)
 	for d, i in dst {
 	for d, i in dst {
@@ -73,9 +63,7 @@ store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
 		if d == nil {
 		if d == nil {
 			break
 			break
 		}
 		}
-		if len(d) != _aes.BLOCK_SIZE {
-			panic_contextless("aes/ct64: invalid block size")
-		}
+		ensure_contextless(len(d) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
 		#force_inline store_interleaved(d, q[i], q[i + 4])
 		#force_inline store_interleaved(d, q[i], q[i + 4])
 	}
 	}
 }
 }

+ 11 - 17
core/crypto/_blake2/blake2.odin

@@ -18,6 +18,8 @@ BLAKE2S_SIZE :: 32
 BLAKE2B_BLOCK_SIZE :: 128
 BLAKE2B_BLOCK_SIZE :: 128
 BLAKE2B_SIZE :: 64
 BLAKE2B_SIZE :: 64
 
 
+MAX_SIZE :: 255
+
 Blake2s_Context :: struct {
 Blake2s_Context :: struct {
 	h:            [8]u32,
 	h:            [8]u32,
 	t:            [2]u32,
 	t:            [2]u32,
@@ -82,16 +84,13 @@ BLAKE2B_IV := [8]u64 {
 	0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
 	0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
 }
 }
 
 
-init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
+init :: proc "contextless" (ctx: ^$T, cfg: ^Blake2_Config) {
 	when T == Blake2s_Context {
 	when T == Blake2s_Context {
 		max_size :: BLAKE2S_SIZE
 		max_size :: BLAKE2S_SIZE
 	} else when T == Blake2b_Context {
 	} else when T == Blake2b_Context {
 		max_size :: BLAKE2B_SIZE
 		max_size :: BLAKE2B_SIZE
 	}
 	}
-
-	if cfg.size > max_size {
-		panic("blake2: requested output size exceeeds algorithm max")
-	}
+	ensure_contextless(cfg.size <= max_size, "blake2: requested output size exceeeds algorithm max")
 
 
 	// To save having to allocate a scratch buffer, use the internal
 	// To save having to allocate a scratch buffer, use the internal
 	// data buffer (`ctx.x`), as it is exactly the correct size.
 	// data buffer (`ctx.x`), as it is exactly the correct size.
@@ -167,8 +166,8 @@ init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
 	ctx.is_initialized = true
 	ctx.is_initialized = true
 }
 }
 
 
-update :: proc(ctx: ^$T, p: []byte) {
-	assert(ctx.is_initialized)
+update :: proc "contextless" (ctx: ^$T, p: []byte) {
+	ensure_contextless(ctx.is_initialized)
 
 
 	p := p
 	p := p
 	when T == Blake2s_Context {
 	when T == Blake2s_Context {
@@ -195,8 +194,8 @@ update :: proc(ctx: ^$T, p: []byte) {
 	ctx.nx += copy(ctx.x[ctx.nx:], p)
 	ctx.nx += copy(ctx.x[ctx.nx:], p)
 }
 }
 
 
-final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
-	assert(ctx.is_initialized)
+final :: proc "contextless" (ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
+	ensure_contextless(ctx.is_initialized)
 
 
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {
@@ -206,24 +205,19 @@ final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
 	}
 	}
 	defer(reset(ctx))
 	defer(reset(ctx))
 
 
+	ensure_contextless(len(hash) >= int(ctx.size), "crypto/blake2: invalid destination digest size")
 	when T == Blake2s_Context {
 	when T == Blake2s_Context {
-		if len(hash) < int(ctx.size) {
-			panic("crypto/blake2s: invalid destination digest size")
-		}
 		blake2s_final(ctx, hash)
 		blake2s_final(ctx, hash)
 	} else when T == Blake2b_Context {
 	} else when T == Blake2b_Context {
-		if len(hash) < int(ctx.size) {
-			panic("crypto/blake2b: invalid destination digest size")
-		}
 		blake2b_final(ctx, hash)
 		blake2b_final(ctx, hash)
 	}
 	}
 }
 }
 
 
-clone :: proc(ctx, other: ^$T) {
+clone :: proc "contextless" (ctx, other: ^$T) {
 	ctx^ = other^
 	ctx^ = other^
 }
 }
 
 
-reset :: proc(ctx: ^$T) {
+reset :: proc "contextless" (ctx: ^$T) {
 	if !ctx.is_initialized {
 	if !ctx.is_initialized {
 		return
 		return
 	}
 	}

+ 11 - 14
core/crypto/_chacha20/chacha20.odin

@@ -45,9 +45,8 @@ Context :: struct {
 // derivation is expected to be handled by the caller, so that the
 // derivation is expected to be handled by the caller, so that the
 // HChaCha call can be suitably accelerated.
 // HChaCha call can be suitably accelerated.
 init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
 init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
-	if len(key) != KEY_SIZE || len(iv) != IV_SIZE {
-		panic_contextless("chacha20: invalid key or IV size")
-	}
+	ensure_contextless(len(key) == KEY_SIZE, "chacha20: invalid key size")
+	ensure_contextless(len(iv) == IV_SIZE, "chacha20: invalid key size")
 
 
 	k, n := key, iv
 	k, n := key, iv
 
 
@@ -75,12 +74,10 @@ init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
 
 
 // seek seeks the (X)ChaCha20 stream counter to the specified block.
 // seek seeks the (X)ChaCha20 stream counter to the specified block.
 seek :: proc(ctx: ^Context, block_nr: u64) {
 seek :: proc(ctx: ^Context, block_nr: u64) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	if ctx._is_ietf_flavor {
 	if ctx._is_ietf_flavor {
-		if block_nr > MAX_CTR_IETF {
-			panic("crypto/chacha20: attempted to seek past maximum counter")
-		}
+		ensure(block_nr <= MAX_CTR_IETF, "crypto/chacha20: attempted to seek past maximum counter")
 	} else {
 	} else {
 		ctx._s[13] = u32(block_nr >> 32)
 		ctx._s[13] = u32(block_nr >> 32)
 	}
 	}
@@ -101,7 +98,7 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
 	// Enforce the maximum consumed keystream per IV.
 	// Enforce the maximum consumed keystream per IV.
 	//
 	//
 	// While all modern "standard" definitions of ChaCha20 use
 	// While all modern "standard" definitions of ChaCha20 use
-	// the IETF 32-bit counter, for XChaCha20 most common
+	// the IETF 32-bit counter, for XChaCha20 historical
 	// implementations allow for a 64-bit counter.
 	// implementations allow for a 64-bit counter.
 	//
 	//
 	// Honestly, the answer here is "use a MRAE primitive", but
 	// Honestly, the answer here is "use a MRAE primitive", but
@@ -109,14 +106,14 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
 
 
 	ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
 	ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
 
 
+	ctr_ok: bool
 	if ctx._is_ietf_flavor {
 	if ctx._is_ietf_flavor {
-		if u64(ctx._s[12]) + u64(nr_blocks) > MAX_CTR_IETF {
-			panic(ERR_CTR_EXHAUSTED)
-		}
+		ctr_ok = u64(ctx._s[12]) + u64(nr_blocks) <= MAX_CTR_IETF
 	} else {
 	} else {
 		ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
 		ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
-		if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
-			panic(ERR_CTR_EXHAUSTED)
-		}
+		_, carry := bits.add_u64(ctr, u64(nr_blocks), 0)
+		ctr_ok = carry == 0
 	}
 	}
+
+	ensure(ctr_ok, "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached")
 }
 }

+ 2 - 6
core/crypto/_edwards25519/edwards25519.odin

@@ -108,9 +108,7 @@ ge_set :: proc "contextless" (ge, a: ^Group_Element) {
 
 
 @(require_results)
 @(require_results)
 ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
 ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
-	if len(b) != 32 {
-		panic_contextless("edwards25519: invalid group element size")
-	}
+	ensure_contextless(len(b) == 32, "edwards25519: invalid group element size")
 	b_ := (^[32]byte)(raw_data(b))
 	b_ := (^[32]byte)(raw_data(b))
 
 
 	// Do the work in a scratch element, so that ge is unchanged on
 	// Do the work in a scratch element, so that ge is unchanged on
@@ -167,9 +165,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
 }
 }
 
 
 ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
 ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
-	if len(dst) != 32 {
-		panic_contextless("edwards25519: invalid group element size")
-	}
+	ensure_contextless(len(dst) == 32, "edwards25519: invalid group element size")
 	dst_ := (^[32]byte)(raw_data(dst))
 	dst_ := (^[32]byte)(raw_data(dst))
 
 
 	// Convert the element to affine (x, y) representation.
 	// Convert the element to affine (x, y) representation.

+ 2 - 6
core/crypto/_edwards25519/edwards25519_scalar.odin

@@ -24,17 +24,13 @@ sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
 
 
 @(require_results)
 @(require_results)
 sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
 sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
-	if len(b) != 32 {
-		panic_contextless("edwards25519: invalid scalar size")
-	}
+	ensure_contextless(len(b) == 32, "edwards25519: invalid scalar size")
 	b_ := (^[32]byte)(raw_data(b))
 	b_ := (^[32]byte)(raw_data(b))
 	return field.fe_from_bytes(sc, b_)
 	return field.fe_from_bytes(sc, b_)
 }
 }
 
 
 sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
 sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
-	if len(b) != 32 {
-		panic_contextless("edwards25519: invalid scalar size")
-	}
+	ensure_contextless(len(b) == 32, "edwards25519: invalid scalar size")
 	b_ := (^[32]byte)(raw_data(b))
 	b_ := (^[32]byte)(raw_data(b))
 	field.fe_from_bytes_rfc8032(sc, b_)
 	field.fe_from_bytes_rfc8032(sc, b_)
 }
 }

+ 1 - 3
core/crypto/_fiat/field_poly1305/field.odin

@@ -28,9 +28,7 @@ fe_from_bytes :: #force_inline proc "contextless" (
 	// makes implementing the actual MAC block processing considerably
 	// makes implementing the actual MAC block processing considerably
 	// neater.
 	// neater.
 
 
-	if len(arg1) != 16 {
-		panic_contextless("poly1305: invalid field element size")
-	}
+	ensure_contextless(len(arg1) == 16, "poly1305: invalid field element size")
 
 
 	// While it may be unwise to do deserialization here on our
 	// While it may be unwise to do deserialization here on our
 	// own when fiat-crypto provides equivalent functionality,
 	// own when fiat-crypto provides equivalent functionality,

+ 3 - 6
core/crypto/_fiat/field_scalar25519/field.odin

@@ -94,9 +94,8 @@ fe_from_bytes_wide :: proc "contextless" (
 @(private)
 @(private)
 _fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
 _fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
 	// INVARIANT: len(arg1) < 32.
 	// INVARIANT: len(arg1) < 32.
-	if len(arg1) >= 32 {
-		panic_contextless("edwards25519: oversized short scalar")
-	}
+	ensure_contextless(len(arg1) < 32, "edwards25519: oversized short scalar")
+
 	tmp: [32]byte
 	tmp: [32]byte
 	copy(tmp[:], arg1)
 	copy(tmp[:], arg1)
 
 
@@ -105,9 +104,7 @@ _fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Eleme
 }
 }
 
 
 fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
 fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
-	if len(out1) != 32 {
-		panic_contextless("edwards25519: oversized scalar output buffer")
-	}
+	ensure_contextless(len(out1) == 32, "edwards25519: oversized scalar output buffer")
 
 
 	tmp: Non_Montgomery_Domain_Field_Element
 	tmp: Non_Montgomery_Domain_Field_Element
 	fe_from_montgomery(&tmp, arg1)
 	fe_from_montgomery(&tmp, arg1)

+ 15 - 18
core/crypto/_sha3/sha3.odin

@@ -122,7 +122,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
 	}
 	}
 }
 }
 
 
-init :: proc(ctx: ^Context) {
+init :: proc "contextless" (ctx: ^Context) {
 	for i := 0; i < 25; i += 1 {
 	for i := 0; i < 25; i += 1 {
 		ctx.st.q[i] = 0
 		ctx.st.q[i] = 0
 	}
 	}
@@ -133,9 +133,9 @@ init :: proc(ctx: ^Context) {
 	ctx.is_finalized = false
 	ctx.is_finalized = false
 }
 }
 
 
-update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx.is_initialized)
-	assert(!ctx.is_finalized)
+update :: proc "contextless" (ctx: ^Context, data: []byte) {
+	ensure_contextless(ctx.is_initialized)
+	ensure_contextless(!ctx.is_finalized)
 
 
 	j := ctx.pt
 	j := ctx.pt
 	for i := 0; i < len(data); i += 1 {
 	for i := 0; i < len(data); i += 1 {
@@ -149,12 +149,9 @@ update :: proc(ctx: ^Context, data: []byte) {
 	ctx.pt = j
 	ctx.pt = j
 }
 }
 
 
-final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
-	assert(ctx.is_initialized)
-
-	if len(hash) < ctx.mdlen {
-		panic("crypto/sha3: invalid destination digest size")
-	}
+final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
+	ensure_contextless(ctx.is_initialized)
+	ensure_contextless(len(hash) >= ctx.mdlen, "crypto/sha3: invalid destination digest size")
 
 
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {
@@ -173,11 +170,11 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 	}
 	}
 }
 }
 
 
-clone :: proc(ctx, other: ^Context) {
+clone :: proc "contextless" (ctx, other: ^Context) {
 	ctx^ = other^
 	ctx^ = other^
 }
 }
 
 
-reset :: proc(ctx: ^Context) {
+reset :: proc "contextless" (ctx: ^Context) {
 	if !ctx.is_initialized {
 	if !ctx.is_initialized {
 		return
 		return
 	}
 	}
@@ -185,9 +182,9 @@ reset :: proc(ctx: ^Context) {
 	mem.zero_explicit(ctx, size_of(ctx^))
 	mem.zero_explicit(ctx, size_of(ctx^))
 }
 }
 
 
-shake_xof :: proc(ctx: ^Context) {
-	assert(ctx.is_initialized)
-	assert(!ctx.is_finalized)
+shake_xof :: proc "contextless" (ctx: ^Context) {
+	ensure_contextless(ctx.is_initialized)
+	ensure_contextless(!ctx.is_finalized)
 
 
 	ctx.st.b[ctx.pt] ~= ctx.dsbyte
 	ctx.st.b[ctx.pt] ~= ctx.dsbyte
 	ctx.st.b[ctx.rsiz - 1] ~= 0x80
 	ctx.st.b[ctx.rsiz - 1] ~= 0x80
@@ -197,9 +194,9 @@ shake_xof :: proc(ctx: ^Context) {
 	ctx.is_finalized = true // No more absorb, unlimited squeeze.
 	ctx.is_finalized = true // No more absorb, unlimited squeeze.
 }
 }
 
 
-shake_out :: proc(ctx: ^Context, hash: []byte) {
-	assert(ctx.is_initialized)
-	assert(ctx.is_finalized)
+shake_out :: proc "contextless" (ctx: ^Context, hash: []byte) {
+	ensure_contextless(ctx.is_initialized)
+	ensure_contextless(ctx.is_finalized)
 
 
 	j := ctx.pt
 	j := ctx.pt
 	for i := 0; i < len(hash); i += 1 {
 	for i := 0; i < len(hash); i += 1 {

+ 9 - 11
core/crypto/_sha3/sp800_185.odin

@@ -3,7 +3,7 @@ package _sha3
 import "core:encoding/endian"
 import "core:encoding/endian"
 import "core:math/bits"
 import "core:math/bits"
 
 
-init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
+init_cshake :: proc "contextless" (ctx: ^Context, n, s: []byte, sec_strength: int) {
 	ctx.mdlen = sec_strength / 8
 	ctx.mdlen = sec_strength / 8
 
 
 	// No domain separator is equivalent to vanilla SHAKE.
 	// No domain separator is equivalent to vanilla SHAKE.
@@ -18,7 +18,7 @@ init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
 	bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
 	bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
 }
 }
 
 
-final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
+final_cshake :: proc "contextless" (ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {
 		tmp_ctx: Context
 		tmp_ctx: Context
@@ -32,7 +32,7 @@ final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
 	shake_out(ctx, dst)
 	shake_out(ctx, dst)
 }
 }
 
 
-rate_cshake :: #force_inline proc(sec_strength: int) -> int {
+rate_cshake :: #force_inline proc "contextless" (sec_strength: int) -> int {
 	switch sec_strength {
 	switch sec_strength {
 	case 128:
 	case 128:
 		return RATE_128
 		return RATE_128
@@ -40,7 +40,7 @@ rate_cshake :: #force_inline proc(sec_strength: int) -> int {
 		return RATE_256
 		return RATE_256
 	}
 	}
 
 
-	panic("crypto/sha3: invalid security strength")
+	panic_contextless("crypto/sha3: invalid security strength")
 }
 }
 
 
 // right_encode and left_encode are defined to support 0 <= x < 2^2040
 // right_encode and left_encode are defined to support 0 <= x < 2^2040
@@ -55,7 +55,7 @@ rate_cshake :: #force_inline proc(sec_strength: int) -> int {
 @(private, rodata)
 @(private, rodata)
 _PAD: [RATE_128]byte // Biggest possible value of w per spec.
 _PAD: [RATE_128]byte // Biggest possible value of w per spec.
 
 
-bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
+bytepad :: proc "contextless" (ctx: ^Context, x_strings: [][]byte, w: int) {
 	// 1. z = left_encode(w) || X.
 	// 1. z = left_encode(w) || X.
 	z_hi: u64
 	z_hi: u64
 	z_lo := left_right_encode(ctx, 0, u64(w), true)
 	z_lo := left_right_encode(ctx, 0, u64(w), true)
@@ -70,9 +70,7 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
 
 
 		// This isn't actually possible, at least with the currently
 		// This isn't actually possible, at least with the currently
 		// defined SP 800-185 routines.
 		// defined SP 800-185 routines.
-		if carry != 0 {
-			panic("crypto/sha3: bytepad input length overflow")
-		}
+		ensure_contextless(carry == 0, "crypto/sha3: bytepad input length overflow")
 	}
 	}
 
 
 	// We skip this step as we are doing a byte-oriented implementation
 	// We skip this step as we are doing a byte-oriented implementation
@@ -95,7 +93,7 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
 	}
 	}
 }
 }
 
 
-encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
+encode_string :: #force_inline proc "contextless" (ctx: ^Context, s: []byte) -> (u64, u64) {
 	l := encode_byte_len(ctx, len(s), true) // left_encode
 	l := encode_byte_len(ctx, len(s), true) // left_encode
 	update(ctx, s)
 	update(ctx, s)
 
 
@@ -104,13 +102,13 @@ encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
 	return hi, lo
 	return hi, lo
 }
 }
 
 
-encode_byte_len :: #force_inline proc(ctx: ^Context, l: int, is_left: bool) -> u64 {
+encode_byte_len :: #force_inline proc "contextless" (ctx: ^Context, l: int, is_left: bool) -> u64 {
 	hi, lo := bits.mul_u64(u64(l), 8)
 	hi, lo := bits.mul_u64(u64(l), 8)
 	return left_right_encode(ctx, hi, lo, is_left)
 	return left_right_encode(ctx, hi, lo, is_left)
 }
 }
 
 
 @(private)
 @(private)
-left_right_encode :: proc(ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
+left_right_encode :: proc "contextless" (ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
 	HI_OFFSET :: 1
 	HI_OFFSET :: 1
 	LO_OFFSET :: HI_OFFSET + 8
 	LO_OFFSET :: HI_OFFSET + 8
 	RIGHT_OFFSET :: LO_OFFSET + 8
 	RIGHT_OFFSET :: LO_OFFSET + 8

+ 3 - 9
core/crypto/aead/low_level.odin

@@ -128,9 +128,7 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
 		reset(ctx)
 		reset(ctx)
 	}
 	}
 
 
-	if len(key) != KEY_SIZES[algorithm] {
-		panic("crypto/aead: invalid key size")
-	}
+	ensure(len(key) == KEY_SIZES[algorithm], "crypto/aead: invalid key size")
 
 
 	// Directly specialize the union by setting the type ID (save a copy).
 	// Directly specialize the union by setting the type ID (save a copy).
 	reflect.set_union_variant_typeid(
 	reflect.set_union_variant_typeid(
@@ -167,9 +165,7 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
 //
 //
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
 seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
-	if len(tag) != TAG_SIZES[ctx._algo] {
-		panic("crypto/aead: invalid tag size")
-	}
+	ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
 
 
 	switch &impl in ctx._impl {
 	switch &impl in ctx._impl {
 	case aes.Context_GCM:
 	case aes.Context_GCM:
@@ -193,9 +189,7 @@ seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 @(require_results)
 @(require_results)
 open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
 open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
-	if len(tag) != TAG_SIZES[ctx._algo] {
-		panic("crypto/aead: invalid tag size")
-	}
+	ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
 
 
 	switch &impl in ctx._impl {
 	switch &impl in ctx._impl {
 	case aes.Context_GCM:
 	case aes.Context_GCM:

+ 4 - 8
core/crypto/aes/aes_ctr.odin

@@ -21,9 +21,7 @@ Context_CTR :: struct {
 
 
 // init_ctr initializes a Context_CTR with the provided key and IV.
 // init_ctr initializes a Context_CTR with the provided key and IV.
 init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
 init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
-	if len(iv) != CTR_IV_SIZE {
-		panic("crypto/aes: invalid CTR IV size")
-	}
+	ensure(len(iv) == CTR_IV_SIZE, "crypto/aes: invalid CTR IV size")
 
 
 	init_impl(&ctx._impl, key, impl)
 	init_impl(&ctx._impl, key, impl)
 	ctx._off = BLOCK_SIZE
 	ctx._off = BLOCK_SIZE
@@ -36,16 +34,14 @@ init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTAT
 // keystream, and writes the resulting output to dst.  dst and src MUST
 // keystream, and writes the resulting output to dst.  dst and src MUST
 // alias exactly or not at all.
 // alias exactly or not at all.
 xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
 xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	src, dst := src, dst
 	src, dst := src, dst
 	if dst_len := len(dst); dst_len < len(src) {
 	if dst_len := len(dst); dst_len < len(src) {
 		src = src[:dst_len]
 		src = src[:dst_len]
 	}
 	}
 
 
-	if bytes.alias_inexactly(dst, src) {
-		panic("crypto/aes: dst and src alias inexactly")
-	}
+	ensure(!bytes.alias_inexactly(dst, src), "crypto/aes: dst and src alias inexactly")
 
 
 	#no_bounds_check for remaining := len(src); remaining > 0; {
 	#no_bounds_check for remaining := len(src); remaining > 0; {
 		// Process multiple blocks at once
 		// Process multiple blocks at once
@@ -82,7 +78,7 @@ xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
 
 
 // keystream_bytes_ctr fills dst with the raw AES-CTR keystream output.
 // keystream_bytes_ctr fills dst with the raw AES-CTR keystream output.
 keystream_bytes_ctr :: proc(ctx: ^Context_CTR, dst: []byte) {
 keystream_bytes_ctr :: proc(ctx: ^Context_CTR, dst: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	dst := dst
 	dst := dst
 	#no_bounds_check for remaining := len(dst); remaining > 0; {
 	#no_bounds_check for remaining := len(dst); remaining > 0; {

+ 6 - 10
core/crypto/aes/aes_ecb.odin

@@ -19,11 +19,9 @@ init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := DEFAULT_IMPLEMENTATION)
 
 
 // encrypt_ecb encrypts the BLOCK_SIZE buffer src, and writes the result to dst.
 // encrypt_ecb encrypts the BLOCK_SIZE buffer src, and writes the result to dst.
 encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
 encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
-	assert(ctx._is_initialized)
-
-	if len(dst) != BLOCK_SIZE || len(src) != BLOCK_SIZE {
-		panic("crypto/aes: invalid buffer size(s)")
-	}
+	ensure(ctx._is_initialized)
+	ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid dst size")
+	ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid src size")
 
 
 	switch &impl in ctx._impl {
 	switch &impl in ctx._impl {
 	case ct64.Context:
 	case ct64.Context:
@@ -35,11 +33,9 @@ encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
 
 
 // decrypt_ecb decrypts the BLOCK_SIZE buffer src, and writes the result to dst.
 // decrypt_ecb decrypts the BLOCK_SIZE buffer src, and writes the result to dst.
 decrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
 decrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
-	assert(ctx._is_initialized)
-
-	if len(dst) != BLOCK_SIZE || len(src) != BLOCK_SIZE {
-		panic("crypto/aes: invalid buffer size(s)")
-	}
+	ensure(ctx._is_initialized)
+	ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid dst size")
+	ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid src size")
 
 
 	switch &impl in ctx._impl {
 	switch &impl in ctx._impl {
 	case ct64.Context:
 	case ct64.Context:

+ 10 - 26
core/crypto/aes/aes_gcm.odin

@@ -36,15 +36,11 @@ init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := DEFAULT_IMPLEMENTATION)
 //
 //
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
 seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	gcm_validate_common_slice_sizes(tag, iv, aad, plaintext)
 	gcm_validate_common_slice_sizes(tag, iv, aad, plaintext)
-	if len(dst) != len(plaintext) {
-		panic("crypto/aes: invalid destination ciphertext size")
-	}
-	if bytes.alias_inexactly(dst, plaintext) {
-		panic("crypto/aes: dst and plaintext alias inexactly")
-	}
+	ensure(len(dst) == len(plaintext), "crypto/aes: invalid destination ciphertext size")
+	ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/aes: dst and plaintext alias inexactly")
 
 
 	if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
 	if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
 		gcm_seal_hw(&impl, dst, tag, iv, aad, plaintext)
 		gcm_seal_hw(&impl, dst, tag, iv, aad, plaintext)
@@ -76,15 +72,11 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 @(require_results)
 @(require_results)
 open_gcm :: proc(ctx: ^Context_GCM, dst, iv, aad, ciphertext, tag: []byte) -> bool {
 open_gcm :: proc(ctx: ^Context_GCM, dst, iv, aad, ciphertext, tag: []byte) -> bool {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	gcm_validate_common_slice_sizes(tag, iv, aad, ciphertext)
 	gcm_validate_common_slice_sizes(tag, iv, aad, ciphertext)
-	if len(dst) != len(ciphertext) {
-		panic("crypto/aes: invalid destination plaintext size")
-	}
-	if bytes.alias_inexactly(dst, ciphertext) {
-		panic("crypto/aes: dst and ciphertext alias inexactly")
-	}
+	ensure(len(dst) == len(ciphertext), "crypto/aes: invalid destination plaintext size")
+	ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/aes: dst and ciphertext alias inexactly")
 
 
 	if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
 	if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
 		return gcm_open_hw(&impl, dst, iv, aad, ciphertext, tag)
 		return gcm_open_hw(&impl, dst, iv, aad, ciphertext, tag)
@@ -122,21 +114,13 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
 
 
 @(private = "file")
 @(private = "file")
 gcm_validate_common_slice_sizes :: proc(tag, iv, aad, text: []byte) {
 gcm_validate_common_slice_sizes :: proc(tag, iv, aad, text: []byte) {
-	if len(tag) != GCM_TAG_SIZE {
-		panic("crypto/aes: invalid GCM tag size")
-	}
+	ensure(len(tag) == GCM_TAG_SIZE, "crypto/aes: invalid GCM tag size")
 
 
 	// The specification supports IVs in the range [1, 2^64) bits.
 	// The specification supports IVs in the range [1, 2^64) bits.
-	if l := len(iv); l == 0 || u64(l) >= GCM_IV_SIZE_MAX {
-		panic("crypto/aes: invalid GCM IV size")
-	}
+	ensure(len(iv) == 0 || u64(len(iv)) <= GCM_IV_SIZE_MAX, "crypto/aes: invalid GCM IV size")
 
 
-	if aad_len := u64(len(aad)); aad_len > GCM_A_MAX {
-		panic("crypto/aes: oversized GCM aad")
-	}
-	if text_len := u64(len(text)); text_len > GCM_P_MAX {
-		panic("crypto/aes: oversized GCM src data")
-	}
+	ensure(u64(len(aad)) <= GCM_A_MAX, "crypto/aes: oversized GCM aad")
+	ensure(u64(len(text)) <= GCM_P_MAX, "crypto/aes: oversized GCM data")
 }
 }
 
 
 @(private = "file")
 @(private = "file")

+ 1 - 1
core/crypto/aes/aes_gcm_hw_intel.odin

@@ -235,7 +235,7 @@ gctr_hw :: proc(
 // BUG: Sticking this in gctr_hw (like the other implementations) crashes
 // BUG: Sticking this in gctr_hw (like the other implementations) crashes
 // the compiler.
 // the compiler.
 //
 //
-// src/check_expr.cpp(7892): Assertion Failure: `c->curr_proc_decl->entity`
+// src/check_expr.cpp(8104): Assertion Failure: `c->curr_proc_decl->entity`
 @(private = "file", enable_target_feature = "sse4.1")
 @(private = "file", enable_target_feature = "sse4.1")
 hw_inc_ctr32 :: #force_inline proc "contextless" (src: ^x86.__m128i, ctr: u32) -> (x86.__m128i, u32) {
 hw_inc_ctr32 :: #force_inline proc "contextless" (src: ^x86.__m128i, ctr: u32) -> (x86.__m128i, u32) {
 	ret := x86._mm_insert_epi32(src^, i32(intrinsics.byte_swap(ctr)), 3)
 	ret := x86._mm_insert_epi32(src^, i32(intrinsics.byte_swap(ctr)), 3)

+ 2 - 3
core/crypto/blake2b/blake2b.odin

@@ -28,9 +28,8 @@ Context :: _blake2.Blake2b_Context
 
 
 // init initializes a Context with the default BLAKE2b config.
 // init initializes a Context with the default BLAKE2b config.
 init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
 init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
-	if digest_size > 255 {
-		panic("blake2b: invalid digest size")
-	}
+	ensure(digest_size <= _blake2.MAX_SIZE, "crypto/blake2b: invalid digest size")
+
 	cfg: _blake2.Blake2_Config
 	cfg: _blake2.Blake2_Config
 	cfg.size = u8(digest_size)
 	cfg.size = u8(digest_size)
 	_blake2.init(ctx, &cfg)
 	_blake2.init(ctx, &cfg)

+ 2 - 3
core/crypto/blake2s/blake2s.odin

@@ -28,9 +28,8 @@ Context :: _blake2.Blake2s_Context
 
 
 // init initializes a Context with the default BLAKE2s config.
 // init initializes a Context with the default BLAKE2s config.
 init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
 init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
-	if digest_size > 255 {
-		panic("blake2s: invalid digest size")
-	}
+	ensure(digest_size <= _blake2.MAX_SIZE, "crypto/blake2s: invalid digest size")
+
 	cfg: _blake2.Blake2_Config
 	cfg: _blake2.Blake2_Config
 	cfg.size = u8(digest_size)
 	cfg.size = u8(digest_size)
 	_blake2.init(ctx, &cfg)
 	_blake2.init(ctx, &cfg)

+ 5 - 11
core/crypto/chacha20/chacha20.odin

@@ -27,12 +27,8 @@ Context :: struct {
 // init inititializes a Context for ChaCha20 or XChaCha20 with the provided
 // init inititializes a Context for ChaCha20 or XChaCha20 with the provided
 // key and iv.
 // key and iv.
 init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
 init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
-	if len(key) != KEY_SIZE {
-		panic("crypto/chacha20: invalid (X)ChaCha20 key size")
-	}
-	if l := len(iv); l != IV_SIZE && l != XIV_SIZE {
-		panic("crypto/chacha20: invalid (X)ChaCha20 IV size")
-	}
+	ensure(len(key) == KEY_SIZE, "crypto/chacha20: invalid (X)ChaCha20 key size")
+	ensure(len(iv) == IV_SIZE || len(iv) == XIV_SIZE, "crypto/chacha20: invalid (X)ChaCha20 IV size")
 
 
 	k, n := key, iv
 	k, n := key, iv
 
 
@@ -67,16 +63,14 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
 // keystream, and writes the resulting output to dst.  Dst and src MUST
 // keystream, and writes the resulting output to dst.  Dst and src MUST
 // alias exactly or not at all.
 // alias exactly or not at all.
 xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
 xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
-	assert(ctx._state._is_initialized)
+	ensure(ctx._state._is_initialized)
 
 
 	src, dst := src, dst
 	src, dst := src, dst
 	if dst_len := len(dst); dst_len < len(src) {
 	if dst_len := len(dst); dst_len < len(src) {
 		src = src[:dst_len]
 		src = src[:dst_len]
 	}
 	}
 
 
-	if bytes.alias_inexactly(dst, src) {
-		panic("crypto/chacha20: dst and src alias inexactly")
-	}
+	ensure(!bytes.alias_inexactly(dst, src), "crypto/chacha20: dst and src alias inexactly")
 
 
 	st := &ctx._state
 	st := &ctx._state
 	#no_bounds_check for remaining := len(src); remaining > 0; {
 	#no_bounds_check for remaining := len(src); remaining > 0; {
@@ -114,7 +108,7 @@ xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
 
 
 // keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
 // keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
 keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
 keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
-	assert(ctx._state._is_initialized)
+	ensure(ctx._state._is_initialized)
 
 
 	dst, st := dst, &ctx._state
 	dst, st := dst, &ctx._state
 	#no_bounds_check for remaining := len(dst); remaining > 0; {
 	#no_bounds_check for remaining := len(dst); remaining > 0; {

+ 8 - 20
core/crypto/chacha20poly1305/chacha20poly1305.odin

@@ -29,13 +29,9 @@ _P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
 
 
 @(private)
 @(private)
 _validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bool) {
 _validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bool) {
-	if len(tag) != TAG_SIZE {
-		panic("crypto/chacha20poly1305: invalid destination tag size")
-	}
 	expected_iv_len := is_xchacha ? XIV_SIZE : IV_SIZE
 	expected_iv_len := is_xchacha ? XIV_SIZE : IV_SIZE
-	if len(iv) != expected_iv_len {
-		panic("crypto/chacha20poly1305: invalid IV size")
-	}
+	ensure(len(tag) == TAG_SIZE, "crypto/chacha20poly1305: invalid destination tag size")
+	ensure(len(iv) == expected_iv_len, "crypto/chacha20poly1305: invalid IV size")
 
 
 	#assert(size_of(int) == 8 || size_of(int) <= 4)
 	#assert(size_of(int) == 8 || size_of(int) <= 4)
 	when size_of(int) == 8 {
 	when size_of(int) == 8 {
@@ -45,9 +41,7 @@ _validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bo
 		// A_MAX is limited by size_of(int), so there is no need to
 		// A_MAX is limited by size_of(int), so there is no need to
 		// enforce it. P_MAX only needs to be checked on 64-bit targets,
 		// enforce it. P_MAX only needs to be checked on 64-bit targets,
 		// for reasons that should be obvious.
 		// for reasons that should be obvious.
-		if text_len := len(text); text_len > _P_MAX {
-			panic("crypto/chacha20poly1305: oversized src data")
-		}
+		ensure(len(text) <= _P_MAX, "crypto/chacha20poly1305: oversized src data")
 	}
 	}
 }
 }
 
 
@@ -71,9 +65,7 @@ Context :: struct {
 
 
 // init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
 // init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
 init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
 init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
-	if len(key) != KEY_SIZE {
-		panic("crypto/chacha20poly1305: invalid key size")
-	}
+	ensure(len(key) == KEY_SIZE, "crypto/chacha20poly1305: invalid key size")
 
 
 	copy(ctx._key[:], key)
 	copy(ctx._key[:], key)
 	ctx._impl = impl
 	ctx._impl = impl
@@ -96,13 +88,11 @@ init_xchacha :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEM
 //
 //
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
 seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	ciphertext := dst
 	ciphertext := dst
 	_validate_common_slice_sizes(tag, iv, aad, plaintext, ctx._is_xchacha)
 	_validate_common_slice_sizes(tag, iv, aad, plaintext, ctx._is_xchacha)
-	if len(ciphertext) != len(plaintext) {
-		panic("crypto/chacha20poly1305: invalid destination ciphertext size")
-	}
+	ensure(len(ciphertext) == len(plaintext), "crypto/chacha20poly1305: invalid destination ciphertext size")
 
 
 	stream_ctx: chacha20.Context = ---
 	stream_ctx: chacha20.Context = ---
 	chacha20.init(&stream_ctx, ctx._key[:],iv, ctx._impl)
 	chacha20.init(&stream_ctx, ctx._key[:],iv, ctx._impl)
@@ -153,13 +143,11 @@ seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 @(require_results)
 @(require_results)
 open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
 open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	plaintext := dst
 	plaintext := dst
 	_validate_common_slice_sizes(tag, iv, aad, ciphertext, ctx._is_xchacha)
 	_validate_common_slice_sizes(tag, iv, aad, ciphertext, ctx._is_xchacha)
-	if len(ciphertext) != len(plaintext) {
-		panic("crypto/chacha20poly1305: invalid destination plaintext size")
-	}
+	ensure(len(ciphertext) == len(plaintext), "crypto/chacha20poly1305: invalid destination plaintext size")
 
 
 	// Note: Unlike encrypt, this can fail early, so use defer for
 	// Note: Unlike encrypt, this can fail early, so use defer for
 	// sanitization rather than assuming control flow reaches certain
 	// sanitization rather than assuming control flow reaches certain

+ 9 - 24
core/crypto/deoxysii/deoxysii.odin

@@ -76,13 +76,8 @@ Context :: struct {
 
 
 @(private)
 @(private)
 _validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
 _validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
-	if len(tag) != TAG_SIZE {
-		panic("crypto/deoxysii: invalid tag size")
-	}
-
-	if len(iv) != IV_SIZE {
-		panic("crypto/deoxysii: invalid IV size")
-	}
+	ensure(len(tag) == TAG_SIZE, "crypto/deoxysii: invalid tag size")
+	ensure(len(iv) == IV_SIZE, "crypto/deoxysii: invalid IV size")
 
 
 	#assert(size_of(int) == 8 || size_of(int) <= 4)
 	#assert(size_of(int) == 8 || size_of(int) <= 4)
 	// For the nonce-misuse resistant mode, the total size of the
 	// For the nonce-misuse resistant mode, the total size of the
@@ -95,9 +90,7 @@ _validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte)
 
 
 // init initializes a Context with the provided key.
 // init initializes a Context with the provided key.
 init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
 init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
-	if len(key) != KEY_SIZE {
-		panic("crypto/deoxysii: invalid key size")
-	}
+	ensure(len(key) == KEY_SIZE, "crypto/deoxysii: invalid key size")
 
 
 	ctx._impl = impl
 	ctx._impl = impl
 	if ctx._impl == .Hardware && !is_hardware_accelerated() {
 	if ctx._impl == .Hardware && !is_hardware_accelerated() {
@@ -114,15 +107,11 @@ init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
 //
 //
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
 seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	_validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
 	_validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
-	if len(dst) != len(plaintext) {
-		panic("crypto/deoxysii: invalid destination ciphertext size")
-	}
-	if bytes.alias_inexactly(dst, plaintext) {
-		panic("crypto/deoxysii: dst and plaintext alias inexactly")
-	}
+	ensure(len(dst) == len(plaintext), "crypto/deoxysii: invalid destination ciphertext size")
+	ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/deoxysii: dst and plaintext alias inexactly")
 
 
 	switch ctx._impl {
 	switch ctx._impl {
 	case .Hardware:
 	case .Hardware:
@@ -140,15 +129,11 @@ seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
 // dst and plaintext MUST alias exactly or not at all.
 // dst and plaintext MUST alias exactly or not at all.
 @(require_results)
 @(require_results)
 open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
 open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	_validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
 	_validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
-	if len(dst) != len(ciphertext) {
-		panic("crypto/deoxysii: invalid destination plaintext size")
-	}
-	if bytes.alias_inexactly(dst, ciphertext) {
-		panic("crypto/deoxysii: dst and ciphertext alias inexactly")
-	}
+	ensure(len(dst) == len(ciphertext), "crypto/deoxysii: invalid destination plaintext size")
+	ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/deoxysii: dst and ciphertext alias inexactly")
 
 
 	ok: bool
 	ok: bool
 	switch ctx._impl {
 	switch ctx._impl {

+ 8 - 24
core/crypto/ed25519/ed25519.odin

@@ -81,12 +81,8 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
 
 
 // private_key_bytes sets dst to byte-encoding of priv_key.
 // private_key_bytes sets dst to byte-encoding of priv_key.
 private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
 private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
-	if !priv_key._is_initialized {
-		panic("crypto/ed25519: uninitialized private key")
-	}
-	if len(dst) != PRIVATE_KEY_SIZE {
-		panic("crypto/ed25519: invalid destination size")
-	}
+	ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
+	ensure(len(dst) == PRIVATE_KEY_SIZE, "crypto/ed25519: invalid destination size")
 
 
 	copy(dst, priv_key._b[:])
 	copy(dst, priv_key._b[:])
 }
 }
@@ -98,12 +94,8 @@ private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
 
 
 // sign writes the signature by priv_key over msg to sig.
 // sign writes the signature by priv_key over msg to sig.
 sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
 sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
-	if !priv_key._is_initialized {
-		panic("crypto/ed25519: uninitialized private key")
-	}
-	if len(sig) != SIGNATURE_SIZE {
-		panic("crypto/ed25519: invalid destination size")
-	}
+	ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
+	ensure(len(sig) == SIGNATURE_SIZE, "crypto/ed25519: invalid destination size")
 
 
 	// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
 	// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
 	// using SHA-512 for Ed25519.  H(d) may be precomputed.
 	// using SHA-512 for Ed25519.  H(d) may be precomputed.
@@ -178,9 +170,7 @@ public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) ->
 
 
 // public_key_set_priv sets pub_key to the public component of priv_key.
 // public_key_set_priv sets pub_key to the public component of priv_key.
 public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
 public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
-	if !priv_key._is_initialized {
-		panic("crypto/ed25519: uninitialized public key")
-	}
+	ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized public key")
 
 
 	src := &priv_key._pub_key
 	src := &priv_key._pub_key
 	copy(pub_key._b[:], src._b[:])
 	copy(pub_key._b[:], src._b[:])
@@ -191,21 +181,15 @@ public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
 
 
 // public_key_bytes sets dst to byte-encoding of pub_key.
 // public_key_bytes sets dst to byte-encoding of pub_key.
 public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
 public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
-	if !pub_key._is_initialized {
-		panic("crypto/ed25519: uninitialized public key")
-	}
-	if len(dst) != PUBLIC_KEY_SIZE {
-		panic("crypto/ed25519: invalid destination size")
-	}
+	ensure(pub_key._is_initialized, "crypto/ed25519: uninitialized public key")
+	ensure(len(dst) == PUBLIC_KEY_SIZE, "crypto/ed25519: invalid destination size")
 
 
 	copy(dst, pub_key._b[:])
 	copy(dst, pub_key._b[:])
 }
 }
 
 
 // public_key_equal returns true iff pub_key is equal to other.
 // public_key_equal returns true iff pub_key is equal to other.
 public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
 public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
-	if !pub_key._is_initialized || !other._is_initialized {
-		panic("crypto/ed25519: uninitialized public key")
-	}
+	ensure(pub_key._is_initialized && other._is_initialized, "crypto/ed25519: uninitialized public key")
 
 
 	return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
 	return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
 }
 }

+ 5 - 8
core/crypto/hmac/hmac.odin

@@ -56,7 +56,7 @@ init :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	hash.update(&ctx._i_hash, data)
 	hash.update(&ctx._i_hash, data)
 }
 }
@@ -64,13 +64,10 @@ update :: proc(ctx: ^Context, data: []byte) {
 // final finalizes the Context, writes the tag to dst, and calls
 // final finalizes the Context, writes the tag to dst, and calls
 // reset on the Context.
 // reset on the Context.
 final :: proc(ctx: ^Context, dst: []byte) {
 final :: proc(ctx: ^Context, dst: []byte) {
-	assert(ctx._is_initialized)
-
 	defer (reset(ctx))
 	defer (reset(ctx))
 
 
-	if len(dst) != ctx._tag_sz {
-		panic("crypto/hmac: invalid destination tag size")
-	}
+	ensure(ctx._is_initialized)
+	ensure(len(dst) == ctx._tag_sz, "crypto/hmac: invalid destination tag size")
 
 
 	hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
 	hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
 
 
@@ -105,14 +102,14 @@ reset :: proc(ctx: ^Context) {
 
 
 // algorithm returns the Algorithm used by a Context instance.
 // algorithm returns the Algorithm used by a Context instance.
 algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
 algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	return hash.algorithm(&ctx._i_hash)
 	return hash.algorithm(&ctx._i_hash)
 }
 }
 
 
 // tag_size returns the tag size of a Context instance in bytes.
 // tag_size returns the tag size of a Context instance in bytes.
 tag_size :: proc(ctx: ^Context) -> int {
 tag_size :: proc(ctx: ^Context) -> int {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	return ctx._tag_sz
 	return ctx._tag_sz
 }
 }

+ 3 - 9
core/crypto/kmac/kmac.odin

@@ -36,6 +36,7 @@ sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
 // tag is valid.
 // tag is valid.
 verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
 verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
 	derived_tag := make([]byte, len(tag), allocator)
 	derived_tag := make([]byte, len(tag), allocator)
+	defer(delete(derived_tag))
 
 
 	sum(sec_strength, derived_tag, msg, key, domain_sep)
 	sum(sec_strength, derived_tag, msg, key, domain_sep)
 
 
@@ -59,8 +60,6 @@ init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx.is_initialized)
-
 	shake.write((^shake.Context)(ctx), data)
 	shake.write((^shake.Context)(ctx), data)
 }
 }
 
 
@@ -68,12 +67,9 @@ update :: proc(ctx: ^Context, data: []byte) {
 // on the Context.  This routine will panic if the dst length is less than
 // on the Context.  This routine will panic if the dst length is less than
 // MIN_TAG_SIZE.
 // MIN_TAG_SIZE.
 final :: proc(ctx: ^Context, dst: []byte) {
 final :: proc(ctx: ^Context, dst: []byte) {
-	assert(ctx.is_initialized)
 	defer reset(ctx)
 	defer reset(ctx)
 
 
-	if len(dst) < MIN_TAG_SIZE {
-		panic("crypto/kmac: invalid KMAC tag_size, too short")
-	}
+	ensure(len(dst) >= MIN_TAG_SIZE, "crypto/kmac: invalid KMAC tag_size, too short")
 
 
 	_sha3.final_cshake((^_sha3.Context)(ctx), dst)
 	_sha3.final_cshake((^_sha3.Context)(ctx), dst)
 }
 }
@@ -103,9 +99,7 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
 		reset(ctx)
 		reset(ctx)
 	}
 	}
 
 
-	if len(key) < sec_strength / 8 {
-		panic("crypto/kmac: invalid KMAC key, too short")
-	}
+	ensure(len(key) >= sec_strength / 8, "crypto/kmac: invalid KMAC key, too short")
 
 
 	ctx_ := (^_sha3.Context)(ctx)
 	ctx_ := (^_sha3.Context)(ctx)
 	_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
 	_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)

+ 9 - 9
core/crypto/legacy/keccak/keccak.odin

@@ -40,37 +40,37 @@ BLOCK_SIZE_512 :: _sha3.RATE_512
 Context :: distinct _sha3.Context
 Context :: distinct _sha3.Context
 
 
 // init_224 initializes a Context for Keccak-224.
 // init_224 initializes a Context for Keccak-224.
-init_224 :: proc(ctx: ^Context) {
+init_224 :: proc "contextless" (ctx: ^Context) {
 	ctx.mdlen = DIGEST_SIZE_224
 	ctx.mdlen = DIGEST_SIZE_224
 	_init(ctx)
 	_init(ctx)
 }
 }
 
 
 // init_256 initializes a Context for Keccak-256.
 // init_256 initializes a Context for Keccak-256.
-init_256 :: proc(ctx: ^Context) {
+init_256 :: proc "contextless" (ctx: ^Context) {
 	ctx.mdlen = DIGEST_SIZE_256
 	ctx.mdlen = DIGEST_SIZE_256
 	_init(ctx)
 	_init(ctx)
 }
 }
 
 
 // init_384 initializes a Context for Keccak-384.
 // init_384 initializes a Context for Keccak-384.
-init_384 :: proc(ctx: ^Context) {
+init_384 :: proc "contextless" (ctx: ^Context) {
 	ctx.mdlen = DIGEST_SIZE_384
 	ctx.mdlen = DIGEST_SIZE_384
 	_init(ctx)
 	_init(ctx)
 }
 }
 
 
 // init_512 initializes a Context for Keccak-512.
 // init_512 initializes a Context for Keccak-512.
-init_512 :: proc(ctx: ^Context) {
+init_512 :: proc "contextless" (ctx: ^Context) {
 	ctx.mdlen = DIGEST_SIZE_512
 	ctx.mdlen = DIGEST_SIZE_512
 	_init(ctx)
 	_init(ctx)
 }
 }
 
 
 @(private)
 @(private)
-_init :: proc(ctx: ^Context) {
+_init :: proc "contextless" (ctx: ^Context) {
 	ctx.dsbyte = _sha3.DS_KECCAK
 	ctx.dsbyte = _sha3.DS_KECCAK
 	_sha3.init((^_sha3.Context)(ctx))
 	_sha3.init((^_sha3.Context)(ctx))
 }
 }
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
-update :: proc(ctx: ^Context, data: []byte) {
+update :: proc "contextless" (ctx: ^Context, data: []byte) {
 	_sha3.update((^_sha3.Context)(ctx), data)
 	_sha3.update((^_sha3.Context)(ctx), data)
 }
 }
 
 
@@ -79,17 +79,17 @@ update :: proc(ctx: ^Context, data: []byte) {
 //
 //
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // which is useful for for calculating rolling digests.
 // which is useful for for calculating rolling digests.
-final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
+final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 	_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
 	_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
 }
 }
 
 
 // clone clones the Context other into ctx.
 // clone clones the Context other into ctx.
-clone :: proc(ctx, other: ^Context) {
+clone :: proc "contextless" (ctx, other: ^Context) {
 	_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
 	_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
 }
 }
 
 
 // reset sanitizes the Context.  The Context must be re-initialized to
 // reset sanitizes the Context.  The Context must be re-initialized to
 // be used again.
 // be used again.
-reset :: proc(ctx: ^Context) {
+reset :: proc "contextless" (ctx: ^Context) {
 	_sha3.reset((^_sha3.Context)(ctx))
 	_sha3.reset((^_sha3.Context)(ctx))
 }
 }

+ 3 - 6
core/crypto/legacy/md5/md5.odin

@@ -53,7 +53,7 @@ init :: proc(ctx: ^Context) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx.is_initialized)
+	ensure(ctx.is_initialized)
 
 
 	for i := 0; i < len(data); i += 1 {
 	for i := 0; i < len(data); i += 1 {
 		ctx.data[ctx.datalen] = data[i]
 		ctx.data[ctx.datalen] = data[i]
@@ -72,11 +72,8 @@ update :: proc(ctx: ^Context, data: []byte) {
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // which is useful for for calculating rolling digests.
 // which is useful for for calculating rolling digests.
 final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
-	assert(ctx.is_initialized)
-
-	if len(hash) < DIGEST_SIZE {
-		panic("crypto/md5: invalid destination digest size")
-	}
+	ensure(ctx.is_initialized)
+	ensure(len(hash) >= DIGEST_SIZE, "crypto/md5: invalid destination digest size")
 
 
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {

+ 3 - 6
core/crypto/legacy/sha1/sha1.odin

@@ -60,7 +60,7 @@ init :: proc(ctx: ^Context) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx.is_initialized)
+	ensure(ctx.is_initialized)
 
 
 	for i := 0; i < len(data); i += 1 {
 	for i := 0; i < len(data); i += 1 {
 		ctx.data[ctx.datalen] = data[i]
 		ctx.data[ctx.datalen] = data[i]
@@ -79,11 +79,8 @@ update :: proc(ctx: ^Context, data: []byte) {
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // which is useful for for calculating rolling digests.
 // which is useful for for calculating rolling digests.
 final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
-	assert(ctx.is_initialized)
-
-	if len(hash) < DIGEST_SIZE {
-		panic("crypto/sha1: invalid destination digest size")
-	}
+	ensure(ctx.is_initialized)
+	ensure(len(hash) >= DIGEST_SIZE, "crypto/sha1: invalid destination digest size")
 
 
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {

+ 4 - 8
core/crypto/poly1305/poly1305.odin

@@ -60,9 +60,7 @@ Context :: struct {
 // init initializes a Context with the specified key.  The key SHOULD be
 // init initializes a Context with the specified key.  The key SHOULD be
 // unique and MUST be unpredictable for each invocation.
 // unique and MUST be unpredictable for each invocation.
 init :: proc(ctx: ^Context, key: []byte) {
 init :: proc(ctx: ^Context, key: []byte) {
-	if len(key) != KEY_SIZE {
-		panic("crypto/poly1305: invalid key size")
-	}
+	ensure(len(key) == KEY_SIZE, "crypto/poly1305: invalid key size")
 
 
 	// r = le_bytes_to_num(key[0..15])
 	// r = le_bytes_to_num(key[0..15])
 	// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
 	// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
@@ -85,7 +83,7 @@ init :: proc(ctx: ^Context, key: []byte) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx._is_initialized)
+	ensure(ctx._is_initialized)
 
 
 	msg := data
 	msg := data
 	msg_len := len(data)
 	msg_len := len(data)
@@ -124,12 +122,10 @@ update :: proc(ctx: ^Context, data: []byte) {
 // final finalizes the Context, writes the tag to dst, and calls
 // final finalizes the Context, writes the tag to dst, and calls
 // reset on the Context.
 // reset on the Context.
 final :: proc(ctx: ^Context, dst: []byte) {
 final :: proc(ctx: ^Context, dst: []byte) {
-	assert(ctx._is_initialized)
 	defer reset(ctx)
 	defer reset(ctx)
 
 
-	if len(dst) != TAG_SIZE {
-		panic("poly1305: invalid destination tag size")
-	}
+	ensure(ctx._is_initialized)
+	ensure(len(dst) == TAG_SIZE, "poly1305: invalid destination tag size")
 
 
 	// Process remaining block
 	// Process remaining block
 	if ctx._leftover > 0 {
 	if ctx._leftover > 0 {

+ 16 - 22
core/crypto/ristretto255/ristretto255.odin

@@ -76,7 +76,7 @@ ge_clear :: proc "contextless" (ge: ^Group_Element) {
 
 
 // ge_set sets `ge = a`.
 // ge_set sets `ge = a`.
 ge_set :: proc(ge, a: ^Group_Element) {
 ge_set :: proc(ge, a: ^Group_Element) {
-	_ge_assert_initialized([]^Group_Element{a})
+	_ge_ensure_initialized([]^Group_Element{a})
 
 
 	grp.ge_set(&ge._p, &a._p)
 	grp.ge_set(&ge._p, &a._p)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -199,9 +199,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
 // ge_set_wide_bytes sets ge to the result of deriving a ristretto255
 // ge_set_wide_bytes sets ge to the result of deriving a ristretto255
 // group element, from a wide (512-bit) byte string.
 // group element, from a wide (512-bit) byte string.
 ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
 ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
-	if len(b) != WIDE_ELEMENT_SIZE {
-		panic("crypto/ristretto255: invalid wide input size")
-	}
+	ensure(len(b) == WIDE_ELEMENT_SIZE, "crypto/ristretto255: invalid wide input size")
 
 
 	// The element derivation function on an input string b proceeds as
 	// The element derivation function on an input string b proceeds as
 	// follows:
 	// follows:
@@ -222,10 +220,8 @@ ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
 
 
 // ge_bytes sets dst to the canonical encoding of ge.
 // ge_bytes sets dst to the canonical encoding of ge.
 ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
 ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
-	_ge_assert_initialized([]^Group_Element{ge})
-	if len(dst) != ELEMENT_SIZE {
-		panic("crypto/ristretto255: invalid destination size")
-	}
+	_ge_ensure_initialized([]^Group_Element{ge})
+	ensure(len(dst) == ELEMENT_SIZE, "crypto/ristretto255: invalid destination size")
 
 
 	x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
 	x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
 
 
@@ -306,7 +302,7 @@ ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
 
 
 // ge_add sets `ge = a + b`.
 // ge_add sets `ge = a + b`.
 ge_add :: proc(ge, a, b: ^Group_Element) {
 ge_add :: proc(ge, a, b: ^Group_Element) {
-	_ge_assert_initialized([]^Group_Element{a, b})
+	_ge_ensure_initialized([]^Group_Element{a, b})
 
 
 	grp.ge_add(&ge._p, &a._p, &b._p)
 	grp.ge_add(&ge._p, &a._p, &b._p)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -314,7 +310,7 @@ ge_add :: proc(ge, a, b: ^Group_Element) {
 
 
 // ge_double sets `ge = a + a`.
 // ge_double sets `ge = a + a`.
 ge_double :: proc(ge, a: ^Group_Element) {
 ge_double :: proc(ge, a: ^Group_Element) {
-	_ge_assert_initialized([]^Group_Element{a})
+	_ge_ensure_initialized([]^Group_Element{a})
 
 
 	grp.ge_double(&ge._p, &a._p)
 	grp.ge_double(&ge._p, &a._p)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -322,7 +318,7 @@ ge_double :: proc(ge, a: ^Group_Element) {
 
 
 // ge_negate sets `ge = -a`.
 // ge_negate sets `ge = -a`.
 ge_negate :: proc(ge, a: ^Group_Element) {
 ge_negate :: proc(ge, a: ^Group_Element) {
-	_ge_assert_initialized([]^Group_Element{a})
+	_ge_ensure_initialized([]^Group_Element{a})
 
 
 	grp.ge_negate(&ge._p, &a._p)
 	grp.ge_negate(&ge._p, &a._p)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -330,7 +326,7 @@ ge_negate :: proc(ge, a: ^Group_Element) {
 
 
 // ge_scalarmult sets `ge = A * sc`.
 // ge_scalarmult sets `ge = A * sc`.
 ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
 ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
-	_ge_assert_initialized([]^Group_Element{A})
+	_ge_ensure_initialized([]^Group_Element{A})
 
 
 	grp.ge_scalarmult(&ge._p, &A._p, sc)
 	grp.ge_scalarmult(&ge._p, &A._p, sc)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -344,7 +340,7 @@ ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar)
 
 
 // ge_scalarmult_vartime sets `ge = A * sc` in variable time.
 // ge_scalarmult_vartime sets `ge = A * sc` in variable time.
 ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
 ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
-	_ge_assert_initialized([]^Group_Element{A})
+	_ge_ensure_initialized([]^Group_Element{A})
 
 
 	grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
 	grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -358,7 +354,7 @@ ge_double_scalarmult_generator_vartime :: proc(
 	A: ^Group_Element,
 	A: ^Group_Element,
 	b: ^Scalar,
 	b: ^Scalar,
 ) {
 ) {
-	_ge_assert_initialized([]^Group_Element{A})
+	_ge_ensure_initialized([]^Group_Element{A})
 
 
 	grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
 	grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -367,7 +363,7 @@ ge_double_scalarmult_generator_vartime :: proc(
 // ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
 // ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
 // Behavior for all other values of ctrl are undefined,
 // Behavior for all other values of ctrl are undefined,
 ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
 ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
-	_ge_assert_initialized([]^Group_Element{a})
+	_ge_ensure_initialized([]^Group_Element{a})
 
 
 	grp.ge_cond_negate(&ge._p, &a._p, ctrl)
 	grp.ge_cond_negate(&ge._p, &a._p, ctrl)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -376,7 +372,7 @@ ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
 // ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
 // ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
 // Behavior for all other values of ctrl are undefined,
 // Behavior for all other values of ctrl are undefined,
 ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
 ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
-	_ge_assert_initialized([]^Group_Element{ge, a})
+	_ge_ensure_initialized([]^Group_Element{ge, a})
 
 
 	grp.ge_cond_assign(&ge._p, &a._p, ctrl)
 	grp.ge_cond_assign(&ge._p, &a._p, ctrl)
 }
 }
@@ -384,7 +380,7 @@ ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
 // ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
 // ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
 // Behavior for all other values of ctrl are undefined,
 // Behavior for all other values of ctrl are undefined,
 ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
 ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
-	_ge_assert_initialized([]^Group_Element{a, b})
+	_ge_ensure_initialized([]^Group_Element{a, b})
 
 
 	grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
 	grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
 	ge._is_initialized = true
 	ge._is_initialized = true
@@ -393,7 +389,7 @@ ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
 // ge_equal returns 1 iff `a == b`, and 0 otherwise.
 // ge_equal returns 1 iff `a == b`, and 0 otherwise.
 @(require_results)
 @(require_results)
 ge_equal :: proc(a, b: ^Group_Element) -> int {
 ge_equal :: proc(a, b: ^Group_Element) -> int {
-	_ge_assert_initialized([]^Group_Element{a, b})
+	_ge_ensure_initialized([]^Group_Element{a, b})
 
 
 	// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
 	// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
 	ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
 	ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
@@ -501,10 +497,8 @@ ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
 }
 }
 
 
 @(private)
 @(private)
-_ge_assert_initialized :: proc(ges: []^Group_Element) {
+_ge_ensure_initialized :: proc(ges: []^Group_Element) {
 	for ge in ges {
 	for ge in ges {
-		if !ge._is_initialized {
-			panic("crypto/ristretto255: uninitialized group element")
-		}
+		ensure(ge._is_initialized, "crypto/ristretto255: uninitialized group element")
 	}
 	}
 }
 }

+ 2 - 6
core/crypto/ristretto255/ristretto255_scalar.odin

@@ -42,9 +42,7 @@ sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool {
 // scalar, from a wide (512-bit) byte string by interpreting b as a
 // scalar, from a wide (512-bit) byte string by interpreting b as a
 // little-endian value, and reducing it mod the group order.
 // little-endian value, and reducing it mod the group order.
 sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
 sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
-	if len(b) != WIDE_SCALAR_SIZE {
-		panic("crypto/ristretto255: invalid wide input size")
-	}
+	ensure(len(b) == WIDE_SCALAR_SIZE, "crypto/ristretto255: invalid wide input size")
 
 
 	b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
 	b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
 	grp.sc_set_bytes_wide(sc, b_)
 	grp.sc_set_bytes_wide(sc, b_)
@@ -52,9 +50,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
 
 
 // sc_bytes sets dst to the canonical encoding of sc.
 // sc_bytes sets dst to the canonical encoding of sc.
 sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
 sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
-	if len(dst) != SCALAR_SIZE {
-		panic("crypto/ristretto255: invalid destination size")
-	}
+	ensure(len(dst) == SCALAR_SIZE, "crypto/ristretto255: invalid destination size")
 
 
 	grp.sc_bytes(dst, sc)
 	grp.sc_bytes(dst, sc)
 }
 }

+ 4 - 7
core/crypto/sha2/sha2.odin

@@ -158,7 +158,7 @@ _init :: proc(ctx: ^$T) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^$T, data: []byte) {
 update :: proc(ctx: ^$T, data: []byte) {
-	assert(ctx.is_initialized)
+	ensure(ctx.is_initialized)
 
 
 	when T == Context_256 {
 	when T == Context_256 {
 		CURR_BLOCK_SIZE :: BLOCK_SIZE_256
 		CURR_BLOCK_SIZE :: BLOCK_SIZE_256
@@ -194,11 +194,8 @@ update :: proc(ctx: ^$T, data: []byte) {
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // which is useful for for calculating rolling digests.
 // which is useful for for calculating rolling digests.
 final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
 final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
-	assert(ctx.is_initialized)
-
-	if len(hash) * 8 < ctx.md_bits {
-		panic("crypto/sha2: invalid destination digest size")
-	}
+	ensure(ctx.is_initialized)
+	ensure(len(hash) * 8 >= ctx.md_bits, "crypto/sha2: invalid destination digest size")
 
 
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {
@@ -238,7 +235,7 @@ final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
 		endian.unchecked_put_u64be(pad[8:], length_lo)
 		endian.unchecked_put_u64be(pad[8:], length_lo)
 		update(ctx, pad[0:16])
 		update(ctx, pad[0:16])
 	}
 	}
-	assert(ctx.bitlength == 0)
+	assert(ctx.bitlength == 0) // Check for bugs
 
 
 	when T == Context_256 {
 	when T == Context_256 {
 		for i := 0; i < ctx.md_bits / 32; i += 1 {
 		for i := 0; i < ctx.md_bits / 32; i += 1 {

+ 6 - 11
core/crypto/siphash/siphash.odin

@@ -219,18 +219,14 @@ verify_4_8 :: proc {
 */
 */
 
 
 init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
 init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
-	if len(key) != KEY_SIZE {
-		panic("crypto/siphash; invalid key size")
-	}
+	ensure(len(key) == KEY_SIZE,"crypto/siphash; invalid key size")
 	ctx.c_rounds = c_rounds
 	ctx.c_rounds = c_rounds
 	ctx.d_rounds = d_rounds
 	ctx.d_rounds = d_rounds
 	is_valid_setting :=
 	is_valid_setting :=
 		(ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
 		(ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
 		(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
 		(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
 		(ctx.c_rounds == 4 && ctx.d_rounds == 8)
 		(ctx.c_rounds == 4 && ctx.d_rounds == 8)
-	if !is_valid_setting {
-		panic("crypto/siphash: incorrect rounds set up")
-	}
+	ensure(is_valid_setting, "crypto/siphash: incorrect rounds set up")
 	ctx.k0 = endian.unchecked_get_u64le(key[:8])
 	ctx.k0 = endian.unchecked_get_u64le(key[:8])
 	ctx.k1 = endian.unchecked_get_u64le(key[8:])
 	ctx.k1 = endian.unchecked_get_u64le(key[8:])
 	ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
 	ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
@@ -245,7 +241,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
 }
 }
 
 
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx.is_initialized, "crypto/siphash: context is not initialized")
+	ensure(ctx.is_initialized)
 
 
 	data := data
 	data := data
 	ctx.total_length += len(data)
 	ctx.total_length += len(data)
@@ -269,7 +265,7 @@ update :: proc(ctx: ^Context, data: []byte) {
 }
 }
 
 
 final :: proc(ctx: ^Context, dst: ^u64) {
 final :: proc(ctx: ^Context, dst: ^u64) {
-	assert(ctx.is_initialized, "crypto/siphash: context is not initialized")
+	ensure(ctx.is_initialized)
 
 
 	tmp: [BLOCK_SIZE]byte
 	tmp: [BLOCK_SIZE]byte
 	copy(tmp[:], ctx.buf[:ctx.last_block])
 	copy(tmp[:], ctx.buf[:ctx.last_block])
@@ -336,9 +332,8 @@ _get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byt
 
 
 @(private)
 @(private)
 _collect_output :: #force_inline proc(dst: []byte, hash: u64) {
 _collect_output :: #force_inline proc(dst: []byte, hash: u64) {
-	if len(dst) < DIGEST_SIZE {
-		panic("crypto/siphash: invalid tag size")
-	}
+	ensure(len(dst) >= DIGEST_SIZE, "crypto/siphash: invalid tag size")
+
 	dst[0] = _get_byte(7, hash)
 	dst[0] = _get_byte(7, hash)
 	dst[1] = _get_byte(6, hash)
 	dst[1] = _get_byte(6, hash)
 	dst[2] = _get_byte(5, hash)
 	dst[2] = _get_byte(5, hash)

+ 4 - 7
core/crypto/sm3/sm3.odin

@@ -53,7 +53,7 @@ init :: proc(ctx: ^Context) {
 
 
 // update adds more data to the Context.
 // update adds more data to the Context.
 update :: proc(ctx: ^Context, data: []byte) {
 update :: proc(ctx: ^Context, data: []byte) {
-	assert(ctx.is_initialized)
+	ensure(ctx.is_initialized)
 
 
 	data := data
 	data := data
 	ctx.length += u64(len(data))
 	ctx.length += u64(len(data))
@@ -83,11 +83,8 @@ update :: proc(ctx: ^Context, data: []byte) {
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // Iff finalize_clone is set, final will work on a copy of the Context,
 // which is useful for for calculating rolling digests.
 // which is useful for for calculating rolling digests.
 final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
-	assert(ctx.is_initialized)
-
-	if len(hash) < DIGEST_SIZE {
-		panic("crypto/sm3: invalid destination digest size")
-	}
+	ensure(ctx.is_initialized)
+	ensure(len(hash) >= DIGEST_SIZE, "crypto/sm3: invalid destination digest size")
 
 
 	ctx := ctx
 	ctx := ctx
 	if finalize_clone {
 	if finalize_clone {
@@ -110,7 +107,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
 	length <<= 3
 	length <<= 3
 	endian.unchecked_put_u64be(pad[:], length)
 	endian.unchecked_put_u64be(pad[:], length)
 	update(ctx, pad[0:8])
 	update(ctx, pad[0:8])
-	assert(ctx.bitlength == 0)
+	assert(ctx.bitlength == 0) // Check for bugs
 
 
 	for i := 0; i < DIGEST_SIZE / 4; i += 1 {
 	for i := 0; i < DIGEST_SIZE / 4; i += 1 {
 		endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
 		endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])

+ 3 - 9
core/crypto/x25519/x25519.odin

@@ -101,15 +101,9 @@ _scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) {
 // scalarmult "multiplies" the provided scalar and point, and writes the
 // scalarmult "multiplies" the provided scalar and point, and writes the
 // resulting point to dst.
 // resulting point to dst.
 scalarmult :: proc(dst, scalar, point: []byte) {
 scalarmult :: proc(dst, scalar, point: []byte) {
-	if len(scalar) != SCALAR_SIZE {
-		panic("crypto/x25519: invalid scalar size")
-	}
-	if len(point) != POINT_SIZE {
-		panic("crypto/x25519: invalid point size")
-	}
-	if len(dst) != POINT_SIZE {
-		panic("crypto/x25519: invalid destination point size")
-	}
+	ensure(len(scalar) == SCALAR_SIZE, "crypto/x25519: invalid scalar size")
+	ensure(len(point) == POINT_SIZE, "crypto/x25519: invalid point size")
+	ensure(len(dst) == POINT_SIZE, "crypto/x25519: invalid destination point size")
 
 
 	// "clamp" the scalar
 	// "clamp" the scalar
 	e: [32]byte = ---
 	e: [32]byte = ---

+ 3 - 9
core/crypto/x448/x448.odin

@@ -127,15 +127,9 @@ _scalarmult :: proc "contextless" (out, scalar, point: ^[56]byte) {
 // scalarmult "multiplies" the provided scalar and point, and writes the
 // scalarmult "multiplies" the provided scalar and point, and writes the
 // resulting point to dst.
 // resulting point to dst.
 scalarmult :: proc(dst, scalar, point: []byte) {
 scalarmult :: proc(dst, scalar, point: []byte) {
-	if len(scalar) != SCALAR_SIZE {
-		panic("crypto/x448: invalid scalar size")
-	}
-	if len(point) != POINT_SIZE {
-		panic("crypto/x448: invalid point size")
-	}
-	if len(dst) != POINT_SIZE {
-		panic("crypto/x448: invalid destination point size")
-	}
+	ensure(len(scalar) == SCALAR_SIZE, "crypto/x448: invalid scalar size")
+	ensure(len(point) == POINT_SIZE, "crypto/x448: invalid point size")
+	ensure(len(dst) == POINT_SIZE, "crypto/x448: invalid destination point size")
 
 
 	// "clamp" the scalar
 	// "clamp" the scalar
 	e: [56]byte = ---
 	e: [56]byte = ---