mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-05 04:27:51 +00:00
These constants and internal routines are not intended for use outside the actual implementations themselves.
166 lines
3.7 KiB
Odin
166 lines
3.7 KiB
Odin
package poly1305
|
|
|
|
import "core:crypto"
|
|
import "core:crypto/util"
|
|
import field "core:crypto/_fiat/field_poly1305"
|
|
import "core:mem"
|
|
|
|
KEY_SIZE :: 32
|
|
TAG_SIZE :: 16
|
|
|
|
@(private)
|
|
_BLOCK_SIZE :: 16
|
|
|
|
sum :: proc (dst, msg, key: []byte) {
|
|
ctx: Context = ---
|
|
|
|
init(&ctx, key)
|
|
update(&ctx, msg)
|
|
final(&ctx, dst)
|
|
}
|
|
|
|
verify :: proc (tag, msg, key: []byte) -> bool {
|
|
ctx: Context = ---
|
|
derived_tag: [16]byte = ---
|
|
|
|
if len(tag) != TAG_SIZE {
|
|
panic("crypto/poly1305: invalid tag size")
|
|
}
|
|
|
|
init(&ctx, key)
|
|
update(&ctx, msg)
|
|
final(&ctx, derived_tag[:])
|
|
|
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
|
}
|
|
|
|
Context :: struct {
|
|
_r: field.Tight_Field_Element,
|
|
_a: field.Tight_Field_Element,
|
|
_s: field.Tight_Field_Element,
|
|
|
|
_buffer: [_BLOCK_SIZE]byte,
|
|
_leftover: int,
|
|
|
|
_is_initialized: bool,
|
|
}
|
|
|
|
init :: proc (ctx: ^Context, key: []byte) {
|
|
if len(key) != KEY_SIZE {
|
|
panic("crypto/poly1305: invalid key size")
|
|
}
|
|
|
|
// r = le_bytes_to_num(key[0..15])
|
|
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
|
tmp_lo := util.U64_LE(key[0:8]) & 0x0ffffffc0fffffff
|
|
tmp_hi := util.U64_LE(key[8:16]) & 0xffffffc0ffffffc
|
|
field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
|
|
|
|
// s = le_bytes_to_num(key[16..31])
|
|
field.fe_from_bytes(&ctx._s, key[16:32], 0)
|
|
|
|
// a = 0
|
|
field.fe_zero(&ctx._a)
|
|
|
|
// No leftover in buffer
|
|
ctx._leftover = 0
|
|
|
|
ctx._is_initialized = true
|
|
}
|
|
|
|
update :: proc (ctx: ^Context, data: []byte) {
|
|
assert(ctx._is_initialized)
|
|
|
|
msg := data
|
|
msg_len := len(data)
|
|
|
|
// Handle leftover
|
|
if ctx._leftover > 0 {
|
|
want := min(_BLOCK_SIZE - ctx._leftover, msg_len)
|
|
copy_slice(ctx._buffer[ctx._leftover:], msg[:want])
|
|
msg_len = msg_len - want
|
|
msg = msg[want:]
|
|
ctx._leftover = ctx._leftover + want
|
|
if ctx._leftover < _BLOCK_SIZE {
|
|
return
|
|
}
|
|
_blocks(ctx, ctx._buffer[:])
|
|
ctx._leftover = 0
|
|
}
|
|
|
|
// Process full blocks
|
|
if msg_len >= _BLOCK_SIZE {
|
|
want := msg_len & (~int(_BLOCK_SIZE - 1))
|
|
_blocks(ctx, msg[:want])
|
|
msg = msg[want:]
|
|
msg_len = msg_len - want
|
|
}
|
|
|
|
// Store leftover
|
|
if msg_len > 0 {
|
|
// TODO: While -donna does it this way, I'm fairly sure that
|
|
// `ctx._leftover == 0` is an invariant at this point.
|
|
copy(ctx._buffer[ctx._leftover:], msg)
|
|
ctx._leftover = ctx._leftover + msg_len
|
|
}
|
|
}
|
|
|
|
final :: proc (ctx: ^Context, dst: []byte) {
|
|
assert(ctx._is_initialized)
|
|
|
|
if len(dst) != TAG_SIZE {
|
|
panic("poly1305: invalid destination tag size")
|
|
}
|
|
|
|
// Process remaining block
|
|
if ctx._leftover > 0 {
|
|
ctx._buffer[ctx._leftover] = 1
|
|
for i := ctx._leftover + 1; i < _BLOCK_SIZE; i = i + 1 {
|
|
ctx._buffer[i] = 0
|
|
}
|
|
_blocks(ctx, ctx._buffer[:], true)
|
|
}
|
|
|
|
// a += s
|
|
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &ctx._s) // _a unreduced
|
|
field.fe_carry(&ctx._a, field.fe_relax_cast(&ctx._a)) // _a reduced
|
|
|
|
// return num_to_16_le_bytes(a)
|
|
tmp: [32]byte = ---
|
|
field.fe_to_bytes(&tmp, &ctx._a)
|
|
copy_slice(dst, tmp[0:16])
|
|
|
|
reset(ctx)
|
|
}
|
|
|
|
reset :: proc (ctx: ^Context) {
|
|
mem.zero_explicit(&ctx._r, size_of(ctx._r))
|
|
mem.zero_explicit(&ctx._a, size_of(ctx._a))
|
|
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
|
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
|
|
|
ctx._is_initialized = false
|
|
}
|
|
|
|
@(private)
|
|
_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
|
|
n: field.Tight_Field_Element = ---
|
|
final_byte := byte(!final)
|
|
|
|
data := msg
|
|
data_len := len(data)
|
|
for data_len >= _BLOCK_SIZE {
|
|
// n = le_bytes_to_num(msg[((i-1)*16)..*i*16] | [0x01])
|
|
field.fe_from_bytes(&n, data[:_BLOCK_SIZE], final_byte, false)
|
|
|
|
// a += n
|
|
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &n) // _a unreduced
|
|
|
|
// a = (r * a) % p
|
|
field.fe_carry_mul(&ctx._a, field.fe_relax_cast(&ctx._a), field.fe_relax_cast(&ctx._r)) // _a reduced
|
|
|
|
data = data[_BLOCK_SIZE:]
|
|
data_len = data_len - _BLOCK_SIZE
|
|
}
|
|
}
|