mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-14 20:00:31 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
904
core/crypto/_bigint/i31.odin
Normal file
904
core/crypto/_bigint/i31.odin
Normal 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)
|
||||
}
|
||||
}
|
||||
361
core/crypto/_bigint/i62.odin
Normal file
361
core/crypto/_bigint/i62.odin
Normal 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)
|
||||
}
|
||||
265
core/crypto/_bigint/i62_primes.odin
Normal file
265
core/crypto/_bigint/i62_primes.odin
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
7
core/crypto/rsa/doc.odin
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
RSA (Rivest–Shamir–Adleman) cryptosystem.
|
||||
|
||||
See:
|
||||
- [[ https://www.rfc-editor.org/info/rfc8017/ ]]
|
||||
*/
|
||||
package rsa
|
||||
444
core/crypto/rsa/rsa.odin
Normal file
444
core/crypto/rsa/rsa.odin
Normal 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)
|
||||
}
|
||||
}
|
||||
197
core/crypto/rsa/rsa_dec_oaep.odin
Normal file
197
core/crypto/rsa/rsa_dec_oaep.odin
Normal 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
|
||||
}
|
||||
55
core/crypto/rsa/rsa_dec_tls_pms.odin
Normal file
55
core/crypto/rsa/rsa_dec_tls_pms.odin
Normal 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
|
||||
}
|
||||
105
core/crypto/rsa/rsa_enc_oaep.odin
Normal file
105
core/crypto/rsa/rsa_enc_oaep.odin
Normal 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
|
||||
}
|
||||
110
core/crypto/rsa/rsa_int.odin
Normal file
110
core/crypto/rsa/rsa_int.odin
Normal 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
|
||||
}
|
||||
367
core/crypto/rsa/rsa_keygen.odin
Normal file
367
core/crypto/rsa/rsa_keygen.odin
Normal 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
|
||||
}
|
||||
49
core/crypto/rsa/rsa_mgf1.odin
Normal file
49
core/crypto/rsa/rsa_mgf1.odin
Normal 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]
|
||||
}
|
||||
}
|
||||
}
|
||||
165
core/crypto/rsa/rsa_modpow_priv.odin
Normal file
165
core/crypto/rsa/rsa_modpow_priv.odin
Normal 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
|
||||
}
|
||||
89
core/crypto/rsa/rsa_modpow_pub.odin
Normal file
89
core/crypto/rsa/rsa_modpow_pub.odin
Normal 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
|
||||
}
|
||||
233
core/crypto/rsa/rsa_sig_pkcs1.odin
Normal file
233
core/crypto/rsa/rsa_sig_pkcs1.odin
Normal 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)
|
||||
}
|
||||
293
core/crypto/rsa/rsa_sig_pss.odin
Normal file
293
core/crypto/rsa/rsa_sig_pss.odin
Normal 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
|
||||
}
|
||||
180
core/crypto/rsa/rsa_test_key.odin
Normal file
180
core/crypto/rsa/rsa_test_key.odin
Normal 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,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
160
tests/benchmark/crypto/benchmark_rsa.odin
Normal file
160
tests/benchmark/crypto/benchmark_rsa.odin
Normal 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),
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
310
tests/core/crypto/bigint/int.odin
Normal file
310
tests/core/crypto/bigint/int.odin
Normal 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)
|
||||
}
|
||||
}
|
||||
577
tests/core/crypto/bigint/test_vectors.odin
Normal file
577
tests/core/crypto/bigint/test_vectors.odin
Normal 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},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
419
tests/core/crypto/test_core_crypto_rsa.odin
Normal file
419
tests/core/crypto/test_core_crypto_rsa.odin
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
541
tests/core/crypto/wycheproof/rsa.odin
Normal file
541
tests/core/crypto/wycheproof/rsa.odin
Normal 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),
|
||||
)
|
||||
}
|
||||
@@ -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"`,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package tests_core
|
||||
|
||||
@(require) import "crypto"
|
||||
@(require) import "crypto/bigint"
|
||||
@(require) import "hash"
|
||||
@(require) import "image"
|
||||
@(require) import "math/big"
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ else
|
||||
echo "SUCCESSFUL 0/1"
|
||||
exit 1
|
||||
fi
|
||||
$ODIN check ../test_issue_6979.odin -no-entry-point $COMMON
|
||||
|
||||
set +x
|
||||
|
||||
|
||||
7
tests/issues/test_issue_6979.odin
Normal file
7
tests/issues/test_issue_6979.odin
Normal 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
|
||||
}
|
||||
8
vendor/box3d/box3d.odin
vendored
8
vendor/box3d/box3d.odin
vendored
@@ -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) ---
|
||||
|
||||
2
vendor/raylib/raylib.odin
vendored
2
vendor/raylib/raylib.odin
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user