core/crypto: Misc cleanups and documentation improvements

This commit is contained in:
Yawning Angel
2024-03-04 17:50:45 +09:00
parent b818ebc02f
commit fa8dd5a13b
5 changed files with 108 additions and 39 deletions
+20 -2
View File
@@ -1,11 +1,21 @@
/*
package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
See:
- https://datatracker.ietf.org/doc/html/rfc8439
- https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/
*/
package chacha20 package chacha20
import "core:encoding/endian" import "core:encoding/endian"
import "core:math/bits" import "core:math/bits"
import "core:mem" import "core:mem"
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
KEY_SIZE :: 32 KEY_SIZE :: 32
// NONCE_SIZE is the ChaCha20 nonce size in bytes.
NONCE_SIZE :: 12 NONCE_SIZE :: 12
// XNONCE_SIZE is the XChaCha20 nonce size in bytes.
XNONCE_SIZE :: 24 XNONCE_SIZE :: 24
@(private) @(private)
@@ -27,16 +37,17 @@ _SIGMA_2 : u32 : 0x79622d32
@(private) @(private)
_SIGMA_3: u32 : 0x6b206574 _SIGMA_3: u32 : 0x6b206574
// Context is a ChaCha20 or XChaCha20 instance.
Context :: struct { Context :: struct {
_s: [_STATE_SIZE_U32]u32, _s: [_STATE_SIZE_U32]u32,
_buffer: [_BLOCK_SIZE]byte, _buffer: [_BLOCK_SIZE]byte,
_off: int, _off: int,
_is_ietf_flavor: bool, _is_ietf_flavor: bool,
_is_initialized: bool, _is_initialized: bool,
} }
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
// key and nonce.
init :: proc(ctx: ^Context, key, nonce: []byte) { init :: proc(ctx: ^Context, key, nonce: []byte) {
if len(key) != KEY_SIZE { if len(key) != KEY_SIZE {
panic("crypto/chacha20: invalid ChaCha20 key size") panic("crypto/chacha20: invalid ChaCha20 key size")
@@ -89,6 +100,7 @@ init :: proc (ctx: ^Context, key, nonce: []byte) {
ctx._is_initialized = true ctx._is_initialized = true
} }
// 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) assert(ctx._is_initialized)
@@ -103,6 +115,9 @@ seek :: proc (ctx: ^Context, block_nr: u64) {
ctx._off = _BLOCK_SIZE ctx._off = _BLOCK_SIZE
} }
// xor_bytes XORs each byte in src with bytes taken from the (X)ChaCha20
// keystream, and writes the resulting output to dst. Dst and src MUST
// alias exactly or not at all.
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) { xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
assert(ctx._is_initialized) assert(ctx._is_initialized)
@@ -147,6 +162,7 @@ xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
} }
} }
// 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._is_initialized) assert(ctx._is_initialized)
@@ -180,6 +196,8 @@ keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
} }
} }
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) { reset :: proc(ctx: ^Context) {
mem.zero_explicit(&ctx._s, size_of(ctx._s)) mem.zero_explicit(&ctx._s, size_of(ctx._s))
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer)) mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
@@ -1,3 +1,10 @@
/*
package chacha20poly1305 implements the AEAD_CHACHA20_POLY1305 Authenticated
Encryption with Additional Data algorithm.
See:
- https://www.rfc-editor.org/rfc/rfc8439
*/
package chacha20poly1305 package chacha20poly1305
import "core:crypto" import "core:crypto"
@@ -6,8 +13,11 @@ import "core:crypto/poly1305"
import "core:encoding/endian" import "core:encoding/endian"
import "core:mem" import "core:mem"
// KEY_SIZE is the chacha20poly1305 key size in bytes.
KEY_SIZE :: chacha20.KEY_SIZE KEY_SIZE :: chacha20.KEY_SIZE
// NONCE_SIZE is the chacha20poly1305 nonce size in bytes.
NONCE_SIZE :: chacha20.NONCE_SIZE NONCE_SIZE :: chacha20.NONCE_SIZE
// TAG_SIZE is the chacha20poly1305 tag size in bytes.
TAG_SIZE :: poly1305.TAG_SIZE TAG_SIZE :: poly1305.TAG_SIZE
@(private) @(private)
@@ -49,6 +59,8 @@ _update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
} }
} }
// encrypt encrypts the plaintext and authenticates the aad and ciphertext,
// with the provided key and nonce, stores the output in ciphertext and tag.
encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) { encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
_validate_common_slice_sizes(tag, key, nonce, aad, plaintext) _validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
if len(ciphertext) != len(plaintext) { if len(ciphertext) != len(plaintext) {
@@ -95,6 +107,11 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context. poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
} }
// decrypt authenticates the aad and ciphertext, and decrypts the ciphertext,
// with the provided key, nonce, and tag, and stores the output in plaintext,
// returning true iff the authentication was successful.
//
// If authentication fails, the destination plaintext buffer will be zeroed.
decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool { decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext) _validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
if len(ciphertext) != len(plaintext) { if len(ciphertext) != len(plaintext) {
+1 -1
View File
@@ -11,7 +11,7 @@ import "core:crypto/hash"
import "core:mem" import "core:mem"
// sum will compute the HMAC with the specified algorithm and key // sum will compute the HMAC with the specified algorithm and key
// over msg, and write the computed digest to dst. It requires that // over msg, and write the computed tag to dst. It requires that
// the dst buffer is the tag size. // the dst buffer is the tag size.
sum :: proc(algorithm: hash.Algorithm, dst, msg, key: []byte) { sum :: proc(algorithm: hash.Algorithm, dst, msg, key: []byte) {
ctx: Context ctx: Context
+26 -5
View File
@@ -1,3 +1,9 @@
/*
package poly1305 implements the Poly1305 one-time MAC algorithm.
See:
- https://datatracker.ietf.org/doc/html/rfc8439
*/
package poly1305 package poly1305
import "core:crypto" import "core:crypto"
@@ -5,12 +11,19 @@ import field "core:crypto/_fiat/field_poly1305"
import "core:encoding/endian" import "core:encoding/endian"
import "core:mem" import "core:mem"
// KEY_SIZE is the Poly1305 key size in bytes.
KEY_SIZE :: 32 KEY_SIZE :: 32
// TAG_SIZE is the Poly1305 tag size in bytes.
TAG_SIZE :: 16 TAG_SIZE :: 16
@(private) @(private)
_BLOCK_SIZE :: 16 _BLOCK_SIZE :: 16
// sum will compute the Poly1305 MAC with the key over msg, and write
// the computed tag to dst. It requires that the dst buffer is the tag
// size.
//
// The key SHOULD be unique and MUST be unpredictable for each invocation.
sum :: proc(dst, msg, key: []byte) { sum :: proc(dst, msg, key: []byte) {
ctx: Context = --- ctx: Context = ---
@@ -19,9 +32,12 @@ sum :: proc (dst, msg, key: []byte) {
final(&ctx, dst) final(&ctx, dst)
} }
// verify will verify the Poly1305 tag computed with the key over msg and
// return true iff the tag is valid. It requires that the tag is correctly
// sized.
verify :: proc(tag, msg, key: []byte) -> bool { verify :: proc(tag, msg, key: []byte) -> bool {
ctx: Context = --- ctx: Context = ---
derived_tag: [16]byte = --- derived_tag: [TAG_SIZE]byte = ---
init(&ctx, key) init(&ctx, key)
update(&ctx, msg) update(&ctx, msg)
@@ -30,17 +46,18 @@ verify :: proc (tag, msg, key: []byte) -> bool {
return crypto.compare_constant_time(derived_tag[:], tag) == 1 return crypto.compare_constant_time(derived_tag[:], tag) == 1
} }
// Context is a Poly1305 instance.
Context :: struct { Context :: struct {
_r: field.Tight_Field_Element, _r: field.Tight_Field_Element,
_a: field.Tight_Field_Element, _a: field.Tight_Field_Element,
_s: field.Tight_Field_Element, _s: field.Tight_Field_Element,
_buffer: [_BLOCK_SIZE]byte, _buffer: [_BLOCK_SIZE]byte,
_leftover: int, _leftover: int,
_is_initialized: bool, _is_initialized: bool,
} }
// init initializes a Context with the specified key. The key SHOULD be
// 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 { if len(key) != KEY_SIZE {
panic("crypto/poly1305: invalid key size") panic("crypto/poly1305: invalid key size")
@@ -64,6 +81,7 @@ init :: proc (ctx: ^Context, key: []byte) {
ctx._is_initialized = true ctx._is_initialized = true
} }
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) { update :: proc(ctx: ^Context, data: []byte) {
assert(ctx._is_initialized) assert(ctx._is_initialized)
@@ -101,8 +119,11 @@ update :: proc (ctx: ^Context, data: []byte) {
} }
} }
// final finalizes the Context, writes the tag to dst, and calls
// reset on the Context.
final :: proc(ctx: ^Context, dst: []byte) { final :: proc(ctx: ^Context, dst: []byte) {
assert(ctx._is_initialized) assert(ctx._is_initialized)
defer reset(ctx)
if len(dst) != TAG_SIZE { if len(dst) != TAG_SIZE {
panic("poly1305: invalid destination tag size") panic("poly1305: invalid destination tag size")
@@ -125,10 +146,10 @@ final :: proc (ctx: ^Context, dst: []byte) {
tmp: [32]byte = --- tmp: [32]byte = ---
field.fe_to_bytes(&tmp, &ctx._a) field.fe_to_bytes(&tmp, &ctx._a)
copy_slice(dst, tmp[0:16]) copy_slice(dst, tmp[0:16])
reset(ctx)
} }
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) { reset :: proc(ctx: ^Context) {
mem.zero_explicit(&ctx._r, size_of(ctx._r)) mem.zero_explicit(&ctx._r, size_of(ctx._r))
mem.zero_explicit(&ctx._a, size_of(ctx._a)) mem.zero_explicit(&ctx._a, size_of(ctx._a))
+13
View File
@@ -1,9 +1,18 @@
/*
package x25519 implements the X25519 (aka curve25519) Elliptic-Curve
Diffie-Hellman key exchange protocol.
See:
- https://www.rfc-editor.org/rfc/rfc7748
*/
package x25519 package x25519
import field "core:crypto/_fiat/field_curve25519" import field "core:crypto/_fiat/field_curve25519"
import "core:mem" import "core:mem"
// SCALAR_SIZE is the size of a X25519 scalar (private key) in bytes.
SCALAR_SIZE :: 32 SCALAR_SIZE :: 32
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
POINT_SIZE :: 32 POINT_SIZE :: 32
@(private) @(private)
@@ -94,6 +103,8 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
mem.zero_explicit(&t1, size_of(t1)) mem.zero_explicit(&t1, size_of(t1))
} }
// scalarmult "multiplies" the provided scalar and point, and writes the
// resulting point to dst.
scalarmult :: proc(dst, scalar, point: []byte) { scalarmult :: proc(dst, scalar, point: []byte) {
if len(scalar) != SCALAR_SIZE { if len(scalar) != SCALAR_SIZE {
panic("crypto/x25519: invalid scalar size") panic("crypto/x25519: invalid scalar size")
@@ -123,6 +134,8 @@ scalarmult :: proc (dst, scalar, point: []byte) {
mem.zero_explicit(&d, size_of(d)) mem.zero_explicit(&d, size_of(d))
} }
// scalarmult_basepoint "multiplies" the provided scalar with the X25519
// base point and writes the resulting point to dst.
scalarmult_basepoint :: proc(dst, scalar: []byte) { scalarmult_basepoint :: proc(dst, scalar: []byte) {
// TODO/perf: Switch to using a precomputed table. // TODO/perf: Switch to using a precomputed table.
scalarmult(dst, scalar, _BASE_POINT[:]) scalarmult(dst, scalar, _BASE_POINT[:])