This commit is contained in:
gingerBill
2026-07-12 00:08:00 +01:00
55 changed files with 6374 additions and 122 deletions

View File

@@ -1277,7 +1277,7 @@ assign_at_elem_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[d
array[index] = arg
ok = true
} else {
resize(array, index+1, loc) or_return
resize(array, index+1) or_return
array[index] = arg
ok = true
}
@@ -1296,7 +1296,7 @@ assign_at_elems_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[
copy(array[index:], args)
ok = true
} else {
resize(array, new_size, loc) or_return
resize(array, new_size) or_return
copy(array[index:], args)
ok = true
}
@@ -1314,7 +1314,7 @@ assign_at_elem_string_fixed_capacity_dynamic_array :: proc "contextless" (array:
copy(array[index:], arg)
ok = true
} else {
resize(array, new_size, loc) or_return
resize(array, new_size) or_return
copy(array[index:], arg)
ok = true
}

View File

@@ -60,13 +60,13 @@ Iterator :: struct($Value: typeid) {
_called_next: bool,
}
// init initializes a tree.
// `init` initializes a tree.
init :: proc {
init_ordered,
init_cmp,
}
// init_cmp initializes a tree.
// `init_cmp` initializes a tree.
init_cmp :: proc(
t: ^$T/Tree($Value),
cmp_fn: proc(a, b: Value) -> Ordering,
@@ -78,7 +78,7 @@ init_cmp :: proc(
t._size = 0
}
// init_ordered initializes a tree containing ordered items, with
// `init_ordered` initializes a tree containing ordered items, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(
t: ^$T/Tree($Value),
@@ -87,7 +87,7 @@ init_ordered :: proc(
init_cmp(t, slice.cmp_proc(Value), node_allocator)
}
// destroy de-initializes a tree.
// `destroy` de-initializes a tree.
destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
iter := iterator(t, Direction.Forward)
for _ in iterator_next(&iter) {
@@ -95,24 +95,24 @@ destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
}
}
// len returns the number of elements in the tree.
// `len` returns the number of elements in the tree.
len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
return t._size
}
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
// `first` returns the first node in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Backward)
}
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
// `last` returns the last element in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Forward)
}
// find finds the value in the tree, and returns the corresponding
// `find` finds the value in the tree, and returns the corresponding
// node or nil if and only if (⟺) the value is not present.
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
cur := t._root
@@ -130,7 +130,7 @@ find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
return cur
}
// find_or_insert attempts to insert the value into the tree, and returns
// `find_or_insert` attempts to insert the value into the tree, and returns
// the node, a boolean indicating if the value was inserted, and the
// node allocator error if relevant. If the value is already
// present, the existing node is returned un-altered.
@@ -168,7 +168,7 @@ find_or_insert :: proc(
return
}
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
// `remove` removes a node or value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove :: proc {
@@ -176,7 +176,7 @@ remove :: proc {
remove_node,
}
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
// `remove_value` removes a value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
@@ -187,7 +187,7 @@ remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = t
return remove_node(t, n, call_on_remove)
}
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
// `remove_node` removes a node from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
@@ -249,7 +249,7 @@ remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: boo
return true
}
// iterator returns a tree iterator in the specified direction.
// `iterator` returns a tree iterator in the specified direction.
iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
it: Iterator(Value)
it._tree = transmute(^Tree(Value))t
@@ -260,7 +260,7 @@ iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> It
return it
}
// iterator_from_pos returns a tree iterator in the specified direction,
// `iterator_from_pos` returns a tree iterator in the specified direction,
// spanning the range [pos, last] (inclusive).
iterator_from_pos :: proc "contextless" (
t: ^$T/Tree($Value),
@@ -280,14 +280,14 @@ iterator_from_pos :: proc "contextless" (
return it
}
// iterator_get returns the node currently pointed to by the iterator,
// `iterator_get` returns the node currently pointed to by the iterator,
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
// of the tree has been reached.
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
return it._cur
}
// iterator_remove removes the node currently pointed to by the iterator,
// `iterator_remove` removes the node currently pointed to by the iterator,
// and returns true if and only if (⟺) the removal was successful. Semantics are the
// same as the Tree remove.
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
@@ -303,7 +303,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -
return ok
}
// iterator_next advances the iterator and returns the (node, true) or
// `iterator_next` advances the iterator and returns the (node, true) or
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
//
// Note: The first call to iterator_next will return the first node instead

View File

@@ -63,13 +63,13 @@ Iterator :: struct($Key: typeid, $Value: typeid) {
_called_next: bool,
}
// init initializes a tree.
// `init` initializes a tree.
init :: proc {
init_ordered,
init_cmp,
}
// init_cmp initializes a tree.
// `init_cmp` initializes a tree.
init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering, node_allocator := context.allocator) {
t._root = nil
t._node_allocator = node_allocator
@@ -77,13 +77,13 @@ init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering,
t._size = 0
}
// init_ordered initializes a tree containing ordered keys, with
// `init_ordered` initializes a tree containing ordered keys, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered(Key) {
init_cmp(t, slice.cmp_proc(Key), node_allocator)
}
// destroy de-initializes a tree.
// `destroy` de-initializes a tree.
destroy :: proc(t: ^$T/Tree($Key, $Value), call_on_remove: bool = true) {
iter := iterator(t, .Forward)
for _ in iterator_next(&iter) {
@@ -95,19 +95,19 @@ len :: proc "contextless" (t: $T/Tree($Key, $Value)) -> (node_count: int) {
return t._size
}
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
// `first` returns the first node in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
first :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
return tree_first_or_last_in_order(t, Direction.Backward)
}
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
// `last` returns the last element in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
last :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
return tree_first_or_last_in_order(t, Direction.Forward)
}
// find finds the key in the tree, and returns the corresponding node, or nil if and only if (⟺) the value is not present.
// `find` finds the key in the tree, and returns the corresponding node, or nil if and only if (⟺) the value is not present.
find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
node = t._root
for node != nil {
@@ -120,7 +120,7 @@ find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
return node
}
// find_value finds the key in the tree, and returns the corresponding value, or nil if and only if (⟺) the value is not present.
// `find_value` finds the key in the tree, and returns the corresponding value, or nil if and only if (⟺) the value is not present.
find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
if n := find(t, key); n != nil {
return n.value, true
@@ -128,10 +128,36 @@ find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: boo
return
}
// find_or_insert attempts to insert the key-value pair into the tree, and returns
// `find_or_insert` attempts to insert the key-value pair into the tree, and returns
// the node, a boolean indicating if a new node was inserted, and the
// node allocator error if relevant. If the key is already present, the existing node is updated and returned.
// node allocator error if relevant. If the key is already present, the existing node is returned un-altered.
find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n: ^Node(Key, Value), inserted: bool, err: runtime.Allocator_Error) {
n_ptr := &t._root
for n_ptr^ != nil {
n = n_ptr^
switch t._cmp_fn(key, n.key) {
case .Less:
n_ptr = &n._left
case .Greater:
n_ptr = &n._right
case .Equal:
return
}
}
_parent := n
n = new_clone(Node(Key, Value){key=key, value=value, _parent=_parent, _color=.Red}, t._node_allocator) or_return
n_ptr^ = n
insert_case1(t, n)
t._size += 1
return n, true, nil
}
// `upsert` attempts to insert the key-value pair into the tree, and returns
// the node, a boolean indicating if a new node was inserted, and the
// node allocator error if relevant. If the key is already present, the existing node's value is updated.
upsert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n: ^Node(Key, Value), inserted: bool, err: runtime.Allocator_Error) {
n_ptr := &t._root
for n_ptr^ != nil {
n = n_ptr^
@@ -154,7 +180,7 @@ find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n:
return n, true, nil
}
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
// `remove` removes a node or value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove :: proc {
@@ -162,7 +188,7 @@ remove :: proc {
remove_node,
}
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
// `remove_value` removes a value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's key + value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true) -> bool {
@@ -173,7 +199,7 @@ remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true)
return remove_node(t, n, call_on_remove)
}
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
// `remove_node` removes a node from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's key + value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_node :: proc(t: ^$T/Tree($Key, $Value), node: ^$N/Node(Key, Value), call_on_remove := true) -> (found: bool) {
@@ -207,7 +233,7 @@ remove_node :: proc(t: ^$T/Tree($Key, $Value), node: ^$N/Node(Key, Value), call_
return true
}
// iterator returns a tree iterator in the specified direction.
// `iterator` returns a tree iterator in the specified direction.
iterator :: proc "contextless" (t: ^$T/Tree($Key, $Value), direction: Direction) -> Iterator(Key, Value) {
it: Iterator(Key, Value)
it._tree = cast(^Tree(Key, Value))t
@@ -218,7 +244,7 @@ iterator :: proc "contextless" (t: ^$T/Tree($Key, $Value), direction: Direction)
return it
}
// iterator_from_pos returns a tree iterator in the specified direction,
// `iterator_from_pos` returns a tree iterator in the specified direction,
// spanning the range [pos, last] (inclusive).
iterator_from_pos :: proc "contextless" (t: ^$T/Tree($Key, $Value), pos: ^Node(Key, Value), direction: Direction) -> Iterator(Key, Value) {
it: Iterator(Key, Value)
@@ -234,14 +260,14 @@ iterator_from_pos :: proc "contextless" (t: ^$T/Tree($Key, $Value), pos: ^Node(K
return it
}
// iterator_get returns the node currently pointed to by the iterator,
// `iterator_get` returns the node currently pointed to by the iterator,
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
// of the tree has been reached.
iterator_get :: proc "contextless" (it: ^$I/Iterator($Key, $Value)) -> ^Node(Key, Value) {
return it._cur
}
// iterator_remove removes the node currently pointed to by the iterator,
// `iterator_remove` removes the node currently pointed to by the iterator,
// and returns true if and only if (⟺) the removal was successful. Semantics are the
// same as the Tree remove.
iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = true) -> bool {
@@ -257,7 +283,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = t
return ok
}
// iterator_next advances the iterator and returns the (node, true) or
// `iterator_next` advances the iterator and returns the (node, true) or
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
//
// Note: The first call to iterator_next will return the first node instead

View File

@@ -13,10 +13,11 @@ constant-time byte comparison.
- The crypto packages are not thread-safe.
- Best-effort is make to mitigate timing side-channels on reasonable
architectures. Architectures that are known to be unreasonable include
but are not limited to i386, i486, and WebAssembly.
but are not limited to i386, i486, VIA Nano 2000, ARM7T/ARM9T/Cortex-M3,
and WASM.
- Implementations assume a 64-bit architecture (64-bit integer arithmetic
is fast, and includes add-with-carry, sub-with-borrow, and full-result
multiply).
is fast, and includes contant-time add-with-carry, sub-with-borrow, and
full-result multiply).
- Hardware sidechannels are explicitly out of scope for this package.
Notable examples include but are not limited to:
- Power/RF side-channels etc.
@@ -29,4 +30,4 @@ constant-time byte comparison.
## License
This library is made available under the zlib license.
This library is made available under the zlib license.

View File

@@ -0,0 +1,904 @@
// Constant time Big Integers
package _bigint
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:crypto"
import subtle "core:crypto/_subtle"
import "core:slice"
// Integers 'i31'
// --------------
//
// The 'i31' functions implement computations on big integers using
// an internal representation as an array of 32-bit integers. For
// an array `x`:
// -- x[0] encodes the array length and the "announced bit length"
// of the integer: namely, if the announced bit length is k,
// then x[0] = ((k / 31) << 5) + (k % 31).
// -- x[1], x[2]... contain the value in little-endian order, 31
// bits per word (x[1] contains the least significant 31 bits).
// The upper bit of each word is 0.
//
// Multiplications rely on the elementary 32x32->64 multiplication.
//
// The announced bit length specifies the number of bits that are
// significant in the subsequent 32-bit words. Unused bits in the
// last (most significant) word are set to 0; subsequent words are
// uninitialized and need not exist at all.
//
// The execution time and memory access patterns of all computations
// depend on the announced bit length, but not on the actual word
// values. For modular integers, the announced bit length of any integer
// modulo `n` is equal to the actual bit length of `n`; thus, computations
// on modular integers are "constant-time" (only the modulus length may leak).
I31_MASK :: 0x7fff_ffff
// Compute the bit length of a 32-bit integer.
// Returned value is between 0 and 32 (inclusive).
@(require_results)
_u32_bit_length :: proc "contextless" (x: u32) -> (length: u32) {
x := x
k := subtle.neq(x, 0)
c := subtle.gt(x, 0xFFFF); x = subtle.csel(x, x >> 16, c); k += c << 4
c = subtle.gt(x, 0x00FF); x = subtle.csel(x, x >> 8, c); k += c << 3
c = subtle.gt(x, 0x000F); x = subtle.csel(x, x >> 4, c); k += c << 2
c = subtle.gt(x, 0x0003); x = subtle.csel(x, x >> 2, c); k += c << 1
k += subtle.gt(x, 0x0001)
return k
}
// Multiply two 31-bit integers, with a 62-bit result. This default
// implementation assumes that the basic multiplication operator
// yields constant-time code.
//
// The mul31_lo() returns only the low 31 bits of the product.
//
// Note/Odin:
// The original BearSSL code provides alternative implemenetations
// of these routines gated behind `BR_CT_MUL31`, however that macro
// is only useful on Intel 80386/80486, VIA Nano 2000, and ARM7T/ARM9T.
@(require_results)
_mul31 :: #force_inline proc "contextless" (x, y: u32) -> (res: u64) {
return u64(x) * u64(y)
}
@(private="file", require_results)
_mul31_lo :: #force_inline proc "contextless" (x, y: u32) -> (res: u32) {
return (x * y) & I31_MASK
}
// Wrapper for `div_rem`; the remainder is returned, and the quotient is
// discarded.
@(private, require_results)
_rem_u32 :: #force_inline proc "contextless" (hi: u32, lo: u32, d: u32) -> (res: u32) {
_, rem := div_rem_u32(hi, lo, d)
return rem
}
// Wrapper for `div_rem`; the quotient is returned, and the remainder is
// discarded.
@(private="file", require_results)
_div_u32 :: #force_inline proc "contextless" (hi: u32, lo: u32, d: u32) -> (quo: u32) {
q, _ := div_rem_u32(hi, lo, d)
return q
}
// Constant-time division. The dividend `hi:lo` is divided by the divisor `d`;
// the quotient and remainder are returned.
//
// If `hi == d`, then the quotient does not fit on 32 bits; returned value is thus truncated.
// If `hi > d`, returned values are indeterminate.
@(require_results)
div_rem_u32 :: proc "contextless" (hi: u32, lo: u32, d: u32) -> (quo: u32, rem: u32) {
// TODO: optimize this
hi := hi
lo := lo
ch := subtle.eq(hi, d)
hi = subtle.csel(hi, 0, ch)
for k := uint(31); k > 0; k -= 1 {
j := 32 - k
w := (hi << j) | (lo >> k)
ctl := subtle.ge(w, d) | (hi >> k)
hi2 := (w - d) >> j
lo2 := lo - (d << k)
hi = subtle.csel(hi, hi2, ctl)
lo = subtle.csel(lo, lo2, ctl)
quo |= ctl << k
}
cf := subtle.ge(lo, d) | hi
quo |= cf
rem = subtle.csel(lo, lo - d, cf)
return
}
// i31_rem computes x / y and returns the remainder.
@(require_results)
i31_rem :: proc "contextless" (x: []u32, y: u32) -> u32 {
words := uint(x[0] + 31) >> 5
x_ := x[1:]
r: u32
for i := int(words-1); i >= 0; i -= 1 {
r = _rem_u32(r, x_[i], y)
}
return r
}
// Test whether an integer `x` is zero.
@(optimization_mode="none", require_results)
i31_is_zero :: proc "contextless" (x: []u32) -> (res: u32) {
z: u32
for u := (x[0] + 31) >> 5; u > 0; u -= 1 {
z |= x[u]
}
return ~(z | -z) >> 31
}
// Add `b` to `a` and return the `carry` (`0` or `1`). if `ctl` is `1`.
// If `ctl` is `0`, `a` is left alone but the `carry` will still be computed.
//
// The slices `a` and `b` MUST have the same announced bit length (in subscript `0`)
//
// `a` and `b` MAY be the same array, but partial overlap is not allowed.
@(require_results)
i31_add :: proc "contextless" (a: []u32, b: []u32, ctl: u32) -> (carry: u32) {
words := uint(a[0] + 63) >> 5
for u in 1..<words {
aw := a[u]
bw := b[u]
naw := aw + bw + carry
carry = naw >> 31
a[u] = subtle.csel(aw, naw & I31_MASK, ctl)
}
return
}
// Subtract `b` from `a` and return the `carry` (`0` or `1`), if `ctl` is `1`.
// If `ctl` is `0`, then `a` is unmodified, but the carry is still computed
// and returned.
//
// The slices `a` and `b` MUST have the same announced bit length (in subscript `0`)
//
// `a` and `b` MAY be the same array, but partial overlap is not allowed.
@(require_results)
i31_sub :: proc "contextless" (a: []u32, b: []u32, ctl: u32) -> (carry: u32) {
words := uint(a[0] + 63) >> 5
for u in 1..<words {
aw := a[u]
bw := b[u]
naw := aw - bw - carry
carry = naw >> 31
a[u] = subtle.csel(aw, naw & I31_MASK, ctl)
}
return
}
// Compute the ENCODED actual bit length of an integer `x`.
// The argument `x` should point to the first (least significant)
// value word of the integer.
//
// The upper bit of each value word MUST be `0`.
//
// Returned value is `((k / 31) << 5) + (k % 31)` if the bit length is `k`.
//
// CT: value or length of `x` does not leak.
@(require_results)
i31_bit_length :: proc "contextless" (x: []u32) -> (res: u32) {
tw, twk: u32
xlen := len(x)
for xlen > 0 {
xlen -= 1
c := subtle.eq(tw, 0)
w := x[xlen]
tw = subtle.csel(tw, w, c)
twk = subtle.csel(twk, u32(xlen), c)
}
return (twk << 5) + _u32_bit_length(tw)
}
// Decode an integer from its big-endian unsigned representation. The
// "true" bit length of the integer is computed and set in the encoded
// announced bit length (`x[0]`), but all words of `x` corresponding to
// the full slice of source bytes.
//
// `x` needs to have a minimum length of: `1 + ((len(src) * 8) + 31) / 31`
//
// CT: value or length of `x` does not leak.
i31_decode :: proc "contextless" (x: []u32, src: []byte) {
u := len(src) - 1
v := 1
acc := u32(0)
acc_len := uint(0)
for u >= 0 {
b := u32(src[u])
acc |= b << acc_len
acc_len += 8
if acc_len >= 31 {
x[v] = acc & I31_MASK
acc_len -= 31
acc = b >> (8 - acc_len)
v += 1
}
u -= 1
}
if acc_len != 0 {
x[v] = acc
v += 1
}
x[0] = i31_bit_length(x[1:])
}
// Decode an integer from its big-endian unsigned representation.
// The integer MUST be lower than `m`; the (encoded) announced bit length
// written in `x` will be equal to that of `m`. All bytes from the
// `src` slice are read.
//
// Returned value is `1` if the decode value fits within the modulus, `0`
// otherwise. In the latter case, the `x` buffer will be set to `0` (but
// still with the announced bit length of `m`).
//
// CT: value or length of `x` does not leak. Memory access pattern depends
// only `src`'s length and the announced bit length of `m`. Whether `x` fits or
// not does not leak either.
@(require_results)
i31_decode_mod :: proc "contextless" (x: []u32, src: []byte, m: []u32) -> (res: u32) {
// Two-pass algorithm: in the first pass, we determine whether the
// value fits; in the second pass, we do the actual write.
//
// During the first pass, `res` contains the comparison result so far:
// 0x00000000 value is equal to the modulus
// 0x00000001 value is greater than the modulus
// 0xFFFFFFFF value is lower than the modulus
//
// Since we iterate starting with the least significant bytes (at
// the end of `src`), each new comparison overrides the previous
// except when the comparison yields 0 (equal).
//
// During the second pass, `res` is either 0xFFFFFFFF (value fits) 0x00000000 (value does not fit).
// We must iterate over all bytes of the source, _and_ possibly
// some extra virtual bytes (with value 0) so as to cover the
// complete modulus as well. We also add 4 such extra bytes beyond
// the modulus length because it then guarantees that no accumulated
// partial word remains to be processed.
_len := uint(len(src))
mlen := uint((m[0] + 31) >> 5)
tlen := uint(mlen << 2)
if tlen < _len {
tlen = _len
}
tlen += 4
for pass in 0..<2 {
v := uint(1)
acc := u32(0)
acc_len := u32(0)
for u in uint(0)..<tlen {
b: u32 = ---
if u < _len {
b = u32(src[_len - 1 - u])
} else {
b = 0
}
acc |= (b << acc_len)
acc_len += 8
if acc_len >= 31 {
xw := acc & I31_MASK
acc_len -= 31
acc = b >> (8 - acc_len)
if v <= mlen {
if pass == 1 {
x[v] = res & xw
} else {
cc := u32(subtle.cmp(xw, m[v]))
res = subtle.csel(cc, res, subtle.eq(cc, 0))
}
} else {
if pass == 0 {
res = subtle.csel(1, res, subtle.eq(xw, 0))
}
}
v += 1
}
}
// When we reach this point at the end of the first pass:
// r is either 0, 1 or -1; we want to set r to 0 if it
// is equal to 0 or 1, and leave it to -1 otherwise.
//
// When we reach this point at the end of the second pass:
// r is either 0 or -1; we want to leave that value
// untouched. This is a subcase of the previous.
res >>= 1
res |= (res << 1)
}
x[0] = m[0]
return res & 1
}
// Zeroize integer `x`. The announced bit length is set to the provided value,
// and the corresponding words are set to 0. The ENCODED bit length is expected
//here.
i31_zero :: proc "contextless" (x: []u32, bit_len: u32) {
x[0] = bit_len
intrinsics.mem_zero(raw_data(x[1:]), ((bit_len + 31) >> 5) * size_of(u32))
}
// Make a random integer of the provided size. The size is encoded.
// The header word is untouched.
i31_mkrand :: proc(x: []u32, esize: u32) {
_len := (esize + 31) >> 5
x_ := slice.reinterpret([]byte, x)
crypto.rand_bytes(x_[4:4 + _len * size_of(u32)])
for u in 1..<_len {
x[u] &= I31_MASK
}
m := _len & 31
if m == 0 {
x[_len] &= I31_MASK
} else {
x[_len] &= I31_MASK >> (31 - m)
}
}
// Right-shift an integer. The shift amount must be lower than 31 bits.
i31_rshift :: proc "contextless" (x: []u32, shift_amount: i32) {
_len := uint(x[0] + 31) >> 5
if _len == 0 {
return
}
count := uint(shift_amount)
r := x[1] >> count
for u in 2..= _len {
w := u32(x[u])
x[u - 1] = ((w << (31 - count)) | r) & I31_MASK
r = w >> count
}
x[_len] = r
}
// Reduce integer `a` modulo `m`. The result is written to `x`,
// and its announced bit length is set to be equal to that of `m`.
//
// `x` MUST be distinct from `a` and `m`.
//
// CT: only announced bit lengths leak, not values of `x`, `a` or `m`.
i31_reduce :: proc "contextless" (x: []u32, a: []u32, m: []u32) {
m_bitlen := m[0]
mlen := uint(m_bitlen + 31) >> 5
x[0] = m_bitlen
if m_bitlen == 0 {
return
}
// If the source is shorter, then simply copy all words from a[]
// and zero out the upper words.
a_bitlen := a[0]
alen := uint(a_bitlen + 31) >> 5
if a_bitlen < m_bitlen {
copy(x[1:], a[1:][:alen])
for u in alen..<mlen {
x[u + 1] = 0
}
return
}
// The source length is at least equal to that of the modulus.
// We must thus copy N-1 words, and input the remaining words one
// by one.
copy(x[1:], a[2 + (alen - mlen):][:mlen - 1])
x[mlen] = 0
for u := 1 + alen - mlen; u > 0; u -= 1 {
i31_muladd_small(x, a[u], m)
}
}
// Decode an integer from its big-endian unsigned representation, and
// reduce it modulo the provided modulus `m`. The announced bit length
// of the result is set to be equal to that of the modulus.
//
// `x` MUST be distinct from `m`.
i31_decode_reduce :: proc "contextless" (x: []u32, src: []byte, m: []u32) {
// Get the encoded bit length.
m_ebitlen := m[0]
// Special case for an invalid (null) modulus.
if m_ebitlen == 0 {
x[0] = 0
return
}
// Clear the destination.
i31_zero(x, m_ebitlen)
// First decode directly as many bytes as possible.
// This requires computing the actual bit length.
m_rbitlen := m_ebitlen >> 5
m_rbitlen = (m_ebitlen & 31) + (m_rbitlen << 5) - m_rbitlen
mblen := uint(m_rbitlen + 7) >> 3
k := mblen - 1
_len := uint(len(src))
if k >= _len {
i31_decode(x, src)
x[0] = m_ebitlen
return
}
i31_decode(x, src[:k])
x[0] = m_ebitlen
// Input remaining bytes, using 31-bit words.
acc := u32(0)
acc_len := uint(0)
for {
v := u32(src[k])
if acc_len >= 23 {
acc_len -= 23
acc <<= (8 - acc_len)
acc |= v >> acc_len
i31_muladd_small(x, acc, m)
acc = v & (0xFF >> (8 - acc_len))
} else {
acc = (acc << 8) | v
acc_len += 8
}
if k += 1; k >= _len {
break
}
}
// We may have some bits accumulated. We then perform a shift to
// be able to inject these bits as a full 31-bit word.
if acc_len != 0 {
acc = (acc | (x[1] << acc_len)) & I31_MASK
i31_rshift(x, i32(31 - acc_len))
i31_muladd_small(x, acc, m)
}
}
// Multiply `x` by 2^31 and then add integer `z`, modulo `m`.
// This function assumes that `x` and `m` have the same announced bit
// length, the announced bit length of `m` matches its true bit length.
//
// `x` and `m` MUST be distinct arrays.
// `z` MUST fit in 31 bits (upper bit set to 0).
//
// CT: only the common announced bit length of `x` and `m` leaks, not
// the values of `x`, `z` or `m`.
i31_muladd_small :: proc "contextless" (x: []u32, z: u32, m: []u32) {
// We can test on the modulus bit length since we accept to leak
// that length.
m_bitlen := m[0]
if m_bitlen == 0 {
return
}
hi: u32
if m_bitlen <= 31 {
hi = x[1] >> 1
lo := (x[1] << 31) | z
x[1] = _rem_u32(hi, lo, m[1])
return
}
mlen := uint(m_bitlen + 31) >> 5
mblr := uint(m_bitlen) & 31
// Principle: we estimate the quotient (x*2^31+z)/m by
// doing a 64/32 division with the high words.
//
// Let:
// w = 2^31
// a = (w*a0 + a1) * w^N + a2
// b = b0 * w^N + b2
// such that:
// 0 <= a0 < w
// 0 <= a1 < w
// 0 <= a2 < w^N
// w/2 <= b0 < w
// 0 <= b2 < w^N
// a < w*b
// I.e. the two top words of a are a0:a1, the top word of b is
// b0, we ensured that b0 is "full" (high bit set), and a is
// such that the quotient q = a/b fits on one word (0 <= q < w).
//
// If a = b*q + r (with 0 <= r < q), we can estimate q by
// doing an Euclidean division on the top words:
// a0*w+a1 = b0*u + v (with 0 <= v < b0)
// Then the following holds:
// 0 <= u <= w
// u-2 <= q <= u
hi = x[mlen]
a0, a1, b0: u32
if mblr == 0 {
a0 = x[mlen]
intrinsics.mem_copy(raw_data(x[2:]), raw_data(x[1:]), (mlen - 1) * size_of(u32))
x[1] = z
a1 = x[mlen]
b0 = m[mlen]
} else {
a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & I31_MASK
intrinsics.mem_copy(raw_data(x[2:]), raw_data(x[1:]), (mlen - 1) * size_of(u32))
x[1] = z
a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & I31_MASK
b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr)) & I31_MASK
}
// We estimate a divisor q. If the quotient returned by div()
// is g:
// -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF.
// -- Otherwise:
// -- if g == 0 then we set q = 0;
// -- otherwise, we set q = g - 1.
// The properties described above then ensure that the true
// quotient is q-1, q or q+1.
//
// Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We
// must adjust the parameters to br_div() accordingly.
g := _div_u32(a0 >> 1, a1 | (a0 << 31), b0)
q := subtle.csel(subtle.csel(g - 1, 0, subtle.eq(g, 0)), I31_MASK, subtle.eq(a0, b0))
// We subtract q*m from x (with the extra high word of value 'hi').
// Since q may be off by 1 (in either direction), we may have to
// add or subtract m afterwards.
//
// The 'tb' flag will be true (1) at the end of the loop if the
// result is greater than or equal to the modulus (not counting
// 'hi' or the carry).
cc := u32(0)
tb := u32(1)
for u in 1..= mlen {
mw := m[u]
zl := _mul31(mw, q) + u64(cc)
cc = u32(zl >> 31)
zw := u32(zl) & I31_MASK
xw := x[u]
nxw := xw - zw
cc += nxw >> 31
nxw &= I31_MASK
x[u] = nxw
tb = subtle.csel(subtle.gt(nxw, mw), tb, subtle.eq(nxw, mw))
}
// If we underestimated q, then either cc < hi (one extra bit
// beyond the top array word), or cc == hi and tb is true (no
// extra bit, but the result is not lower than the modulus). In
// these cases we must subtract m once.
//
// Otherwise, we may have overestimated, which will show as
// cc > hi (thus a negative result). Correction is adding m once.
over := subtle.gt(cc, hi)
under := ~over & (tb | subtle.lt(cc, hi))
_ = i31_add(x, m, over)
_ = i31_sub(x, m, under)
}
// Encode an integer into its big-endian unsigned representation. The
// output length in bytes is provided (parameter 'len'); if the length
// is too short then the integer is appropriately truncated; if it is
// too long then the extra bytes are set to 0.
i31_encode :: proc "contextless" (dst: []byte, x: []u32) {
xlen := uint(x[0] + 31) >> 5
if xlen == 0 {
intrinsics.mem_zero(raw_data(dst[:]), len(dst) * size_of(u32))
return
}
_len := uint(len(dst))
k := uint(1)
acc := u32(0)
acc_len := uint(0)
for _len != 0 {
w := (k <= xlen) ? x[k] : 0
k += 1
if (acc_len == 0) {
acc = w
acc_len = 31
} else {
z := acc | (w << acc_len)
acc_len -= 1
acc = w >> (31 - acc_len)
if _len >= 4 {
_len -= 4
ptr := (^u32be)(raw_data(dst[_len:]))
intrinsics.unaligned_store(ptr, u32be(z))
} else {
switch _len {
case 3:
dst[_len - 3] = byte(z >> 16)
fallthrough
case 2:
dst[_len - 2] = byte(z >> 8)
fallthrough
case 1:
dst[_len - 1] = byte(z)
}
return
}
}
}
}
// Compute `-(1/x) % 2^31`. If `x` is even, then this function returns `0`.
i31_ninv31 :: proc "contextless" (x: u32) -> (y: u32) {
y = 2 - x
y *= 2 - y * x
y *= 2 - y * x
y *= 2 - y * x
y *= 2 - y * x
return subtle.csel(0, -y, x & 1) & I31_MASK
}
// Compute a modular Montgomery multiplication. `d` is filled with the
// value of `x*y/R % m` (where `R` is the Montgomery factor).
//
// The array `d` MUST be distinct from `x`, `y` and `m`[].
// `x` and `y` MUST be numerically lower than `m`.
//
// `x` and `y` MAY be the same array.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^31`, where `m0` is the least
// significant value word of `m` (this works only if `m` is an odd integer).
i31_montymul :: proc "contextless" (d: []u32, x: []u32, y: []u32, m: []u32, m0i: u32) {
// Each outer loop iteration computes:
// `d <- (d + xu*y + f*m) / 2^31`
// We have `xu <= 2^31-1` and `f <= 2^31-1`.
// Thus, if `d <= 2*m-1` on input, then:
// `2*m-1 + 2*(2^31-1)*m <= (2^32)*m-1`
// and the new `d` value is less than `2*m`.
//
// We represent `d` over 31-bit words, with an extra word `dh`,
// which can thus be only 0 or 1.
_len := uint((m[0] + 31) >> 5)
len4 := _len & ~uint(3)
i31_zero(d, m[0])
dh := u32(0)
for u in 0..<_len {
// The carry for each operation fits on 32 bits:
// `d[v+1] <= 2^31-1`
// `xu*y[v+1] <= (2^31-1)*(2^31-1)`
// `f*m[v+1] <= (2^31-1)*(2^31-1)`
// `r <= 2^32-1`
// `(2^31-1) + 2*(2^31-1)*(2^31-1) + (2^32-1) = 2^63 - 2^31`
//
// After division by `2^31`, the new `r` is then at most `2^32-1`
//
// Using a 32-bit carry has performance benefits on 32-bit
// systems; however, on 64-bit architectures, we prefer to
// keep the carry (r) in a 64-bit register, thus avoiding some
// "clear high bits" operations.
xu := x[u + 1]
f := _mul31_lo((d[1] + _mul31_lo(xu, y[1])), m0i)
r := u64(0)
v := uint(0)
for ; v < len4; v += 4 {
z := u64(d[v + 1]) + _mul31(xu, y[v + 1]) + _mul31(f, m[v + 1]) + r
r = z >> 31
d[v + 0] = u32(z) & I31_MASK
z = u64(d[v + 2]) + _mul31(xu, y[v + 2]) + _mul31(f, m[v + 2]) + r
r = z >> 31
d[v + 1] = u32(z) & I31_MASK
z = u64(d[v + 3]) + _mul31(xu, y[v + 3]) + _mul31(f, m[v + 3]) + r
r = z >> 31
d[v + 2] = u32(z) & I31_MASK
z = u64(d[v + 4]) + _mul31(xu, y[v + 4]) + _mul31(f, m[v + 4]) + r
r = z >> 31
d[v + 3] = u32(z) & I31_MASK
}
for ; v < _len; v += 1 {
z := u64(d[v + 1]) + _mul31(xu, y[v + 1]) + _mul31(f, m[v + 1]) + r
r = z >> 31
d[v] = u32(z) & I31_MASK
}
// Since the new `dh` can only be `0` or `1`, the addition of
// the old dh with the carry MUST fit on 32 bits, and
// thus can be done into dh itself.
dh += u32(r)
d[_len] = dh & I31_MASK
dh >>= 31
}
// We must write back the bit length because it was overwritten in
// the loop (not overwriting it would require a test in the loop,
// which would yield bigger and slower code).
d[0] = m[0]
// `d` may still be greater than `m` at that point; notably, the `dh`
// word may be non-zero.
_ = i31_sub(d, m, subtle.neq(dh, 0) | subtle.not(i31_sub(d, m, 0)))
}
// Convert a modular integer to Montgomery representation.
//
// The integer `x` MUST be lower than `m`, but with the same announced bit length.
i31_to_monty :: proc "contextless" (x: []u32, m: []u32) {
// uint32_t k;
for k := (m[0] + 31) >> 5; k > 0; k -= 1 {
i31_muladd_small(x, 0, m)
}
}
// Convert a modular integer back from Montgomery representation.
//
// The integer `x` MUST be lower than `m`[], but with the same announced bit
// length.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^32`, where `m0` is the least
// significant value word of `m` (this works only if `m` is an odd integer).
i31_from_monty :: proc "contextless" (x: []u32, m: []u32, m0i: u32) {
_len := uint(m[0] + 31) >> 5
for _ in 0..<_len {
f := _mul31_lo(x[1], m0i)
cc := u64(0)
for v in 0..<_len {
z := u64(x[v + 1]) + _mul31(f, m[v + 1]) + cc
cc = z >> 31
if v != 0 {
x[v] = u32(z & I31_MASK)
}
}
x[_len] = u32(cc)
}
// We may have to do an extra subtraction, but only if the value in `x`
// is indeed greater than or equal to that of `m`, which is why we must
// do two calls:
// - First call computes the carry
// - Second call performs the subtraction only if the carry is 0).
_ = i31_sub(x, m, subtle.not(i31_sub(x, m, 0)))
}
// Compute a modular exponentiation.
//
// `x` MUST be an integer modulo `m` (same announced bit length, lower value).
// `m` MUST be odd.
//
// The exponent `e` is in big-endian unsigned notation.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^31`, where `m0` is the least
// significant value word of `m` (this works only if `m` is an odd integer).
//
// The `t1` and `t2` parameters must be temporary arrays, each large enough to
// accommodate an integer with the same size as `m`.
i31_modpow :: proc "contextless" (x: []u32, e: []byte, m: []u32, m0i: u32, t1: []u32, t2: []u32) {
// `mlen` is the length of `m` expressed in `u32`'s (including the
// "bit length" first field).
mlen := uint((m[0] + 63) >> 5)
elen := u32(len(e))
// Throughout the algorithm:
// -- `t1` is in Montgomery representation; it contains x, x^2, x^4, x^8...
// -- The result is accumulated, in normal representation, in the `x` array.
// -- `t2` is used as destination buffer for each multiplication.
//
// Note that there is no need to call `i32_from_monty()`.
copy(t1[:mlen], x[:mlen])
i31_to_monty(t1, m)
i31_zero(x, m[0])
x[1] = 1
for k := u32(0); k < (elen << 3); k += 1 {
ctl := (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1
i31_montymul(t2, x, t1, m, m0i)
for &d, i in x[:mlen] {
d = subtle.csel(d, t2[i], ctl)
}
i31_montymul(t2, t1, t1, m, m0i)
copy(t1[:mlen], t2[:mlen])
}
}
// Compute a modular exponentiation.
//
// `x` MUST be an integer modulo `m` (same announced bit length, lower value).
// `m` MUST be odd.
//
// The exponent `e` is in big-endian unsigned notation.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^31`, where `m0` is the least
// significant value word of `m`[] (this works only if m[] is an odd integer).
//
// The `tmp` array is used for temporaries; it must be large enough to accommodate
// at least two temporary values with the same size as `m` (including the leading
// "bit length" word).
//
// If there is room for more temporaries, then this function may use the extra
// room for window-based optimisation, resulting in faster computations.
//
// Returned value is `true` on success, `false` on error. An error is reported if
// the provided `tmp`array is too short.
i31_modpow_opt :: proc "contextless" (x: []u32, e: []byte, m: []u32, m0i: u32, tmp: []u32) -> u32 {
// NOTE/yawning: This is only used by the rsa_i31 code, with the key
// generation taking a function pointer to either this routine,
// or the i62 variant.
//
// If we ever need to support the i32 version, it is used extensively,
// but non e-waste architecutures will all do the right thing with
// the i62 version, albeit with a perforance hit on 32-bit CPUs.
unimplemented_contextless()
// i31_mod_pow(x, e, m, m0i, tmp[:len(m)], tmp[len(m):])
// return 1
}
// Compute `d+a*b`, result in `d`.
//
// The initial announced bit length of `d` MUST match that of `a`[].
//
// The `d` array MUST be large enough to accommodate the full result,
// plus (possibly) an extra word. The resulting announced bit length
// of `d` will be the sum of the announced bit lengths of `a` and `b`
// (therefore, it may be larger than the actual bit length of the numerical result).
//
// `a` and `b` may be the same array. `d` must be disjoint from both `a` and `b`.
i31_mulacc :: proc "contextless" (d: []u32, a: []u32, b: []u32) {
a_len := uint((a[0] + 31) >> 5)
b_len := uint((b[0] + 31) >> 5)
// We want to add the two bit lengths, but these are encoded,
// which requires some extra care.
d_l := (a[0] & 31) + (b[0] & 31)
d_h := (a[0] >> 5) + (b[0] >> 5)
d[0] = (d_h << 5) + d_l + (~u32(d_l - 31) >> 31)
for u in 0..<b_len {
// Carry always fits on 31 bits; we want to keep it in a
// 32-bit register on 32-bit architectures (on a 64-bit
// architecture, cast down from 64 to 32 bits means
// clearing the high bits, which is not free; on a 32-bit
// architecture, the same operation really means ignoring
// the top register, which has negative or zero cost).
f := b[1 + u]
cc := u64(0)
for v in 0..<a_len {
z := u64(d[1 + u + v]) + _mul31(f, a[1 + v]) + cc
cc = z >> 31
d[1 + u + v] = u32(z) & I31_MASK
}
d[1 + u + a_len] = u32(cc)
}
}

View File

@@ -0,0 +1,361 @@
package _bigint
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:math/bits"
import subtle "core:crypto/_subtle"
import "core:slice"
@(private="file")
I62_MASK :: 0x3fff_ffff_ffff_ffff
// Compute x*y+v1+v2. Operands are 64-bit, and result is 128-bit, with
// high word in "hi" and low word in "lo".
@(private="file", require_results)
_fma1 :: #force_inline proc "contextless" (x, y, v1, v2: u64) -> (hi, lo: u64) {
hi, lo = bits.mul_u64(x, y)
carry: u64
lo, carry = bits.add_u64(lo, v1, 0)
hi += carry
lo, carry = bits.add_u64(lo, v2, 0)
hi += carry
return
}
// Compute x1*y1+x2*y2+v1+v2. Operands are 64-bit, and result is 128-bit,
// with high word in "hi" and low word in "lo".
//
// Callers should ensure that the two inner products, and the v1 and v2
// operands, are multiple of 4 (this is not used by this specific definition
// but may help other implementations).
@(private="file", require_results)
_fma2 :: #force_inline proc "contextless" (x1, y1, x2, y2, v1, v2: u64) -> (hi, lo: u64) {
hi_1, lo_1 := bits.mul_u64(x1, y1)
hi_2, lo_2 := bits.mul_u64(x2, y2)
carry: u64
lo, carry = bits.add_u64(lo_1, lo_2, 0)
hi, _ = bits.add_u64(hi_1, hi_2, carry)
lo, carry = bits.add_u64(lo, v1, 0)
hi += carry
lo, carry = bits.add_u64(lo, v2, 0)
hi += carry
return
}
@(private="file", require_results)
_mul62_lo :: #force_inline proc "contextless" (x, y: u64) -> u64 {
return (x * y) & I62_MASK
}
// Subtract b from a, and return the final carry. If 'ctl32' is 0, then
// a[] is kept unmodified, but the final carry is still computed and
// returned.
@(private="file", require_results)
_i62_sub :: proc "contextless" (a, b: []u64, num: int, ctl32: u32) -> u32 {
cc: u64
ctl := -ctl32
mask := u64(ctl) | (u64(ctl) << 32)
for u in 0..<num {
aw := a[u]
bw := b[u]
dw := aw - bw - cc
cc = dw >> 63
dw &= I62_MASK
a[u] = aw ~ (mask & (dw ~ aw))
}
return u32(cc)
}
// Montgomery multiplication, over arrays of 62-bit values. The
// destination array (d) must be distinct from the other operands
// (x, y and m). All arrays are in little-endian format (least
// significant word comes first) over 'num' words.
@(private="file")
_i62_montymul :: proc "contextless" (d, x, y, m: []u64, num: int, m0i: u64) {
dh: u64
num4 := 1 + u64((num - 1) & ~int(3))
intrinsics.mem_zero(raw_data(d), num * size_of(u64))
for u in 0..<num {
xu := x[u] << 2
f := _mul62_lo(d[0] + _mul62_lo(x[u], y[0]), m0i) << 2
hi, lo := _fma2(xu, y[0], f, m[0], d[0] << 2, 0)
r := hi
v: int
for v = 1; v < int(num4); v += 4 {
hi, lo = _fma2(xu, y[v + 0], f, m[v + 0], d[v + 0] << 2, r << 2)
r = hi + (r >> 62)
d[v - 1] = lo >> 2
hi, lo = _fma2(xu, y[v + 1], f, m[v + 1], d[v + 1] << 2, r << 2)
r = hi + (r >> 62)
d[v + 0] = lo >> 2
hi, lo = _fma2(xu, y[v + 2], f, m[v + 2], d[v + 2] << 2, r << 2)
r = hi + (r >> 62)
d[v + 1] = lo >> 2
hi, lo = _fma2(xu, y[v + 3], f, m[v + 3], d[v + 3] << 2, r << 2)
r = hi + (r >> 62)
d[v + 2] = lo >> 2
}
for ; v < num; v += 1 {
hi, lo = _fma2(xu, y[v], f, m[v], d[v] << 2, r << 2)
r = hi + (r >> 62)
d[v - 1] = lo >> 2
}
zh := dh + r
d[num - 1] = zh & I62_MASK
dh = zh >> 62
}
_ = _i62_sub(d, m, num, u32(dh) | subtle.not(_i62_sub(d, m, num, 0)))
}
// Conversion back from Montgomery representation.
@(private="file")
_i62_frommonty :: proc "contextless" (x, m: []u64, num: int, m0i: u64) {
for _ in 0..<num {
cc: u64
f := _mul62_lo(x[0], m0i) << 2
for v in 0..<num {
hi, lo := _fma1(f, m[v], x[v] << 2, cc)
cc = hi << 2
if (v != 0) {
x[v - 1] = lo >> 2
}
}
x[num - 1] = cc >> 2
}
_ = _i62_sub(x, m, num, subtle.not(_i62_sub(x, m, num, 0)))
}
// Variant of i31_modpow_opt() that internally uses 64x64->128
// multiplications. It expects the same parameters as i31_modpow_opt(),
// except that the temporaries should be 64-bit integers, not 32-bit
// integers.
i62_modpow_opt :: proc "contextless" (x31: []u32, e: []byte, m31: []u32, m0i31: u32, tmp: []u64) -> u32 {
twlen := len(tmp)
// Get modulus size, in words.
mw31num := int((m31[0] + 31) >> 5)
mw62num := int((mw31num + 1) >> 1)
// In order to apply this function, we must have enough room to
// copy the operand and modulus into the temporary array, along
// with at least two temporaries. If there is not enough room,
// switch to br_i31_modpow(). We also use br_i31_modpow() if the
// modulus length is not at least four words (94 bits or more).
if mw31num < 4 || mw62num << 2 > twlen {
// We assume here that we can split an aligned uint64_t
// into two properly aligned uint32_t. Since both types
// are supposed to have an exact width with no padding,
// then this property must hold.
txlen := mw31num + 1
if twlen < txlen {
return 0
}
tmp_as_u32s := slice.reinterpret([]u32, tmp)
t1, t2 := tmp_as_u32s[:txlen], tmp_as_u32s[txlen:]
i31_modpow(x31, e, m31, m0i31, t1, t2)
return 1
}
// Convert x to Montgomery representation: this means that
// we replace x with x*2^z mod m, where z is the smallest multiple
// of the word size such that 2^z >= m. We want to reuse the 31-bit
// functions here (for constant-time operation), but we need z
// for a 62-bit word size.
for _ in 0..<mw62num {
i31_muladd_small(x31, 0, m31)
i31_muladd_small(x31, 0, m31)
}
// Assemble operands into arrays of 62-bit words. Note that
// all the arrays of 62-bit words that we will handle here
// are without any leading size word.
//
// We also adjust tmp and twlen to account for the words used
// for these extra arrays.
m := tmp[:mw62num]
x := tmp[mw62num:mw62num*2]
tmp_ := tmp[mw62num << 1:]
twlen -= mw62num << 1
for u := 0; u < mw31num; u += 2 {
v := u >> 1
if u + 1 == mw31num {
m[v] = u64(m31[u + 1])
x[v] = u64(x31[u + 1])
} else {
m[v] = u64(m31[u + 1]) + (u64(m31[u + 2]) << 31)
x[v] = u64(x31[u + 1]) + (u64(x31[u + 2]) << 31)
}
}
// Compute window size. We support windows up to 5 bits; for a
// window of size k bits, we need 2^k+1 temporaries (for k = 1,
// we use special code that uses only 2 temporaries).
win_len: int
for win_len = 5; win_len > 1; win_len -= 1 {
if (1 << uint(win_len) + 1) * mw62num <= twlen {
break
}
}
t1 := tmp_[:mw62num]
t2 := tmp_[mw62num:]
// Compute m0i, which is equal to -(1/m0) mod 2^62. We were
// provided with m0i31, which already fulfills this property
// modulo 2^31; the single expression below is then sufficient.
m0i := u64(m0i31)
m0i = _mul62_lo(m0i, 2 + _mul62_lo(m0i, m[0]))
// Compute window contents. If the window has size one bit only,
// then t2 is set to x; otherwise, t2[0] is left untouched, and
// t2[k] is set to x^k (for k >= 1).
if win_len == 1 {
copy(t2, x)
} else {
copy(t2[mw62num:], x)
base := t2[mw62num:]
for u := 2; u < 1 << uint(win_len); u += 1 {
_i62_montymul(base[mw62num:], base, x, m, mw62num, m0i)
base = base[mw62num:]
}
}
// Set x to 1, in Montgomery representation. We again use the
// 31-bit code.
i31_zero(x31, m31[0])
x31[(m31[0] + 31) >> 5] = 1
i31_muladd_small(x31, 0, m31)
if mw31num & 1 != 0 {
i31_muladd_small(x31, 0, m31)
}
for u := 0; u < mw31num; u+= 2 {
v := u >> 1
if u + 1 == mw31num {
x[v] = u64(x31[u + 1])
} else {
x[v] = u64(x31[u + 1]) + (u64(x31[u + 2]) << 31)
}
}
e_, e_len := e, len(e)
// We process bits from most to least significant. At each
// loop iteration, we have acc_len bits in acc.
acc: u32
acc_len: uint
for acc_len > 0 || e_len > 0 {
// Get the next bits.
k := uint(win_len)
if acc_len < uint(win_len) {
if e_len > 0 {
acc = (acc << 8) | u32(e_[0])
e_ = e_[1:]
e_len -= 1
acc_len += 8
} else {
k = acc_len
}
}
bits := (acc >> (acc_len - k)) & ((u32(1) << k) - 1)
acc_len -= k
// We could get exactly k bits. Compute k squarings.
for _ in 0..<k {
_i62_montymul(t1, x, x, m, mw62num, m0i)
copy(x, t1)
}
// Window lookup: we want to set t2 to the window
// lookup value, assuming the bits are non-zero. If
// the window length is 1 bit only, then t2 is
// already set; otherwise, we do a constant-time lookup.
if win_len > 1 {
intrinsics.mem_zero(raw_data(t2), mw62num * size_of(u64))
base := t2[mw62num:]
for u := u32(1); u < u32(1) << k; u += 1 {
mask := -u64(subtle.eq(u, bits))
for v in 0..<mw62num {
t2[v] |= mask & base[v]
}
base = base[mw62num:]
}
}
// Multiply with the looked-up value. We keep the product
// only if the exponent bits are not all-zero.
_i62_montymul(t1, x, t2, m, mw62num, m0i)
mask1 := -u64(subtle.eq(bits, 0))
mask2 := ~mask1
for u in 0..<mw62num {
x[u] = (mask1 & x[u]) | (mask2 & t1[u])
}
}
// Convert back from Montgomery representation.
_i62_frommonty(x, m, mw62num, m0i)
// Convert result into 31-bit words.
for u := 0; u < mw31num; u += 2 {
zw := u64(x[u >> 1])
x31[u + 1] = u32(zw) & I31_MASK
if u + 1 < mw31num {
x31[u + 2] = u32(zw >> 31)
}
}
return 1
}
// Wrapper for i62_modpow_opt() that uses the same type as
// i31_modpow_opt(); however, it requires its 'tmp' argument to the
// 64-bit aligned.
i62_modpow_opt_as_i31 :: proc "contextless" (x31: []u32, e: []byte, m31: []u32, m0i31: u32, tmp: []u32) -> u32 {
// As documented, this function expects the 'tmp' argument to be
// 64-bit aligned. This is OK since this function is internal (it
// is not part of BearSSL's public API).
ensure_contextless(uintptr(raw_data(tmp)) & 7 == 0)
ensure_contextless(len(tmp) & 1 == 0) // Length MUST be even.
tmp_as_u64s := slice.reinterpret([]u64, tmp)
return i62_modpow_opt(x31, e, m31, m0i31, tmp_as_u64s)
}

View File

@@ -0,0 +1,265 @@
package _bigint
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import subtle "core:crypto/_subtle"
import "core:math/big"
import "core:slice"
// Perform trial divisions on a candidate prime. We opt for the simple
// route and "just" compute a series of trial divisions.
//
// Returned value is 1 on success (none of the small primes
// divides x), 0 on error (a non-trivial GCD is obtained).
@(private="file", require_results)
trial_divisions :: proc "contextless" (x: []u32) -> u32 {
for factor in big._private_prime_table {
if factor <= 11 {
continue
}
if i31_rem(x, u32(factor)) == 0 {
return 0
}
}
return 1
}
// Perform n rounds of Miller-Rabin on the candidate prime x. This
// function assumes that x = 3 mod 4.
//
// WARNING: t MUST be 64-bit aligned, and be large enough such that
// it can hold 4 encoded integers that have the same number of limbs
// as x.
//
// Returned value is 1 on success (all rounds completed successfully),
// 0 otherwise.
@(private="file", require_results)
i62_miller_rabin :: proc(x: []u32, n: int, t: []u32) -> u32 {
// Since x = 3 mod 4, the Miller-Rabin test is simple:
// - get a random base a (such that 1 < a < x-1)
// - compute z = a^((x-1)/2) mod x
// - if z != 1 and z != x-1, the number x is composite
//
// We generate bases 'a' randomly with a size which is
// one bit less than x, which ensures that a < x-1. It
// is not useful to verify that a > 1 because the probability
// that we get a value a equal to 0 or 1 is much smaller
// than the probability of our Miller-Rabin tests not to
// detect a composite, which is already quite smaller than the
// probability of the hardware misbehaving and return a
// composite integer because of some glitch (e.g. bad RAM
// or ill-timed cosmic ray).
// Compute (x-1)/2 (encoded).
xm1d2 := slice.reinterpret([]byte, t)
xm1d2_len := ((x[0] - (x[0] >> 5)) + 7) >> 3
i31_encode(xm1d2[:xm1d2_len], x)
cc: u32
for u in 0..<xm1d2_len {
w := u32(xm1d2[u])
xm1d2[u] = byte((w >> 1) | cc)
cc = w << 7
}
// We used some words of the provided buffer for (x-1)/2.
xm1d2_len_u32 := (xm1d2_len + 3) >> 2
t_ := t[xm1d2_len_u32:]
tlen := len(t_)
xlen := (x[0] + 31) >> 5
asize := x[0] - 1 - subtle.eq0(x[0] & 31)
x0i := i31_ninv31(x[1])
for _ in 0..<n {
// Generate a random base. We don't need the base to be
// really uniform modulo x, so we just get a random
// number which is one bit shorter than x.
a := t_
a[0] = x[0]
a[xlen] = 0
i31_mkrand(a, asize)
// Compute a^((x-1)/2) mod x. We assume here that the
// function will not fail (the temporary array is large
// enough).
t2 := t_[1 + xlen:]
t2len := tlen - 1 - int(xlen)
if (t2len & 1) != 0 {
// Since the source array is 64-bit aligned and
// has an even number of elements (TEMPS), we
// can use the parity of the remaining length to
// detect and adjust alignment.
t2 = t2[1:]
t2len -= 1
}
i62_modpow_opt_as_i31(a, xm1d2[:xm1d2_len], x, x0i, t2[:t2len])
// We must obtain either 1 or x-1. Note that x is odd,
// hence x-1 differs from x only in its low word (no
// carry).
eq1 := a[1] ~ 1
eqm1 := a[1] ~ (x[1] - 1)
for u in 2..=xlen {
eq1 |= a[u]
eqm1 |= a[u] ~ x[u]
}
if ((subtle.eq0(eq1) | subtle.eq0(eqm1)) == 0) {
return 0
}
}
return 1
}
// Create a random prime of the provided size. 'esize' is the _encoded_
// bit length. The two top bits and the two bottom bits are set to 1.
i62_mkprime :: proc(x: []u32, esize: u32, pubexp: u32, t: []u32) {
x[0] = esize
_len := (esize + 31) >> 5
for {
// Generate random bits. We force the two top bits and the
// two bottom bits to 1.
i31_mkrand(x, esize)
if (esize & 31) == 0 {
x[_len] |= 0x60000000
} else if (esize & 31) == 1 {
x[_len] |= 0x00000001
x[_len - 1] |= 0x40000000
} else {
x[_len] |= 0x00000003 << ((esize & 31) - 2)
}
x[1] |= 0x00000003
// Trial division with low primes (3, 5, 7 and 11). We
// use the following properties:
//
// 2^2 = 1 mod 3
// 2^4 = 1 mod 5
// 2^3 = 1 mod 7
// 2^10 = 1 mod 11
m3, m5, m7, m11: u32
s7, s11: uint
for u in 0..<_len {
w := x[1 + u]
w3 := (w & 0xFFFF) + (w >> 16) // max: 98302
w5 := (w & 0xFFFF) + (w >> 16) // max: 98302
w7 := (w & 0x7FFF) + (w >> 15) // max: 98302
w11 := (w & 0xFFFFF) + (w >> 20) // max: 1050622
m3 += w3 << (u & 1)
m3 = (m3 & 0xFF) + (m3 >> 8) // max: 1025
m5 += w5 << ((4 - u) & 3)
m5 = (m5 & 0xFFF) + (m5 >> 12) // max: 4479
m7 += w7 << s7
m7 = (m7 & 0x1FF) + (m7 >> 9) // max: 1280
s7 += 1
if s7 == 3 {
s7 = 0
}
m11 += w11 << s11
s11 += 1
if s11 == 10 {
s11 = 0
}
m11 = (m11 & 0x3FF) + (m11 >> 10) // max: 526847
}
m3 = (m3 & 0x3F) + (m3 >> 6) // max: 78
m3 = (m3 & 0x0F) + (m3 >> 4) // max: 18
m3 = ((m3 * 43) >> 5) & 3
m5 = (m5 & 0xFF) + (m5 >> 8) // max: 271
m5 = (m5 & 0x0F) + (m5 >> 4) // max: 31
m5 -= 20 & -subtle.gt(m5, 19)
m5 -= 10 & -subtle.gt(m5, 9)
m5 -= 5 & -subtle.gt(m5, 4)
m7 = (m7 & 0x3F) + (m7 >> 6) // max: 82
m7 = (m7 & 0x07) + (m7 >> 3) // max: 16
m7 = ((m7 * 147) >> 7) & 7
// 2^5 = 32 = -1 mod 11.
m11 = (m11 & 0x3FF) + (m11 >> 10) // max: 1536
m11 = (m11 & 0x3FF) + (m11 >> 10) // max: 1023
m11 = (m11 & 0x1F) + 33 - (m11 >> 5) // max: 64
m11 -= 44 & -subtle.gt(m11, 43)
m11 -= 22 & -subtle.gt(m11, 21)
m11 -= 11 & -subtle.gt(m11, 10)
// If any of these modulo is 0, then the candidate is
// not prime. Also, if pubexp is 3, 5, 7 or 11, and the
// corresponding modulus is 1, then the candidate must
// be rejected, because we need e to be invertible
// modulo p-1. We can use simple comparisons here
// because they won't leak information on a candidate
// that we keep, only on one that we reject (and is thus
// not secret).
if m3 == 0 || m5 == 0 || m7 == 0 || m11 == 0 {
continue
}
if (pubexp == 3 && m3 == 1) || (pubexp == 5 && m5 == 1) || (pubexp == 7 && m7 == 1) || (pubexp == 11 && m11 == 1) {
continue
}
// More trial divisions.
if trial_divisions(x) == 0 {
continue
}
// Miller-Rabin algorithm. Since we selected a random
// integer, not a maliciously crafted integer, we can use
// relatively few rounds to lower the risk of a false
// positive (i.e. declaring prime a non-prime) under
// 2^(-80). It is not useful to lower the probability much
// below that, since that would be substantially below
// the probability of the hardware misbehaving. Sufficient
// numbers of rounds are extracted from the Handbook of
// Applied Cryptography, note 4.49 (page 149).
//
// Since we work on the encoded size (esize), we need to
// compare with encoded thresholds.
rounds: int
switch {
case esize < 309:
rounds = 12
case esize < 464:
rounds = 9
case esize < 670:
rounds = 6
case esize < 877:
rounds = 4
case esize < 1341:
rounds = 3
case:
rounds = 2
}
if i62_miller_rabin(x, rounds, t) == 1 {
return
}
}
}

View File

@@ -71,7 +71,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
// This will only underflow if and only if (⟺) arg1 == arg2, and we return the borrow,
// which will be 1.
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
is_eq := subtle.eq0(fe_non_zero(&tmp))
fe_clear(&tmp)

View File

@@ -77,7 +77,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
// This will only underflow if and only if (⟺) arg1 == arg2, and we return the borrow,
// which will be 1.
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
is_eq := subtle.eq0(fe_non_zero(&tmp))
fe_clear(&tmp)

View File

@@ -60,7 +60,7 @@ fe_from_bytes :: proc "contextless" (
reduced[3], borrow = bits.sub_u64(tmp[3], ELL[3], borrow)
reduced[4], borrow = bits.sub_u64(tmp[4], ELL[4], borrow)
reduced[5], borrow = bits.sub_u64(tmp[5], ELL[5], borrow)
need_reduced := subtle.u64_is_zero(borrow)
need_reduced := subtle.eq0(borrow)
fe_cond_select(&tmp, &tmp, &reduced, int(need_reduced))
fe_to_montgomery(out1, &tmp)

View File

@@ -130,7 +130,7 @@ poly_frommsg :: proc "contextless" (r: ^Poly, msg: []byte) #no_bounds_check {
for i in 0..<N/8 {
for j in 0..<8 {
r.coeffs[8*i+j] = subtle.csel_i16(0, (Q+1)/2, int(msg[i] >> uint(j))&1)
r.coeffs[8*i+j] = subtle.csel_i16(0, (Q+1)/2, (msg[i] >> uint(j))&1)
}
}
}

View File

@@ -3,76 +3,245 @@ Various useful bit operations in constant time.
*/
package _subtle
import "core:crypto/_fiat"
import "core:math/bits"
import "base:intrinsics"
// byte_eq returns 1 if and only if (⟺) a == b, 0 otherwise.
@(optimization_mode="none")
byte_eq :: proc "contextless" (a, b: byte) -> int {
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Constant-time primitives. These functions manipulate integer values in
// order to provide constant-time comparisons and multiplexers.
//
// Boolean values (the "ctl" bits) MUST have value 0 or 1.
//
// Implementation notes:
// =====================
//
// The uintN_t types are unsigned and with width exactly N bits; the C
// standard guarantees that computations are performed modulo 2^N, and
// there can be no overflow. Negation (unary '-') works on unsigned types
// as well.
//
// The intN_t types are guaranteed to have width exactly N bits, with no
// padding bit, and using two's complement representation. Casting
// intN_t to uintN_t really is conversion modulo 2^N. Beware that intN_t
// types, being signed, trigger implementation-defined behaviour on
// overflow (including raising some signal): with GCC, while modular
// arithmetics are usually applied, the optimizer may assume that
// overflows don't occur (unless the -fwrapv command-line option is
// added); Clang has the additional -ftrapv option to explicitly trap on
// integer overflow or underflow.
// This code only works on a two's complement system.
#assert((-1 & 3) == 3)
// not negates a boolean which MUST be `0` or `1`
@(optimization_mode="none", require_results)
not :: proc "contextless" (ctrl: $T) -> T where intrinsics.type_is_unsigned(T) {
return ctrl ~ 1
}
@(optimization_mode="none", require_results)
byte_eq :: proc "contextless" (a, b: byte) -> byte {
v := a ~ b
// v == 0 if and only if (⟺) a == b. The subtraction will underflow, setting the
// sign bit, which will get returned.
return int((u32(v)-1) >> 31)
return byte((u32(v)-1) >> 31)
}
// u64_eq returns 1 if and only if (⟺) a == b, 0 otherwise.
@(optimization_mode="none")
@(optimization_mode="none", require_results)
u32_eq :: proc "contextless" (a, b: u32) -> u32 {
q := a ~ b
return ((q | -q) >> 31) ~ 1
}
@(optimization_mode="none", require_results)
u64_eq :: proc "contextless" (a, b: u64) -> u64 {
_, borrow := bits.sub_u64(0, a ~ b, 0)
return (~borrow) & 1
q := a ~ b
return ((q | -q) >> 63) ~ 1
}
// eq returns 1 if and only if (⟺) a == b, 0 otherwise.
eq :: proc {
byte_eq,
u32_eq,
u64_eq,
}
// u64_is_zero returns 1 if and only if (⟺) a == 0, 0 otherwise.
@(optimization_mode="none")
u64_is_zero :: proc "contextless" (a: u64) -> u64 {
_, borrow := bits.sub_u64(a, 1, 0)
return borrow
@(require_results)
byte_neq :: proc "contextless" (a, b: byte) -> byte {
return #force_inline byte_eq(a, b) ~ 1
}
// u64_is_non_zero returns 1 if and only if (⟺) a != 0, 0 otherwise.
@(optimization_mode="none")
u64_is_non_zero :: proc "contextless" (a: u64) -> u64 {
is_zero := u64_is_zero(a)
return (~is_zero) & 1
@(optimization_mode="none", require_results)
u32_neq :: proc "contextless" (a, b: u32) -> u32 {
q := a ~ b
return (q | -q) >> 31
}
@(optimization_mode="none")
cmov_bytes :: proc "contextless" (dst, src: []byte, ctrl: int) {
@(optimization_mode="none", require_results)
u64_neq :: proc "contextless" (a, b: u64) -> u64 {
q := a ~ b
return (q | -q) >> 63
}
// neq returns 1 if and only if (⟺) a != b, 0 otherwise.
neq :: proc {
byte_neq,
u32_neq,
u64_neq,
}
@(optimization_mode="none", require_results)
u32_gt :: proc "contextless" (x, y: u32) -> u32 {
/*
* If both x < 2^31 and y < 2^31, then y-x will have its high
* bit set if x > y, cleared otherwise.
*
* If either x >= 2^31 or y >= 2^31 (but not both), then the
* result is the high bit of x.
*
* If both x >= 2^31 and y >= 2^31, then we can virtually
* subtract 2^31 from both, and we are back to the first case.
* Since (y-2^31)-(x-2^31) = y-x, the subtraction is already
* fine.
*/
z := y - x
return (z ~ ((x ~ y) & (x ~ z))) >> 31
}
@(optimization_mode="none", require_results)
u64_gt :: proc "contextless" (x, y: u64) -> u64 {
z := y - x
return (z ~ ((x ~ y) & (x ~ z))) >> 63
}
// gt returns 1 if x > y, 0 otherwise.
gt :: proc {
u32_gt,
u64_gt,
}
// gt returns 1 if x >= y, 0 otherwise.
@(require_results)
ge :: proc "contextless" (x, y: $T) -> T where T == u32 || T == u64 {
return #force_inline(gt(y, x)) ~ 1
}
// lt returns 1 if x < y, 0 otherwise.
@(require_results)
lt :: proc "contextless" (x, y: $T) -> T where T == u32 || T == u64 {
return #force_inline(gt(y, x))
}
// le returns 1 if x <= y, 0 otherwise.
@(require_results)
le :: proc "contextless" (x, y: $T) -> T where T == u32 || T == u64 {
return #force_inline(gt(x, y)) ~ 1
}
@(require_results)
u32_cmp :: proc "contextless" (x, y: u32) -> i32 {
return i32(#force_inline gt(x, y)) | -i32(#force_inline gt(y, x))
}
@(require_results)
u64_cmp :: proc "contextless" (x, y: u64) -> i64 {
return i64(#force_inline gt(x, y)) | -i64(#force_inline gt(y, x))
}
// cmp returns -1, 0, or 1, depending on wheter x is lower than, equal
// to, or greater than y.
cmp :: proc {
u32_cmp,
u64_cmp,
}
// eq0 returns 1 if and only if (⟺) a == 0, 0 otherwise.
@(require_results)
eq0 :: proc "contextless" (a: $T) -> T where T == u32 || T == u64 {
return #force_inline eq(a, 0)
}
// neq0 returns 1 if and only if (⟺) a != 0, 0 otherwise.
@(require_results)
neq0 :: proc "contextless" (a: $T) -> T where T == u32 || T == u64 {
return #force_inline eq(a, 0) ~ 1
}
cmov_bytes :: proc "contextless" (dst, src: []byte, #any_int ctrl: int) {
ensure_contextless(len(src) == len(dst), "crypto: cmov length mismatch")
cmov_impl(dst, src, ctrl)
}
cmov_u32s :: proc "contextless" (dst, src: []u32, #any_int ctrl: int) {
ensure_contextless(len(src) == len(dst), "crypto: cmov length mismatch")
cmov_impl(dst, src, ctrl)
}
@(private="file", optimization_mode="none")
cmov_impl :: proc "contextless"(dst, src: []$T, ctrl: int) {
s_len := len(src)
ensure_contextless(s_len == len(dst), "crypto: cmov length mismatch")
c := -(byte)(ctrl)
c := -(T)(ctrl)
for i in 0..<s_len {
dst[i] ~= c & (dst[i] ~ src[i])
}
}
@(optimization_mode="none")
csel_i16 :: proc "contextless" (a, b: i16, ctrl: int) -> i16 {
c := -(u16)(ctrl)
// cmov copies `src` into `dst` if and only if (⟺) ctrl == 1. `dst` and
// `src` may overlap completely (but not partially).
cmov :: proc {
cmov_bytes,
cmov_u32s,
}
@(optimization_mode="none", require_results)
csel_i16 :: proc "contextless" (a, b: i16, #any_int ctrl: u16) -> i16 {
c := -ctrl
return a ~ i16(c & u16(a ~ b))
}
@(optimization_mode="none")
csel_u16 :: proc "contextless" (a, b: u16, ctrl: int) -> u16 {
c := -(u16)(ctrl)
@(optimization_mode="none", require_results)
csel_u16 :: proc "contextless" (a, b: u16, #any_int ctrl: u16) -> u16 {
c := -ctrl
return a ~ (c & (a ~ b))
}
csel_u32 :: proc "contextless" (a, b: u32, ctrl: int) -> u32 {
return _fiat.cmovznz_u32(_fiat.u1(ctrl), a, b)
@(optimization_mode="none", require_results)
csel_u32 :: proc "contextless" (a, b: u32, #any_int ctrl: u32) -> u32 {
c := -ctrl
return a ~ (c & (a ~ b))
}
csel_u64 :: proc "contextless" (a, b: u64, ctrl: int) -> u64 {
return _fiat.cmovznz_u64(_fiat.u1(ctrl), a, b)
@(optimization_mode="none", require_results)
csel_u64 :: proc "contextless" (a, b: u64, #any_int ctrl: u64) -> u64 {
c := -ctrl
return a ~ (c & (a ~ b))
}
// csel returns `a` if ctl == `0`, `b` if ctl == `1`.
csel :: proc {
csel_i16,
csel_u16,

View File

@@ -196,10 +196,10 @@ fe_gen_y_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) {
@(require_results)
fe_is_zero_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) -> int {
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
return int(subtle.eq0(p256r1.fe_non_zero(fe)))
}
@(require_results)
fe_is_zero_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) -> int {
return int(subtle.u64_is_zero(p384r1.fe_non_zero(fe)))
return int(subtle.eq0(p384r1.fe_non_zero(fe)))
}

View File

@@ -133,10 +133,10 @@ sc_is_zero :: proc {
@(require_results)
sc_is_zero_p256r1 :: proc "contextless" (fe: ^Scalar_p256r1) -> int {
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
return int(subtle.eq0(p256r1.fe_non_zero(fe)))
}
@(require_results)
sc_is_zero_p384r1 :: proc "contextless" (fe: ^Scalar_p384r1) -> int {
return int(subtle.u64_is_zero(p384r1.fe_non_zero(fe)))
return int(subtle.eq0(p384r1.fe_non_zero(fe)))
}

View File

@@ -293,7 +293,7 @@ when crypto.COMPACT_IMPLS == false {
// conditionally select the right result.
pt_add_mixed(tmp, point, &tmp.x, &tmp.y)
ctrl := subtle.u64_is_non_zero(idx)
ctrl := subtle.neq0(idx)
pt_cond_select(point, point, tmp, int(ctrl))
}
}

View File

@@ -49,7 +49,7 @@ compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> i
// After the loop, v == 0 if and only if (⟺) a == b. The subtraction will underflow
// if and only if (⟺) v == 0, setting the sign-bit, which gets returned.
return subtle.eq(0, v)
return int(subtle.eq(0, v))
}
// is_zero_constant_time returns 1 if and only if (⟺) b is all 0s, 0 otherwise.
@@ -59,7 +59,7 @@ is_zero_constant_time :: proc "contextless" (b: []byte) -> int {
v |= b_
}
return subtle.byte_eq(0, v)
return int(subtle.byte_eq(0, v))
}
/*

View File

@@ -50,6 +50,7 @@ Public_Key :: struct {
// private_key_generate uses the system entropy source to generate a new
// Private_Key. This will only fail if and only if (⟺) the system entropy source is
// missing or broken.
@(require_results)
private_key_generate :: proc(priv_key: ^Private_Key) -> bool {
private_key_clear(priv_key)
@@ -61,13 +62,12 @@ private_key_generate :: proc(priv_key: ^Private_Key) -> bool {
defer crypto.zero_explicit(&b, size_of(b))
crypto.rand_bytes(b[:])
private_key_set_bytes(priv_key, b[:])
return true
return private_key_set_bytes(priv_key, b[:])
}
// private_key_set_bytes decodes a byte-encoded private key, and returns
// true if and only if (⟺) the operation was successful.
@(require_results)
private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
if len(b) != PRIVATE_KEY_SIZE {
return false
@@ -189,6 +189,7 @@ sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
// public_key_set_bytes decodes a byte-encoded public key, and returns
// true if and only if (⟺) the operation was successful.
@(require_results)
public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) -> bool {
if len(b) != PUBLIC_KEY_SIZE {
return false
@@ -237,6 +238,7 @@ public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
}
// public_key_equal returns true if and only if (⟺) pub_key is equal to other.
@(require_results)
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
ensure(pub_key._is_initialized && other._is_initialized, "crypto/ed25519: uninitialized public key")
@@ -254,6 +256,7 @@ public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
// implementation strictly compatible with FIPS 186-5, at the expense of
// SBS-security. Doing so is NOT recommended, and the disallowed
// public keys all have a known discrete-log.
@(require_results)
verify :: proc(pub_key: ^Public_Key, msg, sig: []byte, allow_small_order_A := false) -> bool {
switch {
case !pub_key._is_initialized:

7
core/crypto/rsa/doc.odin Normal file
View File

@@ -0,0 +1,7 @@
/*
RSA (RivestShamirAdleman) cryptosystem.
See:
- [[ https://www.rfc-editor.org/info/rfc8017/ ]]
*/
package rsa

444
core/crypto/rsa/rsa.odin Normal file
View File

@@ -0,0 +1,444 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:bytes"
import "core:crypto"
import subtle "core:crypto/_subtle"
import "core:encoding/endian"
// Minimum size for a RSA modulus (in bits).
//
// Note: 1024-bits is arguably insufficient as of this writing, with
// 2048-bits being a more sensible value, however 1024-bits is likely
// still in frequent enough use.
//
// Note: CA signed TLS certificates have a strict requirement of a modulus
// size that is at least 2048-bits [[ https://cabforum.org/working-groups/server/baseline-requirements/documents/]].
MODULUS_MIN_SIZE :: 1024
// Maximum size for a RSA modulus (in bits).
//
// This value MUST be a multiple of 64. This value MUST NOT exceed 47666
// (some computations in RSA key generation rely on the factor size being
// no more than 23833 bits). RSA key sizes beyond 3072 bits don't make a
// lot of sense anyway.
MODULUS_MAX_SIZE :: 4096
// Maxmimum size for a RSA public exponent (in bits).
//
// Note: This implementation supports arbitrary size exponents, however
// limit it to something sensible (some implementations are known to
// choke on exponents >= 2^32), with the most common choice being
// `65537`.
EXPONENT_MAX_SIZE :: 32
// Maximum size for a RSA factor (in bits). This is for RSA private-key
// operations. Default is to support factors up to a bit more than half
// the maximum modulus size.
//
// This value MUST be a multiple of 32.
FACTOR_MAX_SIZE :: (MODULUS_MAX_SIZE + 64) >> 1
// Default size for a RSA key (in bits).
DEFAULT_MODULUS_SIZE :: 2048
// RSA public exponent used for key generation. This MUST be a prime
// number greater than 2.
@(private)
PUBLIC_EXPONENT :: 65537
#assert(EXPONENT_MAX_SIZE <= 32)
// Private_Key is a RSA private key.
Private_Key :: struct {
_pub_key: Public_Key,
_d: Modulus, // Private exponent has the same size as n.
_p: Factor,
_q: Factor,
// CRT coefficients.
_dp: Factor, // d % (p - 1)
_dq: Factor, // d % (q - 1)
_iq: Factor, // q^(-1) mod p
_is_initialized: bool,
}
// Public_Key is a RSA public key.
Public_Key :: struct {
_n: Modulus,
_e: u32,
_is_initialized: bool,
}
// private_key_generate uses the system entropy source to generate a new
// Private_Key. The key size is specified in bits, and must be a multiple
// of 8.
@(require_results)
private_key_generate :: proc(priv_key: ^Private_Key, key_size := DEFAULT_MODULUS_SIZE) -> bool {
if !crypto.HAS_RAND_BYTES {
return false
}
if key_size < MODULUS_MIN_SIZE || key_size > MODULUS_MAX_SIZE {
return false
}
if key_size % 8 != 0 {
return false
}
private_key_clear(priv_key)
defer if !priv_key._is_initialized {
private_key_clear(priv_key)
}
for {
// The only way this can fail is if we get extremely unlucky
// and we fail to derive `iq` (1/d mod p).
if keygen_inner(priv_key, key_size) == 1 {
break
}
}
priv_key._is_initialized = true
priv_key._pub_key._is_initialized = true
// Self-test the key.
priv_key._is_initialized = pkcs1_sig_selftest(priv_key)
return priv_key._is_initialized
}
// private_key_n copies the private key's public modulus to dst if dst is
// non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with `dst = nil` gets the required
// size).
@(require_results)
private_key_n :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return public_key_n(&priv_key._pub_key, dst)
}
// private_key_e returns the private key's public exponent as a u32.
@(require_results)
private_key_e :: proc(priv_key: ^Private_Key) -> u32 {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return public_key_e(&priv_key._pub_key)
}
// private_key_d copies the private key's private exponent `d` to dst if
// dst is non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with `dst = nil` gets the required
// size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_d :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return modulus_copyout(&priv_key._d, dst)
}
// private_key_p copies the private key's first prime factor `p` to dst
// if dst is non-nil and of sufficient size, and returns the number of
// bytes copied/would be copied (ie: calling with `dst = nil` gets the
// required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_p :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._p, dst)
}
// private_key_q copies the private key's second prime factor `q` to dst
// if dst is non-nil and of sufficient size, and returns the number of
// bytes copied/would be copied (ie: calling with `dst = nil` gets the
// required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_q :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._q, dst)
}
// private_key_dp copies the private key's first reduced exponent
// `d % (p-1)` to dst if dst is non-nil and of sufficient size, and
// returns the number of bytes copied/would be copied (ie: calling with
//`dst = nil` gets the required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_dp :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._dp, dst)
}
// private_key_dq copies the private key's second reduced exponent
// `d % (q-1)` to dst if dst is non-nil and of sufficient size, and
// returns the number of bytes copied/would be copied (ie: calling with
//`dst = nil` gets the required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_dq :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._dq, dst)
}
// private_key_iq copies the private key's CRT coefficient `iq` to dst if
// dst is non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with`dst = nil` gets the required
// size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_iq :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._iq, dst)
}
// private_key_size returns the size of the private key's public modulus
// in bytes. All ciphertexts and signatures will also be this size.
@(require_results)
private_key_size :: proc(priv_key: ^Private_Key) -> int {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return priv_key._pub_key._n.v_len
}
// private_key_set_bytes sets a private key from byte-encoded components,
// and returns true if and only if (⟺) the operation was successful.
//
// Note: All values are mandatory, and match the values included in the
// PKCS private key format.
//
// WARNING: This routine validates that it is possible to sign/verify with
// the deserialized values, however d is not checked at all, nor is the
// primality of p and q.
@(require_results)
private_key_set_bytes :: proc(
priv_key: ^Private_Key,
n: []byte,
e: []byte,
d: []byte,
p: []byte,
q: []byte,
dp: []byte,
dq: []byte,
iq: []byte,
) -> bool {
private_key_clear(priv_key)
defer if !priv_key._is_initialized {
private_key_clear(priv_key)
}
if !public_key_set_bytes(&priv_key._pub_key, n, e) {
return false
}
if !modulus_set_bytes(&priv_key._d, d) {
return false
}
if !factor_set_bytes(&priv_key._p, p) {
return false
}
if !factor_set_bytes(&priv_key._q, q) {
return false
}
if !factor_set_bytes(&priv_key._dp, dp) {
return false
}
if !factor_set_bytes(&priv_key._dq, dq) {
return false
}
if !factor_set_bytes(&priv_key._iq, iq) {
return false
}
priv_key._is_initialized = true
// Test the key.
//
// Note: This DOES NOT check that p/q are prime and if d is
// consistent (as it is not used by our implementation).
priv_key._is_initialized = pkcs1_sig_selftest(priv_key)
return priv_key._is_initialized
}
// private_key_set sets priv_key to src.
private_key_set :: proc(priv_key, src: ^Private_Key) {
if src == nil || !src._is_initialized {
private_key_clear(priv_key)
return
}
public_key_set(&priv_key._pub_key, &src._pub_key)
modulus_set(&priv_key._d, &src._d)
factor_set(&priv_key._p, &src._p)
factor_set(&priv_key._q, &src._q)
factor_set(&priv_key._dp, &src._dp)
factor_set(&priv_key._dq, &src._dq)
factor_set(&priv_key._iq, &src._iq)
priv_key._is_initialized = true
}
// private_key_equal returns true if and only if (⟺) priv_key is equal to other.
@(require_results)
private_key_equal :: proc(priv_key, other: ^Private_Key) -> bool {
ensure(priv_key._is_initialized && other._is_initialized, "crypto/rsa: uninitialized private key")
pk_eq := public_key_equal(&priv_key._pub_key, &other._pub_key)
eq := crypto.compare_constant_time(modulus_bytes(&priv_key._d), modulus_bytes(&other._d))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._p), factor_bytes(&other._p))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._q), factor_bytes(&other._q))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._dp), factor_bytes(&other._dp))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._dq), factor_bytes(&other._dq))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._iq), factor_bytes(&other._iq))
return pk_eq & (eq == 1)
}
// private_key_clear clears priv_key to the uninitialized state.
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
crypto.zero_explicit(priv_key, size_of(Private_Key))
}
// public_key_n copies the public key's modulus `n` to dst if dst is
// non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with `dst = nil` gets the
// required size).
@(require_results)
public_key_n :: proc(pub_key: ^Public_Key, dst: []byte) -> (n_len: int) {
ensure(pub_key._is_initialized, "crypto/rsa: uninitialized public key")
return modulus_copyout(&pub_key._n, dst)
}
// public_key_e returns the public key's exponent `e` as a u32.
@(require_results)
public_key_e :: proc(pub_key: ^Public_Key) -> u32 {
ensure(pub_key._is_initialized, "crypto/rsa: uninitialized public key")
return pub_key._e
}
// public_key_size returns the size of the public key's modulus in bytes.
// All ciphertexts and signatures will also be this size.
@(require_results)
public_key_size :: proc(pub_key: ^Public_Key) -> int {
ensure(pub_key._is_initialized, "crypto/rsa: uninitialized public key")
return pub_key._n.v_len
}
// public_key_set_bytes sets a public key from byte-encoded components,
// and returns true if and only if (⟺) the operation was successful.
@(require_results)
public_key_set_bytes :: proc(pub_key: ^Public_Key, n, e: []byte) -> bool {
public_key_clear(pub_key)
defer if !pub_key._is_initialized {
public_key_clear(pub_key)
}
ok := modulus_set_bytes(&pub_key._n, n)
if !ok {
return false
}
if modulus_len(&pub_key._n) < MODULUS_MIN_SIZE >> 3 {
return false
}
if !modulus_is_odd(&pub_key._n) {
return false
}
e_ := bytes.trim_left(e, []byte{0x00})
e_len := len(e_)
if e_len > EXPONENT_MAX_SIZE >> 3 {
return false
}
e_buf: [4]byte
copy(e_buf[4 - e_len:], e)
e_u32 := endian.unchecked_get_u32be(e_buf[:])
if e_u32 < 3 || e_u32 & 1 == 0 {
return false
}
pub_key._e = e_u32
pub_key._is_initialized = true
return true
}
// public_key_set sets pub_key to src.
public_key_set :: proc(pub_key, src: ^Public_Key) {
if src == nil || !src._is_initialized {
public_key_clear(pub_key)
return
}
modulus_set(&pub_key._n, &src._n)
pub_key._e = src._e
pub_key._is_initialized = true
}
// 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) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
pub_key^ = priv_key._pub_key
}
// public_key_equal returns true if and only if (⟺) pub_key is equal to other.
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
ensure(pub_key._is_initialized && other._is_initialized, "crypto/rsa: uninitialized public key")
eq := crypto.compare_constant_time(modulus_bytes(&pub_key._n), modulus_bytes(&other._n))
eq &= int(subtle.eq(pub_key._e, other._e))
return eq == 1
}
// public_key_clear clears pub_key to the uninitialized state.
public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
crypto.zero_explicit(pub_key, size_of(Public_Key))
}
// size returns the size of the key's public modulus in bytes.
// All ciphertexts and signatures will also be this size.
size :: proc "contextless" (key: ^$T) -> int where T == Private_Key || T == Private_Key {
when T == Private_Key {
return private_key_size(key)
} else {
return public_key_size(key)
}
}

View File

@@ -0,0 +1,197 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto"
import subtle "core:crypto/_subtle"
import "core:crypto/hash"
// decrypt_oaep returns the plaintext and true if and only if (⟺) it
// successfully decrypts the ciphertext with OAEP parameterized by
// label, hash_algo, and mgf1_algo, and writes the plaintext into dst.
// If mgf1_algo is unspecified, hash_algo will be used.
//
// Note: dst MUST be large enough to contain the plaintext.
@(require_results)
decrypt_oaep :: proc(
priv_key: ^Private_Key,
hash_algo: hash.Algorithm,
ciphertext: []byte,
dst: []byte,
label: []byte = nil,
mgf1_algo := hash.Algorithm.Invalid,
) -> (plaintext: []byte, ok: bool) {
if !priv_key._is_initialized {
return
}
ct_len := len(ciphertext)
if ct_len != modulus_len(&priv_key._pub_key._n) {
return
}
if hash_algo == .Invalid {
return
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
tmp: [MODULUS_MAX_SIZE >> 3]byte
pt_buf := tmp[:ct_len]
defer crypto.zero_explicit(raw_data(pt_buf), ct_len)
copy(pt_buf, ciphertext)
r := private_modpow(pt_buf, priv_key)
r_, l := oaep_dec_unpad(hash_algo, mgf1_algo_, label, pt_buf)
// Conditional branches are ok as we are past the padding
// verification.
if ok = r & r_ == 1; ok {
if l <= len(dst) {
copy(dst, pt_buf[:l])
plaintext = dst[:l]
} else {
ok = false
}
}
return
}
// oaep_max_plaintext_size returns the maximum supported plaintext size
// for a given key, with OAEP parameterized by hash_algo and mgf1_algo.
// If mgf1_algo is unspecified, hash_algo will be used.
@(require_results)
oaep_max_plaintext_size :: proc(
k: ^$T,
hash_algo: hash.Algorithm,
mgf1_algo := hash.Algorithm.Invalid,
) -> int where T == Private_Key || T == Public_Key {
if !k._is_initialized {
return 0
}
if hash_algo == .Invalid {
return 0
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
overhead := 2 + hash.DIGEST_SIZES[hash_algo] + hash.DIGEST_SIZES[mgf1_algo_]
pub_key: ^Public_Key
when T == Private_Key {
pub_keyk = &k._pub_key
} else {
pub_key = k
}
return modulus_len(&k._n) - overhead
}
@(private="file")
xor_hash_data :: proc(hash_algo: hash.Algorithm, dst: []byte, src: []byte) {
tmp: [hash.MAX_DIGEST_SIZE]byte = ---
hash_len := hash.DIGEST_SIZES[hash_algo]
digest := tmp[:hash_len]
defer crypto.zero_explicit(raw_data(digest), hash_len)
hash.hash_bytes_to_buffer(hash_algo, src, digest)
for v, u in digest {
dst[u] ~= v
}
}
@(private="file")
oaep_dec_unpad :: proc(
hash_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
label: []byte,
data: []byte,
) -> (u32, int) {
hash_len := hash.DIGEST_SIZES[hash_algo]
k := len(data)
buf := data
// There must be room for the padding.
if k < (hash_len << 1) + 2 {
return 0, 0
}
// Unmask the seed, then the DB value.
seed, db := buf[1:1+hash_len], buf[1+hash_len:]
mgf1_xor(seed, mgf1_algo, db)
mgf1_xor(db, mgf1_algo, seed)
// Hash the label and XOR it with the value in the array; if
// they are equal then these should yield only zeros.
xor_hash_data(hash_algo, db, label)
// At that point, if the padding was correct, when we should
// have: 0x00 || seed || 0x00 ... 0x00 0x01 || M
// Padding is valid as long as:
// - There is at least hlen+1 leading bytes of value 0x00.
// - There is at least one non-zero byte.
// - The first (leftmost) non-zero byte has value 0x01.
//
// Ultimately, we may leak the resulting message length, i.e.
// the position of the byte of value 0x01, but we must take care
// to do so only if the number of zero bytes has been verified
// to be at least hlen+1.
//
// The loop below counts the number of bytes of value 0x00, and
// checks that the next byte has value 0x01, in constant-time.
//
// - If the initial byte (before the seed) is not 0x00, then
// r and s are set to 0, and stay there.
// - Value r is 1 until the first non-zero byte is reached
// (after the seed); it switches to 0 at that point.
// - Value s is set to 1 if and only if the data encountered
// at the time of the transition of r from 1 to 0 has value
// exactly 0x01.
// - Value zlen counts the number of leading bytes of value zero
// (after the seed).
r := u32(subtle.eq(buf[0], 0))
s, zlen: u32
for u in hash_len + 1..<k {
w := u32(buf[u])
// nz == 1 only for the first non-zero byte.
nz := r & ((w + 0xFF) >> 8)
s |= nz & subtle.eq(w, 0x01)
r &= subtle.not(nz)
zlen += r
}
// Padding is correct only if s == 1, _and_ zlen >= hlen.
s &= subtle.ge(zlen, u32(hash_len))
// At that point, padding was verified, and we are now allowed
// to make conditional jumps.
if s != 0 {
plen := 2 + hash_len + int(zlen)
k -= plen
copy(buf[:k], buf[plen:])
}
return s, k
}

View File

@@ -0,0 +1,55 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import subtle "core:crypto/_subtle"
// unsafe_decrypt_tls_pms decrypts a TLS RSA-Encrypted Premaster Secret
// Message, unconditionally moves the decrypted plaintext to `data[:48]`,
// and returns 1 if and only if (⟺) the operation was successful.
//
// WARNING: This routine MUST only be used when implementing server-side
// support for TLS 1.2's Client Key Exchange message, and extreme care
// MUST be taken when handling failures. This key exchange scheme was
// removed in TLS 1.3, and not implementing support in the first place
// is strongly RECOMMENDED even for TLS 1.2 servers.
@(require_results)
unsafe_decrypt_tls_pms :: proc(priv_key: ^Private_Key, data: []byte) -> u32 {
// A first check on length. Since this test works only on the
// buffer length, it needs not (and cannot) be constant-time.
_len := len(data)
if _len < 59 || _len != priv_key._pub_key._n.v_len {
return 0
}
x := private_modpow(data, priv_key)
x &= u32(subtle.eq(data[0], 0x00))
x &= u32(subtle.eq(data[1], 0x02))
for u in 2..<(_len-49) {
x &= u32(subtle.neq(data[u], 0))
}
x &= u32(subtle.eq(data[_len - 49], 0x00))
copy(data[:48], data[_len - 48:])
return x
}

View File

@@ -0,0 +1,105 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:crypto"
import "core:crypto/hash"
// encrypt_oaep returns true if and only if (⟺) it successfully
// encrypts the plaintext with OAEP parameterized by label, hash_algo,
// and mgf1_algo, and writes the cipherttext into dst. If mgf1_algo is
// unspecified, hash_algo will be used.
//
// This routine will fail if the system entropy source is unavailable.
encrypt_oaep :: proc(
pub_key: ^Public_Key,
hash_algo: hash.Algorithm,
plaintext: []byte,
dst: []byte,
label: []byte = nil,
mgf1_algo := hash.Algorithm.Invalid,
) -> bool {
if !pub_key._is_initialized {
return false
}
if hash_algo == .Invalid {
return false
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
if len(dst) != modulus_len(&pub_key._n) {
return false
}
if len(plaintext) > oaep_max_plaintext_size(pub_key, hash_algo, mgf1_algo_) {
return false
}
if oaep_enc_pad(hash_algo, mgf1_algo_, label, dst, plaintext) != 1 {
return false
}
return public_modpow(dst, pub_key) == 1
}
@(private="file")
oaep_enc_pad :: proc(
hash_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
label: []byte,
dst: []byte,
src: []byte,
) -> u32 {
hash_len := hash.DIGEST_SIZES[hash_algo]
src_len := len(src)
k := len(dst)
// Note: Length checks are handled by the caller.
// Apply padding. At this point, things cannot fail.
buf := dst
// Assemble: DB = lHash || PS || 0x01 || M
// We first place the source message M with copy(), so that
// overlaps between source and destination buffers are supported.
copy(buf[k - src_len:], src)
hash.hash_bytes_to_buffer(hash_algo, label, buf[1+hash_len:1+hash_len << 1])
intrinsics.mem_zero(raw_data(buf[1 + hash_len << 1:]), k - src_len - (hash_len << 1) - 2)
buf[k - src_len - 1] = 0x01
// Make the random seed.
seed, db := buf[1:1+hash_len], buf[1+hash_len:]
crypto.rand_bytes(seed)
// Mask DB with the mask generated from the seed.
mgf1_xor(db, mgf1_algo, seed)
// Mask the seed with the mask generated from the masked DB.
mgf1_xor(seed, mgf1_algo, db)
// Padding result: EM = 0x00 || maskedSeed || maskedDB.
buf[0] = 0x00
return 1
}

View File

@@ -0,0 +1,110 @@
#+private
package rsa
import "core:bytes"
Big_Int :: struct($N: int) {
v: [N]byte,
v_len: int,
}
Modulus :: Big_Int(MODULUS_MAX_SIZE >> 3)
Factor :: Big_Int(FACTOR_MAX_SIZE >> 3)
@(require_results)
modulus_set_bytes :: proc(n: ^Modulus, b: []byte) -> bool {
b_ := bytes.trim_left(b, []byte{0x00})
b_len := len(b_)
if b_len > size_of(n.v) || b_len == 0 {
return false
}
copy(n.v[:], b_)
n.v_len = b_len
return true
}
modulus_set :: proc "contextless" (n, other: ^Modulus) {
// Copy the full thing.
copy(n.v[:], other.v[:])
n.v_len = other.v_len
}
@(require_results)
modulus_bytes :: #force_inline proc "contextless" (n: ^Modulus) -> []byte {
return n.v[:n.v_len]
}
@(require_results)
modulus_len :: #force_inline proc "contextless" (n: ^Modulus) -> int {
return n.v_len
}
@(require_results)
modulus_copyout :: proc(n: ^Modulus, dst: []byte) -> (n_len: int) {
if n_len = modulus_len(n); n_len == 0 {
return
}
if len(dst) > 0 {
ensure(len(dst) >= n_len, "crypto/rsa: insufficent buffer size")
copy(dst, modulus_bytes(n))
}
return
}
@(require_results)
modulus_is_odd :: proc "contextless" (n: ^Modulus) -> bool {
if n.v_len == 0 || n.v[n.v_len-1] & 1 == 0 {
return false
}
return true
}
@(require_results)
factor_set_bytes :: proc(n: ^Factor, b: []byte) -> bool {
b_ := bytes.trim_left(b, []byte{0x00})
b_len := len(b_)
if b_len > size_of(n.v) || b_len == 0 {
return false
}
copy(n.v[:], b_)
n.v_len = b_len
return true
}
factor_set :: proc "contextless" (n, other: ^Factor) {
// Copy the full thing.
copy(n.v[:], other.v[:])
n.v_len = other.v_len
}
@(require_results)
factor_bytes :: #force_inline proc "contextless" (n: ^Factor) -> []byte {
return n.v[:n.v_len]
}
@(require_results)
factor_len :: #force_inline proc "contextless" (n: ^Factor) -> int {
return n.v_len
}
@(require_results)
factor_copyout :: proc(n: ^Factor, dst: []byte) -> (n_len: int) {
if n_len = factor_len(n); n_len == 0 {
return
}
if len(dst) > 0 {
ensure(len(dst) >= n_len, "crypto/rsa: insufficent buffer size")
copy(dst, factor_bytes(n))
}
return
}

View File

@@ -0,0 +1,367 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto"
import bigint "core:crypto/_bigint"
import subtle "core:crypto/_subtle"
import "core:slice"
// Swap two buffers in RAM. They must be disjoint.
@(private="file")
bufswap_u32 :: proc "contextless" (b1, b2: []u32) {
l := len(b1)
for u in 0..<l {
b1[u], b2[u] = b2[u], b1[u]
}
}
@(private, require_results)
keygen_inner :: proc(sk: ^Private_Key, key_size: int) -> u32 {
// We need temporary values for at least 7 integers of the same size
// as a factor (including header word); more space helps with performance
// (in modular exponentiations), but we much prefer to remain under
// 2 kilobytes in total, to save stack space. The macro TEMPS below
// exceeds 512 (which is a count in 32-bit words) when MODULUS_MAX_SIZE
// is greater than 4464 (default value is 4096, so the 2-kB limit is
// maintained unless MODULUS_MAX_SIZE was modified).
TEMPS :: max(512, ((((7 * ((((MODULUS_MAX_SIZE + 1) >> 1) + 61) / 31))) + 1) >> 1) << 1)
assert(key_size >= MODULUS_MIN_SIZE && key_size <= MODULUS_MAX_SIZE)
t64: [TEMPS >> 1]u64
t32 := slice.reinterpret([]u32, t64[:])
defer crypto.zero_explicit(&t64, size_of(t64))
esize_p := u32(key_size + 1) >> 1
esize_q := u32(key_size) - esize_p
sk._p.v_len = int((esize_p + 7) >> 3)
sk._q.v_len = int((esize_q + 7) >> 3)
sk._dp.v_len = sk._p.v_len
sk._dq.v_len = sk._q.v_len
sk._iq.v_len = sk._p.v_len
pk := &sk._pub_key
pk._n.v_len = (key_size + 7) >> 3
pk._e = PUBLIC_EXPONENT
sk._d.v_len = pk._n.v_len // Private exponent length is that of the modulus.
// We now switch to encoded sizes.
//
// floor((x * 16913) / (2^19)) is equal to floor(x/31) for all
// integers x from 0 to 34966; the intermediate product fits on
// 30 bits, thus we can use MUL31().
esize_p += u32(bigint._mul31(esize_p, 16913) >> 19)
esize_q += u32(bigint._mul31(esize_q, 16913) >> 19)
plen := (esize_p + 31) >> 5
qlen := (esize_q + 31) >> 5
p := t32
q := p[1 + plen:]
t := q[1 + qlen:]
// Since we use a prime exponent, when searching for candidate primes,
// checking if `GCD(e, prime - 1) = 1` is a simple matter of euclidian
// division.
for {
bigint.i62_mkprime(p, esize_p, PUBLIC_EXPONENT, t)
p[1] -= 1
if bigint.i31_rem(p, PUBLIC_EXPONENT) != 0 {
p[1] += 1
break
}
}
for {
bigint.i62_mkprime(q, esize_q, PUBLIC_EXPONENT, t)
q[1] -= 1
if bigint.i31_rem(q, PUBLIC_EXPONENT) != 0 {
q[1] += 1
break
}
}
// If p and q have the same size, then it is possible that q > p
// (when the target modulus size is odd, we generate p with a
// greater bit length than q). If q > p, we want to swap p and q
// for two reasons:
// - The final step below (inversion of q modulo p) is easier if
// p > q.
// - While BearSSL's RSA code is perfectly happy with RSA keys such
// that p < q, some other implementations have restrictions and
// require p > q.
//
// Note that we can do a simple non-constant-time swap here,
// because the only information we leak here is that we insist on
// returning p and q such that p > q, which is not a secret.
if esize_p == esize_q && bigint.i31_sub(p, q, 0) == 1 {
bufswap_u32(p[:1+plen], q)
}
sk_p, sk_q := factor_bytes(&sk._p), factor_bytes(&sk._q)
bigint.i31_encode(sk_p, p)
bigint.i31_encode(sk_q, q)
// The odds of this happening are infinitesimally small, however
// checking for it is cheap.
if crypto.compare_constant_time(sk_p, sk_q) == 1 {
return 0
}
// Compute the public modulus too.
bigint.i31_zero(t, p[0])
bigint.i31_mulacc(t, p, q)
bigint.i31_encode(modulus_bytes(&pk._n), t)
// Compute the private exponent.
//
// Computing p - 1 and q - 1 this way is safe as p and q
// are guaranteed to be odd, thus the LSB will always be
// set.
p[1], q[1] = p[1] - 1, q[1] - 1 // p = p - 1, q = q - 1
if compute_privexp(sk, p, q, pk._e, t) != 1 {
return 0
}
// Compute `d % (p - 1)`.
d_mod := t[:1+plen]
bigint.i31_decode_reduce(d_mod, modulus_bytes(&sk._d), p)
bigint.i31_encode(factor_bytes(&sk._dp), d_mod)
// Compute `d % (q - 1)`.
bigint.i31_decode_reduce(d_mod, modulus_bytes(&sk._d), q)
bigint.i31_encode(factor_bytes(&sk._dq), d_mod)
// Compute `q^(-1) mod p`.
p[1], q[1] = p[1] + 1, q[1] + 1 // Restore p, q.
return compute_qinv(sk, p, q, plen, t)
}
@(private="file")
compute_qinv :: proc "contextless" (sk: ^Private_Key, p, q: []u32, plen: u32, t: []u32) -> u32 {
// Per Fermat's Little Theorem, `q^(-1) mod p = q^(p-2) mod p`.
//
// Note: p is guaranteed to be odd as it is a large prime.
// Compute and encode `p-2`.
p_minus_two := t[:1+plen]
copy(p_minus_two, p[:1+plen])
two := t[1+plen:] // Temporarily use this for 2.
bigint.i31_zero(two, p[0])
bigint.i31_decode(two, []byte{2})
_ = bigint.i31_sub(p_minus_two, two, 1)
iq := factor_bytes(&sk._iq) // Temporarily use this for p - 2.
bigint.i31_encode(iq, p_minus_two)
// Enforce 64-bit alignment.
t_ := t
if len(t_) & 1 != 0 {
t_ = t_[1:]
}
m0i := bigint.i31_ninv31(p[1])
ret := bigint.i62_modpow_opt_as_i31(q, iq, p, m0i, t)
if ret != 0 {
bigint.i31_encode(iq, q)
}
return ret
}
@(private="file")
compute_privexp :: proc "contextless" (sk: ^Private_Key, p_minus_one, q_minus_one: []u32, e: u32, tmp: []u32) -> u32 {
// Compute phi = (p-1)*(q-1). The mulacc function sets the announced
// bit length of t to be the sum of the announced bit lengths of
// p-1 and q-1, which is usually exact but may overshoot by one 1
// bit in some cases; we readjust it to its true length.
phi := tmp
bigint.i31_zero(phi, p_minus_one[0])
bigint.i31_mulacc(phi, p_minus_one, q_minus_one)
_len := (phi[0] + 31) >> 5
phi[0] = bigint.i31_bit_length(phi[1:1+_len])
_len = (phi[0] + 31) >> 5
// Divide phi by public exponent e. The final remainder r must be
// non-zero (otherwise, the key is invalid). The quotient is k,
// which we write over phi, since we don't need phi after that.
r: u32
for u := _len; u >= 1; u -= 1 {
// Upon entry, r < e, and phi[u] < 2^31; hence,
// hi:lo < e*2^31. Thus, the produced word k[u]
// must be lower than 2^31, and the new remainder r
// is lower than e.
hi := r >> 1
lo := (r << 31) + phi[u]
phi[u], r = bigint.div_rem_u32(hi, lo, e)
}
if r == 0 {
return 0
}
k := phi
// Compute u and v such that u*e - v*r = GCD(e,r). We use
// a binary GCD algorithm, with 6 extra integers a, b,
// u0, u1, v0 and v1. Initial values are:
// a = e u0 = 1 v0 = 0
// b = r u1 = r v1 = e-1
// The following invariants are maintained:
// a = u0*e - v0*r
// b = u1*e - v1*r
// 0 < a <= e
// 0 < b <= r
// 0 <= u0 <= r
// 0 <= v0 <= e
// 0 <= u1 <= r
// 0 <= v1 <= e
//
// At each iteration, we reduce either a or b by one bit, and
// adjust u0, u1, v0 and v1 to maintain the invariants:
// - if a is even, then a <- a/2
// - otherwise, if b is even, then b <- b/2
// - otherwise, if a > b, then a <- (a-b)/2
// - otherwise, if b > a, then b <- (b-a)/2
// Algorithm stops when a = b. At that point, the common value
// is the GCD of e and r; it must be 1 (otherwise, the private
// key or public exponent is not valid). The (u0,v0) or (u1,v1)
// pairs are the solution we are looking for.
//
// Since either a or b is reduced by at least 1 bit at each
// iteration, 62 iterations are enough to reach the end
// condition.
//
// To maintain the invariants, we must compute the same operations
// on the u* and v* values that we do on a and b:
// - When a is divided by 2, u0 and v0 must be divided by 2.
// - When b is divided by 2, u1 and v1 must be divided by 2.
// - When b is subtracted from a, u1 and v1 are subtracted from
// u0 and v0, respectively.
// - When a is subtracted from b, u0 and v0 are subtracted from
// u1 and v1, respectively.
//
// However, we want to keep the u* and v* values in their proper
// ranges. The following remarks apply:
//
// - When a is divided by 2, then a is even. Therefore:
//
// * If r is odd, then u0 and v0 must have the same parity;
// if they are both odd, then adding r to u0 and e to v0
// makes them both even, and the division by 2 brings them
// back to the proper range.
//
// * If r is even, then u0 must be even; if v0 is odd, then
// adding r to u0 and e to v0 makes them both even, and the
// division by 2 brings them back to the proper range.
//
// Thus, all we need to do is to look at the parity of v0,
// and add (r,e) to (u0,v0) when v0 is odd. In order to avoid
// a 32-bit overflow, we can add ((r+1)/2,(e/2)+1) after the
// division (r+1 does not overflow since r < e; and (e/2)+1
// is equal to (e+1)/2 since e is odd).
//
// - When we subtract b from a, three cases may occur:
//
// * u1 <= u0 and v1 <= v0: just do the subtractions
//
// * u1 > u0 and v1 > v0: compute:
// (u0, v0) <- (u0 + r - u1, v0 + e - v1)
//
// * u1 <= u0 and v1 > v0: compute:
// (u0, v0) <- (u0 + r - u1, v0 + e - v1)
//
// The fourth case (u1 > u0 and v1 <= v0) is not possible
// because it would contradict "b < a" (which is the reason
// why we subtract b from a).
//
// The tricky case is the third one: from the equations, it
// seems that u0 may go out of range. However, the invariants
// and ranges of other values imply that, in that case, the
// new u0 does not actually exceed the range.
//
// We can thus handle the subtraction by adding (r,e) based
// solely on the comparison between v0 and v1.
a, b: u32 = e, r
u0, v0: u32 = 1, 0
u1, v1: u32 = r, e - 1
hr, he := (r + 1) >> 1, (e >> 1) + 1
for _ in 0..<62 {
oa := a & 1 // 1 if a is odd
ob := b & 1 // 1 if b is odd
agtb := subtle.gt(a, b) // 1 if a > b
bgta := subtle.gt(b, a) // 1 if b > a
sab := oa & ob & agtb // 1 if a <- a-b
sba := oa & ob & bgta // 1 if b <- b-a
// a <- a-b, u0 <- u0-u1, v0 <- v0-v1
ctl := subtle.gt(v1, v0)
a -= b & -sab
u0 -= (u1 - (r & -ctl)) & -sab
v0 -= (v1 - (e & -ctl)) & -sab
// b <- b-a, u1 <- u1-u0 mod r, v1 <- v1-v0 mod e
ctl = subtle.gt(v0, v1)
b -= a & -sba
u1 -= (u0 - (r & -ctl)) & -sba
v1 -= (v0 - (e & -ctl)) & -sba
da := subtle.not(oa) | sab // 1 if a <- a/2
db := (oa & subtle.not(ob)) | sba // 1 if b <- b/2
// a <- a/2, u0 <- u0/2, v0 <- v0/2
ctl = v0 & 1
a ~= (a ~ (a >> 1)) & -da
u0 ~= (u0 ~ ((u0 >> 1) + (hr & -ctl))) & -da
v0 ~= (v0 ~ ((v0 >> 1) + (he & -ctl))) & -da
// b <- b/2, u1 <- u1/2 mod r, v1 <- v1/2 mod e
ctl = v1 & 1
b ~= (b ~ (b >> 1)) & -db
u1 ~= (u1 ~ ((u1 >> 1) + (hr & -ctl))) & -db
v1 ~= (v1 ~ ((v1 >> 1) + (he & -ctl))) & -db
}
// Check that the GCD is indeed 1. If not, then the key is invalid
// (and there's no harm in leaking that piece of information).
if (a != 1) {
return 0
}
// Now we have u0*e - v0*r = 1. Let's compute the result as:
// d = u0 + v0*k
// We still have k in the tmp[] array, and its announced bit
// length is that of phi.
m := k[1+_len:]
m[0] = (1 << 5) + 1 // bit length is 32 bits, encoded
m[1] = v0 & bigint.I31_MASK
m[2] = v0 >> 31
z := m[3:]
bigint.i31_zero(z, k[0])
z[1] = u0 & bigint.I31_MASK
z[2] = u0 >> 31
bigint.i31_mulacc(z, k, m)
// Encode the result.
bigint.i31_encode(modulus_bytes(&sk._d), z)
return 1
}

View File

@@ -0,0 +1,49 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto/hash"
import "core:encoding/endian"
@(private)
mgf1_xor :: proc(data: []byte, hash_algo: hash.Algorithm, seed: []byte) {
tmp: [hash.MAX_DIGEST_SIZE]byte = ---
ctx: hash.Context = ---
buf, blen := data, len(data)
hlen := hash.DIGEST_SIZES[hash_algo]
digest := tmp[:hlen]
for u, c := int(0), u32(0); u < blen; u, c = u + hlen, c + 1 {
hash.init(&ctx, hash_algo)
hash.update(&ctx, seed)
endian.unchecked_put_u32be(tmp[:], c)
hash.update(&ctx, tmp[:4])
hash.final(&ctx, digest)
for v in 0..<hlen {
if u + v >= blen {
break
}
buf[u + v] ~= digest[v]
}
}
}

View File

@@ -0,0 +1,165 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto"
import bigint "core:crypto/_bigint"
import "core:slice"
@(private, require_results)
private_modpow :: proc(x: []byte, sk: ^Private_Key) -> u32 {
U :: (2 + ((FACTOR_MAX_SIZE + 30) / 31))
TLEN :: (4 * U) // TLEN is counted in 64-bit words
ensure(sk._is_initialized, "crypto/rsa: uninitialized private key")
// Compute the actual lengths of p and q, in bytes.
// These lengths are not considered secret (we cannot really hide
// them anyway in constant-time code).
//
// Note/yawning: The factors should already be the correct size,
// with leading `0x00`s stripped.
p := factor_bytes(&sk._p)
plen := len(p)
for plen > 0 && p[0] == 0 {
p = p[1:]
plen -= 1
}
q := factor_bytes(&sk._q)
qlen := len(q)
for qlen > 0 && q[0] == 0 {
q = q[1:]
qlen -= 1
}
// Compute the maximum factor length, in 31-bit words.
z := max(plen, qlen) << 3
fwlen := 1
for z > 0 {
z -= 31
fwlen += 1
}
// Convert size to 62-bit words.
fwlen = (fwlen + 1) >> 1
// We need to fit at least 6 values in the stack buffer.
if 6 * fwlen > TLEN {
return 0
}
// Compute signature length (in bytes).
xlen := modulus_len(&sk._pub_key._n)
tmp_: [TLEN]u64 // WARNING: This must be zeroed out.
defer crypto.zero_explicit(&tmp_, size_of(tmp_))
tmp := tmp_[:]
// Decode q.
mq := slice.reinterpret([]u32, tmp)
bigint.i31_decode(mq, q)
// Decode p.
t1 := slice.reinterpret([]u32, tmp[fwlen:])
bigint.i31_decode(t1, p)
// Upstream recomputes the public modulus n, but we can just
// decode it as our key representation stores all PKCS#1
// private key values,
t2 := slice.reinterpret([]u32, tmp[2*fwlen:])
bigint.i31_decode(t2, modulus_bytes(&sk._pub_key._n))
// We encode the modulus into bytes, to perform the comparison
// with bytes. We know that the product length, in bytes, is
// exactly xlen.
// The comparison actually computes the carry when subtracting
// the modulus from the source value; that carry must be 1 for
// a value in the correct range. We keep it in r, which is our
// accumulator for the error code.
m_buf := slice.reinterpret([]byte, tmp[4*fwlen:])
bigint.i31_encode(m_buf[:xlen], t2)
u := xlen
r: u32
for u > 0 {
u -= 1
wn := u32(m_buf[u])
wx := u32(x[u])
r = ((wx - (wn + r)) >> 8) & 1
}
// Move the decoded p to another temporary buffer.
mp := t2
copy(mp, t1[:2*fwlen])
// Compute s2 = x^dq mod q.
q0i := bigint.i31_ninv31(mq[1])
s2 := t1
bigint.i31_decode_reduce(s2, x, mq)
r &= bigint.i62_modpow_opt(s2, factor_bytes(&sk._dq), mq, q0i, tmp[3*fwlen:])
// Compute s1 = x^dp mod p.
p0i := bigint.i31_ninv31(mp[1])
s1 := slice.reinterpret([]u32, tmp[3*fwlen:])
bigint.i31_decode_reduce(s1, x, mp)
r &= bigint.i62_modpow_opt(s1, factor_bytes(&sk._dp), mp, p0i, tmp[4*fwlen:])
// Compute:
// h = (s1 - s2)*(1/q) mod p
// s1 is an integer modulo p, but s2 is modulo q. PKCS#1 is
// unclear about whether p may be lower than q (some existing,
// widely deployed implementations of RSA don't tolerate p < q),
// but we want to support that occurrence, so we need to use the
// reduction function.
//
// Since we use br_i31_decode_reduce() for iq (purportedly, the
// inverse of q modulo p), we also tolerate improperly large
// values for this parameter.
t1 = slice.reinterpret([]u32, tmp[4*fwlen:])
t2 = slice.reinterpret([]u32, tmp[5*fwlen:])
bigint.i31_reduce(t2, s2, mp)
_ = bigint.i31_add(s1, mp, bigint.i31_sub(s1, t2, 1))
bigint.i31_to_monty(s1, mp)
bigint.i31_decode_reduce(t1, factor_bytes(&sk._iq), mp)
bigint.i31_montymul(t2, s1, t1, mp, p0i)
// h is now in t2. We compute the final result:
// s = s2 + q*h
// All these operations are non-modular.
//
// We need mq, s2 and t2. We use the t3 buffer as destination.
// The buffers mp, s1 and t1 are no longer needed, so we can
// reuse them for t3. Moreover, the first step of the computation
// is to copy s2 into t3, after which s2 is not needed. Right
// now, mq is in slot 0, s2 is in slot 1, and t2 is in slot 5.
// Therefore, we have ample room for t3 by simply using s2.
t3 := s2
bigint.i31_mulacc(t3, mq, t2)
// Encode the result. Since we already checked the value of xlen,
// we can just use it right away.
bigint.i31_encode(x, t3)
// The only error conditions remaining at that point are invalid
// values for p and q (even integers).
return p0i & q0i & r
}

View File

@@ -0,0 +1,89 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import bigint "core:crypto/_bigint"
import "core:encoding/endian"
import "core:slice"
@(private, require_results)
public_modpow :: proc(x: []byte, pk: ^Public_Key) -> u32 {
TLEN :: (2 * (2 + ((MODULUS_MAX_SIZE + 30) / 31)))
ensure(pk._is_initialized, "crypto/rsa: uninitialized public key")
// Get the actual length of the modulus, and see if it fits within
// our stack buffer. We also check that the length of x[] is valid.
//
// Note/yawning: The modulus should already be the correct size,
// with leading `0x00`s stripped.
n := modulus_bytes(&pk._n)
nlen := modulus_len(&pk._n)
for nlen > 0 && n[0] == 0 {
n = n[1:]
nlen -= 1
}
if nlen == 0 || nlen > (MODULUS_MAX_SIZE >> 3) || len(x) != nlen {
return 0
}
z := nlen << 3
fwlen := 1
for z > 0 {
z -= 31
fwlen += 1
}
// Convert fwlen to a count in 62-bit words.
fwlen = (fwlen + 1) >> 1
// The modulus gets decoded into m[].
// The value to exponentiate goes into a[].
tmp: [TLEN]u64 // WARNING: This must be zeroed out.
m := slice.reinterpret([]u32, tmp[:fwlen])
a := slice.reinterpret([]u32, tmp[fwlen:2*fwlen])
// Decode the modulus.
bigint.i31_decode(m, n)
m0i := bigint.i31_ninv31(m[1])
// Note: if m[] is even, then m0i == 0. Otherwise, m0i must be
// an odd integer.
r := m0i & 1
// Decode x[] into a[]; we also check that its value is proper.
r &= bigint.i31_decode_mod(a, x, m)
// Compute the modular exponentiation.
e_: [EXPONENT_MAX_SIZE >> 3]byte
e_off: int
endian.unchecked_put_u32be(e_[:], pk._e)
if e_[0] == 0 {
// `e = 65537` is the most common and sensible value, so this
// is the most sensible value.
e_off = 1
}
bigint.i62_modpow_opt(a, e_[e_off:], m, m0i, tmp[2*fwlen:])
// Encode the result.
bigint.i31_encode(x, a)
return r
}

View File

@@ -0,0 +1,233 @@
package rsa
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:bytes"
import "core:crypto"
import "core:crypto/hash"
// PKCS1_HASH_OIDS maps common hash algorithms to the OIDs for
// use with PKCS#1 signatures.
@(rodata)
PKCS1_HASH_OIDS := #partial [hash.Algorithm][]byte {
// WARNING: Legacy verification ONLY.
.Insecure_SHA1 = []byte{
0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A,
},
.SHA224 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
},
.SHA256 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
},
.SHA384 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
},
.SHA512 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
},
.SHA512_256 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06,
},
}
@(private="file", rodata)
PKCS1_SELFTEST_DIGEST_SHA256 := []byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
}
// verify_pkcs1 returns true if and only if (⟺) sig is a valid PKCS#1
// signature by pub_key over msg, hased using hash_algo. If pre_hashed
// is set to true, it is assumed that msg is already hashed.
@(require_results)
verify_pkcs1 :: proc(pub_key: ^Public_Key, hash_algo: hash.Algorithm, msg, sig: []byte, is_prehashed := false) -> bool {
if !pub_key._is_initialized {
return false
}
if len(sig) != modulus_len(&pub_key._n) {
return false
}
// Lookup the OID.
oid := PKCS1_HASH_OIDS[hash_algo]
if oid == nil {
return false
}
hash_len := hash.DIGEST_SIZES[hash_algo]
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash_len {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
// PKCS #1 V2.2 (RFC 8017) 8.2.2 specifies this as computing
// and comparing the padded hash, with unpadding and extracting
// the hash being an alternative. Upstream BearSSL implements
// the latter, which is not a problem if done correctly (which
// it does), however we will opt to go for implementing this
// as specified as it is more robust against implementation
// errors.
// Compute the expected hash.
sig_buf, padded_hash_buf: [MODULUS_MAX_SIZE >> 3]byte = ---, ---
if len(sig) > len(sig_buf) {
return false
}
padded_hash_ := padded_hash_buf[:len(sig)]
if pkcs1_sig_pad(oid, msg_hash, padded_hash_) != 1 {
return false
}
// Compute the signature's padded hash.
sig_ := sig_buf[:len(sig)]
copy(sig_, sig)
if public_modpow(sig_, pub_key) != 1 {
return false
}
return bytes.equal(sig_, padded_hash_)
}
// sign_pkcs1 returns true if and only if (⟺) it successfully writes
// the PKCS#1 signature by priv_key over msg, hashed using hash_algo.
// If pre_hashed is set to true, it is assumed that msg is already hashed.
@(require_results)
sign_pkcs1 :: proc(priv_key: ^Private_Key, hash_algo: hash.Algorithm, msg, sig: []byte, is_prehashed := false) -> bool {
if !priv_key._is_initialized {
return false
}
if len(sig) != modulus_len(&priv_key._pub_key._n) {
return false
}
// Lookup the OID.
oid := PKCS1_HASH_OIDS[hash_algo]
if oid == nil {
return false
}
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash.DIGEST_SIZES[hash_algo] {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
if pkcs1_sig_pad(oid, msg_hash, sig) != 1 {
return false
}
return private_modpow(sig, priv_key) == 1
}
@(private="file", require_results)
pkcs1_sig_pad :: proc "contextless" (hash_oid, hash, x: []byte) -> u32 {
// Padded hash value has format:
// 00 01 FF .. FF 00 30 x1 30 x2 06 x3 OID 05 00 04 x4 HASH
//
// with the following rules:
//
// -- Total length is equal to the modulus length (unsigned
// encoding).
//
// -- There must be at least eight bytes of value 0xFF.
//
// -- x4 is equal to the hash length (hash_len).
//
// -- x3 is equal to the encoded OID value length (hash_oid[0]).
//
// -- x2 = x3 + 4.
//
// -- x1 = x2 + x4 + 4 = x3 + x4 + 8.
//
// Note: the "05 00" is optional (signatures with and without
// that sequence exist in practice), but notes in PKCS#1 seem to
// indicate that the presence of that sequence (specifically,
// an ASN.1 NULL value for the hash parameters) may be slightly
// more "standard" than the opposite.
xlen, hash_len := len(x), len(hash)
// Note/yawning: The hash OID is mandatory, as is the "05 00".
x3 := hash_oid[0]
// Check that there is enough room for all the elements,
// including at least eight bytes of value 0xFF.
if xlen < int(x3) + hash_len + 21 {
return 0
}
x[0] = 0x00
x[1] = 0x01
u := xlen - int(x3) - hash_len - 11
for i in 2..< u {
x[i] = 0xff
}
x[u] = 0x00
x[u + 1] = 0x30
x[u + 2] = x3 + byte(hash_len) + 8
x[u + 3] = 0x30
x[u + 4] = x3 + 4
x[u + 5] = 0x06
copy(x[u+6:], hash_oid)
u += int(x3) + 7
x[u] = 0x05
u += 1
x[u] = 0x00
u += 1
x[u] = 0x04
u += 1
x[u] = byte(hash_len)
u += 1
copy(x[u:], hash)
return 1
}
@(private)
pkcs1_sig_selftest :: proc(priv_key: ^Private_Key) -> bool {
sig_buf: [MODULUS_MAX_SIZE >> 3]byte = ---
defer crypto.zero_explicit(&sig_buf, size_of(sig_buf))
sig := sig_buf[:private_key_size(priv_key)]
if !sign_pkcs1(priv_key, .SHA256, PKCS1_SELFTEST_DIGEST_SHA256, sig, true) {
return false
}
return verify_pkcs1(&priv_key._pub_key, .SHA256, PKCS1_SELFTEST_DIGEST_SHA256, sig, true)
}

View File

@@ -0,0 +1,293 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:crypto"
import bigint "core:crypto/_bigint"
import subtle "core:crypto/_subtle"
import "core:crypto/hash"
// verify_pss returns true if and only if (⟺) sig is a valid PSS
// signature by pub_key over msg, hashed using hash_algo, and MGF1
// parameterized by mgf1_algo and salt_len. If mgf1_algo is
// unspecified, hash_algo will be used. If pre_hashed is set
// to true, it is assumed that msg is already hashed.
@(require_results)
verify_pss :: proc(
pub_key: ^Public_Key,
hash_algo: hash.Algorithm,
salt_len: int,
msg: []byte,
sig: []byte,
is_prehashed := false,
mgf1_algo := hash.Algorithm.Invalid,
) -> bool {
if !pub_key._is_initialized {
return false
}
if hash_algo == .Invalid {
return false
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
if len(sig) != modulus_len(&pub_key._n) {
return false
}
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
hash_len := hash.DIGEST_SIZES[hash_algo]
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash_len {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
sig_buf: [MODULUS_MAX_SIZE >> 3]byte = ---
sig_ := sig_buf[:len(sig)]
copy(sig_, sig)
if public_modpow(sig_, pub_key) != 1 {
return false
}
return pss_sig_unpad(hash_algo, mgf1_algo_, msg_hash, salt_len, pub_key, sig_) == 1
}
// sign_pss returns true if and only if (⟺) it successfully writes
// the PKCS#1 signature by priv_key over msg, hashed using hash_algo, and
// MGF1 parameterized by mgf1_algo and salt_len. If mgf1_algo is
// unspecified, hash_algo will be used. If pre_hashed is set to true,
// it is assumed that msg is already hashed. A reasonable choice for
// salt_len is the digest size of hash_algo, and FIPS 140-3 mandates
// that as the maximum permissible size.
//
// This routine will fail if the system entropy source is unavailable.
@(require_results)
sign_pss :: proc(
priv_key: ^Private_Key,
hash_algo: hash.Algorithm,
salt_len: int,
msg: []byte,
sig: []byte,
is_prehashed := false,
mgf1_algo := hash.Algorithm.Invalid,
) -> bool {
if !priv_key._is_initialized {
return false
}
if len(sig) != modulus_len(&priv_key._pub_key._n) {
return false
}
if hash_algo == .Invalid {
return false
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
if !crypto.HAS_RAND_BYTES && salt_len != 0 {
return false
}
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
hash_len := hash.DIGEST_SIZES[hash_algo]
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash_len {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
// Work out the exact length of n in bits.
n := modulus_bytes(&priv_key._pub_key._n)
assert(len(n) > 0 && n[0] != 0)
n_bitlen := int(bigint._u32_bit_length(u32(n[0]))) + (len(n) - 1) * 8
if pss_sig_pad(hash_algo, mgf1_algo_, msg_hash, salt_len, n_bitlen, sig) != 1 {
return false
}
return private_modpow(sig, priv_key) == 1
}
@(private="file", require_results)
pss_sig_unpad :: proc(
data_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
digest: []byte,
salt_len: int,
pk: ^Public_Key,
sig: []byte,
) -> u32 {
hash_len := hash.DIGEST_SIZES[data_algo]
x := sig
// Value r will be set to a non-zero value is any test fails.
r: u32
// The value bit length (as an integer) must be strictly less than
// that of the modulus.
//
// Note/yawning: The modulus should already be the correct size,
// with leading `0x00`s stripped.
n := modulus_bytes(&pk._n)
nlen := modulus_len(&pk._n)
u: int
for u = 0; u < nlen; u += 1 {
if n[u] != 0 {
break
}
}
if u == nlen {
return 0
}
n_bitlen := bigint._u32_bit_length(u32(n[u])) + (u32(nlen - u - 1) << 3)
n_bitlen -= 1
if (n_bitlen & 7) == 0 {
r |= u32(x[0])
x = x[1:]
} else {
r |= u32(x[0] & (0xFF << (n_bitlen & 7)))
}
xlen := int((n_bitlen + 7) >> 3)
// Check that the modulus is large enough for the hash value
// length combined with the intended salt length.
if hash_len > xlen || salt_len > xlen || (hash_len + salt_len + 2) > xlen {
return 0
}
// Check value of rightmost byte.
r |= u32(x[xlen - 1] ~ 0xBC)
// Generate the mask and XOR it into the first bytes to reveal PS;
// we must also mask out the leading bits.
seed := x[xlen - hash_len - 1:]
mgf1_xor(x[:xlen - hash_len - 1], mgf1_algo, seed[:hash_len])
if (n_bitlen & 7) != 0 {
x[0] &= 0xFF >> (8 - (n_bitlen & 7))
}
// Check that all padding bytes have the expected value.
for u = 0; u < (xlen - hash_len - salt_len - 2); u += 1 {
r |= u32(x[u])
}
r |= u32(x[xlen - hash_len - salt_len - 2] ~ 0x01)
// Recompute H.
salt := x[xlen - hash_len - salt_len - 1:]
tmp: [hash.MAX_DIGEST_SIZE]byte
h := tmp[:hash_len]
ctx: hash.Context = ---
hash.init(&ctx, data_algo)
hash.update(&ctx, tmp[:8])
hash.update(&ctx, digest)
hash.update(&ctx, salt[:salt_len])
hash.final(&ctx, h)
// Check that the recomputed H value matches the one appearing
// in the string.
x = x[xlen - hash_len - 1:]
r |= subtle.eq0(u32(crypto.compare_constant_time(h, x[:hash_len])))
return subtle.eq0(r)
}
@(private="file", require_results)
pss_sig_pad :: proc(
data_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
digest: []byte,
salt_len: int,
n_bitlen_: int,
sig: []byte,
) -> u32 {
x, n_bitlen := sig, n_bitlen_
hash_len := hash.DIGEST_SIZES[data_algo]
// The padded string is one bit smaller than the modulus;
// notably, if the modulus length is equal to 1 modulo 8, then
// the padded string will be one _byte_ smaller, and the first
// byte will be set to 0. We apply these transformations here.
n_bitlen -= 1
if (n_bitlen & 7) == 0 {
x[0] = 0
x = x[1:]
}
xlen := int((n_bitlen + 7) >> 3)
// Check that the modulus is large enough for the hash value
// length combined with the intended salt length.
if hash_len > xlen || salt_len > xlen || (hash_len + salt_len + 2) > xlen {
return 0
}
// Produce a random salt.
salt := x[xlen - hash_len - salt_len - 1:]
salt = salt[:salt_len]
if salt_len != 0 {
crypto.rand_bytes(salt)
}
// Compute the seed for MGF1.
seed := x[xlen - hash_len - 1:]
seed = seed[:hash_len]
ctx: hash.Context = ---
hash.init(&ctx, data_algo)
intrinsics.mem_zero(raw_data(seed), 8)
hash.update(&ctx, seed[:8])
hash.update(&ctx, digest)
hash.update(&ctx, salt)
hash.final(&ctx, seed)
// Prepare string PS (padded salt). The salt is already at the
// right place.
intrinsics.mem_zero(raw_data(x), xlen - salt_len - hash_len - 2)
x[xlen - salt_len - hash_len - 2] = 0x01
// Generate the mask and XOR it into PS.
mgf1_xor(x[:xlen - hash_len - 1], mgf1_algo, seed)
// Clear the top bits to ensure the value is lower than the
// modulus.
x[0] &= 0xFF >> ((u32(xlen) << 3) - u32(n_bitlen))
// The seed (H) is already in the right place. We just set the
// last byte.
x[xlen - 1] = 0xBC
return 1
}

View File

@@ -0,0 +1,180 @@
package rsa
// private_key_set_insecure_test sets the private key to the
// pregenerated INSECURE test key "testRSA2048" from RFC 9500 2.1.
//
// WARNING: This key MUST only be used for testing purposes.
@(require_results)
private_key_set_insecure_test :: proc(priv_key: ^Private_Key) -> bool {
// RFC 9500 2.1 "testRSA2048"
return private_key_set_bytes(
priv_key,
// n
[]byte{
0xB0, 0xF9, 0xE8, 0x19, 0x43, 0xA7, 0xAE, 0x98,
0x92, 0xAA, 0xDE, 0x17, 0xCA, 0x7C, 0x40, 0xF8,
0x74, 0x4F, 0xED, 0x2F, 0x81, 0x48, 0xE6, 0xC8,
0xEA, 0xA2, 0x7B, 0x7D, 0x00, 0x15, 0x48, 0xFB,
0x51, 0x92, 0xAB, 0x28, 0xB5, 0x6C, 0x50, 0x60,
0xB1, 0x18, 0xCC, 0xD1, 0x31, 0xE5, 0x94, 0x87,
0x4C, 0x6C, 0xA9, 0x89, 0xB5, 0x6C, 0x27, 0x29,
0x6F, 0x09, 0xFB, 0x93, 0xA0, 0x34, 0xDF, 0x32,
0xE9, 0x7C, 0x6F, 0xF0, 0x99, 0x8C, 0xFD, 0x8E,
0x6F, 0x42, 0xDD, 0xA5, 0x8A, 0xCD, 0x1F, 0xA9,
0x79, 0x86, 0xF1, 0x44, 0xF3, 0xD1, 0x54, 0xD6,
0x76, 0x50, 0x17, 0x5E, 0x68, 0x54, 0xB3, 0xA9,
0x52, 0x00, 0x3B, 0xC0, 0x68, 0x87, 0xB8, 0x45,
0x5A, 0xC2, 0xB1, 0x9F, 0x7B, 0x2F, 0x76, 0x50,
0x4E, 0xBC, 0x98, 0xEC, 0x94, 0x55, 0x71, 0xB0,
0x78, 0x92, 0x15, 0x0D, 0xDC, 0x6A, 0x74, 0xCA,
0x0F, 0xBC, 0xD3, 0x54, 0x97, 0xCE, 0x81, 0x53,
0x4D, 0xAF, 0x94, 0x18, 0x84, 0x4B, 0x13, 0xAE,
0xA3, 0x1F, 0x9D, 0x5A, 0x6B, 0x95, 0x57, 0xBB,
0xDF, 0x61, 0x9E, 0xFD, 0x4E, 0x88, 0x7F, 0x2D,
0x42, 0xB8, 0xDD, 0x8B, 0xC9, 0x87, 0xEA, 0xE1,
0xBF, 0x89, 0xCA, 0xB8, 0x5E, 0xE2, 0x1E, 0x35,
0x63, 0x05, 0xDF, 0x6C, 0x07, 0xA8, 0x83, 0x8E,
0x3E, 0xF4, 0x1C, 0x59, 0x5D, 0xCC, 0xE4, 0x3D,
0xAF, 0xC4, 0x91, 0x23, 0xEF, 0x4D, 0x8A, 0xBB,
0xA9, 0x3D, 0x39, 0x05, 0xE4, 0x02, 0x8D, 0x7B,
0xA9, 0x14, 0x84, 0xA2, 0x75, 0x96, 0xE0, 0x7B,
0x4B, 0x6E, 0xD9, 0x92, 0xF0, 0x77, 0xB5, 0x24,
0xD3, 0xDC, 0xFE, 0x7D, 0xDD, 0x55, 0x49, 0xBE,
0x7C, 0xCE, 0x8D, 0xA0, 0x35, 0xCF, 0xA0, 0xB3,
0xFB, 0x8F, 0x9E, 0x46, 0xF7, 0x32, 0xB2, 0xA8,
0x6B, 0x46, 0x01, 0x65, 0xC0, 0x8F, 0x53, 0x13,
},
// e
[]byte{0x01, 0x00, 0x01},
// d
[]byte{
0x41, 0x18, 0x8B, 0x20, 0xCF, 0xDB, 0xDB, 0xC2,
0xCF, 0x1F, 0xFE, 0x75, 0x2D, 0xCB, 0xAA, 0x72,
0x39, 0x06, 0x35, 0x2E, 0x26, 0x15, 0xD4, 0x9D,
0xCE, 0x80, 0x59, 0x7F, 0xCF, 0x0A, 0x05, 0x40,
0x3B, 0xEF, 0x00, 0xFA, 0x06, 0x51, 0x82, 0xF7,
0x2D, 0xEC, 0xFB, 0x59, 0x6F, 0x4B, 0x0C, 0xE8,
0xFF, 0x59, 0x70, 0xBA, 0xF0, 0x7A, 0x89, 0xA5,
0x19, 0xEC, 0xC8, 0x16, 0xB2, 0xF4, 0xFF, 0xAC,
0x50, 0x69, 0xAF, 0x1B, 0x06, 0xBF, 0xEF, 0x7B,
0xF6, 0xBC, 0xD7, 0x9E, 0x4E, 0x81, 0xC8, 0xC5,
0xA3, 0xA7, 0xD9, 0x13, 0x0D, 0xC3, 0xCF, 0xBA,
0xDA, 0xE5, 0xF6, 0xD2, 0x88, 0xF9, 0xAE, 0xE3,
0xF6, 0xFF, 0x92, 0xFA, 0xE0, 0xF8, 0x1A, 0xF5,
0x97, 0xBE, 0xC9, 0x6A, 0xE9, 0xFA, 0xB9, 0x40,
0x2C, 0xD5, 0xFE, 0x41, 0xF7, 0x05, 0xBE, 0xBD,
0xB4, 0x7B, 0xB7, 0x36, 0xD3, 0xFE, 0x6C, 0x5A,
0x51, 0xE0, 0xE2, 0x07, 0x32, 0xA9, 0x7B, 0x5E,
0x46, 0xC1, 0xCB, 0xDB, 0x26, 0xD7, 0x48, 0x54,
0xC6, 0xB6, 0x60, 0x4A, 0xED, 0x46, 0x37, 0x35,
0xFF, 0x90, 0x76, 0x04, 0x65, 0x57, 0xCA, 0xF9,
0x49, 0xBF, 0x44, 0x88, 0x95, 0xC2, 0x04, 0x32,
0xC1, 0xE0, 0x9C, 0x01, 0x4E, 0xA7, 0x56, 0x60,
0x43, 0x4F, 0x1A, 0x0F, 0x3B, 0xE2, 0x94, 0xBA,
0xBC, 0x5D, 0x53, 0x0E, 0x6A, 0x10, 0x21, 0x3F,
0x53, 0xB6, 0x03, 0x75, 0xFC, 0x84, 0xA7, 0x57,
0x3F, 0x2A, 0xF1, 0x21, 0x55, 0x84, 0xF5, 0xB4,
0xBD, 0xA6, 0xD4, 0xE8, 0xF9, 0xE1, 0x7A, 0x78,
0xD9, 0x7E, 0x77, 0xB8, 0x6D, 0xA4, 0xA1, 0x84,
0x64, 0x75, 0x31, 0x8A, 0x7A, 0x10, 0xA5, 0x61,
0x01, 0x4E, 0xFF, 0xA2, 0x3A, 0x81, 0xEC, 0x56,
0xE9, 0xE4, 0x10, 0x9D, 0xEF, 0x8C, 0xB3, 0xF7,
0x97, 0x22, 0x3F, 0x7D, 0x8D, 0x0D, 0x43, 0x51,
},
// p
[]byte{
0xDD, 0x10, 0x57, 0x02, 0x38, 0x2F, 0x23, 0x2B,
0x36, 0x81, 0xF5, 0x37, 0x91, 0xE2, 0x26, 0x17,
0xC7, 0xBF, 0x4E, 0x9A, 0xCB, 0x81, 0xED, 0x48,
0xDA, 0xF6, 0xD6, 0x99, 0x5D, 0xA3, 0xEA, 0xB6,
0x42, 0x83, 0x9A, 0xFF, 0x01, 0x2D, 0x2E, 0xA6,
0x28, 0xB9, 0x0A, 0xF2, 0x79, 0xFD, 0x3E, 0x6F,
0x7C, 0x93, 0xCD, 0x80, 0xF0, 0x72, 0xF0, 0x1F,
0xF2, 0x44, 0x3B, 0x3E, 0xE8, 0xF2, 0x4E, 0xD4,
0x69, 0xA7, 0x96, 0x13, 0xA4, 0x1B, 0xD2, 0x40,
0x20, 0xF9, 0x2F, 0xD1, 0x10, 0x59, 0xBD, 0x1D,
0x0F, 0x30, 0x1B, 0x5B, 0xA7, 0xA9, 0xD3, 0x63,
0x7C, 0xA8, 0xD6, 0x5C, 0x1A, 0x98, 0x15, 0x41,
0x7D, 0x8E, 0xAB, 0x73, 0x4B, 0x0B, 0x4F, 0x3A,
0x2C, 0x66, 0x1D, 0x9A, 0x1A, 0x82, 0xF3, 0xAC,
0x73, 0x4C, 0x40, 0x53, 0x06, 0x69, 0xAB, 0x8E,
0x47, 0x30, 0x45, 0xA5, 0x8E, 0x65, 0x53, 0x9D,
},
// q
[]byte{
0xCC, 0xF1, 0xE5, 0xBB, 0x90, 0xC8, 0xE9, 0x78,
0x1E, 0xA7, 0x5B, 0xEB, 0xF1, 0x0B, 0xC2, 0x52,
0xE1, 0x1E, 0xB0, 0x23, 0xA0, 0x26, 0x0F, 0x18,
0x87, 0x55, 0x2A, 0x56, 0x86, 0x3F, 0x4A, 0x64,
0x21, 0xE8, 0xC6, 0x00, 0xBF, 0x52, 0x3D, 0x6C,
0xB1, 0xB0, 0xAD, 0xBD, 0xD6, 0x5B, 0xFE, 0xE4,
0xA8, 0x8A, 0x03, 0x7E, 0x3D, 0x1A, 0x41, 0x5E,
0x5B, 0xB9, 0x56, 0x48, 0xDA, 0x5A, 0x0C, 0xA2,
0x6B, 0x54, 0xF4, 0xA6, 0x39, 0x48, 0x52, 0x2C,
0x3D, 0x5F, 0x89, 0xB9, 0x4A, 0x72, 0xEF, 0xFF,
0x95, 0x13, 0x4D, 0x59, 0x40, 0xCE, 0x45, 0x75,
0x8F, 0x30, 0x89, 0x80, 0x90, 0x89, 0x56, 0x58,
0x8E, 0xEF, 0x57, 0x5B, 0x3E, 0x4B, 0xC4, 0xC3,
0x68, 0xCF, 0xE8, 0x13, 0xEE, 0x9C, 0x25, 0x2C,
0x2B, 0x02, 0xE0, 0xDF, 0x91, 0xF1, 0xAA, 0x01,
0x93, 0x8D, 0x38, 0x68, 0x5D, 0x60, 0xBA, 0x6F,
},
// dp
[]byte{
0x09, 0xED, 0x54, 0xEA, 0xED, 0x98, 0xF8, 0x4C,
0x55, 0x7B, 0x4A, 0x86, 0xBF, 0x4F, 0x57, 0x84,
0x93, 0xDC, 0xBC, 0x6B, 0xE9, 0x1D, 0xA1, 0x89,
0x37, 0x04, 0x04, 0xA9, 0x08, 0x72, 0x76, 0xF4,
0xCE, 0x51, 0xD8, 0xA1, 0x00, 0xED, 0x85, 0x7D,
0xC2, 0xB0, 0x64, 0x94, 0x74, 0xF3, 0xF1, 0x5C,
0xD2, 0x4C, 0x54, 0xDB, 0x28, 0x71, 0x10, 0xE5,
0x6E, 0x5C, 0xB0, 0x08, 0x68, 0x2F, 0x91, 0x68,
0xAA, 0x81, 0xF3, 0x14, 0x58, 0xB7, 0x43, 0x1E,
0xCC, 0x1C, 0x44, 0x90, 0x6F, 0xDA, 0x87, 0xCA,
0x89, 0x47, 0x10, 0xC3, 0x71, 0xE9, 0x07, 0x6C,
0x1D, 0x49, 0xFB, 0xAE, 0x51, 0x27, 0x69, 0x34,
0xF2, 0xAD, 0x78, 0x77, 0x89, 0xF4, 0x2D, 0x0F,
0xA0, 0xB4, 0xC9, 0x39, 0x85, 0x5D, 0x42, 0x12,
0x09, 0x6F, 0x70, 0x28, 0x0A, 0x4E, 0xAE, 0x7C,
0x8A, 0x27, 0xD9, 0xC8, 0xD0, 0x77, 0x2E, 0x65,
},
// dq
[]byte{
0x8C, 0xB6, 0x85, 0x7A, 0x7B, 0xD5, 0x46, 0x5F,
0x80, 0x04, 0x7E, 0x9B, 0x87, 0xBC, 0x00, 0x27,
0x31, 0x84, 0x05, 0x81, 0xE0, 0x62, 0x61, 0x39,
0x01, 0x2A, 0x5B, 0x50, 0x5F, 0x0A, 0x33, 0x84,
0x7E, 0xB7, 0xB8, 0xC3, 0x28, 0x99, 0x49, 0xAD,
0x48, 0x6F, 0x3B, 0x4B, 0x3D, 0x53, 0x9A, 0xB5,
0xDA, 0x76, 0x30, 0x21, 0xCB, 0xC8, 0x2C, 0x1B,
0xA2, 0x34, 0xA5, 0x66, 0x8D, 0xED, 0x08, 0x01,
0xB8, 0x59, 0xF3, 0x43, 0xF1, 0xCE, 0x93, 0x04,
0xE6, 0xFA, 0xA2, 0xB0, 0x02, 0xCA, 0xD9, 0xB7,
0x8C, 0xDE, 0x5C, 0xDC, 0x2C, 0x1F, 0xB4, 0x17,
0x1C, 0x42, 0x42, 0x16, 0x70, 0xA6, 0xAB, 0x0F,
0x50, 0xCC, 0x4A, 0x19, 0x4E, 0xB3, 0x6D, 0x1C,
0x91, 0xE9, 0x35, 0xBA, 0x01, 0xB9, 0x59, 0xD8,
0x72, 0x8B, 0x9E, 0x64, 0x42, 0x6B, 0x3F, 0xC3,
0xA7, 0x50, 0x6D, 0xEB, 0x52, 0x39, 0xA8, 0xA7,
},
// iq (aka u)
[]byte{
0x0A, 0x81, 0xD8, 0xA6, 0x18, 0x31, 0x4A, 0x80,
0x3A, 0xF6, 0x1C, 0x06, 0x71, 0x1F, 0x2C, 0x39,
0xB2, 0x66, 0xFF, 0x41, 0x4D, 0x53, 0x47, 0x6D,
0x1D, 0xA5, 0x2A, 0x43, 0x18, 0xAA, 0xFE, 0x4B,
0x96, 0xF0, 0xDA, 0x07, 0x15, 0x5F, 0x8A, 0x51,
0x34, 0xDA, 0xB8, 0x8E, 0xE2, 0x9E, 0x81, 0x68,
0x07, 0x6F, 0xCD, 0x78, 0xCA, 0x79, 0x1A, 0xC6,
0x34, 0x42, 0xA8, 0x1C, 0xD0, 0x69, 0x39, 0x27,
0xD8, 0x08, 0xE3, 0x35, 0xE8, 0xD8, 0xCB, 0xF2,
0x12, 0x19, 0x07, 0x50, 0x9A, 0x57, 0x75, 0x9B,
0x4F, 0x9A, 0x18, 0xFA, 0x3A, 0x7B, 0x33, 0x37,
0x79, 0xED, 0xDE, 0x7A, 0x45, 0x93, 0x84, 0xF8,
0x44, 0x4A, 0xDA, 0xEC, 0xFF, 0xEC, 0x95, 0xFD,
0x55, 0x2B, 0x0C, 0xFC, 0xB6, 0xC7, 0xF6, 0x92,
0x62, 0x6D, 0xDE, 0x1E, 0xF2, 0x68, 0xA4, 0x0D,
0x2F, 0x67, 0xB5, 0xC8, 0xAA, 0x38, 0x7F, 0xF7,
},
)
}

View File

@@ -49,6 +49,7 @@ package all
@(require) import "core:crypto/pbkdf2"
@(require) import "core:crypto/poly1305"
@(require) import "core:crypto/ristretto255"
@(require) import "core:crypto/rsa"
@(require) import "core:crypto/sha2"
@(require) import "core:crypto/sha3"
@(require) import "core:crypto/shake"

View File

@@ -54,6 +54,7 @@ package all
@(require) import "core:crypto/pbkdf2"
@(require) import "core:crypto/poly1305"
@(require) import "core:crypto/ristretto255"
@(require) import "core:crypto/rsa"
@(require) import "core:crypto/sha2"
@(require) import "core:crypto/sha3"
@(require) import "core:crypto/shake"

View File

@@ -881,7 +881,7 @@ gb_global TargetMetrics target_freestanding_arm32 = {
TargetOs_freestanding,
TargetArch_arm32,
4, 4, 8, 16,
str_lit("arm-unknown-unknown-gnueabihf"),
str_lit("arm-none-eabihf"),
};
gb_global TargetMetrics target_freestanding_riscv64 = {
TargetOs_freestanding,

View File

@@ -1142,7 +1142,7 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
if (operand->mode == Addressing_Type && is_type_typeid(type)) {
add_type_info_type(c, operand->type);
add_type_and_value(c, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type));
add_type_and_value(c, operand->expr, Addressing_Constant, type, exact_value_typeid(operand->type));
return;
}

View File

@@ -432,11 +432,24 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
// NOTE(bill): Ignore assignments to '_'
if (is_blank_ident(node)) {
check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier"));
if (rhs->mode == Addressing_Invalid) {
return nullptr;
String context_name = str_lit("assignment to '_' identifier");
check_assignment(ctx, rhs, nullptr, context_name);
switch (rhs->mode) {
case Addressing_ProcGroup: {
gbString expr_str = expr_to_string(rhs->expr);
defer (gb_string_free(expr_str));
error(rhs->expr,
"Cannot assign procedure group '%s' in %.*s",
expr_str,
LIT(context_name));
rhs->mode = Addressing_Invalid;
}
case Addressing_Invalid:
return nullptr;
default:
return rhs->type;
}
return rhs->type;
}
Entity *e = nullptr;

View File

@@ -5613,7 +5613,7 @@ gb_inline b32 gb_path_is_absolute(char const *path) {
b32 result = false;
GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS)
result == (gb_strlen(path) > 2) &&
result = (gb_strlen(path) > 2) &&
gb_char_is_alpha(path[0]) &&
(path[1] == ':' && path[2] == GB_PATH_SEPARATOR);
#else

View File

@@ -1571,7 +1571,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) {
GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation);
lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type);
error(e->token, "Objective-C related things are not allowed with '-bedrock'");
if (build_context.bedrock) {
error(e->token, "Objective-C related things are not allowed with '-bedrock'");
}
}
// Ensure classes that have been implicitly referenced through
@@ -1684,7 +1687,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
Type *superclass = tn.objc_superclass;
if (superclass != nullptr) {
auto& superclass_global = string_map_must_get(&global_class_map, tn.objc_class_name);
auto &superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name);
superclass_value = superclass_global.class_value;
}

View File

@@ -2743,10 +2743,11 @@ gb_internal int print_show_help(String const arg0, String command, String option
if (check) {
if (print_flag("-collection:<name>=<filepath>")) {
print_usage_line(2, "Defines a library collection used for imports.");
print_usage_line(2, "Defines a library collection used for imports and foreign imports.");
print_usage_line(2, "Example: -collection:shared=dir/to/shared");
print_usage_line(2, "Usage in Code:");
print_usage_line(3, "import \"shared:foo\"");
print_usage_line(3, "foreign import lib \"shared:libfoo.a\"");
}
if (print_flag("-custom-attribute:<string>")) {
@@ -3100,7 +3101,6 @@ gb_internal int print_show_help(String const arg0, String command, String option
if (print_flag("-stack-protector:<string>")) {
print_usage_line(2, "Specifies the stack protector.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-stack-protector:default");
print_usage_line(3, "-stack-protector:none");
print_usage_line(3, "-stack-protector:base");
print_usage_line(3, "-stack-protector:all");

View File

@@ -6054,6 +6054,19 @@ gb_internal bool is_import_path_valid(String const &path) {
return false;
}
gb_internal bool is_import_path_absolute(String const &path) {
if (path.len > 0 && path[0] == '/') {
return true;
}
if (path.len > 2 &&
gb_char_is_alpha(path[0]) &&
path[1] == ':' &&
(path[2] == '/' || path[2] == '\\')) {
return true;
}
return false;
}
gb_internal bool is_build_flag_path_valid(String const &path) {
if (path.len > 0) {
u8 *start = path.text;
@@ -6116,12 +6129,13 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
do_warning = &syntax_warning;
if (use_check_errors) {
do_error = &error;
do_error = &warning;
do_warning = &warning;
}
// NOTE(bill): if file_mutex == nullptr, this means that the code is used within the semantics stage
String collection_name = {};
bool is_import_decl_path = node->kind == Ast_ImportDecl || node->kind == Ast_ForeignImportDecl;
isize colon_pos = -1;
for (isize j = 0; j < original_string.len; j++) {
@@ -6134,11 +6148,13 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
bool has_windows_drive = false;
#if defined(GB_SYSTEM_WINDOWS)
if (file_mutex == nullptr) {
if (colon_pos == 1 && original_string.len > 2) {
if (original_string[2] == '/' || original_string[2] == '\\') {
colon_pos = -1;
has_windows_drive = true;
}
if (!is_import_decl_path &&
colon_pos == 1 &&
original_string.len > 2 &&
gb_char_is_alpha(original_string[0]) &&
(original_string[2] == '/' || original_string[2] == '\\')) {
colon_pos = -1;
has_windows_drive = true;
}
for (isize i = 0; i < original_string.len; i++) {
@@ -6162,6 +6178,10 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
file_str = original_string;
}
if (is_import_decl_path && is_import_path_absolute(file_str)) {
do_error(node, "Invalid import path: '%.*s'", LIT(file_str));
return false;
}
if (has_windows_drive) {
String sub_file_path = substring(file_str, 3, file_str.len);
@@ -6234,8 +6254,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
if (has_windows_drive) {
*path = file_str;
} else {
bool ok = false;
String fullpath = string_trim_whitespace(get_fullpath_relative(permanent_allocator(), base_dir, file_str, &ok));
String fullpath = string_trim_whitespace(get_fullpath_relative(permanent_allocator(), base_dir, file_str, nullptr));
*path = fullpath;
}
return true;
@@ -6287,6 +6306,12 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas
ast_node(id, ImportDecl, node);
String original_string = string_trim_whitespace(string_value_from_token(f, id->relpath));
if (is_import_path_absolute(original_string)) {
syntax_error(node, "Invalid import path: '%.*s'", LIT(original_string));
decls[i] = ast_bad_decl(f, id->relpath, id->relpath);
continue;
}
String import_path = {};
bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, original_string, &import_path);
if (!ok) {
@@ -6313,6 +6338,12 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas
GB_ASSERT(fp->kind == Ast_BasicLit);
Token fp_token = fp->BasicLit.token;
String file_str = string_trim_whitespace(string_value_from_token(f, fp_token));
if (is_import_path_absolute(file_str)) {
syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str));
decls[i] = ast_bad_decl(f, fp_token, fp_token);
goto end;
}
String fullpath = file_str;
if (!is_arch_wasm() || string_ends_with(fullpath, str_lit(".o"))) {
String foreign_path = {};
@@ -7100,4 +7131,3 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
return ParseFile_None;
}

View File

@@ -643,7 +643,7 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
thread_init(pool, t, idx);
isize stack_size = 0;
isize stack_size = 1 * 1024 * 1024; // 1 MiB (LLVM takes a lot of stack space)
#if defined(GB_SYSTEM_WINDOWS)
t->win32_handle = CreateThread(NULL, stack_size, internal_thread_proc, t, 0, NULL);
@@ -1067,4 +1067,4 @@ void atomic_freelist_put(std::atomic<AtomicFreelist<T> *> &head_list, AtomicFree
}
}
}
}

View File

@@ -0,0 +1,160 @@
package benchmark_core_crypto
import "base:runtime"
import "core:log"
import "core:testing"
import "core:text/table"
import "core:time"
import "core:crypto"
import "core:crypto/rsa"
// RSA key generation is time consuming and high variance, so it takes
// an unreasonable amount of time to get a semi-sensible value, so this
// is skipped by default.
RSA_BENCH_KEYGEN: bool : #config(ODIN_BENCHMARK_RSA_KEYGEN, false)
@(private = "file")
KEYGEN_ITERS :: 100
@(private = "file")
SIGN_ITERS :: 5000
@(private = "file")
ENCRYPT_ITERS :: 5000
@(test)
benchmark_crypto_rsa :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tbl: table.Table
table.init(&tbl)
defer table.destroy(&tbl)
table.caption(&tbl, "RSA")
table.aligned_header_of_values(&tbl, .Right, "Operation", "Avg. Time")
if RSA_BENCH_KEYGEN {
bench_keygen_2048(&tbl)
table.row_of_values(&tbl)
}
bench_pkcs1_2048(&tbl)
table.row_of_values(&tbl)
bench_pss_2048(&tbl)
table.row_of_values(&tbl)
bench_oaep_2048(&tbl)
log_table(&tbl)
}
@(private="file")
bench_keygen_2048 :: proc(tbl: ^table.Table) {
if !crypto.HAS_RAND_BYTES {
log.warnf("rsa: keygen benchmarks skipped, no system entropy source")
}
priv_key: rsa.Private_Key
start := time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
ok := rsa.private_key_generate(&priv_key, 2048)
assert(ok, "keygen should succeed")
}
taken := time.tick_since(start) / KEYGEN_ITERS
append_tbl(tbl, "Keygen/2048", taken)
}
@(private="file")
bench_pkcs1_2048 :: proc(tbl: ^table.Table) {
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
msg_bytes := transmute([]byte)(SIG_MSG)
sig_bytes: [2048 >> 3]byte
start := time.tick_now()
for _ in 0 ..< SIGN_ITERS {
ok := rsa.sign_pkcs1(&priv_key, .SHA256, msg_bytes, sig_bytes[:])
assert(ok, "signing should succeed")
}
taken := time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PKCS1/2048/SHA256/sign", taken)
start = time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
ok := rsa.verify_pkcs1(&priv_key._pub_key, .SHA256, msg_bytes, sig_bytes[:])
assert(ok, "verify should succeed")
}
taken = time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PKCS1/2048/SHA256/verify", taken)
}
@(private="file")
bench_pss_2048 :: proc(tbl: ^table.Table) {
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
msg_bytes := transmute([]byte)(SIG_MSG)
sig_bytes: [2048 >> 3]byte
start := time.tick_now()
for _ in 0 ..< SIGN_ITERS {
ok := rsa.sign_pss(&priv_key, .SHA256, 32, msg_bytes, sig_bytes[:])
assert(ok, "signing should succeed")
}
taken := time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PSS/2048/SHA256/sign", taken)
start = time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
ok := rsa.verify_pss(&priv_key._pub_key, .SHA256, 32, msg_bytes, sig_bytes[:])
assert(ok, "verify should succeed")
}
taken = time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PSS/2048/SHA256/verify", taken)
}
@(private="file")
bench_oaep_2048 :: proc(tbl: ^table.Table) {
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping")
return
}
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
msg_bytes := transmute([]byte)(SIG_MSG)
ciphertext_bytes: [2048 >> 3]byte
buf: [32]byte
start := time.tick_now()
for _ in 0 ..< SIGN_ITERS {
ok := rsa.encrypt_oaep(&priv_key._pub_key, .SHA256, msg_bytes, ciphertext_bytes[:])
assert(ok, "encryption should succeed")
}
taken := time.tick_since(start) / ENCRYPT_ITERS
append_tbl(tbl, "OAEP/2048/SHA256/encrypt", taken)
start = time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
_, ok := rsa.decrypt_oaep(&priv_key, .SHA256, ciphertext_bytes[:], buf[:])
assert(ok, "decrypt should succeed")
}
taken = time.tick_since(start) / ENCRYPT_ITERS
append_tbl(tbl, "OAEP/2048/SHA256/decrypt", taken)
}
@(private="file")
append_tbl :: proc(tbl: ^table.Table, op_name: string, avg_time: time.Duration) {
table.aligned_row_of_values(
tbl,
.Right,
op_name,
table.format(tbl, "%8M", avg_time),
)
}

View File

@@ -135,9 +135,30 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
testing.expect(t, rb.len(tree) == entry_count - 1, "iterator/remove: len should drop by 1")
rb.destroy(&tree)
testing.expect(t, rb.len(tree) == 0, "destroy: len should be 0")
testing.expect(t, rb.len(tree) == 0, "destroy: len should be 0")
testing.expectf(t, callback_count == 0, "remove: on_remove should've been called %v times, it was %v", entry_count, callback_count)
// Test upsert
rb.init(&tree)
clear(&inserted_map)
for i := 0; i < NR_INSERTS * 4; i += 1 {
k := Key(i) & 0x7
v := Value(i)
existing_node, in_map := inserted_map[k]
n, inserted, _ := rb.upsert(&tree, k, v)
testing.expect(t, in_map != inserted, "upsert: inserted should match inverse of map lookup")
if inserted {
inserted_map[k] = n
} else {
testing.expect(t, existing_node == n, "upsert: expecting existing node")
testing.expect_value(t, v, n.value) // And updated value
}
}
rb.destroy(&tree)
// print_tree_node(tree._root)
delete(inserted_map)
delete(inserted_keys)

View File

@@ -0,0 +1,310 @@
// Tests for the constant time RSA primitives
package test_core_crypto_bigint
import "base:runtime"
import "core:crypto/_bigint"
import "core:log"
import "core:slice"
import "core:testing"
ROUNDS :: 100_000
i31_equal :: proc(a, b: []u32) -> bool {
if a[0] != b[0] { return false }
bits := uint(a[0])
idx := 1
for bits > 0 {
ex := min(bits, 31)
mask := u32(1<<ex) - 1
if a[idx] & mask != b[idx] & mask { return false }
bits -= ex
idx += 1
}
return true
}
@(test)
i31_is_zero :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
arr := make([][5]u32, ROUNDS, context.temp_allocator)
for i in 0..<len(arr) {
_bigint.i31_mkrand(arr[i][:], u32((len(arr[i])-1) * 32))
}
for &v in arr {
v[0] = _bigint.i31_bit_length(v[1:])
sum: u64
for w in v[1:] {
sum += u64(w)
}
testing.expect_value(t, _bigint.i31_is_zero(v[:]), 1 if sum == 0 else 0)
slice.zero(v[1:])
testing.expect_value(t, _bigint.i31_is_zero(v[:]), 1)
}
}
@(test)
i31_add :: proc(t: ^testing.T) {
N :: 5
res: [N]u32
for v in i31_add_test_vectors {
if len(v.a) > N || len(v.b) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
if !(len(v.a) == len(v.b) && len(v.b) == len(v.res)) {
log.infof("Skipped %v, expected `a`, `b` and `res` lengths to be equal", v)
continue
}
// Copy into writable memory
copy(res[:], v.a[:])
// Add b to "a" in place
cc := _bigint.i31_add(res[:], v.b[:], 1)
testing.expect(t, slice.equal(res[:len(v.res)], v.res))
testing.expect_value(t, cc, v.carry)
}
}
@(test)
i31_sub :: proc(t: ^testing.T) {
N :: 5
res: [N]u32
for v in i31_sub_test_vectors {
if len(v.a) > N || len(v.b) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
if !(len(v.a) == len(v.b) && len(v.b) == len(v.res)) {
log.infof("Skipped %v, expected `a`, `b` and `res` lengths to be equal", v)
continue
}
// Copy into writable memory
copy(res[:], v.a[:])
// Add b to "a" in place
cc := _bigint.i31_sub(res[:], v.b[:], 1)
testing.expect(t, slice.equal(res[:len(v.res)], v.res))
testing.expect_value(t, cc, v.carry)
}
}
@(test)
i31_bit_length :: proc (t: ^testing.T) {
for v in i31_add_test_vectors {
a_len := _bigint.i31_bit_length(v.a[1:])
b_len := _bigint.i31_bit_length(v.b[1:])
testing.expect_value(t, a_len, v.a[0])
testing.expect_value(t, b_len, v.b[0])
}
}
@(test)
i31_decode :: proc(t: ^testing.T) {
N :: 10
res: [N]u32
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_decode_test_vectors {
if len(v.decode) > N || len(v.mod) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
_bigint.i31_decode(res[:], v.src)
testing.expect(t, slice.equal(res[:len(v.decode)], v.decode))
slice.zero(res[:])
mod_res := _bigint.i31_decode_mod(res[:], v.src, mod)
testing.expect(t, slice.equal(res[:len(v.mod)], v.mod))
testing.expect_value(t, mod_res, v.mod_res)
encoded: [32]u8 = 0
_bigint.i31_encode(encoded[:], v.decode)
testing.expect(t, slice.equal(encoded[32 - len(v.src):], v.src))
}
}
@(test)
i31_rshift :: proc(t: ^testing.T) {
N :: 4
res: [N]u32
for v in i31_rshift_test_vectors {
if len(v.orig) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
if v.shift < 0 || v.shift > 31 {
log.infof("Skipped %v, invalid shift amount", v)
continue
}
copy(res[:], v.orig)
_bigint.i31_rshift(res[:], v.shift)
testing.expect(t, slice.equal(res[:len(v.res)], v.res))
}
}
@(test)
i31_reduce :: proc(t: ^testing.T) {
N :: 12
res: [N]u32 = ---
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_reduce_test_vectors {
if len(v.orig) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
slice.zero(res[:])
_bigint.i31_reduce(res[:], v.orig, mod)
testing.expect(t, i31_equal(res[:], v.res))
}
}
@(test)
i31_decode_reduce :: proc(t: ^testing.T) {
N :: 4
res: [N]u32 = 0
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_decode_reduce_test_vectors {
if len(v.decode) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
res = 0
_bigint.i31_decode_reduce(res[:], v.src, mod)
testing.expect(t, i31_equal(res[:], v.decode))
}
}
@(test)
i31_muladd_small :: proc(t: ^testing.T) {
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_mul_add_test_vectors {
if len(v.orig) > len(mod) || len(v.res) > len(mod) {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
res: [3]u32 = 0
copy(res[:], v.orig)
_bigint.i31_muladd_small(res[:], v.z, mod)
l := len(v.res)
testing.expect(t, slice.equal(res[:l], v.res[:l]))
}
}
@(test)
i31_encode :: proc(t: ^testing.T) {
for v in i31_encode_test_vectors {
decoded: [10]u32 = 0
_bigint.i31_decode(decoded[:], v.encoded)
l := len(v.orig)
testing.expect(t, slice.equal(decoded[:l], v.orig[:l]))
encoded: [32]u8 = 0
_bigint.i31_encode(encoded[:], v.orig)
testing.expect(t, slice.equal(encoded[:], v.encoded))
}
}
@(test)
i31_monty_mul :: proc(t: ^testing.T) {
for v in i31_monty_mul_test_vectors {
res: [6]u32 = 0
m0i := _bigint.i31_ninv31(v.m[1])
if m0i == 0 {
log.infof("Expected _bigint.i31_ninv31(%v) to not be 0, m[1] must be even. Skipped.", v.m[1])
continue
}
_bigint.i31_montymul(res[:], v.x, v.y, v.m, m0i)
testing.expect(t, slice.equal(res[:], v.res))
}
}
@(test)
i31_to_monty :: proc(t: ^testing.T) {
for v in i31_to_monty_test_vectors {
res: [6]u32 = 0
copy(res[:], v.orig)
_bigint.i31_to_monty(res[:], v.m)
testing.expect(t, slice.equal(res[:], v.x))
m0i := _bigint.i31_ninv31(v.m[1])
if m0i == 0 {
log.infof("Expected _bigint.i31_ninv31(%v) to not be 0, m[1] must be even. Skipped.", v.m[1])
continue
}
_bigint.i31_from_monty(res[:], v.m, m0i)
testing.expect(t, slice.equal(res[:], v.orig))
}
}
@(test)
i31_modpow :: proc(t: ^testing.T) {
for v in i31_mod_pow_test_vectors {
x_out: [6]u32
temp: [100]u32
copy(x_out[:], v.orig)
m0i := _bigint.i31_ninv31(v.m[1])
assert(m0i != 0)
_bigint.i31_modpow(x_out[:], v.e, v.m, m0i, temp[:6], temp[6:][:6])
testing.expect(t, slice.equal(x_out[:], v.x))
}
}
@(test)
i31_mulacc :: proc(t: ^testing.T) {
for v in i31_mul_acc_test_vectors {
res: [12]u32 = 0
copy(res[:], v.d)
assert(v.d[0] == v.a[0])
_bigint.i31_mulacc(res[:], v.a, v.b)
testing.expect(t, slice.equal(res[:], v.res))
}
}
@(test)
internal_div_rem_u32 :: proc(t: ^testing.T) {
for v in i31_div_rem_test_vectors {
den := u64(v.hi) << 32 + u64(v.lo)
res := u64(v.quo) * u64(v.div) + u64(v.rem)
assert(den == res)
quo, rem := _bigint.div_rem_u32(v.hi, v.lo, v.div)
testing.expect_value(t, quo, v.quo)
testing.expect_value(t, rem, v.rem)
}
}

View File

@@ -0,0 +1,577 @@
package test_core_crypto_bigint
// Generated using BearSSL bindings.
I31_Test_Vector_Binary :: struct {
a: []u32,
b: []u32,
res: []u32,
carry: u32,
}
@(rodata)
i31_add_test_vectors := []I31_Test_Vector_Binary {
{a = {127, 0x051558fb, 0x578cb6cf, 0x3d2097aa, 0x7512fbf6}, b = {127, 0x6b2123f1, 0x4ad77a17, 0x705c1500, 0x508d965d}, res = {127, 0x70367cec, 0x226430e6, 0x2d7cacab, 0x45a09254}, carry = 1},
{a = {127, 0x0248fd90, 0x7825baae, 0x6de2183c, 0x4f5d0505}, b = {125, 0x492ba26a, 0x1bc7df54, 0x5114950e, 0x1ae989b2}, res = {127, 0x4b749ffa, 0x13ed9a02, 0x3ef6ad4b, 0x6a468eb8}, carry = 0},
{a = {126, 0x4833d6ef, 0x37a112d4, 0x2f71a920, 0x3093d1c3}, b = {127, 0x20574627, 0x39305532, 0x20b76825, 0x6e2eeea3}, res = {126, 0x688b1d16, 0x70d16806, 0x50291145, 0x1ec2c066}, carry = 1},
{a = {127, 0x55fb7b29, 0x21bef5c8, 0x67fe29e8, 0x798dabac}, b = {127, 0x274221db, 0x562cbe9c, 0x785bf5dc, 0x7058bbfa}, res = {127, 0x7d3d9d04, 0x77ebb464, 0x605a1fc4, 0x69e667a7}, carry = 1},
{a = {127, 0x2ffa533d, 0x59e20331, 0x59323704, 0x42cf6e75}, b = {127, 0x7ac812c0, 0x32ca1723, 0x15abc385, 0x410265b0}, res = {127, 0x2ac265fd, 0x0cac1a55, 0x6eddfa8a, 0x03d1d425}, carry = 1},
{a = {126, 0x789b52f9, 0x6535ff6a, 0x2744cf6e, 0x2e4342a6}, b = {127, 0x32dd2f41, 0x64d80309, 0x1ae80d4b, 0x405d1799}, res = {126, 0x2b78823a, 0x4a0e0274, 0x422cdcba, 0x6ea05a3f}, carry = 0},
{a = {127, 0x62003ba4, 0x2b58e836, 0x4587aced, 0x760bdb36}, b = {126, 0x0201e795, 0x50230e49, 0x30039ab5, 0x36f17032}, res = {127, 0x64022339, 0x7b7bf67f, 0x758b47a2, 0x2cfd4b68}, carry = 1},
{a = {124, 0x4ef8170f, 0x65e714cc, 0x5500f2a4, 0x090fac28}, b = {127, 0x0a03290f, 0x0d3fafb1, 0x74ebb1b5, 0x7a30e9c6}, res = {124, 0x58fb401e, 0x7326c47d, 0x49eca459, 0x034095ef}, carry = 1},
{a = {127, 0x34995a2f, 0x58f39080, 0x376ab5ef, 0x555b4fdf}, b = {122, 0x1c0928d9, 0x037fc1ea, 0x35c6d9b0, 0x0206ff68}, res = {127, 0x50a28308, 0x5c73526a, 0x6d318f9f, 0x57624f47}, carry = 0},
{a = {127, 0x7029eb02, 0x465899f5, 0x71ab22fc, 0x604f1ccf}, b = {124, 0x703e163c, 0x50f72eb1, 0x0a0f0bae, 0x0a2028fe}, res = {127, 0x6068013e, 0x174fc8a7, 0x7bba2eab, 0x6a6f45cd}, carry = 0},
{a = {125, 0x70cc03f6, 0x4d984b06, 0x4b990b9d, 0x16dd5097}, b = {125, 0x6eed2b72, 0x6001ddd2, 0x12c50807, 0x14f176cb}, res = {125, 0x5fb92f68, 0x2d9a28d9, 0x5e5e13a5, 0x2bcec762}, carry = 0},
{a = {127, 0x5e6a99af, 0x3ffc66eb, 0x31e47de6, 0x5bcd2f4b}, b = {124, 0x0f6f4335, 0x07d4bf78, 0x61e99233, 0x0fe21745}, res = {127, 0x6dd9dce4, 0x47d12663, 0x13ce1019, 0x6baf4691}, carry = 0},
{a = {127, 0x45de8b13, 0x010684b5, 0x01c77d90, 0x7464b4c8}, b = {127, 0x672a5a37, 0x33cb5670, 0x4ccc9e85, 0x7c0b88e1}, res = {127, 0x2d08e54a, 0x34d1db26, 0x4e941c15, 0x70703da9}, carry = 1},
{a = {127, 0x012fe1a4, 0x741e0733, 0x44d3fb4a, 0x79d78453}, b = {126, 0x66d849d4, 0x427b3ff0, 0x5b2b72e8, 0x3c88987d}, res = {127, 0x68082b78, 0x36994723, 0x1fff6e33, 0x36601cd1}, carry = 1},
{a = {125, 0x3ee54e09, 0x6bc71a68, 0x53cb44c8, 0x1f803699}, b = {124, 0x506ff07c, 0x63b155a0, 0x2c87590f, 0x0c7347e4}, res = {125, 0x0f553e85, 0x4f787009, 0x00529dd8, 0x2bf37e7e}, carry = 0},
{a = {125, 0x4847d126, 0x4c6af2f2, 0x663970b3, 0x11d97a24}, b = {126, 0x67c8e79a, 0x313d0fc2, 0x786fdac1, 0x23c16f5c}, res = {125, 0x3010b8c0, 0x7da802b5, 0x5ea94b74, 0x359ae981}, carry = 0},
{a = {127, 0x035a5f5c, 0x5fad2c0b, 0x6df6c2f3, 0x6cf28780}, b = {125, 0x09719328, 0x6adec2fa, 0x07d51f17, 0x1467d4f6}, res = {127, 0x0ccbf284, 0x4a8bef05, 0x75cbe20b, 0x015a5c76}, carry = 1},
{a = {127, 0x48a484fe, 0x4a2eb1e7, 0x04a04363, 0x52dd3789}, b = {126, 0x136b08b1, 0x4d63762d, 0x52072be9, 0x2f4ef42b}, res = {127, 0x5c0f8daf, 0x17922814, 0x56a76f4d, 0x022c2bb4}, carry = 1},
{a = {123, 0x119e8d05, 0x36510131, 0x38c3b084, 0x07bfe00d}, b = {127, 0x2ecdd4e9, 0x0afaddad, 0x77058092, 0x6b9f72dd}, res = {123, 0x406c61ee, 0x414bdede, 0x2fc93116, 0x735f52eb}, carry = 0},
{a = {126, 0x459b051a, 0x6cfaeee6, 0x461ccc17, 0x36a7eb37}, b = {126, 0x53726a35, 0x34443d10, 0x2d29d052, 0x27d37b9d}, res = {126, 0x190d6f4f, 0x213f2bf7, 0x73469c6a, 0x5e7b66d4}, carry = 0},
{a = {126, 0x73ccf104, 0x352c20d6, 0x580eeeca, 0x28ffb3bc}, b = {126, 0x1aba27fc, 0x31a85479, 0x05e9d689, 0x338a99a8}, res = {126, 0x0e871900, 0x66d47550, 0x5df8c553, 0x5c8a4d64}, carry = 0},
{a = {127, 0x0220ed88, 0x4ccecbb5, 0x31016917, 0x7bc75c14}, b = {127, 0x19d9b7b6, 0x763136a7, 0x783931cf, 0x58fec945}, res = {127, 0x1bfaa53e, 0x4300025c, 0x293a9ae7, 0x54c6255a}, carry = 1},
{a = {127, 0x14696b6a, 0x63348532, 0x71d09a50, 0x77ec660c}, b = {126, 0x67251ffd, 0x1f9606e1, 0x1205eb43, 0x32c3b9f7}, res = {127, 0x7b8e8b67, 0x02ca8c13, 0x03d68594, 0x2ab02004}, carry = 1},
{a = {127, 0x570637d9, 0x22723669, 0x77a7b284, 0x4e1e69ac}, b = {127, 0x32c70945, 0x072c2f80, 0x7ef86c2f, 0x595e375a}, res = {127, 0x09cd411e, 0x299e65ea, 0x76a01eb3, 0x277ca107}, carry = 1},
{a = {126, 0x47ab18d6, 0x4918a98b, 0x51c48283, 0x39a6cb59}, b = {127, 0x2430ac5b, 0x5328674a, 0x1fdffc7d, 0x69dced9b}, res = {126, 0x6bdbc531, 0x1c4110d5, 0x71a47f01, 0x2383b8f4}, carry = 1},
{a = {127, 0x30e85990, 0x65afbf0b, 0x3a20ce3d, 0x5a014c71}, b = {127, 0x27d0d3a9, 0x2483d56f, 0x7e96f8e9, 0x54c679f8}, res = {127, 0x58b92d39, 0x0a33947a, 0x38b7c727, 0x2ec7c66a}, carry = 1},
{a = {127, 0x6ec2ebc5, 0x2fe87e6e, 0x5c214f86, 0x46ce9354}, b = {127, 0x7b6b78e3, 0x0de8a5eb, 0x1cf27da3, 0x43d7077f}, res = {127, 0x6a2e64a8, 0x3dd1245a, 0x7913cd29, 0x0aa59ad3}, carry = 1},
{a = {127, 0x532479dc, 0x38cb5eb9, 0x6b8afdb8, 0x624a0f13}, b = {127, 0x74ab8b56, 0x27e5b9da, 0x62e7b771, 0x50f115a6}, res = {127, 0x47d00532, 0x60b11894, 0x4e72b529, 0x333b24ba}, carry = 1},
{a = {126, 0x7fe1b477, 0x4477a7c5, 0x04b0164d, 0x31d00838}, b = {125, 0x321ef997, 0x579ab7cf, 0x54fb3455, 0x133928f5}, res = {126, 0x3200ae0e, 0x1c125f95, 0x59ab4aa3, 0x4509312d}, carry = 0},
{a = {127, 0x0dd79881, 0x120bcfae, 0x73cc2c5d, 0x43b522e7}, b = {126, 0x0b81b7af, 0x3c00816c, 0x5658e7d3, 0x30483552}, res = {127, 0x19595030, 0x4e0c511a, 0x4a251430, 0x73fd583a}, carry = 0},
{a = {126, 0x0c52d45e, 0x698fd6df, 0x29a362fc, 0x3b6b5882}, b = {127, 0x5e851c19, 0x72428920, 0x417745c0, 0x437e8c53}, res = {126, 0x6ad7f077, 0x5bd25fff, 0x6b1aa8bd, 0x7ee9e4d5}, carry = 0},
{a = {126, 0x5d82d3a5, 0x328636c2, 0x175e0f86, 0x270f7afa}, b = {126, 0x7c06e395, 0x21b77837, 0x5fb98ab1, 0x225d6181}, res = {126, 0x5989b73a, 0x543daefa, 0x77179a37, 0x496cdc7b}, carry = 0},
{a = {127, 0x16be1fea, 0x48971493, 0x4a8f5617, 0x6b2a9968}, b = {127, 0x47387295, 0x2cecd451, 0x59abfdc3, 0x40028930}, res = {127, 0x5df6927f, 0x7583e8e4, 0x243b53da, 0x2b2d2299}, carry = 1},
{a = {127, 0x3cea792f, 0x62a42211, 0x18aa6284, 0x4ca7e07f}, b = {127, 0x5ea60937, 0x5e7c6b69, 0x5bd789bd, 0x774ddc8e}, res = {127, 0x1b908266, 0x41208d7b, 0x7481ec42, 0x43f5bd0d}, carry = 1},
{a = {127, 0x0e21cb57, 0x34087c35, 0x7bcad13b, 0x59c8dc35}, b = {125, 0x08a0ae9f, 0x145d8ebb, 0x1bcb683a, 0x1caefdbd}, res = {127, 0x16c279f6, 0x48660af0, 0x17963975, 0x7677d9f3}, carry = 0},
{a = {127, 0x0b0dd998, 0x1210d9cc, 0x01074e0a, 0x762996da}, b = {126, 0x3c80570a, 0x00fbed3c, 0x77bb5d04, 0x258d2213}, res = {127, 0x478e30a2, 0x130cc708, 0x78c2ab0e, 0x1bb6b8ed}, carry = 1},
{a = {127, 0x4e88ee76, 0x335e5d85, 0x634e3d26, 0x56cff5de}, b = {126, 0x15f3b966, 0x1c0a3f86, 0x1da6a59b, 0x35edda6f}, res = {127, 0x647ca7dc, 0x4f689d0b, 0x00f4e2c1, 0x0cbdd04e}, carry = 1},
{a = {127, 0x374f8369, 0x00230d40, 0x76f627a4, 0x45cb7dff}, b = {127, 0x0e437bcd, 0x5524b249, 0x6aa80d40, 0x578666e8}, res = {127, 0x4592ff36, 0x5547bf89, 0x619e34e4, 0x1d51e4e8}, carry = 1},
{a = {126, 0x7804142f, 0x66b7d326, 0x2f6fbe1b, 0x340e91e8}, b = {127, 0x5650e768, 0x22201fc5, 0x1a849718, 0x608b4950}, res = {126, 0x4e54fb97, 0x08d7f2ec, 0x49f45534, 0x1499db38}, carry = 1},
{a = {127, 0x409a9e5d, 0x6b53d3a4, 0x284aa18f, 0x60899852}, b = {126, 0x340a5074, 0x05dc9720, 0x6c400c75, 0x2c0a2e6d}, res = {127, 0x74a4eed1, 0x71306ac4, 0x148aae04, 0x0c93c6c0}, carry = 1},
{a = {127, 0x484f4d07, 0x45bf1a9a, 0x7679ef61, 0x58da55e9}, b = {125, 0x1672599a, 0x25b9fde9, 0x1dd52ce2, 0x1b792755}, res = {127, 0x5ec1a6a1, 0x6b791883, 0x144f1c43, 0x74537d3f}, carry = 0},
{a = {127, 0x46f0b919, 0x258efe9f, 0x25ac2909, 0x7ee35251}, b = {126, 0x26292ec9, 0x052799f6, 0x26e8cd9f, 0x2a91ea77}, res = {127, 0x6d19e7e2, 0x2ab69895, 0x4c94f6a8, 0x29753cc8}, carry = 1},
{a = {127, 0x662c1549, 0x7d1d8413, 0x2820005b, 0x640fa366}, b = {127, 0x1a1c703a, 0x7c960a8a, 0x0468ab46, 0x76636006}, res = {127, 0x00488583, 0x79b38e9e, 0x2c88aba2, 0x5a73036c}, carry = 1},
{a = {127, 0x32c078a6, 0x09144c2e, 0x7216eb1e, 0x575eb4a5}, b = {127, 0x014b7cd8, 0x7374bc3f, 0x79bad310, 0x41c309b3}, res = {127, 0x340bf57e, 0x7c89086d, 0x6bd1be2e, 0x1921be59}, carry = 1},
{a = {127, 0x5d152378, 0x0db3b779, 0x27cbd808, 0x729e2498}, b = {127, 0x639a2cac, 0x4e2dc8f3, 0x22846201, 0x538c5c6c}, res = {127, 0x40af5024, 0x5be1806d, 0x4a503a09, 0x462a8104}, carry = 1},
{a = {126, 0x4f52ebe5, 0x2e2ce1fe, 0x740060a5, 0x2b602c28}, b = {127, 0x23613e9d, 0x5236ec37, 0x744c1397, 0x6a0e964f}, res = {126, 0x72b42a82, 0x0063ce35, 0x684c743d, 0x156ec278}, carry = 1},
{a = {126, 0x7cab43a4, 0x7b2c3d9d, 0x44c551d5, 0x3e795426}, b = {127, 0x25f21ad6, 0x50b3905d, 0x4477f701, 0x64063b12}, res = {126, 0x229d5e7a, 0x4bdfcdfb, 0x093d48d7, 0x227f8f39}, carry = 1},
{a = {126, 0x54d5908a, 0x4db32799, 0x4e170b52, 0x32d0557d}, b = {126, 0x24f21022, 0x0e431da5, 0x52beb40c, 0x2e5eada4}, res = {126, 0x79c7a0ac, 0x5bf6453e, 0x20d5bf5e, 0x612f0322}, carry = 0},
{a = {127, 0x2fd0c679, 0x429d2164, 0x3b7d1340, 0x478c197e}, b = {126, 0x5b9f625b, 0x15cfdd9b, 0x0eb39653, 0x3f2a9e1a}, res = {127, 0x0b7028d4, 0x586cff00, 0x4a30a993, 0x06b6b798}, carry = 1},
{a = {127, 0x47e483cd, 0x635dad05, 0x334178a6, 0x5be2703c}, b = {127, 0x12487678, 0x0b47445a, 0x2b468da6, 0x5cf39115}, res = {127, 0x5a2cfa45, 0x6ea4f15f, 0x5e88064c, 0x38d60151}, carry = 1},
{a = {127, 0x16bd1190, 0x6f358b1b, 0x43fef060, 0x57246c18}, b = {125, 0x52f9efe3, 0x1d8e35d7, 0x45422cbc, 0x1e9a7d06}, res = {127, 0x69b70173, 0x0cc3c0f2, 0x09411d1d, 0x75bee91f}, carry = 0},
{a = {124, 0x300566bd, 0x4043d1da, 0x07083a42, 0x0ac4490a}, b = {126, 0x7bc7aaa4, 0x089b207d, 0x7d73159e, 0x20475102}, res = {124, 0x2bcd1161, 0x48def258, 0x047b4fe0, 0x2b0b9a0d}, carry = 0},
{a = {127, 0x1879a134, 0x2d5dce4b, 0x4fb09e72, 0x6f409165}, b = {127, 0x224335a0, 0x20c2b717, 0x748b2f65, 0x738d9650}, res = {127, 0x3abcd6d4, 0x4e208562, 0x443bcdd7, 0x62ce27b6}, carry = 1},
{a = {127, 0x49d61045, 0x5b207515, 0x2e191bd8, 0x64903107}, b = {127, 0x08359044, 0x2f1aff57, 0x730d9fb4, 0x7f03816e}, res = {127, 0x520ba089, 0x0a3b746c, 0x2126bb8d, 0x6393b276}, carry = 1},
{a = {127, 0x623eb7a3, 0x4d52e07d, 0x4a90b87c, 0x4641403a}, b = {125, 0x08f857d7, 0x3761ae9d, 0x46ead119, 0x13a35973}, res = {127, 0x6b370f7a, 0x04b48f1a, 0x117b8996, 0x59e499ae}, carry = 0},
{a = {126, 0x71b46b67, 0x1edc966c, 0x42558b0c, 0x29be2808}, b = {127, 0x666102d3, 0x573e7bfe, 0x65d18c72, 0x67b4562e}, res = {126, 0x58156e3a, 0x761b126b, 0x2827177e, 0x11727e37}, carry = 1},
{a = {127, 0x50500966, 0x0c5234da, 0x3e8cf39e, 0x62fbe6ce}, b = {127, 0x6c96a29c, 0x0ffb4fc4, 0x5f3a488f, 0x60d3a913}, res = {127, 0x3ce6ac02, 0x1c4d849f, 0x1dc73c2d, 0x43cf8fe2}, carry = 1},
{a = {125, 0x508f7e5a, 0x2a6c24f8, 0x7cb4ae59, 0x1816c2cd}, b = {126, 0x1e8cc9df, 0x16ed73e1, 0x5910fa15, 0x3a24b307}, res = {125, 0x6f1c4839, 0x415998d9, 0x55c5a86e, 0x523b75d5}, carry = 0},
{a = {127, 0x510325a0, 0x1ded9a89, 0x51e31db4, 0x6ddc474b}, b = {127, 0x22f4ab1e, 0x6fb69633, 0x51a5308e, 0x6eb15ef6}, res = {127, 0x73f7d0be, 0x0da430bc, 0x23884e43, 0x5c8da642}, carry = 1},
{a = {127, 0x12108d0c, 0x386688ec, 0x69d02125, 0x628e00f3}, b = {124, 0x11f211b8, 0x2a6ff08f, 0x33daa305, 0x0c610086}, res = {127, 0x24029ec4, 0x62d6797b, 0x1daac42a, 0x6eef017a}, carry = 0},
{a = {126, 0x0c264fba, 0x0884a2d3, 0x20569035, 0x304b267d}, b = {126, 0x6ff360e6, 0x12c6ad2b, 0x33f44806, 0x21d94783}, res = {126, 0x7c19b0a0, 0x1b4b4ffe, 0x544ad83b, 0x52246e00}, carry = 0},
{a = {127, 0x43ab14a8, 0x7c15842c, 0x636094f1, 0x53d57eea}, b = {126, 0x15682230, 0x35fc075d, 0x59910014, 0x23e605cb}, res = {127, 0x591336d8, 0x32118b89, 0x3cf19506, 0x77bb84b6}, carry = 0},
{a = {126, 0x5a7805ff, 0x32dd0387, 0x75f7a085, 0x3ea23d06}, b = {127, 0x4c55c285, 0x26e33530, 0x03d565c7, 0x724d2fcb}, res = {126, 0x26cdc884, 0x59c038b8, 0x79cd064c, 0x30ef6cd1}, carry = 1},
{a = {125, 0x1a9e1657, 0x1af68cda, 0x4602b940, 0x17acd817}, b = {127, 0x539ac7fe, 0x54adc300, 0x1fe07458, 0x403f5f12}, res = {125, 0x6e38de55, 0x6fa44fda, 0x65e32d98, 0x57ec3729}, carry = 0},
{a = {127, 0x62b5821b, 0x2c747aa2, 0x49572e10, 0x691ea596}, b = {127, 0x2e8da32f, 0x1325dd52, 0x08bf290a, 0x7b103bf2}, res = {127, 0x1143254a, 0x3f9a57f5, 0x5216571a, 0x642ee188}, carry = 1},
{a = {127, 0x215fb007, 0x0dfc51a6, 0x7014c1ee, 0x5e30b9d9}, b = {125, 0x52f164a9, 0x300a98d7, 0x2cab73e3, 0x132af275}, res = {127, 0x745114b0, 0x3e06ea7d, 0x1cc035d1, 0x715bac4f}, carry = 0},
{a = {127, 0x4dfcc7e5, 0x10d63d11, 0x443f7365, 0x7eb2535a}, b = {127, 0x543fd063, 0x041977bb, 0x294f5562, 0x7fabe576}, res = {127, 0x223c9848, 0x14efb4cd, 0x6d8ec8c7, 0x7e5e38d0}, carry = 1},
{a = {127, 0x1a8c810b, 0x49c32b54, 0x5dfbd7fe, 0x49ce1a98}, b = {127, 0x5166f071, 0x0bfea258, 0x3325555d, 0x7ebb7c16}, res = {127, 0x6bf3717c, 0x55c1cdac, 0x11212d5b, 0x488996af}, carry = 1},
{a = {124, 0x4b8a34ea, 0x5b0935d4, 0x3568d516, 0x0d16387a}, b = {126, 0x620a0226, 0x65058ce5, 0x6dd1444f, 0x2f49abb0}, res = {124, 0x2d943710, 0x400ec2ba, 0x233a1966, 0x3c5fe42b}, carry = 0},
{a = {126, 0x5e834973, 0x4242f578, 0x03a1e339, 0x2c61fc03}, b = {127, 0x04a255ce, 0x01ec5f4b, 0x4e56d825, 0x5d998057}, res = {126, 0x63259f41, 0x442f54c3, 0x51f8bb5e, 0x09fb7c5a}, carry = 1},
{a = {127, 0x6cb09c4b, 0x293baae1, 0x7e2830b9, 0x4edf0b6b}, b = {127, 0x4a59137b, 0x4b516bec, 0x0e882e9a, 0x67fd68cb}, res = {127, 0x3709afc6, 0x748d16ce, 0x0cb05f53, 0x36dc7437}, carry = 1},
{a = {126, 0x49d63bd1, 0x20f7d1ab, 0x78eec602, 0x21eea324}, b = {127, 0x4bf2e9c3, 0x55004234, 0x012195b5, 0x725e1f50}, res = {126, 0x15c92594, 0x75f813e0, 0x7a105bb7, 0x144cc274}, carry = 1},
{a = {118, 0x009708e5, 0x0f6eae6a, 0x22717ea3, 0x0027f75e}, b = {125, 0x73b69ce9, 0x05c9d5e7, 0x079ce44c, 0x1e0e4b57}, res = {118, 0x744da5ce, 0x15388451, 0x2a0e62ef, 0x1e3642b5}, carry = 0},
{a = {127, 0x7d5fcca1, 0x703581e8, 0x5ebea294, 0x6d59d7b6}, b = {127, 0x072dc186, 0x2556d525, 0x6550936e, 0x483050d7}, res = {127, 0x048d8e27, 0x158c570e, 0x440f3603, 0x358a288e}, carry = 1},
{a = {125, 0x1ee5e478, 0x6cbc0fd8, 0x5a08ca0d, 0x160799a6}, b = {124, 0x20c5baa0, 0x6893f6b8, 0x2e615746, 0x08b55361}, res = {125, 0x3fab9f18, 0x55500690, 0x086a2154, 0x1ebced08}, carry = 0},
{a = {126, 0x5ba7f924, 0x69e6da01, 0x7012307c, 0x257f31e9}, b = {124, 0x4f354878, 0x5c6c44df, 0x580de770, 0x088dc09c}, res = {126, 0x2add419c, 0x46531ee1, 0x482017ed, 0x2e0cf286}, carry = 0},
{a = {126, 0x53c283ee, 0x19ddd382, 0x401fa681, 0x39b9198a}, b = {126, 0x3e9c90df, 0x3091f3aa, 0x1ec070d3, 0x2f2473fa}, res = {126, 0x125f14cd, 0x4a6fc72d, 0x5ee01754, 0x68dd8d84}, carry = 0},
{a = {125, 0x2aab90f9, 0x0ee5a976, 0x4b5b0203, 0x1672068b}, b = {125, 0x53c43b94, 0x020d4782, 0x260b9c3a, 0x1faca98d}, res = {125, 0x7e6fcc8d, 0x10f2f0f8, 0x71669e3d, 0x361eb018}, carry = 0},
{a = {127, 0x1741cb5d, 0x42adb37f, 0x16f4e290, 0x7c26149a}, b = {127, 0x5c12de2e, 0x77a67be5, 0x2ef8e62e, 0x4a9fdb63}, res = {127, 0x7354a98b, 0x3a542f64, 0x45edc8bf, 0x46c5effd}, carry = 1},
{a = {125, 0x21437747, 0x41ce24dd, 0x70efb0ac, 0x1d4aac67}, b = {126, 0x1235886f, 0x7bc15966, 0x1097debf, 0x29249284}, res = {125, 0x3378ffb6, 0x3d8f7e43, 0x01878f6c, 0x466f3eec}, carry = 0},
{a = {126, 0x0e70b5d5, 0x43bd3fd6, 0x1bdbe98b, 0x2033fc0b}, b = {127, 0x7ce4daf7, 0x16ace9f7, 0x073703bc, 0x5704964f}, res = {126, 0x0b5590cc, 0x5a6a29ce, 0x2312ed47, 0x7738925a}, carry = 0},
{a = {120, 0x62a94a7e, 0x604154fa, 0x3deeb80c, 0x00df6634}, b = {125, 0x6fd3f3b5, 0x3cfc6cef, 0x63fd704a, 0x1678e57f}, res = {120, 0x527d3e33, 0x1d3dc1ea, 0x21ec2857, 0x17584bb4}, carry = 0},
{a = {123, 0x7978a2ad, 0x5d491b8a, 0x71574e8a, 0x0663444b}, b = {126, 0x0bc05c34, 0x7c0f280f, 0x015a49fc, 0x38502a7f}, res = {123, 0x0538fee1, 0x5958439a, 0x72b19887, 0x3eb36eca}, carry = 0},
{a = {125, 0x51d6b6f2, 0x079bd814, 0x6ce81e2d, 0x18c4c981}, b = {127, 0x54f420d0, 0x2dc663dc, 0x13bc81b9, 0x4060575c}, res = {125, 0x26cad7c2, 0x35623bf1, 0x00a49fe6, 0x592520de}, carry = 0},
{a = {127, 0x36485bbe, 0x09796514, 0x63ee7f58, 0x5625e4e2}, b = {127, 0x21458fe6, 0x7c9fa294, 0x54b76a7e, 0x6d695c7f}, res = {127, 0x578deba4, 0x061907a8, 0x38a5e9d7, 0x438f4162}, carry = 1},
{a = {127, 0x33d70ec2, 0x1f127a0b, 0x2717bb82, 0x5fd43cd2}, b = {125, 0x3f0797eb, 0x0d5c370b, 0x3d594b1d, 0x1752300b}, res = {127, 0x72dea6ad, 0x2c6eb116, 0x6471069f, 0x77266cdd}, carry = 0},
{a = {127, 0x37eae763, 0x19004adb, 0x38b7eff2, 0x688fe1c5}, b = {127, 0x2cac471c, 0x37811110, 0x1f3987d5, 0x7fe15712}, res = {127, 0x64972e7f, 0x50815beb, 0x57f177c7, 0x687138d7}, carry = 1},
{a = {126, 0x6c225971, 0x0437a4c8, 0x1bcb8b3e, 0x28ceb662}, b = {127, 0x48cd5436, 0x7e421cca, 0x29d44db3, 0x55711f42}, res = {126, 0x34efada7, 0x0279c193, 0x459fd8f2, 0x7e3fd5a4}, carry = 0},
{a = {126, 0x2574a51d, 0x5de32cbd, 0x6c67c9cc, 0x376694a2}, b = {126, 0x138f84b7, 0x46f2b3f1, 0x47583471, 0x3b15472c}, res = {126, 0x390429d4, 0x24d5e0ae, 0x33bffe3e, 0x727bdbcf}, carry = 0},
{a = {127, 0x588e6a4d, 0x03204eb0, 0x400a06ba, 0x469c62fd}, b = {127, 0x33609fc4, 0x65a624e4, 0x6111951d, 0x7648f1ae}, res = {127, 0x0bef0a11, 0x68c67395, 0x211b9bd7, 0x3ce554ac}, carry = 1},
{a = {127, 0x03178062, 0x2c0793b9, 0x6b91ed2a, 0x436a75d5}, b = {127, 0x364e9acd, 0x20881e30, 0x609f019a, 0x5d437f92}, res = {127, 0x39661b2f, 0x4c8fb1e9, 0x4c30eec4, 0x20adf568}, carry = 1},
{a = {126, 0x582502a7, 0x76d9bbd7, 0x6ec18736, 0x3d15fed5}, b = {127, 0x45e8a9e7, 0x0cfae275, 0x4c164029, 0x641d8e58}, res = {126, 0x1e0dac8e, 0x03d49e4d, 0x3ad7c760, 0x21338d2e}, carry = 1},
{a = {127, 0x31a44c73, 0x4c970755, 0x433f481c, 0x63704395}, b = {127, 0x1a3979dc, 0x0e9d5437, 0x592a1251, 0x5cb4e212}, res = {127, 0x4bddc64f, 0x5b345b8c, 0x1c695a6d, 0x402525a8}, carry = 1},
{a = {126, 0x31499a0a, 0x2df3b6ac, 0x4cf3e9b4, 0x3fdaa87e}, b = {126, 0x6749f6f2, 0x28c62023, 0x3e54e11b, 0x30a577bf}, res = {126, 0x189390fc, 0x56b9d6d0, 0x0b48cacf, 0x7080203e}, carry = 0},
{a = {126, 0x2d6e6ed4, 0x3c9f5183, 0x27326fac, 0x35b95ad6}, b = {125, 0x28a46b0d, 0x4143c903, 0x182ab95c, 0x17b09437}, res = {126, 0x5612d9e1, 0x7de31a86, 0x3f5d2908, 0x4d69ef0d}, carry = 0},
{a = {126, 0x26f20408, 0x2af94d52, 0x440bf192, 0x360df26a}, b = {127, 0x7b2198cb, 0x763bc4c5, 0x78ac7d5a, 0x5de9efc9}, res = {126, 0x22139cd3, 0x21351218, 0x3cb86eed, 0x13f7e234}, carry = 1},
{a = {127, 0x47128b80, 0x670a3dbb, 0x63efbba7, 0x74a10d1e}, b = {124, 0x7f481747, 0x59187cd8, 0x51e741bc, 0x0a8ea0cd}, res = {127, 0x465aa2c7, 0x4022ba94, 0x35d6fd64, 0x7f2fadec}, carry = 0},
{a = {127, 0x5a47d043, 0x052b9ec0, 0x401cbe7c, 0x49104294}, b = {127, 0x7427cee8, 0x06334bc8, 0x4528bd14, 0x741e6df6}, res = {127, 0x4e6f9f2b, 0x0b5eea89, 0x05457b90, 0x3d2eb08b}, carry = 1},
{a = {127, 0x6520f6a0, 0x5b37902f, 0x31d3c910, 0x5f9fe4e6}, b = {127, 0x6aa5d0dc, 0x61ad0b30, 0x3b782608, 0x7dd0cd5f}, res = {127, 0x4fc6c77c, 0x3ce49b60, 0x6d4bef19, 0x5d70b245}, carry = 1},
{a = {126, 0x34bdc731, 0x2de09f84, 0x3896e197, 0x3fee6ac9}, b = {127, 0x5b5d8627, 0x3c22bdb9, 0x5e7b4aaf, 0x555e4e2a}, res = {126, 0x101b4d58, 0x6a035d3e, 0x17122c46, 0x154cb8f4}, carry = 1},
}
@(rodata)
i31_sub_test_vectors := []I31_Test_Vector_Binary {
{a = {126, 0x75832201, 0x72b1ddb3, 0x3e8d7744, 0x325d7cb5}, b = {127, 0x52a23cfc, 0x51a47476, 0x19fd5fa3, 0x5a5e5d48}, res = {126, 0x22e0e505, 0x210d693d, 0x249017a1, 0x57ff1f6d}, carry = 1},
{a = {127, 0x02585014, 0x15b77a75, 0x66fa4e0f, 0x5368a27c}, b = {126, 0x1aaa3973, 0x288030e7, 0x51ede1ff, 0x37bbad71}, res = {127, 0x67ae16a1, 0x6d37498d, 0x150c6c0f, 0x1bacf50b}, carry = 0},
{a = {125, 0x3b43d5aa, 0x3de09ea7, 0x18004f89, 0x113b0b5e}, b = {127, 0x3a318699, 0x10ee38f1, 0x6d8c06d8, 0x7ac6ef8f}, res = {125, 0x01124f11, 0x2cf265b6, 0x2a7448b1, 0x16741bce}, carry = 1},
{a = {127, 0x047e69b6, 0x5455ee19, 0x012e5e13, 0x73a56c35}, b = {127, 0x10606c28, 0x33f1e053, 0x18f48327, 0x69bc2be6}, res = {127, 0x741dfd8e, 0x20640dc5, 0x6839daec, 0x09e9404e}, carry = 0},
{a = {127, 0x02c391a7, 0x07feb24a, 0x3a32b686, 0x5ee14ddd}, b = {126, 0x3f3b74ff, 0x2c1596d7, 0x677a7df4, 0x2ce6395d}, res = {127, 0x43881ca8, 0x5be91b72, 0x52b83891, 0x31fb147f}, carry = 0},
{a = {125, 0x4b26152d, 0x24f5c3a3, 0x6f1d3b6f, 0x1586f40a}, b = {125, 0x7380829e, 0x1bd90254, 0x382393b6, 0x1b85a0c5}, res = {125, 0x57a5928f, 0x091cc14e, 0x36f9a7b9, 0x7a015345}, carry = 1},
{a = {126, 0x4fa59569, 0x643c2005, 0x72e9a332, 0x2e315696}, b = {121, 0x6de61d40, 0x6b932a39, 0x52568d6d, 0x01e8488c}, res = {126, 0x61bf7829, 0x78a8f5cb, 0x209315c4, 0x2c490e0a}, carry = 0},
{a = {127, 0x5379b430, 0x467ac863, 0x297696ab, 0x70622b6f}, b = {127, 0x37aac8dc, 0x1adda229, 0x58df1d01, 0x505ee996}, res = {127, 0x1bceeb54, 0x2b9d263a, 0x509779aa, 0x200341d8}, carry = 0},
{a = {127, 0x1ccf6e2c, 0x11dc002c, 0x561b6361, 0x46990123}, b = {127, 0x3e9f8554, 0x42397834, 0x4fdd5416, 0x7e39420f}, res = {127, 0x5e2fe8d8, 0x4fa287f7, 0x063e0f4a, 0x485fbf14}, carry = 1},
{a = {126, 0x2150add9, 0x26e965d1, 0x5e4ac34d, 0x2a55c9de}, b = {127, 0x6669738d, 0x3d3ff599, 0x582b736f, 0x4dd82557}, res = {126, 0x3ae73a4c, 0x69a97037, 0x061f4fdd, 0x5c7da487}, carry = 1},
{a = {127, 0x61a3eb42, 0x2796bb5e, 0x5b90dcd6, 0x78085440}, b = {127, 0x51c6a80f, 0x0a65c487, 0x6179dc58, 0x562966ef}, res = {127, 0x0fdd4333, 0x1d30f6d7, 0x7a17007e, 0x21deed50}, carry = 0},
{a = {127, 0x51d4085f, 0x72b1daa2, 0x7560d03e, 0x47df6842}, b = {125, 0x27993d6b, 0x1b36ba5a, 0x78a56068, 0x1811037d}, res = {127, 0x2a3acaf4, 0x577b2048, 0x7cbb6fd6, 0x2fce64c4}, carry = 0},
{a = {125, 0x2cb4937a, 0x48c3af52, 0x7c4e70a7, 0x1578b00c}, b = {127, 0x20263089, 0x3feb52a0, 0x1b877614, 0x4f5677bc}, res = {125, 0x0c8e62f1, 0x08d85cb2, 0x60c6fa93, 0x46223850}, carry = 1},
{a = {126, 0x338493e9, 0x4ff90c99, 0x5dc3827f, 0x2a9898da}, b = {122, 0x588232b4, 0x1c53bb05, 0x17ef86ab, 0x03bce2b4}, res = {126, 0x5b026135, 0x33a55193, 0x45d3fbd4, 0x26dbb626}, carry = 0},
{a = {124, 0x40625bc9, 0x204b059d, 0x48deea64, 0x0a019e61}, b = {127, 0x661bd15a, 0x48ead2c5, 0x2a0e651f, 0x7999457b}, res = {124, 0x5a468a6f, 0x576032d7, 0x1ed08544, 0x106858e6}, carry = 1},
{a = {127, 0x5874a542, 0x71947250, 0x054ff642, 0x7d66c0f6}, b = {125, 0x780fcb4e, 0x290dc73d, 0x2687ab55, 0x107555db}, res = {127, 0x6064d9f4, 0x4886ab12, 0x5ec84aed, 0x6cf16b1a}, carry = 0},
{a = {126, 0x6fd19206, 0x50f14417, 0x7f82f1ed, 0x2bbb502b}, b = {127, 0x0468cf8a, 0x67942b1c, 0x7c399be0, 0x6306501c}, res = {126, 0x6b68c27c, 0x695d18fb, 0x0349560c, 0x48b5000f}, carry = 1},
{a = {124, 0x51a0623b, 0x7b984ebd, 0x0fb05efe, 0x0fd13b63}, b = {127, 0x2a6b91ec, 0x68eaf51b, 0x3265f0fd, 0x4a4f4fb3}, res = {124, 0x2734d04f, 0x12ad59a2, 0x5d4a6e01, 0x4581ebaf}, carry = 1},
{a = {127, 0x0f1be1af, 0x76ef0418, 0x489dab08, 0x4998335f}, b = {127, 0x24c297f4, 0x6fabbbe8, 0x01529fff, 0x6c5b3c15}, res = {127, 0x6a5949bb, 0x0743482f, 0x474b0b09, 0x5d3cf74a}, carry = 1},
{a = {125, 0x53a96664, 0x6e05147a, 0x1017a26d, 0x19d02282}, b = {127, 0x46f7e29b, 0x466f513b, 0x1c9bb1fc, 0x56817f94}, res = {125, 0x0cb183c9, 0x2795c33f, 0x737bf071, 0x434ea2ed}, carry = 1},
{a = {127, 0x2fcad8be, 0x60aea5d7, 0x63f1ddde, 0x5e392547}, b = {127, 0x3006e08d, 0x560753cd, 0x7e2304f9, 0x48c18d08}, res = {127, 0x7fc3f831, 0x0aa75209, 0x65ced8e5, 0x1577983e}, carry = 0},
{a = {125, 0x140a0bd3, 0x0af4e07b, 0x1d4d97a3, 0x12530f8a}, b = {124, 0x66378508, 0x26164d79, 0x0b3ff9ba, 0x0b94a13a}, res = {125, 0x2dd286cb, 0x64de9301, 0x120d9de8, 0x06be6e50}, carry = 0},
{a = {127, 0x16d0c935, 0x6e1afbd1, 0x08ef273a, 0x694e551c}, b = {125, 0x0f34a643, 0x167cbfb7, 0x2c1977e7, 0x1bd4f3cc}, res = {127, 0x079c22f2, 0x579e3c1a, 0x5cd5af53, 0x4d79614f}, carry = 0},
{a = {127, 0x3d62a534, 0x3c284b2d, 0x53524377, 0x6016b0bd}, b = {125, 0x7cf4d88b, 0x68fb44c3, 0x61c8f0ad, 0x10e57b36}, res = {127, 0x406dcca9, 0x532d0669, 0x718952c9, 0x4f313586}, carry = 0},
{a = {127, 0x6f015d33, 0x536fbab1, 0x26915f4b, 0x562f74ee}, b = {122, 0x63e75212, 0x68694786, 0x3f82de9a, 0x02f4382a}, res = {127, 0x0b1a0b21, 0x6b06732b, 0x670e80b0, 0x533b3cc3}, carry = 0},
{a = {127, 0x66a82756, 0x4025f122, 0x42ad0938, 0x58fbe956}, b = {123, 0x150d6a84, 0x23439905, 0x7f8687ac, 0x057badf7}, res = {127, 0x519abcd2, 0x1ce2581d, 0x4326818c, 0x53803b5e}, carry = 0},
{a = {127, 0x1b0466bd, 0x69accc1e, 0x55da7183, 0x71f64927}, b = {126, 0x12a653b6, 0x4be3b926, 0x174ecdc5, 0x2e28e368}, res = {127, 0x085e1307, 0x1dc912f8, 0x3e8ba3be, 0x43cd65bf}, carry = 0},
{a = {119, 0x119a6115, 0x130e1918, 0x4e2c62c0, 0x00586779}, b = {127, 0x7042f5f4, 0x6c596133, 0x0b8e23ed, 0x4fded5b4}, res = {119, 0x21576b21, 0x26b4b7e4, 0x429e3ed2, 0x307991c5}, carry = 1},
{a = {127, 0x05b5cfd3, 0x2ae55120, 0x46abe106, 0x70b46cd4}, b = {127, 0x4a8e2790, 0x5557a5e9, 0x3e14dfb6, 0x62ff997b}, res = {127, 0x3b27a843, 0x558dab36, 0x0897014f, 0x0db4d359}, carry = 0},
{a = {126, 0x40c5bd09, 0x1bc7d36e, 0x5787d3c1, 0x2dadafd4}, b = {127, 0x7d36195e, 0x67d20645, 0x384c8cf2, 0x7bc9c106}, res = {126, 0x438fa3ab, 0x33f5cd28, 0x1f3b46ce, 0x31e3eece}, carry = 1},
{a = {121, 0x582c3425, 0x5e591a55, 0x64bc368c, 0x01171496}, b = {127, 0x7ff455d7, 0x1d082b60, 0x69a595c6, 0x7506f22f}, res = {121, 0x5837de4e, 0x4150eef4, 0x7b16a0c6, 0x0c102266}, carry = 1},
{a = {126, 0x76e444c8, 0x5d162950, 0x0b4c9cb8, 0x350172ce}, b = {126, 0x6fae6b50, 0x457e2833, 0x22e790fd, 0x222f32e8}, res = {126, 0x0735d978, 0x1798011d, 0x68650bbb, 0x12d23fe5}, carry = 0},
{a = {126, 0x0a94f6cb, 0x7e659900, 0x21e27381, 0x20898f13}, b = {125, 0x6c0e1d51, 0x26db9379, 0x4aa78374, 0x13e8525f}, res = {126, 0x1e86d97a, 0x578a0586, 0x573af00d, 0x0ca13cb3}, carry = 0},
{a = {126, 0x2df85daa, 0x089e925f, 0x372ad1cd, 0x282da3da}, b = {126, 0x5f6eb57d, 0x666b94e8, 0x72523b11, 0x2e872674}, res = {126, 0x4e89a82d, 0x2232fd76, 0x44d896bb, 0x79a67d65}, carry = 1},
{a = {127, 0x59c5b936, 0x14069767, 0x00e21797, 0x7ab2135d}, b = {127, 0x4a74250d, 0x65c5e230, 0x5562bf62, 0x5c25dec5}, res = {127, 0x0f519429, 0x2e40b537, 0x2b7f5834, 0x1e8c3497}, carry = 0},
{a = {125, 0x74ce4c1e, 0x59cb9a18, 0x5e0560c1, 0x142bdb97}, b = {126, 0x0e08a60d, 0x4c39a45f, 0x0929589f, 0x22096dd5}, res = {125, 0x66c5a611, 0x0d91f5b9, 0x54dc0822, 0x72226dc2}, carry = 1},
{a = {127, 0x65273636, 0x653e4647, 0x2cf1b1f4, 0x74f9fddd}, b = {122, 0x6e960ace, 0x7ea2dfda, 0x35a317bd, 0x0202a322}, res = {127, 0x76912b68, 0x669b666c, 0x774e9a36, 0x72f75aba}, carry = 0},
{a = {126, 0x45582ae7, 0x6996c2bb, 0x09380ea2, 0x30b4897c}, b = {127, 0x246ce79f, 0x758393b5, 0x00926c53, 0x40dd24dc}, res = {126, 0x20eb4348, 0x74132f06, 0x08a5a24e, 0x6fd764a0}, carry = 1},
{a = {127, 0x7face3b5, 0x4e099393, 0x1e3f7fe6, 0x50946a47}, b = {126, 0x50b06b20, 0x36db7586, 0x40ec226f, 0x33e64381}, res = {127, 0x2efc7895, 0x172e1e0d, 0x5d535d77, 0x1cae26c5}, carry = 0},
{a = {124, 0x5bae9309, 0x23a74c8d, 0x4c7f0704, 0x0fd4a29c}, b = {127, 0x07e40cc7, 0x31e632d3, 0x1ae2cbd2, 0x6f8f930a}, res = {124, 0x53ca8642, 0x71c119ba, 0x319c3b31, 0x20450f92}, carry = 1},
{a = {126, 0x69c3634b, 0x0c4ffbaa, 0x390a326e, 0x25e998b2}, b = {123, 0x4d164145, 0x6cf31f2c, 0x60273791, 0x05ea1210}, res = {126, 0x1cad2206, 0x1f5cdc7e, 0x58e2fadc, 0x1fff86a1}, carry = 0},
{a = {124, 0x25a0c384, 0x38438ee6, 0x67b92d68, 0x0b8d4052}, b = {127, 0x65152a5c, 0x6af4d890, 0x78f3790f, 0x516ebf7b}, res = {124, 0x408b9928, 0x4d4eb655, 0x6ec5b458, 0x3a1e80d6}, carry = 1},
{a = {120, 0x27e60366, 0x1158cd09, 0x7f574484, 0x00eb60e2}, b = {126, 0x5ee2ec9b, 0x17d9507a, 0x283fcdf8, 0x3d95a50e}, res = {120, 0x490316cb, 0x797f7c8e, 0x5717768b, 0x4355bbd4}, carry = 1},
{a = {126, 0x7a6cfbf1, 0x3b47fde4, 0x1f96a1b0, 0x3415815d}, b = {127, 0x6db4865c, 0x68d15d67, 0x29492ffd, 0x55b34d59}, res = {126, 0x0cb87595, 0x5276a07d, 0x764d71b2, 0x5e623403}, carry = 1},
{a = {126, 0x78bc926b, 0x06872ef9, 0x77dc59ee, 0x341e1fe5}, b = {127, 0x4aafbaf4, 0x0f29aa6c, 0x6e2dcd74, 0x5ae5ab56}, res = {126, 0x2e0cd777, 0x775d848d, 0x09ae8c79, 0x5938748f}, carry = 1},
{a = {127, 0x5339599f, 0x6d388285, 0x1fb30e96, 0x653d70d1}, b = {127, 0x26e44c38, 0x360bdfc0, 0x317ed433, 0x4e44f71e}, res = {127, 0x2c550d67, 0x372ca2c5, 0x6e343a63, 0x16f879b2}, carry = 0},
{a = {126, 0x5f57436e, 0x5d7b8fc4, 0x3b913a49, 0x289c171d}, b = {127, 0x63106f0e, 0x181ab5a1, 0x1a35ecdc, 0x4f6fd3de}, res = {126, 0x7c46d460, 0x4560da22, 0x215b4d6d, 0x592c433f}, carry = 1},
{a = {125, 0x0a9e7523, 0x4011ed51, 0x573e92c1, 0x1465e918}, b = {125, 0x190d6359, 0x595c3a46, 0x76ee73b0, 0x1163ef5f}, res = {125, 0x719111ca, 0x66b5b30a, 0x60501f10, 0x0301f9b8}, carry = 0},
{a = {126, 0x5b4c4e1c, 0x368e93d7, 0x1d7f383f, 0x3893cc55}, b = {126, 0x15361741, 0x192e05a0, 0x69808f62, 0x3d0b8de0}, res = {126, 0x461636db, 0x1d608e37, 0x33fea8dd, 0x7b883e74}, carry = 1},
{a = {127, 0x711129f3, 0x1221b384, 0x75a6ce65, 0x75cc9dda}, b = {125, 0x05242048, 0x29449c5e, 0x299b4ec4, 0x1179eca2}, res = {127, 0x6bed09ab, 0x68dd1726, 0x4c0b7fa0, 0x6452b138}, carry = 0},
{a = {126, 0x23d8928a, 0x6bc1710b, 0x4f1a2853, 0x3920c780}, b = {127, 0x3ed60b0f, 0x639f16e2, 0x26d5472a, 0x7a6d17fb}, res = {126, 0x6502877b, 0x08225a28, 0x2844e129, 0x3eb3af85}, carry = 1},
{a = {127, 0x0d9cfe5d, 0x619328e7, 0x542c700d, 0x6997d292}, b = {127, 0x1cedd3b7, 0x5edfbdf6, 0x45d76cf4, 0x7357a00a}, res = {127, 0x70af2aa6, 0x02b36af0, 0x0e550319, 0x76403288}, carry = 1},
{a = {127, 0x03e30b55, 0x4013c48f, 0x729f2a80, 0x58b486a3}, b = {126, 0x786328fe, 0x37cc20dc, 0x25a645e1, 0x27cf6abb}, res = {127, 0x0b7fe257, 0x0847a3b2, 0x4cf8e49f, 0x30e51be8}, carry = 0},
{a = {125, 0x7ac1580f, 0x2c93cbec, 0x527026f9, 0x124b767e}, b = {127, 0x68ba663a, 0x4df0c1d0, 0x4a5c9da6, 0x5da272db}, res = {125, 0x1206f1d5, 0x5ea30a1c, 0x08138952, 0x34a903a3}, carry = 1},
{a = {127, 0x56b6b76a, 0x3ece11a5, 0x296c5e68, 0x59ab3003}, b = {127, 0x6e69a755, 0x7099639a, 0x357c87d6, 0x4c9f33fd}, res = {127, 0x684d1015, 0x4e34ae0a, 0x73efd691, 0x0d0bfc05}, carry = 0},
{a = {127, 0x4404fa8a, 0x5866a5c4, 0x0c22add4, 0x5ef97523}, b = {127, 0x1c21bae1, 0x256e56eb, 0x77662e5f, 0x7376f17a}, res = {127, 0x27e33fa9, 0x32f84ed9, 0x14bc7f75, 0x6b8283a8}, carry = 1},
{a = {126, 0x2c0d6109, 0x3509e87e, 0x53519777, 0x3c09bbc0}, b = {127, 0x3a60646e, 0x3f868a8e, 0x461edbdd, 0x777a0c2c}, res = {126, 0x71acfc9b, 0x75835def, 0x0d32bb99, 0x448faf94}, carry = 1},
{a = {126, 0x5f8c1f7a, 0x40041120, 0x463c5e66, 0x35a4cd6e}, b = {127, 0x08cf40d3, 0x2edbcf34, 0x784ab86d, 0x4de2eb71}, res = {126, 0x56bcdea7, 0x112841ec, 0x4df1a5f9, 0x67c1e1fc}, carry = 1},
{a = {127, 0x2266e65d, 0x0553ee53, 0x0c667741, 0x4fd147e2}, b = {127, 0x3fb1e4a2, 0x7cb1c12a, 0x535dbe75, 0x67bfcc87}, res = {127, 0x62b501bb, 0x08a22d28, 0x3908b8cb, 0x68117b5a}, carry = 1},
{a = {127, 0x76bfd2fb, 0x40e3235f, 0x7110f948, 0x55262313}, b = {126, 0x56f35745, 0x288792fc, 0x3759df43, 0x36acfcf9}, res = {127, 0x1fcc7bb6, 0x185b9063, 0x39b71a05, 0x1e79261a}, carry = 0},
{a = {125, 0x79452908, 0x28cb7d35, 0x033fb36e, 0x1e3d1dc2}, b = {126, 0x625452fe, 0x6f0e270e, 0x4a783375, 0x21ed361b}, res = {125, 0x16f0d60a, 0x39bd5627, 0x38c77ff8, 0x7c4fe7a6}, carry = 1},
{a = {126, 0x7372a659, 0x3cd43111, 0x79c40547, 0x2d1afece}, b = {127, 0x7f8726fd, 0x24d0aa04, 0x1f6ecb14, 0x4e053ba9}, res = {126, 0x73eb7f5c, 0x1803870c, 0x5a553a33, 0x5f15c325}, carry = 1},
{a = {126, 0x2f15690a, 0x4de81324, 0x3e9f55fe, 0x373cfdd3}, b = {125, 0x02bb7d7f, 0x3f2d94bb, 0x769d851b, 0x10f5260a}, res = {126, 0x2c59eb8b, 0x0eba7e69, 0x4801d0e3, 0x2647d7c8}, carry = 0},
{a = {125, 0x54d7b5c6, 0x3932181a, 0x53521bf5, 0x1c5cf5ee}, b = {125, 0x03d8109e, 0x4d5cbcea, 0x49faabc0, 0x1d3e3e9a}, res = {125, 0x50ffa528, 0x6bd55b30, 0x09577034, 0x7f1eb754}, carry = 1},
{a = {125, 0x43482a38, 0x1ce74ab0, 0x624445b9, 0x176cc140}, b = {125, 0x202ec3d7, 0x1cdd0bba, 0x4493150c, 0x13241d43}, res = {125, 0x23196661, 0x000a3ef6, 0x1db130ad, 0x0448a3fd}, carry = 0},
{a = {126, 0x63a9b9c1, 0x5557f9c9, 0x4a536191, 0x3c4994c8}, b = {127, 0x5ee2af7f, 0x7dda218e, 0x53e931bb, 0x4270e93f}, res = {126, 0x04c70a42, 0x577dd83b, 0x766a2fd5, 0x79d8ab88}, carry = 1},
{a = {127, 0x4950f74f, 0x6aad0a67, 0x43e36683, 0x76afb633}, b = {127, 0x2213721e, 0x76557c01, 0x299aae91, 0x44c18e55}, res = {127, 0x273d8531, 0x74578e66, 0x1a48b7f1, 0x31ee27de}, carry = 0},
{a = {126, 0x0935ca8f, 0x1200cc80, 0x2f3661fb, 0x23960e06}, b = {126, 0x4624b76e, 0x14091fd6, 0x6286cb7a, 0x3e1aa872}, res = {126, 0x43111321, 0x7df7aca9, 0x4caf9680, 0x657b6593}, carry = 1},
{a = {124, 0x52ac128b, 0x45fa9e02, 0x6427aa53, 0x0feaf8b5}, b = {127, 0x18aa3cc4, 0x19e96b87, 0x6c0e4dcc, 0x45da3929}, res = {124, 0x3a01d5c7, 0x2c11327b, 0x78195c87, 0x4a10bf8b}, carry = 1},
{a = {126, 0x4111b645, 0x1c825213, 0x2a8b75b3, 0x213c3a3e}, b = {126, 0x23f60c22, 0x490b2da9, 0x353a3844, 0x29d5dd47}, res = {126, 0x1d1baa23, 0x5377246a, 0x75513d6e, 0x77665cf6}, carry = 1},
{a = {127, 0x09c853b4, 0x7541862d, 0x36e91f14, 0x6e89e67d}, b = {127, 0x21d3bfe2, 0x61403f04, 0x554b57fc, 0x76d413df}, res = {127, 0x67f493d2, 0x14014728, 0x619dc718, 0x77b5d29d}, carry = 1},
{a = {127, 0x67761ecf, 0x3837a139, 0x335e0875, 0x79923e83}, b = {127, 0x76813044, 0x7c0f5491, 0x59acc858, 0x5a7a5795}, res = {127, 0x70f4ee8b, 0x3c284ca7, 0x59b1401c, 0x1f17e6ed}, carry = 0},
{a = {126, 0x6429a8c8, 0x635056e0, 0x76e76b1d, 0x3837df4c}, b = {127, 0x531005db, 0x115acaa3, 0x69231eee, 0x43d19db2}, res = {126, 0x1119a2ed, 0x51f58c3d, 0x0dc44c2f, 0x7466419a}, carry = 1},
{a = {127, 0x5014a598, 0x068dbb0f, 0x22fdc5bf, 0x6a670715}, b = {126, 0x7c4f94e9, 0x2e78116b, 0x224d3768, 0x2b95833a}, res = {127, 0x53c510af, 0x5815a9a3, 0x00b08e56, 0x3ed183db}, carry = 0},
{a = {126, 0x75b94a38, 0x71d62d52, 0x6c557c79, 0x2d8cfb9f}, b = {127, 0x743c6840, 0x3b04bcff, 0x50744d2c, 0x608611a3}, res = {126, 0x017ce1f8, 0x36d17053, 0x1be12f4d, 0x4d06e9fc}, carry = 1},
{a = {126, 0x04d24ee0, 0x37e1c03c, 0x50704c96, 0x2577ba6b}, b = {127, 0x6dd14735, 0x03c4f523, 0x379aa43a, 0x4fbfbf80}, res = {126, 0x170107ab, 0x341ccb18, 0x18d5a85c, 0x55b7faeb}, carry = 1},
{a = {123, 0x572d0f53, 0x732fb14c, 0x7975e187, 0x05706d99}, b = {127, 0x51a90823, 0x707dd091, 0x2f6c63b4, 0x789adddf}, res = {123, 0x05840730, 0x02b1e0bb, 0x4a097dd3, 0x0cd58fba}, carry = 1},
{a = {126, 0x68306d82, 0x51d883c8, 0x52a45e7f, 0x339e6fc7}, b = {127, 0x57da71c8, 0x72d5dd1c, 0x21a11ff7, 0x5d6ea34b}, res = {126, 0x1055fbba, 0x5f02a6ac, 0x31033e87, 0x562fcc7c}, carry = 1},
{a = {125, 0x47b20e6e, 0x39411f59, 0x0324640b, 0x19996204}, b = {124, 0x4e55588b, 0x64846ee8, 0x57d16f9e, 0x0a3db8bc}, res = {125, 0x795cb5e3, 0x54bcb070, 0x2b52f46c, 0x0f5ba947}, carry = 0},
{a = {127, 0x5a6ab51b, 0x0051b988, 0x1a192e89, 0x42b3d43a}, b = {125, 0x2daa3aa4, 0x063f37e9, 0x7ba7850f, 0x13dcea8c}, res = {127, 0x2cc07a77, 0x7a12819f, 0x1e71a979, 0x2ed6e9ad}, carry = 0},
{a = {125, 0x46813953, 0x6920da78, 0x5b6db2f9, 0x12219a9c}, b = {126, 0x5666e4fb, 0x5458ccf2, 0x097952e7, 0x38f4e37f}, res = {125, 0x701a5458, 0x14c80d85, 0x51f46012, 0x592cb71d}, carry = 1},
{a = {127, 0x47633dea, 0x6405989b, 0x1d019f24, 0x7dab4fa6}, b = {127, 0x686dbe43, 0x5715424a, 0x2027ac93, 0x70c8d29c}, res = {127, 0x5ef57fa7, 0x0cf05650, 0x7cd9f291, 0x0ce27d09}, carry = 0},
{a = {127, 0x22705ca5, 0x2c180e47, 0x393c2083, 0x40d0fea1}, b = {124, 0x37ce1e5a, 0x17b2b88d, 0x49bd2a90, 0x0d5d9087}, res = {127, 0x6aa23e4b, 0x146555b9, 0x6f7ef5f3, 0x33736e19}, carry = 0},
{a = {126, 0x5d61d089, 0x1391e7a4, 0x780aeb70, 0x3eb6e8e2}, b = {127, 0x7a54a013, 0x28f31254, 0x3d63ef25, 0x5d2cf7ff}, res = {126, 0x630d3076, 0x6a9ed54f, 0x3aa6fc4a, 0x6189f0e3}, carry = 1},
{a = {126, 0x505f4672, 0x275eb845, 0x665c7a55, 0x31c26f48}, b = {127, 0x3af113f0, 0x6a0358aa, 0x009cd3d5, 0x551cd2f6}, res = {126, 0x156e3282, 0x3d5b5f9b, 0x65bfa67f, 0x5ca59c52}, carry = 1},
{a = {126, 0x1a313b22, 0x04332012, 0x4cfb19fe, 0x3aa25e6c}, b = {127, 0x0525c494, 0x1a177649, 0x10bd52f2, 0x5d114ab1}, res = {126, 0x150b768e, 0x6a1ba9c9, 0x3c3dc70b, 0x5d9113bb}, carry = 1},
{a = {126, 0x52981c2d, 0x4c46b130, 0x37b3c3cf, 0x231f6fc4}, b = {126, 0x76da8c93, 0x5e52130f, 0x7efb6a4f, 0x2e3c4907}, res = {126, 0x5bbd8f9a, 0x6df49e20, 0x38b8597f, 0x74e326bc}, carry = 1},
{a = {127, 0x05600262, 0x0bb65331, 0x19f8d9cb, 0x5ddc2c62}, b = {127, 0x687b227e, 0x27348976, 0x072f2464, 0x4fbe7ebe}, res = {127, 0x1ce4dfe4, 0x6481c9ba, 0x12c9b566, 0x0e1dada4}, carry = 0},
{a = {127, 0x6a2d2e4a, 0x1b8b56dd, 0x50946d73, 0x64b0886d}, b = {122, 0x68ceaba5, 0x5344cea7, 0x7aeae02f, 0x03444737}, res = {127, 0x015e82a5, 0x48468836, 0x55a98d43, 0x616c4135}, carry = 0},
{a = {126, 0x2be80971, 0x7a1ffbd6, 0x7d5cd745, 0x310e6aa8}, b = {126, 0x31c62e38, 0x08a3a28d, 0x4d67f9a3, 0x30493db3}, res = {126, 0x7a21db39, 0x717c5948, 0x2ff4dda2, 0x00c52cf5}, carry = 0},
{a = {125, 0x030ba3d3, 0x162c1b78, 0x3f878cb3, 0x1a5108ff}, b = {127, 0x1f37aec7, 0x01e1153a, 0x325aab1e, 0x4b9a6619}, res = {125, 0x63d3f50c, 0x144b063d, 0x0d2ce195, 0x4eb6a2e6}, carry = 1},
{a = {126, 0x15a0528b, 0x29cbf0ad, 0x3048884b, 0x2c03d1c4}, b = {127, 0x5c32f40c, 0x192de971, 0x21a7f87d, 0x558c0542}, res = {126, 0x396d5e7f, 0x109e073b, 0x0ea08fce, 0x5677cc82}, carry = 1},
{a = {126, 0x0b48d92b, 0x5b09cd3a, 0x73960e62, 0x3f0ed1cd}, b = {124, 0x7a3d70fe, 0x5ff9c6b1, 0x4bd0e555, 0x0cc51487}, res = {126, 0x110b682d, 0x7b100688, 0x27c5290c, 0x3249bd46}, carry = 0},
{a = {127, 0x1bf5bd1c, 0x54104aef, 0x0c7ce316, 0x420fe407}, b = {126, 0x409409a2, 0x3d04fecd, 0x3e06417a, 0x3078c80b}, res = {127, 0x5b61b37a, 0x170b4c21, 0x4e76a19c, 0x11971bfb}, carry = 0},
{a = {125, 0x33d00217, 0x1c394fa4, 0x574f8331, 0x1d57d174}, b = {127, 0x6630d00d, 0x666f444c, 0x1a90af22, 0x484d8ca0}, res = {125, 0x4d9f320a, 0x35ca0b57, 0x3cbed40e, 0x550a44d4}, carry = 1},
{a = {127, 0x166e17c2, 0x1ecc3694, 0x1d8ad0ad, 0x5cfd9dd8}, b = {125, 0x130183de, 0x537699d8, 0x353fa45d, 0x114feeeb}, res = {127, 0x036c93e4, 0x4b559cbc, 0x684b2c4f, 0x4badaeec}, carry = 0},
{a = {127, 0x2736ea4c, 0x34a77f3b, 0x5d62185c, 0x49f65789}, b = {126, 0x10ef92f4, 0x6d50c8cd, 0x3b2c8da0, 0x26d91a49}, res = {127, 0x16475758, 0x4756b66e, 0x22358abb, 0x231d3d40}, carry = 0},
{a = {127, 0x7c90f6d6, 0x6e8c2441, 0x56303ec7, 0x648b71e2}, b = {127, 0x5efcffab, 0x35738e3f, 0x6a0010b5, 0x54fd6ef0}, res = {127, 0x1d93f72b, 0x39189602, 0x6c302e12, 0x0f8e02f1}, carry = 0},
{a = {127, 0x09d4aa13, 0x39bee362, 0x48f85dfd, 0x54d4270d}, b = {121, 0x75609829, 0x5a20f480, 0x2a01c588, 0x0146be72}, res = {127, 0x147411ea, 0x5f9deee1, 0x1ef69874, 0x538d689b}, carry = 0},
{a = {123, 0x5ca62e2f, 0x5bd68bd9, 0x009238b5, 0x04524739}, b = {126, 0x0f74d025, 0x68d0ff9b, 0x3cd174c2, 0x23b408c2}, res = {123, 0x4d315e0a, 0x73058c3e, 0x43c0c3f2, 0x609e3e76}, carry = 1},
}
I31_Test_Vector_Decode :: struct {
src: []byte,
decode: []u32,
mod: []u32,
mod_res: u32,
}
@(rodata)
i31_decode_test_vectors := []I31_Test_Vector_Decode {
{src = {0x49}, decode = {7, 0x00000049}, mod = {42, 0x00000049}, mod_res = 1},
{src = {0x27, 0xdb}, decode = {14, 0x000027db}, mod = {42, 0x000027db}, mod_res = 1},
{src = {0xb8, 0xd7, 0x72}, decode = {24, 0x00b8d772}, mod = {42, 0x00b8d772}, mod_res = 1},
{src = {0xa0, 0x94, 0x6e, 0xac}, decode = {33, 0x20946eac, 0x00000001}, mod = {42, 0x20946eac, 0x00000001}, mod_res = 1},
{src = {0xed, 0x8a, 0xc2, 0xe4, 0x45}, decode = {41, 0x0ac2e445, 0x000001db}, mod = {42, 0x0ac2e445, 0x000001db}, mod_res = 1},
{src = {0xfb, 0x37, 0x36, 0x46, 0x28, 0x4e}, decode = {49, 0x3646284e, 0x0001f66e}, mod = {42, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd8, 0xcc, 0xbb, 0xf2, 0x13, 0x66, 0xdf}, decode = {57, 0x721366df, 0x01b19977}, mod = {42, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xcb, 0xc3, 0xe2, 0xa8, 0x4c, 0xfd, 0x9e, 0x8a}, decode = {66, 0x4cfd9e8a, 0x1787c550, 0x00000003}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x0d, 0xd5, 0x76, 0xc1, 0xb4, 0x02, 0xdb, 0xb4, 0xb9}, decode = {70, 0x02dbb4b9, 0x2aed8368, 0x00000037}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xf6, 0xb6, 0x08, 0xb3, 0xfb, 0x0c, 0x7e, 0x8e, 0x58, 0xa7}, decode = {82, 0x7e8e58a7, 0x1167f618, 0x0003dad8}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xf9, 0x51, 0xde, 0x6a, 0x29, 0x54, 0x48, 0xf5, 0x3c, 0x78, 0x85}, decode = {90, 0x753c7885, 0x5452a891, 0x03e54779}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xb5, 0x5a, 0x92, 0x18, 0x67, 0x09, 0x6e, 0x21, 0x5d, 0x84, 0x10, 0xce}, decode = {99, 0x5d8410ce, 0x4e12dc42, 0x556a4861, 0x00000005}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd3, 0xa4, 0x7b, 0xa0, 0x90, 0x26, 0x36, 0x73, 0xa6, 0x7f, 0x28, 0x66, 0x4e}, decode = {107, 0x7f28664e, 0x4c6ce74c, 0x11ee8240, 0x0000069d}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x23, 0xfa, 0xe7, 0x0c, 0x6a, 0x07, 0x00, 0x86, 0xa2, 0xcf, 0x22, 0xa1, 0x1f, 0x62}, decode = {113, 0x22a11f62, 0x010d459e, 0x1c31a81c, 0x00011fd7}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x9c, 0x39, 0xf8, 0x67, 0x8a, 0x9f, 0x85, 0xb4, 0xdb, 0x92, 0x4b, 0xe6, 0xdc, 0x12, 0xa5}, decode = {123, 0x66dc12a5, 0x69b72497, 0x1e2a7e16, 0x04e1cfc3}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x8f, 0xc7, 0xcc, 0xda, 0x43, 0x21, 0x0b, 0x39, 0x0e, 0xc3, 0x51, 0xb0, 0xbd, 0xce, 0xbb, 0x73}, decode = {132, 0x3dcebb73, 0x1d86a361, 0x0c842ce4, 0x7e3e66d2, 0x00000008}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xc5, 0x8d, 0xd3, 0x1b, 0xda, 0x0d, 0x21, 0xfd, 0xb2, 0x69, 0xca, 0x12, 0xd4, 0x47, 0x44, 0x4e, 0x61}, decode = {140, 0x47444e61, 0x539425a8, 0x3487f6c9, 0x6e98ded0, 0x00000c58}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x4f, 0x00, 0x7f, 0x9c, 0x00, 0x11, 0x29, 0x8d, 0x94, 0xd6, 0x7e, 0x1c, 0xc3, 0x26, 0x78, 0xb7, 0xfb, 0xbb}, decode = {147, 0x78b7fbbb, 0x7c39864c, 0x26365359, 0x7ce00089, 0x0004f007}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xb8, 0x85, 0x3f, 0xe0, 0x03, 0x89, 0x20, 0x72, 0xf8, 0xa6, 0x66, 0x48, 0x0f, 0xdb, 0x63, 0x19, 0x8e, 0xbb, 0x00}, decode = {156, 0x198ebb00, 0x101fb6c6, 0x4be29999, 0x001c4903, 0x0b8853fe}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x3c, 0x88, 0x99, 0xf2, 0x23, 0xc8, 0x65, 0x6d, 0x6a, 0x93, 0xa0, 0xf6, 0xef, 0x8d, 0x28, 0xd8, 0x85, 0x9e, 0x8f, 0x85}, decode = {163, 0x059e8f85, 0x5f1a51b1, 0x2a4e83db, 0x1e432b6b, 0x48899f22, 0x00000007}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd8, 0x28, 0x9e, 0xae, 0x7c, 0xc2, 0xf6, 0x9c, 0x72, 0xe7, 0x25, 0xf6, 0x0c, 0xec, 0xcc, 0x78, 0x5d, 0xf9, 0xb0, 0xe9, 0x9f}, decode = {173, 0x79b0e99f, 0x5998f0bb, 0x1c97d833, 0x17b4e397, 0x09eae7cc, 0x00001b05}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xec, 0x5f, 0x68, 0xa7, 0xb6, 0xa4, 0x7d, 0x01, 0x88, 0x39, 0xeb, 0x88, 0x3a, 0x33, 0x60, 0x8a, 0x59, 0x67, 0xaa, 0xfe, 0x67, 0xc2}, decode = {181, 0x2afe67c2, 0x4114b2cf, 0x2e20e8cd, 0x680c41cf, 0x0a7b6a47, 0x001d8bed}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x34, 0x8a, 0x87, 0xdb, 0xbf, 0x60, 0xa6, 0x58, 0x23, 0xe3, 0x99, 0x9a, 0x43, 0x5c, 0x43, 0x9e, 0xfe, 0xb2, 0xbd, 0xd7, 0xd2, 0xa3, 0x74}, decode = {187, 0x57d2a374, 0x3dfd657b, 0x690d710e, 0x411f1ccc, 0x3bf60a65, 0x069150fb}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xe6, 0xe7, 0x48, 0x95, 0x31, 0x9e, 0xdb, 0x97, 0xd7, 0x37, 0x17, 0x1a, 0x9a, 0x51, 0xde, 0x02, 0xa4, 0x7f, 0x84, 0xbd, 0x25, 0x02, 0xc7, 0xe1}, decode = {198, 0x2502c7e1, 0x48ff097a, 0x6947780a, 0x39b8b8d4, 0x19edb97d, 0x5ce912a6, 0x00000039}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x5c, 0xd5, 0xa2, 0x66, 0xf4, 0x01, 0x12, 0xba, 0xe8, 0x05, 0x63, 0xa0, 0xc0, 0xfa, 0xaf, 0xe1, 0x3d, 0xe4, 0xc5, 0x21, 0xab, 0xa9, 0x52, 0x99, 0x3f}, decode = {205, 0x2952993f, 0x498a4357, 0x6abf84f7, 0x2b1d0607, 0x112bae80, 0x344cde80, 0x00001735}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x8f, 0x93, 0xfc, 0x0c, 0x73, 0x29, 0x7d, 0x28, 0xdc, 0x71, 0x7b, 0x01, 0x53, 0x4f, 0x87, 0xdd, 0xf5, 0x43, 0xe2, 0xb0, 0x97, 0x1d, 0xe1, 0xf7, 0x7f, 0xcf}, decode = {214, 0x61f77fcf, 0x45612e3b, 0x1f77d50f, 0x580a9a7c, 0x528dc717, 0x018e652f, 0x0023e4ff}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd7, 0x86, 0x9b, 0x8d, 0x91, 0x97, 0xff, 0x98, 0xbe, 0xa7, 0x67, 0x73, 0xda, 0x7a, 0x07, 0xe7, 0x07, 0xf3, 0x61, 0xc4, 0x2d, 0x01, 0x96, 0x68, 0xbf, 0xd6, 0x36}, decode = {222, 0x68bfd636, 0x085a032c, 0x1c1fcd87, 0x1ed3d03f, 0x0bea7677, 0x3232fff3, 0x35e1a6e3}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xf7, 0xec, 0xfa, 0x44, 0x18, 0x5e, 0x6a, 0x51, 0x98, 0x3d, 0x3b, 0xc9, 0xcf, 0xb5, 0x9f, 0x97, 0xa8, 0x7d, 0xdd, 0xda, 0xf3, 0xf3, 0x1c, 0x02, 0x9c, 0x40, 0x5c, 0x8a}, decode = {231, 0x1c405c8a, 0x67e63805, 0x21f7776b, 0x7dacfcbd, 0x03d3bc9c, 0x0bcd4a33, 0x7b3e9106, 0x0000007b}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xca, 0x32, 0x04, 0xef, 0xc8, 0x40, 0x55, 0xc9, 0x67, 0x2e, 0x3b, 0x1a, 0xbe, 0x2c, 0xf1, 0xdd, 0xe0, 0xaa, 0x46, 0x31, 0x40, 0xc5, 0xf6, 0xaa, 0x25, 0x88, 0x0d, 0x0f, 0x30}, decode = {239, 0x080d0f30, 0x0bed544b, 0x2918c503, 0x678eef05, 0x63b1abe2, 0x0ab92ce5, 0x013bf210, 0x00006519}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x96, 0xb5, 0x62, 0x03, 0x50, 0x9f, 0x04, 0xfc, 0xc3, 0x06, 0x25, 0xc1, 0xa7, 0x21, 0x6d, 0xeb, 0x60, 0x8d, 0xb0, 0x02, 0x34, 0x18, 0xaf, 0xd1, 0xb0, 0x23, 0x0f, 0x6a, 0xe6, 0x63}, decode = {247, 0x0f6ae663, 0x5fa36046, 0x4008d062, 0x6f5b046d, 0x5c1a7216, 0x1f9860c4, 0x00d427c1, 0x004b5ab1}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x5c, 0x00, 0x1a, 0x67, 0x07, 0x8e, 0x49, 0x72, 0x30, 0x6f, 0x2b, 0xa6, 0x93, 0x37, 0x6a, 0x2b, 0x5a, 0x8f, 0xb4, 0x30, 0x65, 0x89, 0xa1, 0x7b, 0xbc, 0xf9, 0x85, 0x3f, 0x44, 0x64, 0x48}, decode = {254, 0x3f446448, 0x7779f30a, 0x41962685, 0x5ad47da1, 0x693376a2, 0x460de574, 0x41e3925c, 0x2e000d33, 0x00000000}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
}
I31_Test_RShift :: struct {
orig: []u32,
res: []u32,
shift: i32,
}
@(rodata)
i31_rshift_test_vectors := []I31_Test_RShift {
{orig = {92, 0x4b451fff, 0x2874869d, 0x0d1b97a7}, res = {92, 0x4b451fff, 0x2874869d, 0x0d1b97a7}, shift = 0},
{orig = {94, 0x44709cd4, 0x60d36574, 0x3a587e31}, res = {94, 0x22384e6a, 0x7069b2ba, 0x1d2c3f18}, shift = 1},
{orig = {94, 0x77ea5771, 0x78324ed5, 0x3d9ad39d}, res = {94, 0x3dfa95dc, 0x3e0c93b5, 0x0f66b4e7}, shift = 2},
{orig = {94, 0x631db445, 0x248c243a, 0x3f1cc589}, res = {94, 0x2c63b688, 0x14918487, 0x07e398b1}, shift = 3},
{orig = {95, 0x6f23a8ed, 0x7d5a3c91, 0x7e913a13}, res = {95, 0x0ef23a8e, 0x1fd5a3c9, 0x07e913a1}, shift = 4},
{orig = {94, 0x03dd8abf, 0x5f02ee65, 0x249f3f58}, res = {94, 0x141eec55, 0x62f81773, 0x0124f9fa}, shift = 5},
{orig = {94, 0x2927bee6, 0x4f308116, 0x3279228a}, res = {94, 0x2ca49efb, 0x153cc204, 0x00c9e48a}, shift = 6},
{orig = {95, 0x22687024, 0x73cb5fd3, 0x52d71799}, res = {95, 0x5344d0e0, 0x19e796bf, 0x00a5ae2f}, shift = 7},
{orig = {94, 0x71a3bca0, 0x27642d52, 0x23e2059f}, res = {94, 0x2971a3bc, 0x4fa7642d, 0x0023e205}, shift = 8},
{orig = {95, 0x180d85aa, 0x12a8516d, 0x4797741e}, res = {95, 0x5b4c06c2, 0x07895428, 0x0023cbba}, shift = 9},
{orig = {95, 0x7b30aeeb, 0x3ababf4f, 0x6a446aff}, res = {95, 0x69fecc2b, 0x5feeaeaf, 0x001a911a}, shift = 10},
{orig = {94, 0x3b2d5f09, 0x7bde80e9, 0x33ed8ce0}, res = {94, 0x0e9765ab, 0x4e0f7bd0, 0x00067db1}, shift = 11},
{orig = {93, 0x62c89828, 0x20d78d11, 0x14bbd081}, res = {93, 0x688e2c89, 0x040a0d78, 0x00014bbd}, shift = 12},
{orig = {95, 0x102c28e6, 0x31f2a99f, 0x6c337ed1}, res = {95, 0x267c8161, 0x7b458f95, 0x0003619b}, shift = 13},
{orig = {94, 0x78097c9b, 0x6b3f99c8, 0x2ea10fbb}, res = {94, 0x3391e025, 0x1f77acfe, 0x0000ba84}, shift = 14},
{orig = {92, 0x4684f360, 0x56d54074, 0x0dd7bfa7}, res = {92, 0x40748d09, 0x3fa7adaa, 0x00001baf}, shift = 15},
{orig = {93, 0x59e9b272, 0x1792ca1d, 0x134ce6d8}, res = {93, 0x650ed9e9, 0x736c1792, 0x0000134c}, shift = 16},
{orig = {94, 0x2be2d592, 0x566508a1, 0x3a8622ab}, res = {94, 0x422855f1, 0x08aaeb32, 0x00001d43}, shift = 17},
{orig = {95, 0x3082be20, 0x118cde6a, 0x57a1832c}, res = {95, 0x1bcd4c20, 0x30658463, 0x000015e8}, shift = 18},
{orig = {95, 0x7b7ec9fc, 0x30304a84, 0x7cb524f7}, res = {95, 0x04a84f6f, 0x524f7606, 0x00000f96}, shift = 19},
{orig = {94, 0x7a038786, 0x5902ae15, 0x2ee36b72}, res = {94, 0x1570afa0, 0x1b5b9590, 0x000002ee}, shift = 20},
{orig = {95, 0x2a127f63, 0x5d1b323f, 0x7dc23bd7}, res = {95, 0x6cc8fd50, 0x08ef5ee8, 0x000003ee}, shift = 21},
{orig = {95, 0x3cf76a47, 0x30310973, 0x69bc9430}, res = {95, 0x6212e6f3, 0x792860c0, 0x000001a6}, shift = 22},
{orig = {94, 0x3ca70baa, 0x07747e80, 0x3a710be4}, res = {94, 0x747e8079, 0x710be40e, 0x00000074}, shift = 23},
{orig = {95, 0x6bc0e3b9, 0x77b2972f, 0x5b9f893c}, res = {95, 0x594b97eb, 0x4fc49e77, 0x0000005b}, shift = 24},
{orig = {90, 0x49e59ccc, 0x7d67d332, 0x02b4f127}, res = {90, 0x59f4cca4, 0x2d3c49fe, 0x00000001}, shift = 25},
{orig = {94, 0x55e1b1d7, 0x7a1c0bdb, 0x217e8547}, res = {94, 0x43817b75, 0x2fd0a8fe, 0x00000008}, shift = 26},
{orig = {94, 0x67fb980c, 0x20a9410b, 0x25de2ffd}, res = {94, 0x0a9410bc, 0x5de2ffd4, 0x00000004}, shift = 27},
{orig = {92, 0x18363569, 0x0b492aba, 0x0a37f42c}, res = {92, 0x5a4955d1, 0x51bfa160, 0x00000000}, shift = 28},
{orig = {95, 0x23bb0e53, 0x4bb6c3aa, 0x77932915}, res = {95, 0x2edb0ea9, 0x5e4ca456, 0x00000003}, shift = 29},
{orig = {95, 0x725acb18, 0x1da81862, 0x6d6c306b}, res = {95, 0x3b5030c5, 0x5ad860d6, 0x00000001}, shift = 30},
{orig = {95, 0x70d51c12, 0x44e8b652, 0x4a0d2a6b}, res = {95, 0x44e8b652, 0x4a0d2a6b, 0x00000000}, shift = 31},
}
I31_Test_Reduce :: struct {
orig: []u32,
res: []u32,
}
@(rodata)
i31_reduce_test_vectors := []I31_Test_Reduce {
{orig = {62, 0x27da8fd9, 0x2fea2339}, res = {42, 0x27f284e9, 0x00000339}},
{orig = {95, 0x37856cc1, 0x54ad3e73, 0x718777f1}, res = {42, 0x33efdfc1, 0x0000022f, 0x718777f1}},
{orig = {123, 0x4787b519, 0x47fa51cf, 0x11ef98ae, 0x058b1a99}, res = {42, 0x567ed6bd, 0x000002fe, 0x11ef98ae, 0x058b1a99}},
{orig = {158, 0x328fbfb6, 0x6e9ec225, 0x0241df84, 0x4a3627d0, 0x3a79e4a4}, res = {42, 0x005f59f0, 0x000001aa, 0x0241df84, 0x4a3627d0, 0x3a79e4a4}},
{orig = {191, 0x451321f5, 0x45f677b9, 0x2c43b0b8, 0x7cb722c1, 0x70e594e6, 0x4c81757c}, res = {42, 0x684ba25d, 0x0000008d, 0x2c43b0b8, 0x7cb722c1, 0x70e594e6, 0x4c81757c}},
{orig = {222, 0x0749fe0b, 0x3c45795b, 0x21e6f14c, 0x265b264d, 0x37def307, 0x35019b6b, 0x25ab0a3e}, res = {42, 0x36c99d3d, 0x00000173, 0x21e6f14c, 0x265b264d, 0x37def307, 0x35019b6b, 0x25ab0a3e}},
{orig = {255, 0x1c2630cd, 0x0c0d9a7e, 0x375154d0, 0x67249adf, 0x0df9ec39, 0x73b5ad9e, 0x396d0f52, 0x4ed9b56a}, res = {42, 0x79997cc9, 0x000002b4, 0x375154d0, 0x67249adf, 0x0df9ec39, 0x73b5ad9e, 0x396d0f52, 0x4ed9b56a}},
{orig = {286, 0x35b7eae2, 0x033002a6, 0x149766aa, 0x5d4a5a16, 0x7d09704c, 0x380c8cf2, 0x249df2ff, 0x03caabad, 0x214fc645}, res = {42, 0x20f488ba, 0x000003e1, 0x149766aa, 0x5d4a5a16, 0x7d09704c, 0x380c8cf2, 0x249df2ff, 0x03caabad, 0x214fc645}},
{orig = {315, 0x7a5813dc, 0x76309cbf, 0x03e432bc, 0x218df9a1, 0x1a0d0525, 0x793ad550, 0x280cbf6e, 0x18356492, 0x4b6f39a1, 0x04adeb0d}, res = {42, 0x0016ad0c, 0x000000a8, 0x03e432bc, 0x218df9a1, 0x1a0d0525, 0x793ad550, 0x280cbf6e, 0x18356492, 0x4b6f39a1, 0x04adeb0d}},
{orig = {351, 0x0ded15c8, 0x3875535e, 0x4627ebc1, 0x101a8369, 0x7300acd4, 0x22113509, 0x2a441bc0, 0x25902fec, 0x230133c0, 0x7ecaa587, 0x439bfdcd}, res = {42, 0x60547c04, 0x00000144, 0x4627ebc1, 0x101a8369, 0x7300acd4, 0x22113509, 0x2a441bc0, 0x25902fec, 0x230133c0, 0x7ecaa587, 0x439bfdcd}},
}
@(rodata)
i31_decode_reduce_test_vectors := []I31_Test_Vector_Decode {
{src = {171, 54, 46}, decode = {42, 0x00ab362e, 0x00000000, 0x00000000}},
{src = {87, 80, 187, 242}, decode = {42, 0x5750bbf2, 0x00000000, 0x00000000}},
{src = {181, 9, 43, 65, 203}, decode = {42, 0x092b41cb, 0x0000016a, 0x00000000}},
{src = {196, 160, 88, 214, 25, 234}, decode = {42, 0x58d61aae, 0x00000140, 0x00000000}},
{src = {223, 248, 213, 188, 56, 226, 125}, decode = {42, 0x3c39c275, 0x000001ab, 0x00000000}},
{src = {252, 152, 213, 96, 59, 205, 38, 68}, decode = {42, 0x3cc9bf18, 0x000002c0, 0x00000000}},
{src = {61, 151, 139, 227, 56, 129, 245, 7, 245}, decode = {42, 0x3f8c93d7, 0x00000271, 0x00000000}},
{src = {50, 242, 36, 43, 28, 76, 75, 80, 191, 213}, decode = {42, 0x3d74eaf1, 0x000000fe, 0x00000000}},
{src = {45, 174, 63, 171, 108, 66, 180, 223, 196, 24, 202}, decode = {42, 0x1f6f853a, 0x000000c6, 0x00000000}},
{src = {106, 140, 119, 25, 145, 178, 50, 134, 118, 181, 241, 205}, decode = {42, 0x10480e8b, 0x000001fb, 0x00000000}},
}
I31_Test_Mul_Add_Small :: struct {
orig: []u32,
res: []u32,
z: u32,
}
@(rodata)
i31_mul_add_test_vectors := []I31_Test_Mul_Add_Small {
{orig = {63, 0x157df37a, 0x5e97b10e}, res = {63, 0x438abf24, 0x7fffff7a}, z = 42},
{orig = {63, 0x6c11d92a, 0x74b6b943}, res = {63, 0x50f60940, 0x0000012a}, z = 84},
{orig = {63, 0x0bf17f5a, 0x756c85bb}, res = {63, 0x6ec5f93e, 0x7fffff5a}, z = 126},
{orig = {63, 0x0d24e4eb, 0x5891203e}, res = {63, 0x0f86931a, 0x000000eb}, z = 168},
{orig = {63, 0x41dda071, 0x6deb6411}, res = {63, 0x0460efa2, 0x00000071}, z = 210},
{orig = {63, 0x29793d56, 0x4d5204e5}, res = {63, 0x3954bd9a, 0x00000156}, z = 252},
{orig = {58, 0x40e72062, 0x039ba033}, res = {58, 0x0ce074b6, 0x00000062}, z = 294},
{orig = {63, 0x6e9b147b, 0x60dcf3ef}, res = {63, 0x7bf74edc, 0x7ffffc7c}, z = 336},
{orig = {62, 0x74572099, 0x3af8369a}, res = {62, 0x26ba2d0a, 0x0000009a}, z = 378},
}
I31_Test_Vector_Encode :: struct {
orig: []u32,
encoded: []u8,
}
@(rodata)
i31_encode_test_vectors := []I31_Test_Vector_Encode {
{orig = {30, 0x2003ca33}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 3, 202, 51}},
{orig = {61, 0x13d86a42, 0x1c0fc55a}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 7, 226, 173, 19, 216, 106, 66}},
{orig = {93, 0x0adffd1b, 0x3a03c1bc, 0x19682bb9}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 90, 10, 238, 93, 1, 224, 222, 10, 223, 253, 27}},
{orig = {127, 0x193841a2, 0x5cf0aa2f, 0x57594f6d, 0x4fc77899}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 248, 239, 19, 53, 214, 83, 219, 110, 120, 85, 23, 153, 56, 65, 162}},
{orig = {158, 0x219d0fd8, 0x623e21ae, 0x5c9ad413, 0x6dc292d4, 0x2b2f85fe}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 178, 248, 95, 237, 184, 82, 90, 151, 38, 181, 4, 241, 31, 16, 215, 33, 157, 15, 216}},
{orig = {189, 0x69363092, 0x4b751e5d, 0x663b4745, 0x2e93e19e, 0x1a3dabce, 0x1d4caa3f}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 101, 81, 249, 163, 218, 188, 229, 210, 124, 51, 217, 142, 209, 209, 101, 186, 143, 46, 233, 54, 48, 146}},
{orig = {222, 0x016b86ed, 0x29c31304, 0x15cca452, 0x6abefa4a, 0x7410f562, 0x450559df, 0x28ad9252}, encoded = {0, 0, 0, 0, 0, 162, 182, 73, 74, 40, 42, 206, 255, 65, 15, 86, 45, 87, 223, 73, 69, 115, 41, 20, 148, 225, 137, 130, 1, 107, 134, 237}},
{orig = {253, 0x4b6d0cd3, 0x357f8b7d, 0x4bd86436, 0x5838a86c, 0x10280aa0, 0x22f9b902, 0x4bdb5c1c, 0x19a20f9e}, encoded = {0, 51, 68, 31, 61, 47, 109, 112, 113, 23, 205, 200, 17, 2, 128, 170, 11, 7, 21, 13, 146, 246, 25, 13, 154, 191, 197, 190, 203, 109, 12, 211}},
}
I31_Test_Ninv :: struct {
orig: []u32,
ninv: []u32,
}
@(rodata)
i31_ninv_test_vectors := I31_Test_Ninv {
orig = {0x00000c5c, 0x47faf728, 0x69a8e9d5, 0x49f5015c, 0x4ea9aea5, 0x164bcf32, 0x3fc395b4, 0x1c0a908a, 0x795f47f2, 0x79aa0c9a, 0x1a37680f, 0x2834cf17, 0x499235c8, 0x6d239632, 0x0560438b, 0x2cf4b82b, 0x5133fd25, 0x3b63a8b9, 0x45b66eca, 0x0213c13c, 0x6cd589f3, 0x4ed03f68, 0x722eb913, 0x670c76f4, 0x444d31d0, 0x1809da41, 0x3656a4af, 0x2ab43d09, 0x281c0e85, 0x426b3fd3, 0x680413c0, 0x065884c2, 0x55e4db17, 0x3839a23f, 0x3cc508b0, 0x3c492cda, 0x43325992, 0x5bc31283, 0x3891deaa, 0x5ddddd43, 0x64314225, 0x43a1c73b, 0x5e0ec431, 0x27f583bf, 0x491a73dc, 0x377982b8, 0x0b760791, 0x7835c61d, 0x0192b0f7, 0x129ca7ca, 0x16333e2d, 0x39c07864, 0x4d4b0d1b, 0x252fdb31, 0x2ca1bef8, 0x0358e216, 0x6aeda289, 0x201bca8f, 0x6ded1451, 0x5fbb04bb, 0x03d86c3b, 0x2f402c96, 0x4567a0ad, 0x6159d4de, 0x494f89b9, 0x7035fbdf, 0x5c3cfbd6, 0x40bfdbc8, 0x5e27b49c, 0x4fee508e, 0x76bd2f29, 0x45b49e47, 0x7da53921, 0x3d9380aa, 0x5d5bce86, 0x212b2912, 0x2e3e0a61, 0x218cfe04, 0x22ebe499, 0x70713a1c, 0x5b690632, 0x3f5c8dc1, 0x323815a5, 0x1f9f4c8f, 0x56a54a21, 0x469a2122, 0x6fb2beea, 0x643ac847, 0x01d2c5bc, 0x7dc51683, 0x0b876ebe, 0x214a069a, 0x535258ed, 0x529b3dce, 0x2760b2c2, 0x78cf5cd3, 0x19d1d3f2, 0x572ebc02, 0x1ab91604, 0x0b5f09a0},
ninv = {0x00000000, 0x00000000, 0x66e2f883, 0x00000000, 0x210176d3, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40ad7911, 0x3954a759, 0x00000000, 0x00000000, 0x657333dd, 0x11c1b97d, 0x1c122953, 0x40642277, 0x00000000, 0x00000000, 0x7f3dc8c5, 0x00000000, 0x166a06e5, 0x00000000, 0x00000000, 0x605cca3f, 0x3548cdb1, 0x35b19ec7, 0x681845b3, 0x1c438fa5, 0x00000000, 0x00000000, 0x2beff359, 0x79f2b241, 0x00000000, 0x00000000, 0x00000000, 0x6040b3d5, 0x00000000, 0x1ee96895, 0x1f18f653, 0x7f80860d, 0x3901eb2f, 0x56ff93c1, 0x00000000, 0x00000000, 0x562e668f, 0x72bad3cb, 0x3de0ef39, 0x00000000, 0x6be23e5b, 0x00000000, 0x3a327aed, 0x0fae622f, 0x00000000, 0x00000000, 0x004d8c47, 0x5a1feb91, 0x76f34b4f, 0x68675f8d, 0x7dec730d, 0x00000000, 0x5a953cdb, 0x00000000, 0x65605377, 0x7aa67fe1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4eaf22e7, 0x4a585489, 0x3fa2751f, 0x00000000, 0x00000000, 0x00000000, 0x7107e65f, 0x00000000, 0x5f81d057, 0x00000000, 0x00000000, 0x2d7b7dbf, 0x0e2f35d3, 0x75e7ad91, 0x4ed7461f, 0x00000000, 0x00000000, 0x4a199e89, 0x00000000, 0x45bf97d5, 0x00000000, 0x00000000, 0x4ada3b1b, 0x00000000, 0x00000000, 0x7456a4a5, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
}
I31_Test_Monty_Mul :: struct {
x: []u32,
y: []u32,
m: []u32,
res: []u32,
}
@(rodata)
i31_monty_mul_test_vectors := []I31_Test_Monty_Mul {
{x = {127, 0x7c5a766e, 0x6ce75a35, 0x1c64cb75, 0x51263ee0, 0x00000000}, y = {127, 0x75063ae3, 0x3f506a6f, 0x1d1ad6d5, 0x47a85d53, 0x00000000}, m = {159, 0x7e0a5a33, 0x41c36cc5, 0x065bfbbd, 0x4608e748, 0x69ac6740, 0x00000000}, res = {159, 0x3e2b1a07, 0x4990ba63, 0x15f1a1ea, 0x6e8ff678, 0x13ba5b66}},
{x = {124, 0x5232b140, 0x341f2854, 0x3e6bde54, 0x092324e1, 0x00000000}, y = {127, 0x6c66ba58, 0x51577b9e, 0x72a600d4, 0x53bd0450, 0x00000000}, m = {159, 0x153010a5, 0x7d0ddec2, 0x13c7f180, 0x1cd216b3, 0x5818bdd7, 0x00000000}, res = {159, 0x6de73758, 0x21d8ff38, 0x69b18e1e, 0x5d1c7d47, 0x41c75523}},
{x = {126, 0x176e0f5b, 0x0a98100a, 0x475e0a72, 0x348f9141, 0x00000000}, y = {127, 0x334e2dfe, 0x2c988363, 0x73e23cf8, 0x606dcc5f, 0x00000000}, m = {159, 0x7023de77, 0x54103e3b, 0x338cd291, 0x0e2c1e77, 0x6fa41147, 0x00000000}, res = {159, 0x488db6ba, 0x3ec6b26b, 0x0dddf8b5, 0x4e0334db, 0x2125e61c}},
{x = {127, 0x1018738e, 0x233c04d7, 0x46a665c0, 0x79c84e0a, 0x00000000}, y = {127, 0x783e44fe, 0x14a1ea17, 0x31bea107, 0x66c49111, 0x00000000}, m = {158, 0x580ca6c1, 0x61ad5aad, 0x238b9bdb, 0x0ce14c93, 0x2749fc5c, 0x00000000}, res = {158, 0x37557af5, 0x5ca2f6fb, 0x7fdca455, 0x4d436f9b, 0x18d60f4f}},
{x = {126, 0x45da293a, 0x1665982a, 0x0e8fc303, 0x231c2772, 0x00000000}, y = {126, 0x542485c1, 0x3f2f7d3f, 0x684047a6, 0x3abaed33, 0x00000000}, m = {159, 0x03d31fd1, 0x6f0c8ff3, 0x12825696, 0x199fda81, 0x76457daf, 0x00000000}, res = {159, 0x6c6852a3, 0x0d1fd2b0, 0x67e96590, 0x768c4fca, 0x3ad66382}},
{x = {126, 0x21d23a0e, 0x5ba5d464, 0x29697487, 0x338d2cff, 0x00000000}, y = {125, 0x29e49a2c, 0x144564ae, 0x3d17958d, 0x101f7e41, 0x00000000}, m = {158, 0x4545452b, 0x6e68f705, 0x2302e752, 0x07b5d5d6, 0x3657a220, 0x00000000}, res = {158, 0x3e90a0ed, 0x457cb17b, 0x53b4f9f5, 0x7a6324ce, 0x35d2cdd7}},
{x = {125, 0x4732d8b6, 0x45573e7d, 0x07f4963f, 0x12a290dd, 0x00000000}, y = {127, 0x39baf88d, 0x5d858830, 0x4580eb58, 0x6d196a63, 0x00000000}, m = {159, 0x0cb6812b, 0x482b08af, 0x4f1b863c, 0x3cef2e92, 0x50f7c7f1, 0x00000000}, res = {159, 0x6ad503c6, 0x27c67766, 0x5fca2845, 0x15bbcb6b, 0x46c3cb6c}},
{x = {127, 0x354a4328, 0x651d0b7f, 0x43a452fb, 0x7bd74c98, 0x00000000}, y = {126, 0x3cfc2842, 0x71256a54, 0x3719176f, 0x34407271, 0x00000000}, m = {159, 0x306eba87, 0x772cb87d, 0x4907c954, 0x5dc5e91b, 0x74d5eb17, 0x00000000}, res = {159, 0x49e12a70, 0x7e89a281, 0x1ea3548a, 0x54711053, 0x713bfe04}},
{x = {126, 0x4affb101, 0x52a6864d, 0x7152422e, 0x3cca5f4e, 0x00000000}, y = {126, 0x3f4b1e18, 0x32aaa89a, 0x635dcdc7, 0x3c44b1de, 0x00000000}, m = {158, 0x01432ccf, 0x2bcf7f6d, 0x7561291c, 0x7064f0cb, 0x3381e843, 0x00000000}, res = {158, 0x03895437, 0x6792e4bb, 0x68391714, 0x5a6104d6, 0x16bbc670}},
{x = {127, 0x56c42650, 0x2723c136, 0x4d64beac, 0x5e656e91, 0x00000000}, y = {126, 0x1aa1d10f, 0x01fffab5, 0x44fd097e, 0x3483b271, 0x00000000}, m = {159, 0x2f77d067, 0x1f191f5b, 0x1a1a8568, 0x52026c09, 0x4692f6e6, 0x00000000}, res = {159, 0x1b1bfa15, 0x3726bd88, 0x1af57d8e, 0x2de30fb3, 0x3f96e86f}},
}
I31_Test_To_Monty :: struct {
orig: []u32,
x: []u32,
m: []u32,
}
@(rodata)
i31_to_monty_test_vectors := []I31_Test_To_Monty {
{orig = {123, 0x3f3aff0e, 0x459fe053, 0x4bba523d, 0x04b91a78, 0x00000000}, x = {123, 0x2eab2260, 0x4d3d8e5d, 0x2943569d, 0x01d1c5f3, 0x00000000}, m = {123, 0x3f3aff1b, 0x459fe061, 0x4bba524b, 0x04b91a78, 0x00000000}},
{orig = {127, 0x111641a2, 0x4fcfb2b4, 0x1cf1576b, 0x6b088658, 0x00000000}, x = {127, 0x25c08f99, 0x3bdc976a, 0x3a808f23, 0x5be48931, 0x00000000}, m = {127, 0x111641af, 0x4fcfb2c1, 0x1cf15779, 0x6b088658, 0x00000000}},
{orig = {126, 0x03accd4a, 0x34a7e2d7, 0x749af3f3, 0x25febe05, 0x00000000}, x = {126, 0x763fca85, 0x61a5498c, 0x28eb0856, 0x1dab3aee, 0x00000000}, m = {126, 0x03accd57, 0x34a7e2e5, 0x749af401, 0x25febe05, 0x00000000}},
{orig = {127, 0x6d8c4617, 0x01cffa24, 0x3475f32e, 0x7c9ba743, 0x00000000}, x = {127, 0x7304a0f6, 0x653ea916, 0x6c412db9, 0x427e57a8, 0x00000000}, m = {127, 0x6d8c4625, 0x01cffa31, 0x3475f33b, 0x7c9ba743, 0x00000000}},
{orig = {127, 0x2da2a286, 0x152f4795, 0x3f383173, 0x6622cb58, 0x00000000}, x = {127, 0x1e118541, 0x63461082, 0x57a7e060, 0x3529988e, 0x00000000}, m = {127, 0x2da2a293, 0x152f47a3, 0x3f383181, 0x6622cb58, 0x00000000}},
{orig = {127, 0x2a17a287, 0x52b707dd, 0x30d2b30f, 0x6334fa45, 0x00000000}, x = {127, 0x06823214, 0x2c2ddf09, 0x286443db, 0x231af948, 0x00000000}, m = {127, 0x2a17a295, 0x52b707eb, 0x30d2b31d, 0x6334fa45, 0x00000000}},
{orig = {127, 0x41152b43, 0x0a898780, 0x5a517da6, 0x72eb605e, 0x00000000}, x = {127, 0x2761082a, 0x1e1ff23b, 0x3f2516a3, 0x0b165b25, 0x00000000}, m = {127, 0x41152b51, 0x0a89878d, 0x5a517db3, 0x72eb605e, 0x00000000}},
{orig = {127, 0x6b29c7ff, 0x6297d9f9, 0x3225bebb, 0x71bc4932, 0x00000000}, x = {127, 0x4bb7368d, 0x12320331, 0x5cc9ce0e, 0x1eb2ad59, 0x00000000}, m = {127, 0x6b29c80d, 0x6297da07, 0x3225bec9, 0x71bc4932, 0x00000000}},
{orig = {127, 0x6cb514c0, 0x3ff230d8, 0x6a6915f5, 0x73655603, 0x00000000}, x = {127, 0x1ebae285, 0x28f81b38, 0x37f80ed8, 0x21dc723e, 0x00000000}, m = {127, 0x6cb514cd, 0x3ff230e5, 0x6a691603, 0x73655603, 0x00000000}},
{orig = {127, 0x632339fa, 0x30508879, 0x038d4f85, 0x4bff3ced, 0x00000000}, x = {127, 0x3a3502a9, 0x4364df08, 0x3c8b1fdc, 0x20b2fa04, 0x00000000}, m = {127, 0x63233a07, 0x30508887, 0x038d4f93, 0x4bff3ced, 0x00000000}},
}
I31_Test_Mod_Pow :: struct {
orig: []u32,
x: []u32,
e: []u8,
m: []u32,
}
@(rodata)
i31_mod_pow_test_vectors := []I31_Test_Mod_Pow {
{orig = {123, 0x2eab2260, 0x4d3d8e5d, 0x2943569d, 0x01d1c5f3, 0x00000000}, x = {123, 0x030692d4, 0x14836f1f, 0x7c343636, 0x014fdd77, 0x00000000}, e = {81, 188, 252, 55, 213, 144, 254, 197, 116, 215, 162}, m = {123, 0x3f3aff1b, 0x459fe061, 0x4bba524b, 0x04b91a78, 0x00000000}},
{orig = {127, 0x25c08f99, 0x3bdc976a, 0x3a808f23, 0x5be48931, 0x00000000}, x = {127, 0x3cd4569e, 0x6474f743, 0x23fc7461, 0x05e72909, 0x00000000}, e = {145, 145, 164, 213, 254, 94, 63, 192, 72, 205, 16}, m = {127, 0x111641af, 0x4fcfb2c1, 0x1cf15779, 0x6b088658, 0x00000000}},
{orig = {126, 0x763fca85, 0x61a5498c, 0x28eb0856, 0x1dab3aee, 0x00000000}, x = {126, 0x0eb01d51, 0x178d8313, 0x044cbdfa, 0x022d38b4, 0x00000000}, e = {254, 224, 156, 88, 224, 176, 213, 125, 112, 43, 72}, m = {126, 0x03accd57, 0x34a7e2e5, 0x749af401, 0x25febe05, 0x00000000}},
{orig = {127, 0x7304a0f6, 0x653ea916, 0x6c412db9, 0x427e57a8, 0x00000000}, x = {127, 0x3b1cad0e, 0x15e996b3, 0x6ccc145e, 0x63b7aa04, 0x00000000}, e = {250, 142, 147, 53, 107, 204, 201, 168, 122, 147, 143}, m = {127, 0x6d8c4625, 0x01cffa31, 0x3475f33b, 0x7c9ba743, 0x00000000}},
{orig = {127, 0x1e118541, 0x63461082, 0x57a7e060, 0x3529988e, 0x00000000}, x = {127, 0x3ed716f7, 0x09f828bb, 0x00840e9d, 0x19c59712, 0x00000000}, e = {244, 245, 26, 174, 174, 246, 9, 106, 176, 161, 120}, m = {127, 0x2da2a293, 0x152f47a3, 0x3f383181, 0x6622cb58, 0x00000000}},
{orig = {127, 0x06823214, 0x2c2ddf09, 0x286443db, 0x231af948, 0x00000000}, x = {127, 0x79062be3, 0x5787887e, 0x59a8999f, 0x3f1c71f1, 0x00000000}, e = {212, 118, 203, 181, 125, 193, 238, 33, 133, 239, 12}, m = {127, 0x2a17a295, 0x52b707eb, 0x30d2b31d, 0x6334fa45, 0x00000000}},
{orig = {127, 0x2761082a, 0x1e1ff23b, 0x3f2516a3, 0x0b165b25, 0x00000000}, x = {127, 0x7a632df1, 0x1775c47f, 0x30b8030b, 0x562da059, 0x00000000}, e = {139, 161, 176, 68, 175, 27, 137, 101, 59, 37, 210}, m = {127, 0x41152b51, 0x0a89878d, 0x5a517db3, 0x72eb605e, 0x00000000}},
{orig = {127, 0x4bb7368d, 0x12320331, 0x5cc9ce0e, 0x1eb2ad59, 0x00000000}, x = {127, 0x00db078c, 0x078b7daf, 0x2c19dc27, 0x5e45284f, 0x00000000}, e = {154, 81, 208, 248, 214, 247, 12, 230, 127, 200, 184}, m = {127, 0x6b29c80d, 0x6297da07, 0x3225bec9, 0x71bc4932, 0x00000000}},
{orig = {127, 0x1ebae285, 0x28f81b38, 0x37f80ed8, 0x21dc723e, 0x00000000}, x = {127, 0x1f004d80, 0x15576afe, 0x7dccb09d, 0x31928c9a, 0x00000000}, e = {164, 131, 3, 178, 189, 171, 249, 186, 241, 118, 179}, m = {127, 0x6cb514cd, 0x3ff230e5, 0x6a691603, 0x73655603, 0x00000000}},
{orig = {127, 0x3a3502a9, 0x4364df08, 0x3c8b1fdc, 0x20b2fa04, 0x00000000}, x = {127, 0x13718bdd, 0x3ea119db, 0x1d2488e5, 0x02324bed, 0x00000000}, e = {107, 45, 140, 104, 190, 245, 72, 158, 102, 160, 168}, m = {127, 0x63233a07, 0x30508887, 0x038d4f93, 0x4bff3ced, 0x00000000}},
}
I31_Test_Mul_Acc :: struct {
res: []u32,
d: []u32,
a: []u32,
b: []u32,
}
@(rodata)
i31_mul_acc_test_vectors := []I31_Test_Mul_Acc {
{res = {317, 0x535a1b12, 0x44ccd6ec, 0x1fa7b212, 0x584b46e2, 0x222bf023, 0x4580794b, 0x67411ea6, 0x47b4ac1d, 0x229d50a9, 0x1818f1a8, 0x00000000}, d = {157, 0x79ef7e23, 0x4387243c, 0x0f107f29, 0x3567d945, 0x24e08c78, 0x00000000}, a = {157, 0x06deb1d1, 0x7bba988f, 0x46c80867, 0x490dafbc, 0x1f998ea4, 0x00000000}, b = {159, 0x1c1f52bf, 0x56cb5e69, 0x4f743315, 0x56bd7776, 0x619c427b, 0x00000000}},
{res = {317, 0x3ef33eed, 0x50471631, 0x244d5b99, 0x3aace6d9, 0x3152bb58, 0x13b7c355, 0x0e321425, 0x567217ce, 0x31b3e5b9, 0x0d74845e, 0x00000000}, d = {157, 0x736962be, 0x7712398c, 0x7b098d1f, 0x43804b77, 0x3bd3f8bf, 0x00000000}, a = {157, 0x12a5d163, 0x4ae379f1, 0x1387fd7f, 0x1dec4842, 0x195d1d4c, 0x00000000}, b = {159, 0x6023c9c5, 0x3740c9c3, 0x612c755b, 0x6f3d0941, 0x43e704ff, 0x00000000}},
{res = {318, 0x5c85ea39, 0x54f32864, 0x507dd4d4, 0x6250b7ce, 0x1334f373, 0x445b332d, 0x14557fc2, 0x7e1738be, 0x21bab745, 0x2f0aa313, 0x00000000}, d = {158, 0x77f935c9, 0x1e13e902, 0x43c028fd, 0x4937d854, 0x7e23bf79, 0x00000000}, a = {158, 0x1d618801, 0x79681bf4, 0x7f92559f, 0x2f1cccaf, 0x3faefa11, 0x00000000}, b = {159, 0x32413470, 0x5aff5bf1, 0x05f98105, 0x53055486, 0x5e8cf955, 0x00000000}},
{res = {319, 0x3e013eac, 0x07fe4a8a, 0x65782090, 0x5b880ea5, 0x7408456c, 0x5bf575a0, 0x2a3cd440, 0x23d148fa, 0x418bc6d7, 0x31cc34a1, 0x00000000}, d = {159, 0x102ef9f4, 0x1bc81608, 0x02f901f1, 0x668b0bd6, 0x283c9d2b, 0x00000000}, a = {159, 0x7da4773e, 0x360df7cb, 0x2b3be4f2, 0x79faa607, 0x5e09cc20, 0x00000000}, b = {159, 0x57ef4024, 0x32c96c31, 0x4559d5d3, 0x3b4d08ae, 0x43c831a5, 0x00000000}},
{res = {317, 0x2588c09a, 0x7a89223b, 0x41126c82, 0x321cdef0, 0x3faf4f56, 0x54b95fd5, 0x44abcdbd, 0x4c9acfbb, 0x5d732ab6, 0x1e44b843, 0x00000000}, d = {158, 0x524131a8, 0x6388c83c, 0x4a50e580, 0x0df9f5b8, 0x4fb833b3, 0x00000000}, a = {158, 0x523a18b9, 0x465a2251, 0x2dd0ebec, 0x7a14c3c3, 0x3caa78eb, 0x00000000}, b = {158, 0x30ec8982, 0x37707644, 0x7f244b46, 0x6f958529, 0x3fdd26db, 0x00000000}},
{res = {318, 0x61bad587, 0x761aa039, 0x6ab9a7d4, 0x41f18ba1, 0x0f77af87, 0x557292c8, 0x3428f9a7, 0x7c028945, 0x405a01c3, 0x1f3c11e1, 0x00000000}, d = {159, 0x62a6cc33, 0x07c52956, 0x4b78094d, 0x0210300b, 0x59a0cc24, 0x00000000}, a = {159, 0x6538ecfc, 0x666ac7d2, 0x62ada152, 0x1b6f0537, 0x40b88005, 0x00000000}, b = {158, 0x4f69016b, 0x2b90ac7e, 0x547c3f0e, 0x2f1de4c9, 0x3dc60ec7, 0x00000000}},
{res = {318, 0x7c1d72ed, 0x4c4d8e1f, 0x461bb74f, 0x38b75f85, 0x0c340951, 0x37882272, 0x5bb93d3e, 0x5f8ed31e, 0x7bd1add0, 0x258ae081, 0x00000000}, d = {158, 0x007d10b7, 0x7e03550f, 0x28189fea, 0x3407d2f0, 0x44f16726, 0x00000000}, a = {158, 0x152afd5f, 0x71ab1c22, 0x6fb41945, 0x522bd719, 0x3025871d, 0x00000000}, b = {159, 0x7272538a, 0x795fd146, 0x7111af44, 0x2b400ac9, 0x63cef8dc, 0x00000000}},
{res = {317, 0x6ac9a927, 0x37bf839e, 0x72166cf4, 0x66e97192, 0x0f1010a8, 0x475dd251, 0x730eb0ff, 0x295b1515, 0x340bb235, 0x14da57c9, 0x00000000}, d = {157, 0x026ce5ab, 0x49a7c219, 0x7d86a513, 0x47701297, 0x56e7b169, 0x00000000}, a = {157, 0x6f6a9a0e, 0x7e018fa4, 0x23b35254, 0x2566e0a9, 0x158799e8, 0x00000000}, b = {159, 0x12e6bed2, 0x3b186bb1, 0x3bddcfc9, 0x75ca6013, 0x7bf9ee6e, 0x00000000}},
{res = {317, 0x277fe2cd, 0x00e40ce6, 0x2c35b175, 0x59bd9a99, 0x2a53689e, 0x3972c269, 0x4db20926, 0x67d98c0d, 0x57bbbc3f, 0x0bf1a543, 0x00000000}, d = {159, 0x17d3efaf, 0x59a9c4ff, 0x69349a09, 0x535b0031, 0x677b2a0d, 0x00000000}, a = {159, 0x67e1ef0e, 0x7d1e406e, 0x5111e847, 0x321572bc, 0x547d3b3a, 0x00000000}, b = {157, 0x1c685fb9, 0x2f3d09a2, 0x6611e3c9, 0x25575675, 0x12184aec, 0x00000000}},
{res = {317, 0x11c8df23, 0x0d48a117, 0x0a6b8c50, 0x0fef02e9, 0x3eb1ae96, 0x32cd060b, 0x0cdb0651, 0x2dc87323, 0x16ea6bb3, 0x09cf6776, 0x00000000}, d = {159, 0x53b8638b, 0x4e180172, 0x3a3cdcac, 0x614f28ca, 0x0f644498, 0x00000000}, a = {159, 0x72ac2c95, 0x196ed06f, 0x43412b53, 0x59441827, 0x4364974f, 0x00000000}, b = {157, 0x0e220f38, 0x02785026, 0x6566e3fe, 0x1a270c17, 0x12a1eeaa, 0x00000000}},
}
I31_Test_Div_Rem :: struct {
hi: u32,
lo: u32,
div: u32,
quo: u32,
rem: u32,
}
@(rodata)
i31_div_rem_test_vectors := []I31_Test_Div_Rem {
{hi = 0x632c115c, lo = 0x4b2bf821, div = 0xb8481290, quo = 0x89c490d1, rem = 0x07a3d091},
{hi = 0x1cb7e3aa, lo = 0x63e1d659, div = 0xd94a9eb0, quo = 0x21d58fe1, rem = 0x02380da9},
{hi = 0xbe78690c, lo = 0x88cc1bd7, div = 0xbf1211a9, quo = 0xff32200f, rem = 0x4a85f2f0},
{hi = 0xa91488a1, lo = 0x2f8f64ac, div = 0xb5027f82, quo = 0xef20d04a, rem = 0x86fce918},
{hi = 0x074838b4, lo = 0xc1b7bbbe, div = 0x0cc1ef1e, quo = 0x92207a42, rem = 0x0c03ca02},
{hi = 0x415c651d, lo = 0xe696b3e5, div = 0x5a782289, quo = 0xb8f37d3e, rem = 0x149671b7},
{hi = 0x26454389, lo = 0x986fc51a, div = 0xebc02a27, quo = 0x298ec804, rem = 0x27dea47e},
{hi = 0x5a3b3e87, lo = 0xf475705b, div = 0x5e6f1d1a, quo = 0xf49b708c, rem = 0x4c382623},
{hi = 0x8b1580f2, lo = 0x4ca1db17, div = 0xafd69ac8, quo = 0xca7d730d, rem = 0x938c26ef},
{hi = 0x0dd6f07e, lo = 0xde23b70c, div = 0x5bd60fda, quo = 0x26943342, rem = 0x05c332d8},
{hi = 0xede68f1d, lo = 0x07813b19, div = 0xff2b167e, quo = 0xeead1023, rem = 0x1c0f47df},
{hi = 0x5c5ec31f, lo = 0xd985218f, div = 0xae020a42, quo = 0x87e50b9b, rem = 0x6cce1599},
{hi = 0x6639e3c0, lo = 0xe8690a35, div = 0x6c241512, quo = 0xf1ff7b0a, rem = 0x69f29181},
{hi = 0x0a45fb42, lo = 0x113dd8cc, div = 0x2ee12611, quo = 0x3819d4aa, rem = 0x0c8b7d82},
{hi = 0x3ea35d11, lo = 0x5e2a590f, div = 0xd465b470, quo = 0x4b7f3d2b, rem = 0x21865a3f},
{hi = 0xcb7cee11, lo = 0x25cea707, div = 0xe191debc, quo = 0xe6f0751f, rem = 0x7218c243},
{hi = 0x816b74f4, lo = 0xb66d7312, div = 0x83e048ef, quo = 0xfb3b4f1d, rem = 0x6b6e6eff},
{hi = 0x4863d8a6, lo = 0x7de42a8b, div = 0x588fc2d3, quo = 0xd140fa7c, rem = 0x3c3fbe57},
{hi = 0x2540246a, lo = 0xe24d9d05, div = 0x8320712a, quo = 0x48b98123, rem = 0x047dfa47},
{hi = 0xc074131e, lo = 0xdc4d465e, div = 0xe1dc958c, quo = 0xda22448b, rem = 0x8d36e35a},
{hi = 0xf6ef68bc, lo = 0xc22f3f3e, div = 0xfa5c4162, quo = 0xfc7f66e0, rem = 0x07cafd7e},
{hi = 0x061d173b, lo = 0xeccf58ec, div = 0xb792ff7a, quo = 0x08869175, rem = 0x3a107c2a},
{hi = 0xb2ee2b9d, lo = 0x7805c28d, div = 0xf610e48b, quo = 0xba276d3a, rem = 0xb7b5cc0f},
{hi = 0x38849ac0, lo = 0x67df2bd4, div = 0xa314f95e, quo = 0x58b84824, rem = 0x0739aa9c},
{hi = 0x32dc45d0, lo = 0x2c7f42e9, div = 0xfafc8a2a, quo = 0x33e05b19, rem = 0xa1f8d6cf},
{hi = 0x4240d91f, lo = 0xec3b2dd8, div = 0x7a1bc3fe, quo = 0x8ae65d59, rem = 0x602cc48a},
{hi = 0x3c0836b4, lo = 0x275cd2a6, div = 0x871c1085, quo = 0x71bf0a5e, rem = 0x6a2e8fd0},
{hi = 0x49de09c7, lo = 0xf62ac65b, div = 0x9b7ae8a1, quo = 0x799f9edf, rem = 0x1587c41c},
{hi = 0x3da17dbc, lo = 0xe4437bf3, div = 0xf64f9bca, quo = 0x400e1fa2, rem = 0x5cf9701f},
{hi = 0x4e22607b, lo = 0xa8798c83, div = 0xfe90aa77, quo = 0x4e931fa6, rem = 0xedb19a59},
{hi = 0x43dd8e64, lo = 0xd2aee2d3, div = 0xfa05bf14, quo = 0x457ceca2, rem = 0x5d35882b},
{hi = 0x74ae120e, lo = 0x4a567457, div = 0x80e12b3b, quo = 0xe7c46e8b, rem = 0x3954a14e},
{hi = 0x25c33e85, lo = 0xd4fe503d, div = 0xab5e1c7a, quo = 0x38698d75, rem = 0x4f421a7b},
{hi = 0xba818477, lo = 0xf1edb77a, div = 0xf5028225, quo = 0xc2df32d7, rem = 0x472c3067},
{hi = 0xce85c4d5, lo = 0xc1530b0c, div = 0xe5a6d9ab, quo = 0xe63795d9, rem = 0x94770219},
{hi = 0x08adb5bb, lo = 0xa277cb09, div = 0xab1ee4c6, quo = 0x0cfbb916, rem = 0x045b0c05},
{hi = 0x8dc1580e, lo = 0xe73d2e39, div = 0xadf26902, quo = 0xd09f833f, rem = 0x349b50bb},
{hi = 0x4435ddf6, lo = 0x42a05702, div = 0x594f5cd1, quo = 0xc3850b8d, rem = 0x3d583ce5},
{hi = 0x6cb4f9c6, lo = 0xcc9b6e48, div = 0xa7bbed72, quo = 0xa5e948ca, rem = 0x00c80254},
{hi = 0x3cb3b260, lo = 0xf2f7a398, div = 0xecf16edf, quo = 0x419586bc, rem = 0x6ad67dd4},
{hi = 0x1720b73c, lo = 0x347ff447, div = 0x1724839d, quo = 0xffd5fbb6, rem = 0x0ede73a9},
{hi = 0xe3003f2c, lo = 0xe1cb1bdf, div = 0xf2298637, quo = 0xeff8ef99, rem = 0x04648c00},
{hi = 0x558a5621, lo = 0x1f717ddc, div = 0xf03e2bca, quo = 0x5b269cff, rem = 0xa0d8c7a6},
{hi = 0x949fb36a, lo = 0xce76c920, div = 0xd215ba56, quo = 0xb51b3884, rem = 0x456de4c8},
{hi = 0xa0f2fc11, lo = 0xd0259ff1, div = 0xc8626cac, quo = 0xcd9ea15a, rem = 0xa90b3f79},
{hi = 0x70c3fa84, lo = 0x74d29bb9, div = 0xf9e5a764, quo = 0x7384fbe9, rem = 0x9c1e35b5},
{hi = 0x2da900de, lo = 0xcf74abff, div = 0x99abc1da, quo = 0x4c10ae3a, rem = 0x6b28949b},
{hi = 0x3664397e, lo = 0x2ba486dc, div = 0x7ea469c9, quo = 0x6df304d3, rem = 0x39af3231},
{hi = 0x095d67a6, lo = 0xe3b31742, div = 0xdbe7549c, quo = 0x0ae6ee14, rem = 0x88cf7312},
{hi = 0x9a402804, lo = 0x462e5dcd, div = 0xa736e128, quo = 0xec272359, rem = 0x76399ee5},
{hi = 0x12809274, lo = 0x09011d2f, div = 0xac4c1332, quo = 0x1b7d9f66, rem = 0x7d5b6943},
{hi = 0x6a897aed, lo = 0x1d121571, div = 0xef42fe4b, quo = 0x71fd7b8c, rem = 0xa921fb6d},
{hi = 0x55ed8b10, lo = 0x9090ea62, div = 0x9cd2c065, quo = 0x8c45083c, rem = 0x21efaab6},
{hi = 0x3930a3f2, lo = 0x541cf31f, div = 0xa7c1fef1, quo = 0x5745c1d4, rem = 0x884d228b},
{hi = 0xa7167582, lo = 0x0abb9b9f, div = 0xf63dc17e, quo = 0xadb5a66e, rem = 0x3ca4c37b},
{hi = 0x74b72666, lo = 0x4abb1c94, div = 0x7ee27c4a, quo = 0xeb7b903b, rem = 0x0110d786},
{hi = 0x88a89e33, lo = 0x2dada2d6, div = 0xe8725e91, quo = 0x9681856f, rem = 0x5be44cf7},
{hi = 0x5affad0b, lo = 0x68c7b744, div = 0x8bf402e7, quo = 0xa6740fb8, rem = 0x6a8e183c},
{hi = 0x18400a64, lo = 0x91c19fee, div = 0xececa37b, quo = 0x1a33df48, rem = 0xbd4a8056},
{hi = 0x0ca1d509, lo = 0x5ace5b38, div = 0x405fe35b, quo = 0x323c1088, rem = 0x1a53e2e0},
{hi = 0x49c3d579, lo = 0xb57e3052, div = 0x8fe8c087, quo = 0x833871ec, rem = 0x1b691cde},
{hi = 0x055e153e, lo = 0x876ba83b, div = 0xd0a7b0e1, quo = 0x0695deae, rem = 0x1119514d},
{hi = 0x1ab22cf1, lo = 0xc9c28af3, div = 0x626e63e5, quo = 0x456e54c0, rem = 0x18ca7b33},
{hi = 0x5ce6ba97, lo = 0x46dbc829, div = 0xbf4a582f, quo = 0x7c53ef3f, rem = 0x6fff3398},
{hi = 0x280dd6e5, lo = 0x13c19c79, div = 0xe67fd7e2, quo = 0x2c7c3e0b, rem = 0xcc8299c3},
{hi = 0x1c7c2a51, lo = 0x8aecb540, div = 0x3decec9d, quo = 0x75c1d104, rem = 0x02afd5cc},
{hi = 0x3a6e806b, lo = 0x142a6ad0, div = 0x56b24a8d, quo = 0xac89eeee, rem = 0x17a505ba},
{hi = 0x23a1b1d0, lo = 0xda933d16, div = 0x85938fe1, quo = 0x4449c850, rem = 0x08e57ec6},
{hi = 0x1f177f2e, lo = 0x107c53c1, div = 0xdef2389a, quo = 0x23b390c4, rem = 0x7d845dd9},
{hi = 0x735cb354, lo = 0xbcf96584, div = 0xacc0d360, quo = 0xaaf3fec8, rem = 0x107b0284},
{hi = 0x5ecb810e, lo = 0x51b4a6d1, div = 0x84db55d8, quo = 0xb6a8bd00, rem = 0x7d942ed1},
{hi = 0x4765b107, lo = 0xded18aa4, div = 0x540b7c9f, quo = 0xd979b39b, rem = 0x4592e95f},
{hi = 0xddb19fc7, lo = 0xe30727aa, div = 0xfaf579d9, quo = 0xe225a80a, rem = 0xcf1cfd30},
{hi = 0x85f9707c, lo = 0x5511a037, div = 0x8e318efd, quo = 0xf133d2a5, rem = 0x5e6ded26},
{hi = 0x0c2c41f1, lo = 0xbf357e4a, div = 0x62d254f1, quo = 0x1f88bf7d, rem = 0x421a359d},
{hi = 0x335d8188, lo = 0x00fd4b21, div = 0xfcf1e085, quo = 0x33fc54b7, rem = 0x507e280e},
{hi = 0x4eef80e4, lo = 0xfcd9e9de, div = 0xf2d2e5c0, quo = 0x53380265, rem = 0x07d9c51e},
{hi = 0x431aeca5, lo = 0x07963290, div = 0xe6ca948b, quo = 0x4a6f5427, rem = 0xcfb6f563},
{hi = 0x13638612, lo = 0x71f2880c, div = 0x25f44434, quo = 0x82c6db4c, rem = 0x0eddcc9c},
{hi = 0x24dd96bb, lo = 0xd6b8ffca, div = 0xb3697b2b, quo = 0x349a55fc, rem = 0x61207a76},
{hi = 0x3201b855, lo = 0x0e6b7026, div = 0x65a232ae, quo = 0x7df5a4a0, rem = 0x2a0e4b66},
{hi = 0x1e2536d5, lo = 0xfbef9964, div = 0x97c9027e, quo = 0x32d7cea9, rem = 0x54699036},
{hi = 0x03486ba3, lo = 0x4ae30d34, div = 0x5f768217, quo = 0x08cdbad7, rem = 0x1f6c15e3},
{hi = 0x7000b42d, lo = 0xd9357d65, div = 0xc0b7f01f, quo = 0x94c7bd20, rem = 0x61d79685},
{hi = 0x2f088bd1, lo = 0x098fe330, div = 0x3a2fc060, quo = 0xceee1d21, rem = 0x075d36d0},
{hi = 0x30ede94b, lo = 0x1200278e, div = 0x42fc5880, quo = 0xbafe65ba, rem = 0x08bd5a8e},
{hi = 0x5ed1fc0f, lo = 0x0537fbbf, div = 0x6ae246f5, quo = 0xe31b295f, rem = 0x630b69d4},
{hi = 0x1d2496a2, lo = 0xecca6436, div = 0xaa60d4ea, quo = 0x2bc9d3a0, rem = 0x270e73f6},
{hi = 0x97ae9a4e, lo = 0xba69862d, div = 0xea9984e9, quo = 0xa584c095, rem = 0x563c6a90},
{hi = 0x12cf1435, lo = 0x99a99414, div = 0x2644910d, quo = 0x7dd36379, rem = 0x1871fdef},
{hi = 0x7fc1828d, lo = 0x70c995ca, div = 0xdebea8f1, quo = 0x92d45c4d, rem = 0x9326294d},
{hi = 0x32a003d1, lo = 0x6c09aff4, div = 0xd460becc, quo = 0x3d05fb48, rem = 0x7fc60294},
{hi = 0x3add17a2, lo = 0xb4e2c499, div = 0x45b4eb3a, quo = 0xd82db19b, rem = 0x20833e7b},
{hi = 0xc4a6d715, lo = 0xb1576cc0, div = 0xcf15af86, quo = 0xf31a46cc, rem = 0xb874e9f8},
{hi = 0x2da93388, lo = 0x980b3f98, div = 0xf8e18784, quo = 0x2ef78f93, rem = 0x3b7bb2cc},
{hi = 0x5d77f50a, lo = 0xc8f5c09b, div = 0xaa3cc6c4, quo = 0x8c8e703a, rem = 0x2f82f833},
{hi = 0x4898d91b, lo = 0xe7956338, div = 0x4e4a2805, quo = 0xed62bc94, rem = 0x3c689454},
{hi = 0xb9620ae8, lo = 0x0e9ed51d, div = 0xc281fe4d, quo = 0xf3fd8e35, rem = 0x458d792c},
{hi = 0xc342d71a, lo = 0xecafbf43, div = 0xfbfa840c, quo = 0xc6609971, rem = 0x2db049f7},
{hi = 0x4e2d4c4b, lo = 0x4609de2e, div = 0xc737fab5, quo = 0x64757da5, rem = 0x40d1e685},
}

View File

@@ -1,6 +1,7 @@
package test_crypto_common
import "core:bytes"
import "core:encoding/base64"
import "core:encoding/hex"
// Common helpers for cryptography tests.
@@ -23,3 +24,13 @@ hexbytes_decode :: proc(x: Hex_Bytes, allocator := context.allocator) -> []byte
return dst
}
Jwk_Bytes :: string
jwkbytes_decode :: proc(s: Jwk_Bytes, allocator := context.allocator) -> []byte {
dst, err := base64.decode(s, base64.DEC_URL_TABLE, allocator = allocator)
if err != nil {
panic("Jwk_Bytes: invalid hex encoding")
}
return dst
}

View File

@@ -0,0 +1,419 @@
#+build !riscv64
package test_core_crypto
import "core:bytes"
import "core:encoding/hex"
import "core:log"
import "core:testing"
import "core:crypto"
import "core:crypto/rsa"
// BUG/yawning: RISC-V fails the PSS test in CI with a nonsensical
// bounds-checking error suggesting something spooky, as the failure
// was not reproducible on qemu whole system emulation.
@(private="file")
TEST_MSG: string : "don't let them immanentize the eschaton"
@(private="file", rodata)
TEST_MSG_SHA256 := []byte{
0x50, 0x95, 0x66, 0x8e, 0x7c, 0xd0, 0xd5, 0x8e,
0x9d, 0x59, 0xf8, 0x4a, 0x1a, 0x46, 0x5a, 0x8a,
0x2e, 0x69, 0xcc, 0xad, 0x6a, 0xca, 0x8b, 0xb7,
0x55, 0x55, 0x15, 0x71, 0x9b, 0xc7, 0x50, 0x88,
}
@(private="file", rodata)
TEST_TLS_PMS := []byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
}
@(test)
test_rsa_private_key :: proc(t: ^testing.T) {
priv_key: rsa.Private_Key
if !testing.expectf(
t,
rsa.private_key_set_insecure_test(&priv_key),
"rsa: failed to set test key",
) {
return
}
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping generate")
return
}
if !testing.expectf(
t,
rsa.private_key_generate(&priv_key),
"rsa: failed to generate private key",
) {
return
}
log.debugf("n=0x%s", hex.encode(priv_key._pub_key._n.v[:priv_key._pub_key._n.v_len], context.temp_allocator))
log.debugf("e=%d", priv_key._pub_key._e)
log.debugf("d=0x%s", hex.encode(priv_key._d.v[:priv_key._d.v_len], context.temp_allocator))
log.debugf("p=0x%s", hex.encode(priv_key._p.v[:priv_key._p.v_len], context.temp_allocator))
log.debugf("q=0x%s", hex.encode(priv_key._q.v[:priv_key._q.v_len], context.temp_allocator))
log.debugf("dp=0x%s", hex.encode(priv_key._dp.v[:priv_key._dp.v_len], context.temp_allocator))
log.debugf("dq=0x%s", hex.encode(priv_key._dq.v[:priv_key._dq.v_len], context.temp_allocator))
log.debugf("iq=0x%s", hex.encode(priv_key._iq.v[:priv_key._iq.v_len], context.temp_allocator))
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
if !testing.expectf(
t,
rsa.public_key_equal(&priv_key._pub_key, &pub_key),
"rsa: failed clone public key",
) {
return
}
}
@(test)
test_rsa_sign_pkcs1 :: proc(t: ^testing.T) {
test_msg := transmute([]byte)(TEST_MSG)
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
// Generated with Go's crypto code. Signatures are
// deterministic.
expected_sig := []byte{
0x65, 0x0e, 0xa9, 0xd1, 0x66, 0xb3, 0x61, 0x4c,
0x67, 0x8f, 0xd6, 0x9d, 0x2e, 0xc9, 0x24, 0xdd,
0xcc, 0xa1, 0x0b, 0xdc, 0xbb, 0x35, 0x60, 0xc5,
0x03, 0xb1, 0xa7, 0x10, 0x64, 0x53, 0x83, 0x02,
0x7a, 0x8b, 0xc2, 0x83, 0x7f, 0xd6, 0xfc, 0xc3,
0xe1, 0x4d, 0x33, 0x57, 0x90, 0x81, 0x52, 0xea,
0xcd, 0x7c, 0xaa, 0xa5, 0x98, 0x59, 0x90, 0xd1,
0x88, 0x21, 0x87, 0xc2, 0x9d, 0x51, 0x4d, 0x45,
0x18, 0x06, 0xa2, 0xde, 0x7a, 0xc9, 0xc9, 0x1b,
0x3d, 0x27, 0x07, 0xe4, 0xad, 0x46, 0xe8, 0x09,
0xe1, 0xd5, 0xbd, 0x33, 0x1a, 0x9c, 0x7f, 0x3e,
0x1b, 0x22, 0xc0, 0xfa, 0xa1, 0x30, 0xfb, 0xda,
0x3b, 0x09, 0x9f, 0x6f, 0x44, 0x0e, 0xa8, 0x9b,
0x65, 0x36, 0x48, 0x07, 0x95, 0xfc, 0xf7, 0x2b,
0x61, 0xa0, 0x95, 0x36, 0x94, 0x66, 0x01, 0x90,
0x5f, 0xf6, 0x72, 0x05, 0x64, 0x0c, 0x50, 0x8a,
0xce, 0xfc, 0x75, 0xc7, 0x1e, 0x05, 0x62, 0xd9,
0x8e, 0xdf, 0x85, 0x7e, 0x5d, 0x84, 0x8c, 0x0b,
0x18, 0x88, 0x71, 0x4a, 0x94, 0x11, 0x5e, 0x16,
0x53, 0xd0, 0xdc, 0x8e, 0x62, 0x73, 0x9d, 0x94,
0x66, 0x1a, 0xdf, 0xf4, 0x01, 0xaa, 0xa5, 0x33,
0xb3, 0x0c, 0x14, 0x53, 0x5d, 0xe7, 0xdb, 0x66,
0xf0, 0x3e, 0xf0, 0x08, 0x81, 0x77, 0x40, 0x79,
0xa5, 0xc6, 0x48, 0x68, 0xcc, 0xa2, 0xb6, 0xf3,
0x1f, 0xf9, 0xd5, 0xb5, 0x26, 0x04, 0x97, 0xbc,
0x93, 0xa3, 0x19, 0x29, 0x26, 0x86, 0x89, 0xf8,
0x11, 0xe5, 0xdd, 0xbf, 0x35, 0x6b, 0x96, 0x25,
0xa5, 0x78, 0xab, 0x02, 0x69, 0xdf, 0x54, 0x95,
0xc5, 0x6b, 0x95, 0xcc, 0x38, 0x7b, 0x11, 0xfa,
0x8c, 0x98, 0xa9, 0x95, 0x7e, 0x39, 0x7c, 0xf5,
0x2f, 0xea, 0x42, 0x6d, 0xf5, 0xaa, 0xb3, 0x16,
0x10, 0x2f, 0x29, 0xf3, 0x4a, 0xbc, 0x47, 0xfb,
}
// Note: The key generate/set routines use the prehashed
// mode.
sig: [2048 >> 3]byte
ok := rsa.sign_pkcs1(
&priv_key,
.SHA256,
test_msg,
sig[:],
)
if !testing.expectf(t, ok, "rsa/pkcs1: signing failed") {
return
}
if !testing.expectf(
t,
bytes.equal(sig[:], expected_sig),
"rsa/pkcs1: signature mismatch: %x (expected %x)",
sig[:],
expected_sig,
) {
return
}
ok = rsa.verify_pkcs1(
&pub_key,
.SHA256,
test_msg,
expected_sig,
)
if !testing.expectf(t, ok, "rsa/pkcs1: verify failed") {
return
}
}
@(test)
test_rsa_sign_pss :: proc(t: ^testing.T) {
test_msg := transmute([]byte)(TEST_MSG)
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
// Generated with Go's crypto code. Signatures are
// NOT deterministic.
test_sig := []byte{
0x46, 0xd3, 0x7e, 0xb2, 0x2c, 0x82, 0xe0, 0x68,
0x80, 0x58, 0x8c, 0x07, 0x0e, 0xe9, 0x41, 0x3f,
0xde, 0x45, 0x4a, 0xa0, 0x3f, 0xff, 0xa3, 0xa8,
0xc2, 0x76, 0x74, 0xf0, 0xe8, 0x44, 0x07, 0x2d,
0xdb, 0x12, 0xd5, 0x57, 0xcc, 0x28, 0x15, 0xfa,
0xeb, 0xb6, 0x14, 0x76, 0x10, 0x3f, 0xfc, 0xba,
0xb1, 0x6e, 0x7f, 0x65, 0x6c, 0x9b, 0x1a, 0x62,
0x60, 0xc4, 0xfa, 0xb0, 0x03, 0x07, 0x2b, 0x6b,
0x6a, 0x5a, 0x84, 0x10, 0xe4, 0xf5, 0x64, 0xad,
0xfa, 0xd3, 0x5f, 0xc9, 0xf4, 0xac, 0x70, 0xde,
0x54, 0x06, 0x14, 0x59, 0xf7, 0x60, 0x15, 0xc9,
0xa2, 0xe2, 0x54, 0xbc, 0x79, 0xa8, 0x02, 0x72,
0xba, 0x6a, 0x68, 0x08, 0x15, 0x6b, 0xcb, 0xc8,
0x55, 0x83, 0x63, 0x06, 0xe4, 0x28, 0x36, 0x6b,
0xe6, 0x15, 0x2a, 0x21, 0xc9, 0x4f, 0xc0, 0x0e,
0x30, 0x54, 0x3b, 0xf1, 0xa4, 0x79, 0x83, 0xb9,
0x02, 0xd9, 0x2e, 0xa1, 0x31, 0xf4, 0x5b, 0x1b,
0xbe, 0x44, 0x17, 0xd8, 0x42, 0x6d, 0xb3, 0x38,
0x3f, 0x23, 0x82, 0x9e, 0x76, 0x52, 0x0c, 0x5e,
0xa0, 0xcd, 0xd1, 0xcc, 0xe2, 0x5b, 0x71, 0xb5,
0xca, 0x28, 0x33, 0xc3, 0x03, 0x30, 0xc5, 0xa6,
0xdc, 0x6e, 0xfd, 0xd7, 0x34, 0x0a, 0xd2, 0x30,
0x2c, 0x80, 0x2d, 0x31, 0xea, 0xe9, 0x44, 0x2a,
0x7e, 0x1d, 0xde, 0x71, 0xa3, 0x7e, 0xe2, 0x5e,
0x8a, 0x91, 0x61, 0x23, 0x5b, 0x26, 0x6d, 0x3b,
0x44, 0xfc, 0x6b, 0x36, 0x40, 0xb1, 0xdb, 0xe7,
0xf9, 0xe7, 0x8a, 0x12, 0x7c, 0xba, 0xd8, 0x33,
0x1b, 0xac, 0x70, 0x59, 0x24, 0x83, 0x3a, 0x8b,
0x2a, 0x51, 0x1b, 0x97, 0xa3, 0x8e, 0x34, 0xd1,
0xbb, 0xc1, 0x4e, 0x00, 0xab, 0x21, 0x12, 0x53,
0xeb, 0xda, 0x36, 0x77, 0x30, 0xea, 0x82, 0x92,
0xb5, 0xfb, 0x07, 0xb1, 0x34, 0x37, 0x78, 0x69,
}
ok := rsa.verify_pss(
&pub_key,
.SHA256,
32,
test_msg,
test_sig,
)
if !testing.expectf(t, ok, "rsa/pss: verify (pregenerated) failed") {
return
}
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping round trip tests")
return
}
// Just do a simple round-trip test, as the failure
// cases are covered by wycheproof, and de-randomizing
// PSS is an API nightmare.
sig: [2048 >> 3]byte
ok = rsa.sign_pss(
&priv_key,
.SHA256,
32,
TEST_MSG_SHA256,
sig[:],
true,
)
if !testing.expectf(t, ok, "rsa/pss: signing failed") {
return
}
ok = rsa.verify_pss(
&pub_key,
.SHA256,
32,
TEST_MSG_SHA256,
sig[:],
true,
)
if !testing.expectf(t, ok, "rsa/pss: verify failed") {
return
}
}
@(test)
test_rsa_enc_dec_oaep :: proc(t: ^testing.T) {
test_msg := transmute([]byte)(TEST_MSG)
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
// Generated with Go's crypto code. Ciphertexts are
// NOT deterministic.
test_ciphertext := []byte{
0x51, 0x7e, 0x55, 0xe8, 0xf8, 0x69, 0x9e, 0x68,
0x8e, 0x2f, 0x38, 0xec, 0x11, 0xfb, 0x5f, 0x1e,
0xf1, 0x9b, 0x2c, 0xea, 0x8a, 0xfb, 0x13, 0x04,
0x2a, 0xbd, 0x4f, 0x69, 0xca, 0x11, 0x31, 0x1b,
0xb0, 0x00, 0x18, 0x69, 0x32, 0x88, 0xe3, 0x07,
0x82, 0xe8, 0x1d, 0x34, 0x09, 0x26, 0xdf, 0x41,
0x1c, 0xc3, 0xf1, 0x39, 0x31, 0x45, 0xb6, 0x67,
0xa5, 0x7c, 0xa4, 0xaf, 0x48, 0x5e, 0x96, 0x26,
0x9b, 0x78, 0x76, 0x4e, 0xd2, 0x6e, 0x53, 0xd0,
0x51, 0xc9, 0x80, 0x71, 0xea, 0x67, 0x8a, 0x44,
0x1e, 0xb0, 0x81, 0x2e, 0xce, 0x43, 0x9a, 0xd9,
0x1c, 0xea, 0x5c, 0x8b, 0x94, 0x3e, 0x1e, 0x5c,
0xb0, 0x17, 0xb9, 0x50, 0x44, 0x22, 0xc9, 0x17,
0xd0, 0x73, 0x54, 0x2b, 0x15, 0x68, 0xe2, 0xcf,
0xbe, 0x0b, 0xef, 0x91, 0x11, 0xfc, 0xa6, 0x78,
0x14, 0xdd, 0x62, 0xf3, 0xba, 0x8c, 0x8d, 0x4b,
0x7f, 0x4b, 0xfa, 0x8b, 0x9c, 0x91, 0x08, 0x9f,
0x39, 0x47, 0x27, 0xba, 0x9a, 0xfd, 0x2a, 0xb8,
0x1e, 0x70, 0xa3, 0x9c, 0xe1, 0x23, 0x21, 0xc5,
0xca, 0x00, 0x2a, 0x9b, 0x23, 0x0f, 0x15, 0xe2,
0x9a, 0x62, 0xc2, 0x20, 0xb6, 0xe8, 0x85, 0x5f,
0x94, 0xba, 0x72, 0x06, 0x55, 0xcf, 0x5a, 0xd6,
0xc6, 0xc0, 0x89, 0xff, 0xd3, 0x72, 0xf9, 0x34,
0x7a, 0x12, 0xfc, 0xe3, 0x74, 0x64, 0x00, 0xfe,
0xa1, 0x35, 0x78, 0x66, 0x56, 0x1a, 0xde, 0x6a,
0x83, 0x6b, 0x20, 0x06, 0xe2, 0x51, 0xae, 0xc7,
0x27, 0x44, 0x5b, 0x21, 0x4f, 0xdf, 0xf6, 0x52,
0x8e, 0x3a, 0x84, 0x07, 0x26, 0xc8, 0xe3, 0x6a,
0x18, 0xd4, 0x49, 0x44, 0xd8, 0x24, 0x08, 0x94,
0xe1, 0x67, 0xde, 0x4a, 0x8e, 0x6a, 0x2a, 0x28,
0x72, 0x0e, 0x68, 0x9c, 0x7f, 0x55, 0x13, 0x54,
0x13, 0x32, 0xdb, 0xe7, 0x31, 0x84, 0x90, 0xaf,
}
buf: [2048 >> 3]byte
derived_msg, ok := rsa.decrypt_oaep(
&priv_key,
.SHA256,
test_ciphertext,
buf[:],
)
if !testing.expectf(t, ok, "rsa/oaep: decryption (pregenerated) failed") {
return
}
if !testing.expectf(
t,
bytes.equal(test_msg, derived_msg),
"rsa/oaep: unexpected plaintext: %x",
derived_msg,
) {
return
}
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping")
return
}
// Just do a simple round-trip test, as the failure
// cases are covered by wycheproof, and de-randomizing
// OAEP is an API nightmare.
ok = rsa.encrypt_oaep(
&pub_key,
.SHA512,
test_msg,
buf[:],
)
if !testing.expectf(t, ok, "rsa/oaep: encryption failed") {
return
}
derived_msg, ok = rsa.decrypt_oaep(
&priv_key,
.SHA512,
buf[:],
buf[:],
)
if !testing.expectf(t, ok, "rsa/oaep: decryption failed") {
return
}
if !testing.expectf(
t,
bytes.equal(test_msg, derived_msg),
"rsa/oaep: unexpected plaintext: %x",
derived_msg,
) {
return
}
}
@(test)
test_rsa_dec_pms :: proc(t: ^testing.T) {
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
// Generated with Go's crypto code. Go's PKCS1v15
// encryption output is NOT deterministic.
test_ciphertext := []byte{
0x0c, 0x18, 0x48, 0x23, 0x0b, 0x4a, 0xa9, 0x20,
0xbb, 0xa6, 0x38, 0xbf, 0x25, 0xda, 0x16, 0xd7,
0x92, 0x72, 0x3a, 0x81, 0xbc, 0xee, 0x74, 0x6b,
0xd8, 0x55, 0x26, 0x72, 0x81, 0x70, 0x73, 0x32,
0xd4, 0x30, 0x56, 0xe2, 0xeb, 0x1b, 0x57, 0xf3,
0xf5, 0x7d, 0xea, 0x3e, 0x3c, 0x62, 0x35, 0x78,
0x06, 0x88, 0x27, 0x4d, 0x6e, 0xa0, 0x45, 0x91,
0x62, 0x8c, 0xe9, 0x93, 0xd0, 0xde, 0x85, 0x71,
0x01, 0x24, 0x7a, 0x1a, 0x5d, 0x03, 0x49, 0x92,
0x73, 0x5a, 0x5b, 0x1e, 0x1e, 0x3b, 0xf0, 0xf2,
0xea, 0x04, 0x9a, 0x87, 0x32, 0x20, 0x52, 0xbd,
0x72, 0xc8, 0xa8, 0x38, 0xd1, 0x29, 0x97, 0x87,
0x0b, 0x76, 0xc1, 0x68, 0xe5, 0x05, 0x72, 0xb4,
0x4d, 0xd1, 0x95, 0xb2, 0xa8, 0x19, 0x3f, 0xc3,
0x1e, 0xee, 0x34, 0x19, 0x72, 0xac, 0x1e, 0x4b,
0x01, 0xd7, 0x60, 0xeb, 0x27, 0xf1, 0x12, 0x7c,
0xbc, 0x07, 0x5d, 0xf6, 0xb9, 0xac, 0xf9, 0xdb,
0x1a, 0xaf, 0x47, 0x13, 0x22, 0x16, 0xb5, 0x05,
0x5a, 0x9c, 0x45, 0x69, 0x0a, 0xf1, 0x36, 0x6b,
0xab, 0x96, 0xd7, 0x7f, 0x66, 0xff, 0x16, 0x3c,
0x29, 0xc4, 0x10, 0x03, 0xe1, 0x35, 0xcc, 0xae,
0x71, 0x08, 0x14, 0xff, 0x57, 0x4f, 0x3d, 0x79,
0x7a, 0xa7, 0x19, 0x2e, 0x23, 0x08, 0xad, 0xb2,
0xe5, 0xb1, 0xe8, 0x47, 0x6d, 0xe1, 0x24, 0x4a,
0xd8, 0x1f, 0xe4, 0x52, 0x21, 0x3e, 0xf1, 0xcd,
0x07, 0x72, 0xa4, 0xb8, 0x06, 0x98, 0x3a, 0x17,
0xfd, 0xca, 0x74, 0x93, 0xb1, 0x2b, 0xd8, 0x76,
0x7c, 0x6f, 0x71, 0xfc, 0x16, 0xef, 0x99, 0xa1,
0xf9, 0x13, 0xeb, 0xfc, 0x34, 0x90, 0xb5, 0x00,
0xbf, 0xdc, 0x19, 0x99, 0xb4, 0x12, 0x85, 0x25,
0x3a, 0x49, 0x70, 0x63, 0x2f, 0xfc, 0xbc, 0xca,
0x38, 0x2f, 0x7a, 0x0e, 0x78, 0x8d, 0x7b, 0x87,
}
ok := rsa.unsafe_decrypt_tls_pms(&priv_key, test_ciphertext)
if !testing.expectf(
t,
ok == 1,
"rsa/tls_pms: decryption failed: %x",
test_ciphertext[:48],
) {
return
}
if !testing.expectf(
t,
bytes.equal(test_ciphertext[:48], TEST_TLS_PMS),
"rsa/tls_pms: unexpected plaintext: %x",
test_ciphertext[:48],
) {
return
}
}

View File

@@ -51,6 +51,45 @@ import "core:testing"
// - pbkdf2_hmacsha256_test.json
// - pbkdf2_hmacsha384_test.json
// - pbkdf2_hmacsha512_test.json
// - crypto/rsa
// - rsa_pkcs1_1024_sig_gen_test.json
// - rsa_pkcs1_1536_sig_gen_test.json
// - rsa_pkcs1_2048_sig_gen_test.json
// - rsa_pkcs1_3072_sig_gen_test.json
// - rsa_pkcs1_4096_sig_gen_test.json
// - rsa_pss_2048_sha1_mgf1_20_test.json
// - rsa_pss_2048_sha256_mgf1_0_test.json
// - rsa_pss_2048_sha256_mgf1_32_test.json
// - rsa_pss_2048_sha256_mgf1sha1_20_test.json
// - rsa_pss_2048_sha384_mgf1_48_test.json
// - rsa_pss_2048_sha512_256_mgf1_32_test.json
// - rsa_pss_3072_sha256_mgf1_32_test.json
// - rsa_pss_4096_sha256_mgf1_32_test.json
// - rsa_pss_4096_sha384_mgf1_48_test.json
// - rsa_pss_4096_sha512_mgf1_32_test.json
// - rsa_pss_4096_sha512_mgf1_64_test.json
// - rsa_pss_misc_test.json
// - rsa_oaep_2048_sha1_mgf1sha1_test.json
// - rsa_oaep_2048_sha224_mgf1sha1_test.json
// - rsa_oaep_2048_sha224_mgf1sha224_test.json
// - rsa_oaep_2048_sha256_mgf1sha1_test.json
// - rsa_oaep_2048_sha256_mgf1sha256_test.json
// - rsa_oaep_2048_sha384_mgf1sha1_test.json
// - rsa_oaep_2048_sha384_mgf1sha384_test.json
// - rsa_oaep_2048_sha512_224_mgf1sha1_test.json
// - rsa_oaep_2048_sha512_mgf1sha1_test.json
// - rsa_oaep_2048_sha512_mgf1sha512_test.json
// - rsa_oaep_3072_sha256_mgf1sha1_test.json
// - rsa_oaep_3072_sha256_mgf1sha256_test.json
// - rsa_oaep_3072_sha512_256_mgf1sha1_test.json
// - rsa_oaep_3072_sha512_256_mgf1sha512_256_test.json
// - rsa_oaep_3072_sha512_mgf1sha1_test.json
// - rsa_oaep_3072_sha512_mgf1sha512_test.json
// - rsa_oaep_4096_sha256_mgf1sha1_test.json
// - rsa_oaep_4096_sha256_mgf1sha256_test.json
// - rsa_oaep_4096_sha512_mgf1sha1_test.json
// - rsa_oaep_4096_sha512_mgf1sha512_test.json
// - rsa_oaep_misc_test.json
// - crypto/siphash
// - siphash_1_3_test.json
// - siphash_2_4_test.json

View File

@@ -0,0 +1,541 @@
package test_wycheproof
import "core:encoding/hex"
import "core:fmt"
import "core:log"
import "core:mem"
import "core:os"
import "core:testing"
import "core:crypto/rsa"
import "../common"
@(test)
test_rsa_pkcs1_signature :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("rsa/pkcs1/signatures: starting")
files := []string {
"rsa_pkcs1_1024_sig_gen_test.json",
"rsa_pkcs1_1536_sig_gen_test.json",
"rsa_pkcs1_2048_sig_gen_test.json",
"rsa_pkcs1_3072_sig_gen_test.json",
"rsa_pkcs1_4096_sig_gen_test.json",
}
for f in files {
mem.free_all()
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Rsa_Pkcs1_Sig_Test_Group)
load_ok := load(&test_vectors, fn)
if !testing.expectf(t, load_ok, "Unable to load {}", f) {
continue
}
testing.expectf(t, test_rsa_pkcs1_sig(t, &test_vectors), "RSA PKCS1 signature failed")
}
}
test_rsa_pkcs1_sig :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Rsa_Pkcs1_Sig_Test_Group)) -> bool {
JWK_KTY :: "RSA"
params_str := fmt.aprintf("RSA-%d/PKCS1/Signature", test_vectors.test_groups[0].key_size)
log.debugf("%s: starting", params_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, tg_id in test_vectors.test_groups {
hash_str := test_group.sha
hash_alg, _ := hash_name_to_algorithm(hash_str)
if hash_alg == .Invalid {
log.infof("%s: unsupported hash: %s", params_str, hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
priv_key: rsa.Private_Key
pub_key := &priv_key._pub_key
ok, have_priv: bool
if test_group.private_key_jwk.kty == JWK_KTY {
ok = jwk_to_private_key(&test_group.private_key_jwk, &priv_key)
have_priv = true
} else {
ok = rsa.public_key_set_bytes(
pub_key,
common.hexbytes_decode(test_group.private_key.modulus),
common.hexbytes_decode(test_group.private_key.public_exponent),
)
}
if !testing.expectf(t, ok, "%s/%d: invalid RSA key: %v", params_str, tg_id, test_group.private_key) {
num_ran += len(test_group.tests)
num_failed += len(test_group.tests)
continue
}
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"%s/%s/%d/%d: %s: %+v",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("%s/%s/%d/%d: %+v", params_str, hash_str, tg_id, test_vector.tc_id, test_vector.flags)
}
msg := common.hexbytes_decode(test_vector.msg)
sig := common.hexbytes_decode(test_vector.sig)
verify_ok := rsa.verify_pkcs1(pub_key, hash_alg, msg, sig)
if !testing.expectf(
t,
result_check(test_vector.result, verify_ok, false),
"%s/%s/%d/%d: verify failed: expected %s actual %v",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
test_vector.result,
verify_ok,
) {
num_failed += 1
continue
}
if have_priv && verify_ok {
sign_ok := rsa.sign_pkcs1(&priv_key, hash_alg, msg, sig)
if !testing.expectf(
t,
sign_ok,
"%s/%s/%d/%d: sign failed",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
) {
num_failed += 1
continue
}
if !testing.expectf(
t,
common.hexbytes_compare(test_vector.sig, sig),
"%s/%s/%d/%d: sign failed: expected %s actual %s",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
test_vector.sig,
hex.encode(sig),
) {
num_failed += 1
continue
}
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"%s: ran %d, passed %d, failed %d, skipped %d",
params_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(test)
test_rsa_pss_signature :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("rsa/pss/signatures: starting")
files := []string {
"rsa_pss_2048_sha1_mgf1_20_test.json",
"rsa_pss_2048_sha256_mgf1_0_test.json",
"rsa_pss_2048_sha256_mgf1_32_test.json",
"rsa_pss_2048_sha256_mgf1sha1_20_test.json",
"rsa_pss_2048_sha384_mgf1_48_test.json",
"rsa_pss_2048_sha512_256_mgf1_32_test.json",
"rsa_pss_3072_sha256_mgf1_32_test.json",
"rsa_pss_4096_sha256_mgf1_32_test.json",
"rsa_pss_4096_sha384_mgf1_48_test.json",
"rsa_pss_4096_sha512_mgf1_32_test.json",
"rsa_pss_4096_sha512_mgf1_64_test.json",
"rsa_pss_misc_test.json",
// These tests include the MGF1 parameters in the public key,
// which we do not support with our existing API.
//
// "rsa_pss_2048_sha1_mgf1_20_params_test.json",
// "rsa_pss_2048_sha256_mgf1_0_params_test.json",
// "rsa_pss_2048_sha256_mgf1_32_params_test.json",
// "rsa_pss_2048_sha512_mgf1sha256_32_params_test.json",
// "rsa_pss_3072_sha256_mgf1_32_params_test.json",
// "rsa_pss_4096_sha512_mgf1_32_params_test.json",
// "rsa_pss_4096_sha512_mgf1_64_params_test.json",
// "rsa_pss_misc_params_test.json",
// Unsupported hash algorithm:
// "rsa_pss_2048_sha512_224_mgf1_28_test.json",
}
for f in files {
mem.free_all()
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Rsa_Pss_Sig_Test_Group)
load_ok := load(&test_vectors, fn)
if !testing.expectf(t, load_ok, "Unable to load {}", f) {
continue
}
testing.expectf(t, test_rsa_pss_sig(t, &test_vectors), "RSA PSS signature failed")
}
}
test_rsa_pss_sig :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Rsa_Pss_Sig_Test_Group)) -> bool {
MGF1 :: "MGF1"
params_str := fmt.aprintf("RSA-%d/PSS/Signature", test_vectors.test_groups[0].key_size)
log.debugf("%s: starting", params_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, tg_id in test_vectors.test_groups {
if test_group.mgf != MGF1 {
log.infof("%s: unsupported MGF: %s", params_str, test_group.mgf)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_str := test_group.sha
hash_alg, _ := hash_name_to_algorithm(hash_str)
if hash_alg == .Invalid {
log.infof("%s: unsupported hash: %s", params_str, hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
mgf_hash_str := test_group.mfg_sha
mgf_hash_alg, _ := hash_name_to_algorithm(mgf_hash_str)
if mgf_hash_alg == .Invalid {
log.infof("%s: unsupported MGF hash: %s", params_str, mgf_hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_params_str := fmt.aprintf("%s/%s(%s)", hash_str, test_group.mgf, mgf_hash_str)
pub_key: rsa.Public_Key
ok := rsa.public_key_set_bytes(
&pub_key,
common.hexbytes_decode(test_group.public_key.modulus),
common.hexbytes_decode(test_group.public_key.public_exponent),
)
if !testing.expectf(t, ok, "%s/%d: invalid RSA key: %v", params_str, tg_id, test_group.public_key) {
num_ran += len(test_group.tests)
num_failed += len(test_group.tests)
continue
}
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"%s/%s/%d/%d: %s: %+v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("%s/%s/%d/%d: %+v", params_str, hash_str, tg_id, test_vector.tc_id, test_vector.flags)
}
msg := common.hexbytes_decode(test_vector.msg)
sig := common.hexbytes_decode(test_vector.sig)
verify_ok := rsa.verify_pss(
&pub_key,
hash_alg,
test_group.s_len,
msg,
sig,
false,
mgf_hash_alg,
)
if !testing.expectf(
t,
result_check(test_vector.result, verify_ok, false),
"%s/%s/%d/%d: verify failed: expected %s actual %v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
test_vector.result,
verify_ok,
) {
num_failed += 1
continue
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"%s: ran %d, passed %d, failed %d, skipped %d",
params_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(test)
test_rsa_oaep_decryption :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("rsa/oaep/decryption: starting")
files := []string {
"rsa_oaep_2048_sha1_mgf1sha1_test.json",
"rsa_oaep_2048_sha224_mgf1sha1_test.json",
"rsa_oaep_2048_sha224_mgf1sha224_test.json",
"rsa_oaep_2048_sha256_mgf1sha1_test.json",
"rsa_oaep_2048_sha256_mgf1sha256_test.json",
"rsa_oaep_2048_sha384_mgf1sha1_test.json",
"rsa_oaep_2048_sha384_mgf1sha384_test.json",
"rsa_oaep_2048_sha512_224_mgf1sha1_test.json",
"rsa_oaep_2048_sha512_mgf1sha1_test.json",
"rsa_oaep_2048_sha512_mgf1sha512_test.json",
"rsa_oaep_3072_sha256_mgf1sha1_test.json",
"rsa_oaep_3072_sha256_mgf1sha256_test.json",
"rsa_oaep_3072_sha512_256_mgf1sha1_test.json",
"rsa_oaep_3072_sha512_256_mgf1sha512_256_test.json",
"rsa_oaep_3072_sha512_mgf1sha1_test.json",
"rsa_oaep_3072_sha512_mgf1sha512_test.json",
"rsa_oaep_4096_sha256_mgf1sha1_test.json",
"rsa_oaep_4096_sha256_mgf1sha256_test.json",
"rsa_oaep_4096_sha512_mgf1sha1_test.json",
"rsa_oaep_4096_sha512_mgf1sha512_test.json",
"rsa_oaep_misc_test.json",
// Unsupported hash algorithm:
// "rsa_oaep_2048_sha512_224_mgf1sha512_224_test.json",
}
for f in files {
mem.free_all()
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Rsa_Oaep_Dec_Test_Group)
load_ok := load(&test_vectors, fn)
if !testing.expectf(t, load_ok, "Unable to load {}", f) {
continue
}
testing.expectf(t, test_rsa_oaep_dec(t, &test_vectors), "RSA OAEP decryption failed")
}
}
test_rsa_oaep_dec :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Rsa_Oaep_Dec_Test_Group)) -> bool {
MGF1 :: "MGF1"
params_str := fmt.aprintf("RSA-%d/OAEP/Decryption", test_vectors.test_groups[0].key_size)
log.debugf("%s: starting", params_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, tg_id in test_vectors.test_groups {
if test_group.key_size > rsa.MODULUS_MAX_SIZE {
log.infof("%s: unsupported key size: %d", params_str, test_group.key_size)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
if test_group.mgf != MGF1 {
log.infof("%s: unsupported MGF: %s", params_str, test_group.mgf)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_str := test_group.sha
hash_alg, _ := hash_name_to_algorithm(hash_str)
if hash_alg == .Invalid {
log.infof("%s: unsupported hash: %s", params_str, hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
mgf_hash_str := test_group.mfg_sha
mgf_hash_alg, _ := hash_name_to_algorithm(mgf_hash_str)
if mgf_hash_alg == .Invalid {
log.infof("%s: unsupported MGF hash: %s", params_str, mgf_hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_params_str := fmt.aprintf("%s/%s(%s)", hash_str, test_group.mgf, mgf_hash_str)
priv_key: rsa.Private_Key
ok := json_to_private_key(&test_group.private_key, &priv_key)
if !testing.expectf(t, ok, "%s/%d: invalid RSA key: %v", params_str, tg_id, test_group.private_key) {
num_ran += len(test_group.tests)
num_failed += len(test_group.tests)
continue
}
dst: [rsa.MODULUS_MAX_SIZE >> 3]byte = ---
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"%s/%s/%d/%d: %s: %+v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("%s/%s/%d/%d: %+v", params_str, hash_str, tg_id, test_vector.tc_id, test_vector.flags)
}
label := common.hexbytes_decode(test_vector.label)
ct := common.hexbytes_decode(test_vector.ct)
pt, decrypt_ok := rsa.decrypt_oaep(
&priv_key,
hash_alg,
ct,
dst[:],
label,
mgf_hash_alg,
)
if !testing.expectf(
t,
result_check(test_vector.result, decrypt_ok, false),
"%s/%s/%d/%d: decrypt failed: expected %s actual %v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
test_vector.result,
decrypt_ok,
) {
num_failed += 1
continue
}
if decrypt_ok {
if !testing.expectf(
t,
common.hexbytes_compare(test_vector.msg, pt),
"%s/%s/%d/%d: decrypt failed: expected %s actual %s",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
test_vector.msg,
hex.encode(pt),
) {
num_failed += 1
continue
}
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"%s: ran %d, passed %d, failed %d, skipped %d",
params_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(private="file")
jwk_to_private_key :: proc(jwk: ^Rsa_Jwk_Private_Key, priv_key: ^rsa.Private_Key) -> bool {
return rsa.private_key_set_bytes(
priv_key,
common.jwkbytes_decode(jwk.n),
common.jwkbytes_decode(jwk.e),
common.jwkbytes_decode(jwk.d),
common.jwkbytes_decode(jwk.p),
common.jwkbytes_decode(jwk.q),
common.jwkbytes_decode(jwk.dp),
common.jwkbytes_decode(jwk.dq),
common.jwkbytes_decode(jwk.qi),
)
}
@(private="file")
json_to_private_key :: proc(json: ^Rsa_Private_Key, priv_key: ^rsa.Private_Key) -> bool {
return rsa.private_key_set_bytes(
priv_key,
common.hexbytes_decode(json.modulus),
common.hexbytes_decode(json.public_exponent),
common.hexbytes_decode(json.private_exponent),
common.hexbytes_decode(json.prime1),
common.hexbytes_decode(json.prime2),
common.hexbytes_decode(json.exponent1),
common.hexbytes_decode(json.exponent2),
common.hexbytes_decode(json.coefficient),
)
}

View File

@@ -150,10 +150,10 @@ Eddsa_Key :: struct {
}
Eddsa_Jwk :: struct {
kid: string `json:"kid"`,
crv: string `json:"crv"`,
kty: string `json:"kty"`,
x: string `json:"x"`,
kid: string `json:"kid"`,
crv: string `json:"crv"`,
kty: string `json:"kty"`,
x: common.Jwk_Bytes `json:"x"`,
}
Ecdsa_Key :: struct {
@@ -241,3 +241,96 @@ Mldsa_Test_Vector :: struct {
result: Result `json:"result"`,
flags: []string `json:"flags"`,
}
Rsa_Pkcs1_Sig_Test_Group :: struct {
private_key: Rsa_Private_Key `json:"privateKey"`,
key_asn: common.Hex_Bytes `json:"keyAsn"`,
key_der: common.Hex_Bytes `json:"keyDer"`,
key_pem: string `json:"keyPem"`,
key_size: int `json:"keySize"`,
private_key_jwk: Rsa_Jwk_Private_Key `json:"privateKeyJwk"`,
private_key_pem: string `json:"privateKeyPem"`,
private_key_pkcs8: common.Hex_Bytes `json:"privateKeyPkcs8"`,
sha: string `json:"sha"`,
type: string `json:"type"`,
source: Test_Group_Source `json:"source"`,
tests: []Rsa_Sig_Test_Vector `json:"tests"`,
}
Rsa_Sig_Test_Vector :: struct {
tc_id: int `json:"tcId"`,
comment: string `json:"comment"`,
msg: common.Hex_Bytes `json:"msg"`,
sig: common.Hex_Bytes `json:"sig"`,
result: Result `json:"result"`,
flags: []string `json:"flags"`,
}
Rsa_Pss_Sig_Test_Group :: struct {
public_key: Rsa_Public_Key `json:"publicKey"`,
public_key_asn: common.Hex_Bytes `json:"publicKeyAsn"`,
public_key_der: common.Hex_Bytes `json:"publicKeyDer"`,
public_key_pem: string `json:"publicKeyPem"`,
key_size: int `json:"keySize"`,
sha: string `json:"sha"`,
mgf: string `json:"mgf"`,
mfg_sha: string `json:"mgfSha"`,
s_len: int `json:"sLen"`,
type: string `json:"type"`,
source: Test_Group_Source `json:"source"`,
tests: []Rsa_Sig_Test_Vector `json:"tests"`,
}
Rsa_Oaep_Dec_Test_Group :: struct {
key_size: int `json:"keySize"`,
private_key: Rsa_Private_Key `json:"privateKey"`,
private_key_jwk: Rsa_Jwk_Private_Key `json:"privateKeyJwk"`,
private_key_pem: string `json:"privateKeyPem"`,
private_key_pkcs8: common.Hex_Bytes `json:"privateKeyPkcs8"`,
sha: string `json:"sha"`,
mgf: string `json:"mgf"`,
mfg_sha: string `json:"mgfSha"`,
type: string `json:"type"`,
source: Test_Group_Source `json:"source"`,
tests: []Rsa_Oaep_Test_Vector `json:"tests"`,
}
Rsa_Oaep_Test_Vector :: struct {
tc_id: int `json:"tcId"`,
comment: string `json:"comment"`,
msg: common.Hex_Bytes `json:"msg"`,
ct: common.Hex_Bytes `json:"ct"`,
label: common.Hex_Bytes `json:"label"`,
result: Result `json:"result"`,
flags: []string `json:"flags"`,
}
Rsa_Public_Key :: struct {
modulus: common.Hex_Bytes `json:"modulus"`,
public_exponent: common.Hex_Bytes `json:"publicExponent"`,
}
Rsa_Private_Key :: struct {
modulus: common.Hex_Bytes `json:"modulus"`,
private_exponent: common.Hex_Bytes `json:"privateExponent"`,
public_exponent: common.Hex_Bytes `json:"publicExponent"`,
prime1: common.Hex_Bytes `json:"prime1"`,
prime2: common.Hex_Bytes `json:"prime2"`,
exponent1: common.Hex_Bytes `json:"exponent1"`,
exponent2: common.Hex_Bytes `json:"exponent2"`,
coefficient: common.Hex_Bytes `json:"coefficient"`,
}
Rsa_Jwk_Private_Key :: struct {
akg: string `json:"alg"`,
kid: string `json:"kid"`,
kty: string `json:"kty"`,
d: common.Jwk_Bytes `json:"d"`,
dp: common.Jwk_Bytes `json:"dp"`,
dq: common.Jwk_Bytes `json:"dq"`,
e: common.Jwk_Bytes `json:"e"`,
n: common.Jwk_Bytes `json:"n"`,
p: common.Jwk_Bytes `json:"p"`,
q: common.Jwk_Bytes `json:"q"`,
qi: common.Jwk_Bytes `json:"qi"`,
}

View File

@@ -2,6 +2,7 @@
package tests_core
@(require) import "crypto"
@(require) import "crypto/bigint"
@(require) import "hash"
@(require) import "image"
@(require) import "math/big"

View File

@@ -37,6 +37,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused
..\..\..\odin test ..\test_pr_6476.odin %COMMON% || exit /b
..\..\..\odin check ..\test_issue_6484.odin -no-entry-point %COMMON% || exit /b
..\..\..\odin check ..\test_issue_6874.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b
..\..\..\odin check ..\test_issue_6979.odin -no-entry-point %COMMON% || exit /b
@echo off

View File

@@ -79,6 +79,7 @@ else
echo "SUCCESSFUL 0/1"
exit 1
fi
$ODIN check ../test_issue_6979.odin -no-entry-point $COMMON
set +x

View File

@@ -0,0 +1,7 @@
// Tests issue https://github.com/odin-lang/Odin/issues/6979
package test_issues
error :: proc() -> typeid {
data :: struct{type: typeid}{int}
return data.type
}

View File

@@ -389,7 +389,7 @@ foreign lib {
World_SetUserData :: proc(worldId: WorldId, userData: rawptr) ---
// Get the user data pointer.
World_GetUserData :: proc(worldId: WorldId) ---
World_GetUserData :: proc(worldId: WorldId) -> rawptr ---
// Set the friction callback. Passing NULL resets to default.
World_SetFrictionCallback :: proc(worldId: WorldId, callback: FrictionCallback) ---
@@ -638,7 +638,7 @@ foreign lib {
Body_SetUserData :: proc(bodyId: BodyId, userData: rawptr) ---
// Get the user data stored in a body
Body_GetUserData :: proc(bodyId: BodyId) ---
Body_GetUserData :: proc(bodyId: BodyId) -> rawptr ---
// Get the world position of a body. This is the location of the body origin.
Body_GetPosition :: proc(bodyId: BodyId) -> Pos ---
@@ -971,7 +971,7 @@ foreign lib {
// Get the user data for a shape. This is useful when you get a shape id
// from an event or query.
Shape_GetUserData :: proc(shapeId: ShapeId) ---
Shape_GetUserData :: proc(shapeId: ShapeId) -> rawptr ---
// Set the mass density of a shape, usually in kg/m^3.
// This will optionally update the mass properties on the parent body.
@@ -1178,7 +1178,7 @@ foreign lib {
Joint_SetUserData :: proc(jointId: JointId, userData: rawptr) ---
// Get the user data on a joint
Joint_GetUserData :: proc(jointId: JointId) ---
Joint_GetUserData :: proc(jointId: JointId) -> rawptr ---
// Wake the bodies connect to this joint
Joint_WakeBodies :: proc(jointId: JointId) ---

View File

@@ -377,7 +377,7 @@ Model :: struct #align(align_of(uintptr)) {
materialCount: c.int, // Number of materials
meshes: [^]Mesh, // Meshes array
materials: [^]Material, // Materials array
meshMaterial: ^c.int, // Mesh material number
meshMaterial: [^]c.int, // Mesh material number
// Animation data
skeleton: ModelSkeleton, // Skeleton for animation