Merge branch 'master' into bill/rexcode

This commit is contained in:
gingerBill committed 2026-07-13 12:10:57 +01:00
commit 2bbb04e4f6
256 files changed
+83377 -6086

No files matched your search

+19
View File
@@ -14,3 +14,22 @@ vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll filter=lfs
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib filter=lfs diff=lfs merge=lfs -text
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib filter=lfs diff=lfs merge=lfs -text
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.pdb filter=lfs diff=lfs merge=lfs -text
vendor/raylib/linux/libraygui.so filter=lfs diff=lfs merge=lfs -text
vendor/raylib/linux/libraygui.a filter=lfs diff=lfs merge=lfs -text
vendor/raylib/linux/libraylib.a filter=lfs diff=lfs merge=lfs -text
vendor/raylib/linux/libraylib.so.600 filter=lfs diff=lfs merge=lfs -text
vendor/raylib/linux-arm64/libraylib.a filter=lfs diff=lfs merge=lfs -text
vendor/raylib/linux-arm64/libraylib.so.600 filter=lfs diff=lfs merge=lfs -text
vendor/raylib/macos/libraygui-arm64.dylib filter=lfs diff=lfs merge=lfs -text
vendor/raylib/macos/libraygui.dylib filter=lfs diff=lfs merge=lfs -text
vendor/raylib/macos/libraylib.a filter=lfs diff=lfs merge=lfs -text
vendor/raylib/macos/libraylib.600.dylib filter=lfs diff=lfs merge=lfs -text
vendor/raylib/wasm/libraylib.web.a filter=lfs diff=lfs merge=lfs -text
vendor/raylib/windows/raylib.dll filter=lfs diff=lfs merge=lfs -text
vendor/raylib/windows/raylib.lib filter=lfs diff=lfs merge=lfs -text
vendor/raylib/windows/raylibdll.lib filter=lfs diff=lfs merge=lfs -text
vendor/raylib/windows/raygui.dll filter=lfs diff=lfs merge=lfs -text
vendor/raylib/windows/raygui.lib filter=lfs diff=lfs merge=lfs -text
vendor/raylib/windows/rayguidll.lib filter=lfs diff=lfs merge=lfs -text
vendor/box3d/lib/linux-amd64/libbox3d.a filter=lfs diff=lfs merge=lfs -text
vendor/box3d/lib/darwin/libbox3d.a filter=lfs diff=lfs merge=lfs -text
-1
View File
@@ -277,7 +277,6 @@ odin
*.bin
demo.bin
libLLVM*.so*
*.a
# core:rexcode commits its generated encode/decode tables: each arch's tablegen
# emits human-readable Odin, serialized to tables/*.bin, which the library
+3 -3
View File
@@ -1277,7 +1277,7 @@ assign_at_elem_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[d
array[index] = arg
ok = true
} else {
resize(array, index+1, loc) or_return
resize(array, index+1) or_return
array[index] = arg
ok = true
}
@@ -1296,7 +1296,7 @@ assign_at_elems_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[
copy(array[index:], args)
ok = true
} else {
resize(array, new_size, loc) or_return
resize(array, new_size) or_return
copy(array[index:], args)
ok = true
}
@@ -1314,7 +1314,7 @@ assign_at_elem_string_fixed_capacity_dynamic_array :: proc "contextless" (array:
copy(array[index:], arg)
ok = true
} else {
resize(array, new_size, loc) or_return
resize(array, new_size) or_return
copy(array[index:], arg)
ok = true
}
+2 -2
View File
@@ -21,7 +21,7 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
where /Q git.exe || goto skip_git_hash
if not exist .git\ goto skip_git_hash
for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m-%%d" --no-patch --no-notes HEAD') do (
for /f "tokens=1,2" %%i IN ('git -c "log.showSignature=false" show "--pretty=%%cd %%h" "--date=format:%%Y-%%m-%%d" --no-patch --no-notes HEAD') do (
set CURR_DATE_TIME=%%i
set GIT_SHA=%%j
)
@@ -138,4 +138,4 @@ if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat
del *.obj > NUL 2> NUL
:end_of_build
:end_of_build
+26 -7
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env sh
set -eu
SUPPORTED_LLVM_VERSIONS="22 21 20 19 18 17"
SUGGESTED_LLVM_VERSION="22"
MINIMUM_LLVM_VERSION="17"
: ${CPPFLAGS=}
: ${CXXFLAGS=}
: ${LDFLAGS=}
@@ -13,8 +17,10 @@ OS_ARCH="$(uname -m)"
OS_NAME="$(uname -s)"
if [ -d ".git" ] && [ -n "$(command -v git)" ]; then
GIT_SHA=$(git show --pretty='%h' --no-patch --no-notes HEAD)
GIT_DATE=$(git show "--pretty=%cd" "--date=format:%Y-%m" --no-patch --no-notes HEAD)
# Counter the user's git config to show the signature in logs.
gitnosig="-c log.showSignature=false"
GIT_SHA=$(git $gitnosig show --pretty='%h' --no-patch --no-notes HEAD)
GIT_DATE=$(git $gitnosig show "--pretty=%cd" "--date=format:%Y-%m" --no-patch --no-notes HEAD)
CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
else
GIT_DATE=$(date +"%Y-%m")
@@ -26,8 +32,6 @@ error() {
exit 1
}
SUPPORTED_LLVM_VERSIONS="22 21 20 19 18 17 14"
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
for V in $SUPPORTED_LLVM_VERSIONS; do
@@ -61,7 +65,22 @@ if [ -z "$LLVM_CONFIG" ]; then
done
if [ -z "$LLVM_CONFIG" ]; then
error "No supported llvm-config command found. Set LLVM_CONFIG to proceed."
if [ -f "/etc/os-release" ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian|linuxmint|pop|zorin|kali|elementary|raspbian)
echo "ERROR: No supported llvm-config command found. Set LLVM_CONFIG to proceed."
echo "We suggest installing LLVM $SUGGESTED_LLVM_VERSION from https://apt.llvm.org"
exit 1
;;
fedora|centos|rocky|almalinux|rhel|amzn|ol|centos-stream)
echo "ERROR: No supported llvm-config command found. Set LLVM_CONFIG to proceed."
echo "We suggest installing LLVM $SUGGESTED_LLVM_VERSION via COPR, e.g. dnf copr enable fedora-llvm-team/llvm-snapshots"
exit 1
;;
esac
fi
error "No supported llvm-config command found. Set LLVM_CONFIG to proceed. We suggest LLVM $SUGGESTED_LLVM_VERSION."
fi
fi
@@ -78,8 +97,8 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
if [ $LLVM_VERSION_MAJOR -lt 14 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 22 ]; then
error "Invalid LLVM version $LLVM_VERSION: must be 14, 17, 18, 19, 20, 21 or 22"
if [ $LLVM_VERSION_MAJOR -lt $MINIMUM_LLVM_VERSION ]; then
error "Unsupported LLVM version $LLVM_VERSION: must be 17, 18, 19, 20, 21 or 22"
fi
case "$OS_NAME" in
+17 -17
View File
@@ -60,13 +60,13 @@ Iterator :: struct($Value: typeid) {
_called_next: bool,
}
// init initializes a tree.
// `init` initializes a tree.
init :: proc {
init_ordered,
init_cmp,
}
// init_cmp initializes a tree.
// `init_cmp` initializes a tree.
init_cmp :: proc(
t: ^$T/Tree($Value),
cmp_fn: proc(a, b: Value) -> Ordering,
@@ -78,7 +78,7 @@ init_cmp :: proc(
t._size = 0
}
// init_ordered initializes a tree containing ordered items, with
// `init_ordered` initializes a tree containing ordered items, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(
t: ^$T/Tree($Value),
@@ -87,7 +87,7 @@ init_ordered :: proc(
init_cmp(t, slice.cmp_proc(Value), node_allocator)
}
// destroy de-initializes a tree.
// `destroy` de-initializes a tree.
destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
iter := iterator(t, Direction.Forward)
for _ in iterator_next(&iter) {
@@ -95,24 +95,24 @@ destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
}
}
// len returns the number of elements in the tree.
// `len` returns the number of elements in the tree.
len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
return t._size
}
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
// `first` returns the first node in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Backward)
}
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
// `last` returns the last element in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Forward)
}
// find finds the value in the tree, and returns the corresponding
// `find` finds the value in the tree, and returns the corresponding
// node or nil if and only if (⟺) the value is not present.
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
cur := t._root
@@ -130,7 +130,7 @@ find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
return cur
}
// find_or_insert attempts to insert the value into the tree, and returns
// `find_or_insert` attempts to insert the value into the tree, and returns
// the node, a boolean indicating if the value was inserted, and the
// node allocator error if relevant. If the value is already
// present, the existing node is returned un-altered.
@@ -168,7 +168,7 @@ find_or_insert :: proc(
return
}
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
// `remove` removes a node or value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove :: proc {
@@ -176,7 +176,7 @@ remove :: proc {
remove_node,
}
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
// `remove_value` removes a value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
@@ -187,7 +187,7 @@ remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = t
return remove_node(t, n, call_on_remove)
}
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
// `remove_node` removes a node from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
@@ -249,7 +249,7 @@ remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: boo
return true
}
// iterator returns a tree iterator in the specified direction.
// `iterator` returns a tree iterator in the specified direction.
iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
it: Iterator(Value)
it._tree = transmute(^Tree(Value))t
@@ -260,7 +260,7 @@ iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> It
return it
}
// iterator_from_pos returns a tree iterator in the specified direction,
// `iterator_from_pos` returns a tree iterator in the specified direction,
// spanning the range [pos, last] (inclusive).
iterator_from_pos :: proc "contextless" (
t: ^$T/Tree($Value),
@@ -280,14 +280,14 @@ iterator_from_pos :: proc "contextless" (
return it
}
// iterator_get returns the node currently pointed to by the iterator,
// `iterator_get` returns the node currently pointed to by the iterator,
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
// of the tree has been reached.
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
return it._cur
}
// iterator_remove removes the node currently pointed to by the iterator,
// `iterator_remove` removes the node currently pointed to by the iterator,
// and returns true if and only if (⟺) the removal was successful. Semantics are the
// same as the Tree remove.
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
@@ -303,7 +303,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -
return ok
}
// iterator_next advances the iterator and returns the (node, true) or
// `iterator_next` advances the iterator and returns the (node, true) or
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
//
// Note: The first call to iterator_next will return the first node instead
@@ -1,7 +1,6 @@
package container_handle_map
import "base:runtime"
import "base:builtin"
import "base:intrinsics"
@(require) import "core:container/xar"
@@ -139,4 +138,4 @@ dynamic_iterate :: proc "contextless" (it: ^$DHI/Dynamic_Handle_Map_Iterator($D/
}
it.index = 0
return
}
}
+1 -1
View File
@@ -406,7 +406,7 @@ push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T, loc := #caller_location) -
}
/*
Consume `n` elements from the back of the queue.
Consume `n` elements from the front of the queue.
This will raise a bounds checking error if the queue does not have enough elements.
*/
+44 -18
View File
@@ -63,13 +63,13 @@ Iterator :: struct($Key: typeid, $Value: typeid) {
_called_next: bool,
}
// init initializes a tree.
// `init` initializes a tree.
init :: proc {
init_ordered,
init_cmp,
}
// init_cmp initializes a tree.
// `init_cmp` initializes a tree.
init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering, node_allocator := context.allocator) {
t._root = nil
t._node_allocator = node_allocator
@@ -77,13 +77,13 @@ init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering,
t._size = 0
}
// init_ordered initializes a tree containing ordered keys, with
// `init_ordered` initializes a tree containing ordered keys, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered(Key) {
init_cmp(t, slice.cmp_proc(Key), node_allocator)
}
// destroy de-initializes a tree.
// `destroy` de-initializes a tree.
destroy :: proc(t: ^$T/Tree($Key, $Value), call_on_remove: bool = true) {
iter := iterator(t, .Forward)
for _ in iterator_next(&iter) {
@@ -95,19 +95,19 @@ len :: proc "contextless" (t: $T/Tree($Key, $Value)) -> (node_count: int) {
return t._size
}
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
// `first` returns the first node in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
first :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
return tree_first_or_last_in_order(t, Direction.Backward)
}
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
// `last` returns the last element in the tree (in-order) or nil if and only if (⟺)
// the tree is empty.
last :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
return tree_first_or_last_in_order(t, Direction.Forward)
}
// find finds the key in the tree, and returns the corresponding node, or nil if and only if (⟺) the value is not present.
// `find` finds the key in the tree, and returns the corresponding node, or nil if and only if (⟺) the value is not present.
find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
node = t._root
for node != nil {
@@ -120,7 +120,7 @@ find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
return node
}
// find_value finds the key in the tree, and returns the corresponding value, or nil if and only if (⟺) the value is not present.
// `find_value` finds the key in the tree, and returns the corresponding value, or nil if and only if (⟺) the value is not present.
find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
if n := find(t, key); n != nil {
return n.value, true
@@ -128,10 +128,36 @@ find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: boo
return
}
// find_or_insert attempts to insert the key-value pair into the tree, and returns
// `find_or_insert` attempts to insert the key-value pair into the tree, and returns
// the node, a boolean indicating if a new node was inserted, and the
// node allocator error if relevant. If the key is already present, the existing node is updated and returned.
// node allocator error if relevant. If the key is already present, the existing node is returned un-altered.
find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n: ^Node(Key, Value), inserted: bool, err: runtime.Allocator_Error) {
n_ptr := &t._root
for n_ptr^ != nil {
n = n_ptr^
switch t._cmp_fn(key, n.key) {
case .Less:
n_ptr = &n._left
case .Greater:
n_ptr = &n._right
case .Equal:
return
}
}
_parent := n
n = new_clone(Node(Key, Value){key=key, value=value, _parent=_parent, _color=.Red}, t._node_allocator) or_return
n_ptr^ = n
insert_case1(t, n)
t._size += 1
return n, true, nil
}
// `upsert` attempts to insert the key-value pair into the tree, and returns
// the node, a boolean indicating if a new node was inserted, and the
// node allocator error if relevant. If the key is already present, the existing node's value is updated.
upsert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n: ^Node(Key, Value), inserted: bool, err: runtime.Allocator_Error) {
n_ptr := &t._root
for n_ptr^ != nil {
n = n_ptr^
@@ -154,7 +180,7 @@ find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n:
return n, true, nil
}
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
// `remove` removes a node or value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove :: proc {
@@ -162,7 +188,7 @@ remove :: proc {
remove_node,
}
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
// `remove_value` removes a value from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's key + value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true) -> bool {
@@ -173,7 +199,7 @@ remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true)
return remove_node(t, n, call_on_remove)
}
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
// `remove_node` removes a node from the tree, and returns true if and only if (⟺) the
// removal was successful. While the node's key + value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_node :: proc(t: ^$T/Tree($Key, $Value), node: ^$N/Node(Key, Value), call_on_remove := true) -> (found: bool) {
@@ -207,7 +233,7 @@ remove_node :: proc(t: ^$T/Tree($Key, $Value), node: ^$N/Node(Key, Value), call_
return true
}
// iterator returns a tree iterator in the specified direction.
// `iterator` returns a tree iterator in the specified direction.
iterator :: proc "contextless" (t: ^$T/Tree($Key, $Value), direction: Direction) -> Iterator(Key, Value) {
it: Iterator(Key, Value)
it._tree = cast(^Tree(Key, Value))t
@@ -218,7 +244,7 @@ iterator :: proc "contextless" (t: ^$T/Tree($Key, $Value), direction: Direction)
return it
}
// iterator_from_pos returns a tree iterator in the specified direction,
// `iterator_from_pos` returns a tree iterator in the specified direction,
// spanning the range [pos, last] (inclusive).
iterator_from_pos :: proc "contextless" (t: ^$T/Tree($Key, $Value), pos: ^Node(Key, Value), direction: Direction) -> Iterator(Key, Value) {
it: Iterator(Key, Value)
@@ -234,14 +260,14 @@ iterator_from_pos :: proc "contextless" (t: ^$T/Tree($Key, $Value), pos: ^Node(K
return it
}
// iterator_get returns the node currently pointed to by the iterator,
// `iterator_get` returns the node currently pointed to by the iterator,
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
// of the tree has been reached.
iterator_get :: proc "contextless" (it: ^$I/Iterator($Key, $Value)) -> ^Node(Key, Value) {
return it._cur
}
// iterator_remove removes the node currently pointed to by the iterator,
// `iterator_remove` removes the node currently pointed to by the iterator,
// and returns true if and only if (⟺) the removal was successful. Semantics are the
// same as the Tree remove.
iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = true) -> bool {
@@ -257,7 +283,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = t
return ok
}
// iterator_next advances the iterator and returns the (node, true) or
// `iterator_next` advances the iterator and returns the (node, true) or
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
//
// Note: The first call to iterator_next will return the first node instead
+2 -2
View File
@@ -265,7 +265,7 @@ array_push_back_elem :: proc(x: ^$X/Array($T, $SHIFT), value: T, loc := #caller_
chunk_idx, elem_idx, chunk_cap := _meta_get(SHIFT, uint(x.len))
if x.chunks[chunk_idx] == nil {
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator) or_return
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator, loc) or_return
}
x.chunks[chunk_idx][elem_idx] = value
x.len += 1
@@ -328,7 +328,7 @@ array_push_back_elem_and_get_ptr :: proc(x: ^$X/Array($T, $SHIFT), value: T, loc
chunk_idx, elem_idx, chunk_cap := _meta_get(SHIFT, uint(x.len))
if x.chunks[chunk_idx] == nil {
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator) or_return
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator, loc) or_return
}
x.chunks[chunk_idx][elem_idx] = value
x.len += 1
+5 -4
View File
@@ -13,10 +13,11 @@ constant-time byte comparison.
- The crypto packages are not thread-safe.
- Best-effort is make to mitigate timing side-channels on reasonable
architectures. Architectures that are known to be unreasonable include
but are not limited to i386, i486, and WebAssembly.
but are not limited to i386, i486, VIA Nano 2000, ARM7T/ARM9T/Cortex-M3,
and WASM.
- Implementations assume a 64-bit architecture (64-bit integer arithmetic
is fast, and includes add-with-carry, sub-with-borrow, and full-result
multiply).
is fast, and includes contant-time add-with-carry, sub-with-borrow, and
full-result multiply).
- Hardware sidechannels are explicitly out of scope for this package.
Notable examples include but are not limited to:
- Power/RF side-channels etc.
@@ -29,4 +30,4 @@ constant-time byte comparison.
## License
This library is made available under the zlib license.
This library is made available under the zlib license.
+904
View File
@@ -0,0 +1,904 @@
// Constant time Big Integers
package _bigint
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:crypto"
import subtle "core:crypto/_subtle"
import "core:slice"
// Integers 'i31'
// --------------
//
// The 'i31' functions implement computations on big integers using
// an internal representation as an array of 32-bit integers. For
// an array `x`:
// -- x[0] encodes the array length and the "announced bit length"
// of the integer: namely, if the announced bit length is k,
// then x[0] = ((k / 31) << 5) + (k % 31).
// -- x[1], x[2]... contain the value in little-endian order, 31
// bits per word (x[1] contains the least significant 31 bits).
// The upper bit of each word is 0.
//
// Multiplications rely on the elementary 32x32->64 multiplication.
//
// The announced bit length specifies the number of bits that are
// significant in the subsequent 32-bit words. Unused bits in the
// last (most significant) word are set to 0; subsequent words are
// uninitialized and need not exist at all.
//
// The execution time and memory access patterns of all computations
// depend on the announced bit length, but not on the actual word
// values. For modular integers, the announced bit length of any integer
// modulo `n` is equal to the actual bit length of `n`; thus, computations
// on modular integers are "constant-time" (only the modulus length may leak).
I31_MASK :: 0x7fff_ffff
// Compute the bit length of a 32-bit integer.
// Returned value is between 0 and 32 (inclusive).
@(require_results)
_u32_bit_length :: proc "contextless" (x: u32) -> (length: u32) {
x := x
k := subtle.neq(x, 0)
c := subtle.gt(x, 0xFFFF); x = subtle.csel(x, x >> 16, c); k += c << 4
c = subtle.gt(x, 0x00FF); x = subtle.csel(x, x >> 8, c); k += c << 3
c = subtle.gt(x, 0x000F); x = subtle.csel(x, x >> 4, c); k += c << 2
c = subtle.gt(x, 0x0003); x = subtle.csel(x, x >> 2, c); k += c << 1
k += subtle.gt(x, 0x0001)
return k
}
// Multiply two 31-bit integers, with a 62-bit result. This default
// implementation assumes that the basic multiplication operator
// yields constant-time code.
//
// The mul31_lo() returns only the low 31 bits of the product.
//
// Note/Odin:
// The original BearSSL code provides alternative implemenetations
// of these routines gated behind `BR_CT_MUL31`, however that macro
// is only useful on Intel 80386/80486, VIA Nano 2000, and ARM7T/ARM9T.
@(require_results)
_mul31 :: #force_inline proc "contextless" (x, y: u32) -> (res: u64) {
return u64(x) * u64(y)
}
@(private="file", require_results)
_mul31_lo :: #force_inline proc "contextless" (x, y: u32) -> (res: u32) {
return (x * y) & I31_MASK
}
// Wrapper for `div_rem`; the remainder is returned, and the quotient is
// discarded.
@(private, require_results)
_rem_u32 :: #force_inline proc "contextless" (hi: u32, lo: u32, d: u32) -> (res: u32) {
_, rem := div_rem_u32(hi, lo, d)
return rem
}
// Wrapper for `div_rem`; the quotient is returned, and the remainder is
// discarded.
@(private="file", require_results)
_div_u32 :: #force_inline proc "contextless" (hi: u32, lo: u32, d: u32) -> (quo: u32) {
q, _ := div_rem_u32(hi, lo, d)
return q
}
// Constant-time division. The dividend `hi:lo` is divided by the divisor `d`;
// the quotient and remainder are returned.
//
// If `hi == d`, then the quotient does not fit on 32 bits; returned value is thus truncated.
// If `hi > d`, returned values are indeterminate.
@(require_results)
div_rem_u32 :: proc "contextless" (hi: u32, lo: u32, d: u32) -> (quo: u32, rem: u32) {
// TODO: optimize this
hi := hi
lo := lo
ch := subtle.eq(hi, d)
hi = subtle.csel(hi, 0, ch)
for k := uint(31); k > 0; k -= 1 {
j := 32 - k
w := (hi << j) | (lo >> k)
ctl := subtle.ge(w, d) | (hi >> k)
hi2 := (w - d) >> j
lo2 := lo - (d << k)
hi = subtle.csel(hi, hi2, ctl)
lo = subtle.csel(lo, lo2, ctl)
quo |= ctl << k
}
cf := subtle.ge(lo, d) | hi
quo |= cf
rem = subtle.csel(lo, lo - d, cf)
return
}
// i31_rem computes x / y and returns the remainder.
@(require_results)
i31_rem :: proc "contextless" (x: []u32, y: u32) -> u32 {
words := uint(x[0] + 31) >> 5
x_ := x[1:]
r: u32
for i := int(words-1); i >= 0; i -= 1 {
r = _rem_u32(r, x_[i], y)
}
return r
}
// Test whether an integer `x` is zero.
@(optimization_mode="none", require_results)
i31_is_zero :: proc "contextless" (x: []u32) -> (res: u32) {
z: u32
for u := (x[0] + 31) >> 5; u > 0; u -= 1 {
z |= x[u]
}
return ~(z | -z) >> 31
}
// Add `b` to `a` and return the `carry` (`0` or `1`). if `ctl` is `1`.
// If `ctl` is `0`, `a` is left alone but the `carry` will still be computed.
//
// The slices `a` and `b` MUST have the same announced bit length (in subscript `0`)
//
// `a` and `b` MAY be the same array, but partial overlap is not allowed.
@(require_results)
i31_add :: proc "contextless" (a: []u32, b: []u32, ctl: u32) -> (carry: u32) {
words := uint(a[0] + 63) >> 5
for u in 1..<words {
aw := a[u]
bw := b[u]
naw := aw + bw + carry
carry = naw >> 31
a[u] = subtle.csel(aw, naw & I31_MASK, ctl)
}
return
}
// Subtract `b` from `a` and return the `carry` (`0` or `1`), if `ctl` is `1`.
// If `ctl` is `0`, then `a` is unmodified, but the carry is still computed
// and returned.
//
// The slices `a` and `b` MUST have the same announced bit length (in subscript `0`)
//
// `a` and `b` MAY be the same array, but partial overlap is not allowed.
@(require_results)
i31_sub :: proc "contextless" (a: []u32, b: []u32, ctl: u32) -> (carry: u32) {
words := uint(a[0] + 63) >> 5
for u in 1..<words {
aw := a[u]
bw := b[u]
naw := aw - bw - carry
carry = naw >> 31
a[u] = subtle.csel(aw, naw & I31_MASK, ctl)
}
return
}
// Compute the ENCODED actual bit length of an integer `x`.
// The argument `x` should point to the first (least significant)
// value word of the integer.
//
// The upper bit of each value word MUST be `0`.
//
// Returned value is `((k / 31) << 5) + (k % 31)` if the bit length is `k`.
//
// CT: value or length of `x` does not leak.
@(require_results)
i31_bit_length :: proc "contextless" (x: []u32) -> (res: u32) {
tw, twk: u32
xlen := len(x)
for xlen > 0 {
xlen -= 1
c := subtle.eq(tw, 0)
w := x[xlen]
tw = subtle.csel(tw, w, c)
twk = subtle.csel(twk, u32(xlen), c)
}
return (twk << 5) + _u32_bit_length(tw)
}
// Decode an integer from its big-endian unsigned representation. The
// "true" bit length of the integer is computed and set in the encoded
// announced bit length (`x[0]`), but all words of `x` corresponding to
// the full slice of source bytes.
//
// `x` needs to have a minimum length of: `1 + ((len(src) * 8) + 31) / 31`
//
// CT: value or length of `x` does not leak.
i31_decode :: proc "contextless" (x: []u32, src: []byte) {
u := len(src) - 1
v := 1
acc := u32(0)
acc_len := uint(0)
for u >= 0 {
b := u32(src[u])
acc |= b << acc_len
acc_len += 8
if acc_len >= 31 {
x[v] = acc & I31_MASK
acc_len -= 31
acc = b >> (8 - acc_len)
v += 1
}
u -= 1
}
if acc_len != 0 {
x[v] = acc
v += 1
}
x[0] = i31_bit_length(x[1:])
}
// Decode an integer from its big-endian unsigned representation.
// The integer MUST be lower than `m`; the (encoded) announced bit length
// written in `x` will be equal to that of `m`. All bytes from the
// `src` slice are read.
//
// Returned value is `1` if the decode value fits within the modulus, `0`
// otherwise. In the latter case, the `x` buffer will be set to `0` (but
// still with the announced bit length of `m`).
//
// CT: value or length of `x` does not leak. Memory access pattern depends
// only `src`'s length and the announced bit length of `m`. Whether `x` fits or
// not does not leak either.
@(require_results)
i31_decode_mod :: proc "contextless" (x: []u32, src: []byte, m: []u32) -> (res: u32) {
// Two-pass algorithm: in the first pass, we determine whether the
// value fits; in the second pass, we do the actual write.
//
// During the first pass, `res` contains the comparison result so far:
// 0x00000000 value is equal to the modulus
// 0x00000001 value is greater than the modulus
// 0xFFFFFFFF value is lower than the modulus
//
// Since we iterate starting with the least significant bytes (at
// the end of `src`), each new comparison overrides the previous
// except when the comparison yields 0 (equal).
//
// During the second pass, `res` is either 0xFFFFFFFF (value fits) 0x00000000 (value does not fit).
// We must iterate over all bytes of the source, _and_ possibly
// some extra virtual bytes (with value 0) so as to cover the
// complete modulus as well. We also add 4 such extra bytes beyond
// the modulus length because it then guarantees that no accumulated
// partial word remains to be processed.
_len := uint(len(src))
mlen := uint((m[0] + 31) >> 5)
tlen := uint(mlen << 2)
if tlen < _len {
tlen = _len
}
tlen += 4
for pass in 0..<2 {
v := uint(1)
acc := u32(0)
acc_len := u32(0)
for u in uint(0)..<tlen {
b: u32 = ---
if u < _len {
b = u32(src[_len - 1 - u])
} else {
b = 0
}
acc |= (b << acc_len)
acc_len += 8
if acc_len >= 31 {
xw := acc & I31_MASK
acc_len -= 31
acc = b >> (8 - acc_len)
if v <= mlen {
if pass == 1 {
x[v] = res & xw
} else {
cc := u32(subtle.cmp(xw, m[v]))
res = subtle.csel(cc, res, subtle.eq(cc, 0))
}
} else {
if pass == 0 {
res = subtle.csel(1, res, subtle.eq(xw, 0))
}
}
v += 1
}
}
// When we reach this point at the end of the first pass:
// r is either 0, 1 or -1; we want to set r to 0 if it
// is equal to 0 or 1, and leave it to -1 otherwise.
//
// When we reach this point at the end of the second pass:
// r is either 0 or -1; we want to leave that value
// untouched. This is a subcase of the previous.
res >>= 1
res |= (res << 1)
}
x[0] = m[0]
return res & 1
}
// Zeroize integer `x`. The announced bit length is set to the provided value,
// and the corresponding words are set to 0. The ENCODED bit length is expected
//here.
i31_zero :: proc "contextless" (x: []u32, bit_len: u32) {
x[0] = bit_len
intrinsics.mem_zero(raw_data(x[1:]), ((bit_len + 31) >> 5) * size_of(u32))
}
// Make a random integer of the provided size. The size is encoded.
// The header word is untouched.
i31_mkrand :: proc(x: []u32, esize: u32) {
_len := (esize + 31) >> 5
x_ := slice.reinterpret([]byte, x)
crypto.rand_bytes(x_[4:4 + _len * size_of(u32)])
for u in 1..<_len {
x[u] &= I31_MASK
}
m := _len & 31
if m == 0 {
x[_len] &= I31_MASK
} else {
x[_len] &= I31_MASK >> (31 - m)
}
}
// Right-shift an integer. The shift amount must be lower than 31 bits.
i31_rshift :: proc "contextless" (x: []u32, shift_amount: i32) {
_len := uint(x[0] + 31) >> 5
if _len == 0 {
return
}
count := uint(shift_amount)
r := x[1] >> count
for u in 2..= _len {
w := u32(x[u])
x[u - 1] = ((w << (31 - count)) | r) & I31_MASK
r = w >> count
}
x[_len] = r
}
// Reduce integer `a` modulo `m`. The result is written to `x`,
// and its announced bit length is set to be equal to that of `m`.
//
// `x` MUST be distinct from `a` and `m`.
//
// CT: only announced bit lengths leak, not values of `x`, `a` or `m`.
i31_reduce :: proc "contextless" (x: []u32, a: []u32, m: []u32) {
m_bitlen := m[0]
mlen := uint(m_bitlen + 31) >> 5
x[0] = m_bitlen
if m_bitlen == 0 {
return
}
// If the source is shorter, then simply copy all words from a[]
// and zero out the upper words.
a_bitlen := a[0]
alen := uint(a_bitlen + 31) >> 5
if a_bitlen < m_bitlen {
copy(x[1:], a[1:][:alen])
for u in alen..<mlen {
x[u + 1] = 0
}
return
}
// The source length is at least equal to that of the modulus.
// We must thus copy N-1 words, and input the remaining words one
// by one.
copy(x[1:], a[2 + (alen - mlen):][:mlen - 1])
x[mlen] = 0
for u := 1 + alen - mlen; u > 0; u -= 1 {
i31_muladd_small(x, a[u], m)
}
}
// Decode an integer from its big-endian unsigned representation, and
// reduce it modulo the provided modulus `m`. The announced bit length
// of the result is set to be equal to that of the modulus.
//
// `x` MUST be distinct from `m`.
i31_decode_reduce :: proc "contextless" (x: []u32, src: []byte, m: []u32) {
// Get the encoded bit length.
m_ebitlen := m[0]
// Special case for an invalid (null) modulus.
if m_ebitlen == 0 {
x[0] = 0
return
}
// Clear the destination.
i31_zero(x, m_ebitlen)
// First decode directly as many bytes as possible.
// This requires computing the actual bit length.
m_rbitlen := m_ebitlen >> 5
m_rbitlen = (m_ebitlen & 31) + (m_rbitlen << 5) - m_rbitlen
mblen := uint(m_rbitlen + 7) >> 3
k := mblen - 1
_len := uint(len(src))
if k >= _len {
i31_decode(x, src)
x[0] = m_ebitlen
return
}
i31_decode(x, src[:k])
x[0] = m_ebitlen
// Input remaining bytes, using 31-bit words.
acc := u32(0)
acc_len := uint(0)
for {
v := u32(src[k])
if acc_len >= 23 {
acc_len -= 23
acc <<= (8 - acc_len)
acc |= v >> acc_len
i31_muladd_small(x, acc, m)
acc = v & (0xFF >> (8 - acc_len))
} else {
acc = (acc << 8) | v
acc_len += 8
}
if k += 1; k >= _len {
break
}
}
// We may have some bits accumulated. We then perform a shift to
// be able to inject these bits as a full 31-bit word.
if acc_len != 0 {
acc = (acc | (x[1] << acc_len)) & I31_MASK
i31_rshift(x, i32(31 - acc_len))
i31_muladd_small(x, acc, m)
}
}
// Multiply `x` by 2^31 and then add integer `z`, modulo `m`.
// This function assumes that `x` and `m` have the same announced bit
// length, the announced bit length of `m` matches its true bit length.
//
// `x` and `m` MUST be distinct arrays.
// `z` MUST fit in 31 bits (upper bit set to 0).
//
// CT: only the common announced bit length of `x` and `m` leaks, not
// the values of `x`, `z` or `m`.
i31_muladd_small :: proc "contextless" (x: []u32, z: u32, m: []u32) {
// We can test on the modulus bit length since we accept to leak
// that length.
m_bitlen := m[0]
if m_bitlen == 0 {
return
}
hi: u32
if m_bitlen <= 31 {
hi = x[1] >> 1
lo := (x[1] << 31) | z
x[1] = _rem_u32(hi, lo, m[1])
return
}
mlen := uint(m_bitlen + 31) >> 5
mblr := uint(m_bitlen) & 31
// Principle: we estimate the quotient (x*2^31+z)/m by
// doing a 64/32 division with the high words.
//
// Let:
// w = 2^31
// a = (w*a0 + a1) * w^N + a2
// b = b0 * w^N + b2
// such that:
// 0 <= a0 < w
// 0 <= a1 < w
// 0 <= a2 < w^N
// w/2 <= b0 < w
// 0 <= b2 < w^N
// a < w*b
// I.e. the two top words of a are a0:a1, the top word of b is
// b0, we ensured that b0 is "full" (high bit set), and a is
// such that the quotient q = a/b fits on one word (0 <= q < w).
//
// If a = b*q + r (with 0 <= r < q), we can estimate q by
// doing an Euclidean division on the top words:
// a0*w+a1 = b0*u + v (with 0 <= v < b0)
// Then the following holds:
// 0 <= u <= w
// u-2 <= q <= u
hi = x[mlen]
a0, a1, b0: u32
if mblr == 0 {
a0 = x[mlen]
intrinsics.mem_copy(raw_data(x[2:]), raw_data(x[1:]), (mlen - 1) * size_of(u32))
x[1] = z
a1 = x[mlen]
b0 = m[mlen]
} else {
a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & I31_MASK
intrinsics.mem_copy(raw_data(x[2:]), raw_data(x[1:]), (mlen - 1) * size_of(u32))
x[1] = z
a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & I31_MASK
b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr)) & I31_MASK
}
// We estimate a divisor q. If the quotient returned by div()
// is g:
// -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF.
// -- Otherwise:
// -- if g == 0 then we set q = 0;
// -- otherwise, we set q = g - 1.
// The properties described above then ensure that the true
// quotient is q-1, q or q+1.
//
// Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We
// must adjust the parameters to br_div() accordingly.
g := _div_u32(a0 >> 1, a1 | (a0 << 31), b0)
q := subtle.csel(subtle.csel(g - 1, 0, subtle.eq(g, 0)), I31_MASK, subtle.eq(a0, b0))
// We subtract q*m from x (with the extra high word of value 'hi').
// Since q may be off by 1 (in either direction), we may have to
// add or subtract m afterwards.
//
// The 'tb' flag will be true (1) at the end of the loop if the
// result is greater than or equal to the modulus (not counting
// 'hi' or the carry).
cc := u32(0)
tb := u32(1)
for u in 1..= mlen {
mw := m[u]
zl := _mul31(mw, q) + u64(cc)
cc = u32(zl >> 31)
zw := u32(zl) & I31_MASK
xw := x[u]
nxw := xw - zw
cc += nxw >> 31
nxw &= I31_MASK
x[u] = nxw
tb = subtle.csel(subtle.gt(nxw, mw), tb, subtle.eq(nxw, mw))
}
// If we underestimated q, then either cc < hi (one extra bit
// beyond the top array word), or cc == hi and tb is true (no
// extra bit, but the result is not lower than the modulus). In
// these cases we must subtract m once.
//
// Otherwise, we may have overestimated, which will show as
// cc > hi (thus a negative result). Correction is adding m once.
over := subtle.gt(cc, hi)
under := ~over & (tb | subtle.lt(cc, hi))
_ = i31_add(x, m, over)
_ = i31_sub(x, m, under)
}
// Encode an integer into its big-endian unsigned representation. The
// output length in bytes is provided (parameter 'len'); if the length
// is too short then the integer is appropriately truncated; if it is
// too long then the extra bytes are set to 0.
i31_encode :: proc "contextless" (dst: []byte, x: []u32) {
xlen := uint(x[0] + 31) >> 5
if xlen == 0 {
intrinsics.mem_zero(raw_data(dst[:]), len(dst) * size_of(u32))
return
}
_len := uint(len(dst))
k := uint(1)
acc := u32(0)
acc_len := uint(0)
for _len != 0 {
w := (k <= xlen) ? x[k] : 0
k += 1
if (acc_len == 0) {
acc = w
acc_len = 31
} else {
z := acc | (w << acc_len)
acc_len -= 1
acc = w >> (31 - acc_len)
if _len >= 4 {
_len -= 4
ptr := (^u32be)(raw_data(dst[_len:]))
intrinsics.unaligned_store(ptr, u32be(z))
} else {
switch _len {
case 3:
dst[_len - 3] = byte(z >> 16)
fallthrough
case 2:
dst[_len - 2] = byte(z >> 8)
fallthrough
case 1:
dst[_len - 1] = byte(z)
}
return
}
}
}
}
// Compute `-(1/x) % 2^31`. If `x` is even, then this function returns `0`.
i31_ninv31 :: proc "contextless" (x: u32) -> (y: u32) {
y = 2 - x
y *= 2 - y * x
y *= 2 - y * x
y *= 2 - y * x
y *= 2 - y * x
return subtle.csel(0, -y, x & 1) & I31_MASK
}
// Compute a modular Montgomery multiplication. `d` is filled with the
// value of `x*y/R % m` (where `R` is the Montgomery factor).
//
// The array `d` MUST be distinct from `x`, `y` and `m`[].
// `x` and `y` MUST be numerically lower than `m`.
//
// `x` and `y` MAY be the same array.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^31`, where `m0` is the least
// significant value word of `m` (this works only if `m` is an odd integer).
i31_montymul :: proc "contextless" (d: []u32, x: []u32, y: []u32, m: []u32, m0i: u32) {
// Each outer loop iteration computes:
// `d <- (d + xu*y + f*m) / 2^31`
// We have `xu <= 2^31-1` and `f <= 2^31-1`.
// Thus, if `d <= 2*m-1` on input, then:
// `2*m-1 + 2*(2^31-1)*m <= (2^32)*m-1`
// and the new `d` value is less than `2*m`.
//
// We represent `d` over 31-bit words, with an extra word `dh`,
// which can thus be only 0 or 1.
_len := uint((m[0] + 31) >> 5)
len4 := _len & ~uint(3)
i31_zero(d, m[0])
dh := u32(0)
for u in 0..<_len {
// The carry for each operation fits on 32 bits:
// `d[v+1] <= 2^31-1`
// `xu*y[v+1] <= (2^31-1)*(2^31-1)`
// `f*m[v+1] <= (2^31-1)*(2^31-1)`
// `r <= 2^32-1`
// `(2^31-1) + 2*(2^31-1)*(2^31-1) + (2^32-1) = 2^63 - 2^31`
//
// After division by `2^31`, the new `r` is then at most `2^32-1`
//
// Using a 32-bit carry has performance benefits on 32-bit
// systems; however, on 64-bit architectures, we prefer to
// keep the carry (r) in a 64-bit register, thus avoiding some
// "clear high bits" operations.
xu := x[u + 1]
f := _mul31_lo((d[1] + _mul31_lo(xu, y[1])), m0i)
r := u64(0)
v := uint(0)
for ; v < len4; v += 4 {
z := u64(d[v + 1]) + _mul31(xu, y[v + 1]) + _mul31(f, m[v + 1]) + r
r = z >> 31
d[v + 0] = u32(z) & I31_MASK
z = u64(d[v + 2]) + _mul31(xu, y[v + 2]) + _mul31(f, m[v + 2]) + r
r = z >> 31
d[v + 1] = u32(z) & I31_MASK
z = u64(d[v + 3]) + _mul31(xu, y[v + 3]) + _mul31(f, m[v + 3]) + r
r = z >> 31
d[v + 2] = u32(z) & I31_MASK
z = u64(d[v + 4]) + _mul31(xu, y[v + 4]) + _mul31(f, m[v + 4]) + r
r = z >> 31
d[v + 3] = u32(z) & I31_MASK
}
for ; v < _len; v += 1 {
z := u64(d[v + 1]) + _mul31(xu, y[v + 1]) + _mul31(f, m[v + 1]) + r
r = z >> 31
d[v] = u32(z) & I31_MASK
}
// Since the new `dh` can only be `0` or `1`, the addition of
// the old dh with the carry MUST fit on 32 bits, and
// thus can be done into dh itself.
dh += u32(r)
d[_len] = dh & I31_MASK
dh >>= 31
}
// We must write back the bit length because it was overwritten in
// the loop (not overwriting it would require a test in the loop,
// which would yield bigger and slower code).
d[0] = m[0]
// `d` may still be greater than `m` at that point; notably, the `dh`
// word may be non-zero.
_ = i31_sub(d, m, subtle.neq(dh, 0) | subtle.not(i31_sub(d, m, 0)))
}
// Convert a modular integer to Montgomery representation.
//
// The integer `x` MUST be lower than `m`, but with the same announced bit length.
i31_to_monty :: proc "contextless" (x: []u32, m: []u32) {
// uint32_t k;
for k := (m[0] + 31) >> 5; k > 0; k -= 1 {
i31_muladd_small(x, 0, m)
}
}
// Convert a modular integer back from Montgomery representation.
//
// The integer `x` MUST be lower than `m`[], but with the same announced bit
// length.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^32`, where `m0` is the least
// significant value word of `m` (this works only if `m` is an odd integer).
i31_from_monty :: proc "contextless" (x: []u32, m: []u32, m0i: u32) {
_len := uint(m[0] + 31) >> 5
for _ in 0..<_len {
f := _mul31_lo(x[1], m0i)
cc := u64(0)
for v in 0..<_len {
z := u64(x[v + 1]) + _mul31(f, m[v + 1]) + cc
cc = z >> 31
if v != 0 {
x[v] = u32(z & I31_MASK)
}
}
x[_len] = u32(cc)
}
// We may have to do an extra subtraction, but only if the value in `x`
// is indeed greater than or equal to that of `m`, which is why we must
// do two calls:
// - First call computes the carry
// - Second call performs the subtraction only if the carry is 0).
_ = i31_sub(x, m, subtle.not(i31_sub(x, m, 0)))
}
// Compute a modular exponentiation.
//
// `x` MUST be an integer modulo `m` (same announced bit length, lower value).
// `m` MUST be odd.
//
// The exponent `e` is in big-endian unsigned notation.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^31`, where `m0` is the least
// significant value word of `m` (this works only if `m` is an odd integer).
//
// The `t1` and `t2` parameters must be temporary arrays, each large enough to
// accommodate an integer with the same size as `m`.
i31_modpow :: proc "contextless" (x: []u32, e: []byte, m: []u32, m0i: u32, t1: []u32, t2: []u32) {
// `mlen` is the length of `m` expressed in `u32`'s (including the
// "bit length" first field).
mlen := uint((m[0] + 63) >> 5)
elen := u32(len(e))
// Throughout the algorithm:
// -- `t1` is in Montgomery representation; it contains x, x^2, x^4, x^8...
// -- The result is accumulated, in normal representation, in the `x` array.
// -- `t2` is used as destination buffer for each multiplication.
//
// Note that there is no need to call `i32_from_monty()`.
copy(t1[:mlen], x[:mlen])
i31_to_monty(t1, m)
i31_zero(x, m[0])
x[1] = 1
for k := u32(0); k < (elen << 3); k += 1 {
ctl := (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1
i31_montymul(t2, x, t1, m, m0i)
for &d, i in x[:mlen] {
d = subtle.csel(d, t2[i], ctl)
}
i31_montymul(t2, t1, t1, m, m0i)
copy(t1[:mlen], t2[:mlen])
}
}
// Compute a modular exponentiation.
//
// `x` MUST be an integer modulo `m` (same announced bit length, lower value).
// `m` MUST be odd.
//
// The exponent `e` is in big-endian unsigned notation.
//
// The `m0i` parameter is equal to `-(1/m0) mod 2^31`, where `m0` is the least
// significant value word of `m`[] (this works only if m[] is an odd integer).
//
// The `tmp` array is used for temporaries; it must be large enough to accommodate
// at least two temporary values with the same size as `m` (including the leading
// "bit length" word).
//
// If there is room for more temporaries, then this function may use the extra
// room for window-based optimisation, resulting in faster computations.
//
// Returned value is `true` on success, `false` on error. An error is reported if
// the provided `tmp`array is too short.
i31_modpow_opt :: proc "contextless" (x: []u32, e: []byte, m: []u32, m0i: u32, tmp: []u32) -> u32 {
// NOTE/yawning: This is only used by the rsa_i31 code, with the key
// generation taking a function pointer to either this routine,
// or the i62 variant.
//
// If we ever need to support the i32 version, it is used extensively,
// but non e-waste architecutures will all do the right thing with
// the i62 version, albeit with a perforance hit on 32-bit CPUs.
unimplemented_contextless()
// i31_mod_pow(x, e, m, m0i, tmp[:len(m)], tmp[len(m):])
// return 1
}
// Compute `d+a*b`, result in `d`.
//
// The initial announced bit length of `d` MUST match that of `a`[].
//
// The `d` array MUST be large enough to accommodate the full result,
// plus (possibly) an extra word. The resulting announced bit length
// of `d` will be the sum of the announced bit lengths of `a` and `b`
// (therefore, it may be larger than the actual bit length of the numerical result).
//
// `a` and `b` may be the same array. `d` must be disjoint from both `a` and `b`.
i31_mulacc :: proc "contextless" (d: []u32, a: []u32, b: []u32) {
a_len := uint((a[0] + 31) >> 5)
b_len := uint((b[0] + 31) >> 5)
// We want to add the two bit lengths, but these are encoded,
// which requires some extra care.
d_l := (a[0] & 31) + (b[0] & 31)
d_h := (a[0] >> 5) + (b[0] >> 5)
d[0] = (d_h << 5) + d_l + (~u32(d_l - 31) >> 31)
for u in 0..<b_len {
// Carry always fits on 31 bits; we want to keep it in a
// 32-bit register on 32-bit architectures (on a 64-bit
// architecture, cast down from 64 to 32 bits means
// clearing the high bits, which is not free; on a 32-bit
// architecture, the same operation really means ignoring
// the top register, which has negative or zero cost).
f := b[1 + u]
cc := u64(0)
for v in 0..<a_len {
z := u64(d[1 + u + v]) + _mul31(f, a[1 + v]) + cc
cc = z >> 31
d[1 + u + v] = u32(z) & I31_MASK
}
d[1 + u + a_len] = u32(cc)
}
}
+361
View File
@@ -0,0 +1,361 @@
package _bigint
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:math/bits"
import subtle "core:crypto/_subtle"
import "core:slice"
@(private="file")
I62_MASK :: 0x3fff_ffff_ffff_ffff
// Compute x*y+v1+v2. Operands are 64-bit, and result is 128-bit, with
// high word in "hi" and low word in "lo".
@(private="file", require_results)
_fma1 :: #force_inline proc "contextless" (x, y, v1, v2: u64) -> (hi, lo: u64) {
hi, lo = bits.mul_u64(x, y)
carry: u64
lo, carry = bits.add_u64(lo, v1, 0)
hi += carry
lo, carry = bits.add_u64(lo, v2, 0)
hi += carry
return
}
// Compute x1*y1+x2*y2+v1+v2. Operands are 64-bit, and result is 128-bit,
// with high word in "hi" and low word in "lo".
//
// Callers should ensure that the two inner products, and the v1 and v2
// operands, are multiple of 4 (this is not used by this specific definition
// but may help other implementations).
@(private="file", require_results)
_fma2 :: #force_inline proc "contextless" (x1, y1, x2, y2, v1, v2: u64) -> (hi, lo: u64) {
hi_1, lo_1 := bits.mul_u64(x1, y1)
hi_2, lo_2 := bits.mul_u64(x2, y2)
carry: u64
lo, carry = bits.add_u64(lo_1, lo_2, 0)
hi, _ = bits.add_u64(hi_1, hi_2, carry)
lo, carry = bits.add_u64(lo, v1, 0)
hi += carry
lo, carry = bits.add_u64(lo, v2, 0)
hi += carry
return
}
@(private="file", require_results)
_mul62_lo :: #force_inline proc "contextless" (x, y: u64) -> u64 {
return (x * y) & I62_MASK
}
// Subtract b from a, and return the final carry. If 'ctl32' is 0, then
// a[] is kept unmodified, but the final carry is still computed and
// returned.
@(private="file", require_results)
_i62_sub :: proc "contextless" (a, b: []u64, num: int, ctl32: u32) -> u32 {
cc: u64
ctl := -ctl32
mask := u64(ctl) | (u64(ctl) << 32)
for u in 0..<num {
aw := a[u]
bw := b[u]
dw := aw - bw - cc
cc = dw >> 63
dw &= I62_MASK
a[u] = aw ~ (mask & (dw ~ aw))
}
return u32(cc)
}
// Montgomery multiplication, over arrays of 62-bit values. The
// destination array (d) must be distinct from the other operands
// (x, y and m). All arrays are in little-endian format (least
// significant word comes first) over 'num' words.
@(private="file")
_i62_montymul :: proc "contextless" (d, x, y, m: []u64, num: int, m0i: u64) {
dh: u64
num4 := 1 + u64((num - 1) & ~int(3))
intrinsics.mem_zero(raw_data(d), num * size_of(u64))
for u in 0..<num {
xu := x[u] << 2
f := _mul62_lo(d[0] + _mul62_lo(x[u], y[0]), m0i) << 2
hi, lo := _fma2(xu, y[0], f, m[0], d[0] << 2, 0)
r := hi
v: int
for v = 1; v < int(num4); v += 4 {
hi, lo = _fma2(xu, y[v + 0], f, m[v + 0], d[v + 0] << 2, r << 2)
r = hi + (r >> 62)
d[v - 1] = lo >> 2
hi, lo = _fma2(xu, y[v + 1], f, m[v + 1], d[v + 1] << 2, r << 2)
r = hi + (r >> 62)
d[v + 0] = lo >> 2
hi, lo = _fma2(xu, y[v + 2], f, m[v + 2], d[v + 2] << 2, r << 2)
r = hi + (r >> 62)
d[v + 1] = lo >> 2
hi, lo = _fma2(xu, y[v + 3], f, m[v + 3], d[v + 3] << 2, r << 2)
r = hi + (r >> 62)
d[v + 2] = lo >> 2
}
for ; v < num; v += 1 {
hi, lo = _fma2(xu, y[v], f, m[v], d[v] << 2, r << 2)
r = hi + (r >> 62)
d[v - 1] = lo >> 2
}
zh := dh + r
d[num - 1] = zh & I62_MASK
dh = zh >> 62
}
_ = _i62_sub(d, m, num, u32(dh) | subtle.not(_i62_sub(d, m, num, 0)))
}
// Conversion back from Montgomery representation.
@(private="file")
_i62_frommonty :: proc "contextless" (x, m: []u64, num: int, m0i: u64) {
for _ in 0..<num {
cc: u64
f := _mul62_lo(x[0], m0i) << 2
for v in 0..<num {
hi, lo := _fma1(f, m[v], x[v] << 2, cc)
cc = hi << 2
if (v != 0) {
x[v - 1] = lo >> 2
}
}
x[num - 1] = cc >> 2
}
_ = _i62_sub(x, m, num, subtle.not(_i62_sub(x, m, num, 0)))
}
// Variant of i31_modpow_opt() that internally uses 64x64->128
// multiplications. It expects the same parameters as i31_modpow_opt(),
// except that the temporaries should be 64-bit integers, not 32-bit
// integers.
i62_modpow_opt :: proc "contextless" (x31: []u32, e: []byte, m31: []u32, m0i31: u32, tmp: []u64) -> u32 {
twlen := len(tmp)
// Get modulus size, in words.
mw31num := int((m31[0] + 31) >> 5)
mw62num := int((mw31num + 1) >> 1)
// In order to apply this function, we must have enough room to
// copy the operand and modulus into the temporary array, along
// with at least two temporaries. If there is not enough room,
// switch to br_i31_modpow(). We also use br_i31_modpow() if the
// modulus length is not at least four words (94 bits or more).
if mw31num < 4 || mw62num << 2 > twlen {
// We assume here that we can split an aligned uint64_t
// into two properly aligned uint32_t. Since both types
// are supposed to have an exact width with no padding,
// then this property must hold.
txlen := mw31num + 1
if twlen < txlen {
return 0
}
tmp_as_u32s := slice.reinterpret([]u32, tmp)
t1, t2 := tmp_as_u32s[:txlen], tmp_as_u32s[txlen:]
i31_modpow(x31, e, m31, m0i31, t1, t2)
return 1
}
// Convert x to Montgomery representation: this means that
// we replace x with x*2^z mod m, where z is the smallest multiple
// of the word size such that 2^z >= m. We want to reuse the 31-bit
// functions here (for constant-time operation), but we need z
// for a 62-bit word size.
for _ in 0..<mw62num {
i31_muladd_small(x31, 0, m31)
i31_muladd_small(x31, 0, m31)
}
// Assemble operands into arrays of 62-bit words. Note that
// all the arrays of 62-bit words that we will handle here
// are without any leading size word.
//
// We also adjust tmp and twlen to account for the words used
// for these extra arrays.
m := tmp[:mw62num]
x := tmp[mw62num:mw62num*2]
tmp_ := tmp[mw62num << 1:]
twlen -= mw62num << 1
for u := 0; u < mw31num; u += 2 {
v := u >> 1
if u + 1 == mw31num {
m[v] = u64(m31[u + 1])
x[v] = u64(x31[u + 1])
} else {
m[v] = u64(m31[u + 1]) + (u64(m31[u + 2]) << 31)
x[v] = u64(x31[u + 1]) + (u64(x31[u + 2]) << 31)
}
}
// Compute window size. We support windows up to 5 bits; for a
// window of size k bits, we need 2^k+1 temporaries (for k = 1,
// we use special code that uses only 2 temporaries).
win_len: int
for win_len = 5; win_len > 1; win_len -= 1 {
if (1 << uint(win_len) + 1) * mw62num <= twlen {
break
}
}
t1 := tmp_[:mw62num]
t2 := tmp_[mw62num:]
// Compute m0i, which is equal to -(1/m0) mod 2^62. We were
// provided with m0i31, which already fulfills this property
// modulo 2^31; the single expression below is then sufficient.
m0i := u64(m0i31)
m0i = _mul62_lo(m0i, 2 + _mul62_lo(m0i, m[0]))
// Compute window contents. If the window has size one bit only,
// then t2 is set to x; otherwise, t2[0] is left untouched, and
// t2[k] is set to x^k (for k >= 1).
if win_len == 1 {
copy(t2, x)
} else {
copy(t2[mw62num:], x)
base := t2[mw62num:]
for u := 2; u < 1 << uint(win_len); u += 1 {
_i62_montymul(base[mw62num:], base, x, m, mw62num, m0i)
base = base[mw62num:]
}
}
// Set x to 1, in Montgomery representation. We again use the
// 31-bit code.
i31_zero(x31, m31[0])
x31[(m31[0] + 31) >> 5] = 1
i31_muladd_small(x31, 0, m31)
if mw31num & 1 != 0 {
i31_muladd_small(x31, 0, m31)
}
for u := 0; u < mw31num; u+= 2 {
v := u >> 1
if u + 1 == mw31num {
x[v] = u64(x31[u + 1])
} else {
x[v] = u64(x31[u + 1]) + (u64(x31[u + 2]) << 31)
}
}
e_, e_len := e, len(e)
// We process bits from most to least significant. At each
// loop iteration, we have acc_len bits in acc.
acc: u32
acc_len: uint
for acc_len > 0 || e_len > 0 {
// Get the next bits.
k := uint(win_len)
if acc_len < uint(win_len) {
if e_len > 0 {
acc = (acc << 8) | u32(e_[0])
e_ = e_[1:]
e_len -= 1
acc_len += 8
} else {
k = acc_len
}
}
bits := (acc >> (acc_len - k)) & ((u32(1) << k) - 1)
acc_len -= k
// We could get exactly k bits. Compute k squarings.
for _ in 0..<k {
_i62_montymul(t1, x, x, m, mw62num, m0i)
copy(x, t1)
}
// Window lookup: we want to set t2 to the window
// lookup value, assuming the bits are non-zero. If
// the window length is 1 bit only, then t2 is
// already set; otherwise, we do a constant-time lookup.
if win_len > 1 {
intrinsics.mem_zero(raw_data(t2), mw62num * size_of(u64))
base := t2[mw62num:]
for u := u32(1); u < u32(1) << k; u += 1 {
mask := -u64(subtle.eq(u, bits))
for v in 0..<mw62num {
t2[v] |= mask & base[v]
}
base = base[mw62num:]
}
}
// Multiply with the looked-up value. We keep the product
// only if the exponent bits are not all-zero.
_i62_montymul(t1, x, t2, m, mw62num, m0i)
mask1 := -u64(subtle.eq(bits, 0))
mask2 := ~mask1
for u in 0..<mw62num {
x[u] = (mask1 & x[u]) | (mask2 & t1[u])
}
}
// Convert back from Montgomery representation.
_i62_frommonty(x, m, mw62num, m0i)
// Convert result into 31-bit words.
for u := 0; u < mw31num; u += 2 {
zw := u64(x[u >> 1])
x31[u + 1] = u32(zw) & I31_MASK
if u + 1 < mw31num {
x31[u + 2] = u32(zw >> 31)
}
}
return 1
}
// Wrapper for i62_modpow_opt() that uses the same type as
// i31_modpow_opt(); however, it requires its 'tmp' argument to the
// 64-bit aligned.
i62_modpow_opt_as_i31 :: proc "contextless" (x31: []u32, e: []byte, m31: []u32, m0i31: u32, tmp: []u32) -> u32 {
// As documented, this function expects the 'tmp' argument to be
// 64-bit aligned. This is OK since this function is internal (it
// is not part of BearSSL's public API).
ensure_contextless(uintptr(raw_data(tmp)) & 7 == 0)
ensure_contextless(len(tmp) & 1 == 0) // Length MUST be even.
tmp_as_u64s := slice.reinterpret([]u64, tmp)
return i62_modpow_opt(x31, e, m31, m0i31, tmp_as_u64s)
}
+265
View File
@@ -0,0 +1,265 @@
package _bigint
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import subtle "core:crypto/_subtle"
import "core:math/big"
import "core:slice"
// Perform trial divisions on a candidate prime. We opt for the simple
// route and "just" compute a series of trial divisions.
//
// Returned value is 1 on success (none of the small primes
// divides x), 0 on error (a non-trivial GCD is obtained).
@(private="file", require_results)
trial_divisions :: proc "contextless" (x: []u32) -> u32 {
for factor in big._private_prime_table {
if factor <= 11 {
continue
}
if i31_rem(x, u32(factor)) == 0 {
return 0
}
}
return 1
}
// Perform n rounds of Miller-Rabin on the candidate prime x. This
// function assumes that x = 3 mod 4.
//
// WARNING: t MUST be 64-bit aligned, and be large enough such that
// it can hold 4 encoded integers that have the same number of limbs
// as x.
//
// Returned value is 1 on success (all rounds completed successfully),
// 0 otherwise.
@(private="file", require_results)
i62_miller_rabin :: proc(x: []u32, n: int, t: []u32) -> u32 {
// Since x = 3 mod 4, the Miller-Rabin test is simple:
// - get a random base a (such that 1 < a < x-1)
// - compute z = a^((x-1)/2) mod x
// - if z != 1 and z != x-1, the number x is composite
//
// We generate bases 'a' randomly with a size which is
// one bit less than x, which ensures that a < x-1. It
// is not useful to verify that a > 1 because the probability
// that we get a value a equal to 0 or 1 is much smaller
// than the probability of our Miller-Rabin tests not to
// detect a composite, which is already quite smaller than the
// probability of the hardware misbehaving and return a
// composite integer because of some glitch (e.g. bad RAM
// or ill-timed cosmic ray).
// Compute (x-1)/2 (encoded).
xm1d2 := slice.reinterpret([]byte, t)
xm1d2_len := ((x[0] - (x[0] >> 5)) + 7) >> 3
i31_encode(xm1d2[:xm1d2_len], x)
cc: u32
for u in 0..<xm1d2_len {
w := u32(xm1d2[u])
xm1d2[u] = byte((w >> 1) | cc)
cc = w << 7
}
// We used some words of the provided buffer for (x-1)/2.
xm1d2_len_u32 := (xm1d2_len + 3) >> 2
t_ := t[xm1d2_len_u32:]
tlen := len(t_)
xlen := (x[0] + 31) >> 5
asize := x[0] - 1 - subtle.eq0(x[0] & 31)
x0i := i31_ninv31(x[1])
for _ in 0..<n {
// Generate a random base. We don't need the base to be
// really uniform modulo x, so we just get a random
// number which is one bit shorter than x.
a := t_
a[0] = x[0]
a[xlen] = 0
i31_mkrand(a, asize)
// Compute a^((x-1)/2) mod x. We assume here that the
// function will not fail (the temporary array is large
// enough).
t2 := t_[1 + xlen:]
t2len := tlen - 1 - int(xlen)
if (t2len & 1) != 0 {
// Since the source array is 64-bit aligned and
// has an even number of elements (TEMPS), we
// can use the parity of the remaining length to
// detect and adjust alignment.
t2 = t2[1:]
t2len -= 1
}
i62_modpow_opt_as_i31(a, xm1d2[:xm1d2_len], x, x0i, t2[:t2len])
// We must obtain either 1 or x-1. Note that x is odd,
// hence x-1 differs from x only in its low word (no
// carry).
eq1 := a[1] ~ 1
eqm1 := a[1] ~ (x[1] - 1)
for u in 2..=xlen {
eq1 |= a[u]
eqm1 |= a[u] ~ x[u]
}
if ((subtle.eq0(eq1) | subtle.eq0(eqm1)) == 0) {
return 0
}
}
return 1
}
// Create a random prime of the provided size. 'esize' is the _encoded_
// bit length. The two top bits and the two bottom bits are set to 1.
i62_mkprime :: proc(x: []u32, esize: u32, pubexp: u32, t: []u32) {
x[0] = esize
_len := (esize + 31) >> 5
for {
// Generate random bits. We force the two top bits and the
// two bottom bits to 1.
i31_mkrand(x, esize)
if (esize & 31) == 0 {
x[_len] |= 0x60000000
} else if (esize & 31) == 1 {
x[_len] |= 0x00000001
x[_len - 1] |= 0x40000000
} else {
x[_len] |= 0x00000003 << ((esize & 31) - 2)
}
x[1] |= 0x00000003
// Trial division with low primes (3, 5, 7 and 11). We
// use the following properties:
//
// 2^2 = 1 mod 3
// 2^4 = 1 mod 5
// 2^3 = 1 mod 7
// 2^10 = 1 mod 11
m3, m5, m7, m11: u32
s7, s11: uint
for u in 0..<_len {
w := x[1 + u]
w3 := (w & 0xFFFF) + (w >> 16) // max: 98302
w5 := (w & 0xFFFF) + (w >> 16) // max: 98302
w7 := (w & 0x7FFF) + (w >> 15) // max: 98302
w11 := (w & 0xFFFFF) + (w >> 20) // max: 1050622
m3 += w3 << (u & 1)
m3 = (m3 & 0xFF) + (m3 >> 8) // max: 1025
m5 += w5 << ((4 - u) & 3)
m5 = (m5 & 0xFFF) + (m5 >> 12) // max: 4479
m7 += w7 << s7
m7 = (m7 & 0x1FF) + (m7 >> 9) // max: 1280
s7 += 1
if s7 == 3 {
s7 = 0
}
m11 += w11 << s11
s11 += 1
if s11 == 10 {
s11 = 0
}
m11 = (m11 & 0x3FF) + (m11 >> 10) // max: 526847
}
m3 = (m3 & 0x3F) + (m3 >> 6) // max: 78
m3 = (m3 & 0x0F) + (m3 >> 4) // max: 18
m3 = ((m3 * 43) >> 5) & 3
m5 = (m5 & 0xFF) + (m5 >> 8) // max: 271
m5 = (m5 & 0x0F) + (m5 >> 4) // max: 31
m5 -= 20 & -subtle.gt(m5, 19)
m5 -= 10 & -subtle.gt(m5, 9)
m5 -= 5 & -subtle.gt(m5, 4)
m7 = (m7 & 0x3F) + (m7 >> 6) // max: 82
m7 = (m7 & 0x07) + (m7 >> 3) // max: 16
m7 = ((m7 * 147) >> 7) & 7
// 2^5 = 32 = -1 mod 11.
m11 = (m11 & 0x3FF) + (m11 >> 10) // max: 1536
m11 = (m11 & 0x3FF) + (m11 >> 10) // max: 1023
m11 = (m11 & 0x1F) + 33 - (m11 >> 5) // max: 64
m11 -= 44 & -subtle.gt(m11, 43)
m11 -= 22 & -subtle.gt(m11, 21)
m11 -= 11 & -subtle.gt(m11, 10)
// If any of these modulo is 0, then the candidate is
// not prime. Also, if pubexp is 3, 5, 7 or 11, and the
// corresponding modulus is 1, then the candidate must
// be rejected, because we need e to be invertible
// modulo p-1. We can use simple comparisons here
// because they won't leak information on a candidate
// that we keep, only on one that we reject (and is thus
// not secret).
if m3 == 0 || m5 == 0 || m7 == 0 || m11 == 0 {
continue
}
if (pubexp == 3 && m3 == 1) || (pubexp == 5 && m5 == 1) || (pubexp == 7 && m7 == 1) || (pubexp == 11 && m11 == 1) {
continue
}
// More trial divisions.
if trial_divisions(x) == 0 {
continue
}
// Miller-Rabin algorithm. Since we selected a random
// integer, not a maliciously crafted integer, we can use
// relatively few rounds to lower the risk of a false
// positive (i.e. declaring prime a non-prime) under
// 2^(-80). It is not useful to lower the probability much
// below that, since that would be substantially below
// the probability of the hardware misbehaving. Sufficient
// numbers of rounds are extracted from the Handbook of
// Applied Cryptography, note 4.49 (page 149).
//
// Since we work on the encoded size (esize), we need to
// compare with encoded thresholds.
rounds: int
switch {
case esize < 309:
rounds = 12
case esize < 464:
rounds = 9
case esize < 670:
rounds = 6
case esize < 877:
rounds = 4
case esize < 1341:
rounds = 3
case:
rounds = 2
}
if i62_miller_rabin(x, rounds, t) == 1 {
return
}
}
}
+1 -1
View File
@@ -71,7 +71,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
// This will only underflow if and only if (⟺) arg1 == arg2, and we return the borrow,
// which will be 1.
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
is_eq := subtle.eq0(fe_non_zero(&tmp))
fe_clear(&tmp)
+1 -1
View File
@@ -77,7 +77,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
// This will only underflow if and only if (⟺) arg1 == arg2, and we return the borrow,
// which will be 1.
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
is_eq := subtle.eq0(fe_non_zero(&tmp))
fe_clear(&tmp)
@@ -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)
+1 -1
View File
@@ -130,7 +130,7 @@ poly_frommsg :: proc "contextless" (r: ^Poly, msg: []byte) #no_bounds_check {
for i in 0..<N/8 {
for j in 0..<8 {
r.coeffs[8*i+j] = subtle.csel_i16(0, (Q+1)/2, int(msg[i] >> uint(j))&1)
r.coeffs[8*i+j] = subtle.csel_i16(0, (Q+1)/2, (msg[i] >> uint(j))&1)
}
}
}
+203 -34
View File
@@ -3,76 +3,245 @@ Various useful bit operations in constant time.
*/
package _subtle
import "core:crypto/_fiat"
import "core:math/bits"
import "base:intrinsics"
// byte_eq returns 1 if and only if (⟺) a == b, 0 otherwise.
@(optimization_mode="none")
byte_eq :: proc "contextless" (a, b: byte) -> int {
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Constant-time primitives. These functions manipulate integer values in
// order to provide constant-time comparisons and multiplexers.
//
// Boolean values (the "ctl" bits) MUST have value 0 or 1.
//
// Implementation notes:
// =====================
//
// The uintN_t types are unsigned and with width exactly N bits; the C
// standard guarantees that computations are performed modulo 2^N, and
// there can be no overflow. Negation (unary '-') works on unsigned types
// as well.
//
// The intN_t types are guaranteed to have width exactly N bits, with no
// padding bit, and using two's complement representation. Casting
// intN_t to uintN_t really is conversion modulo 2^N. Beware that intN_t
// types, being signed, trigger implementation-defined behaviour on
// overflow (including raising some signal): with GCC, while modular
// arithmetics are usually applied, the optimizer may assume that
// overflows don't occur (unless the -fwrapv command-line option is
// added); Clang has the additional -ftrapv option to explicitly trap on
// integer overflow or underflow.
// This code only works on a two's complement system.
#assert((-1 & 3) == 3)
// not negates a boolean which MUST be `0` or `1`
@(optimization_mode="none", require_results)
not :: proc "contextless" (ctrl: $T) -> T where intrinsics.type_is_unsigned(T) {
return ctrl ~ 1
}
@(optimization_mode="none", require_results)
byte_eq :: proc "contextless" (a, b: byte) -> byte {
v := a ~ b
// v == 0 if and only if (⟺) a == b. The subtraction will underflow, setting the
// sign bit, which will get returned.
return int((u32(v)-1) >> 31)
return byte((u32(v)-1) >> 31)
}
// u64_eq returns 1 if and only if (⟺) a == b, 0 otherwise.
@(optimization_mode="none")
@(optimization_mode="none", require_results)
u32_eq :: proc "contextless" (a, b: u32) -> u32 {
q := a ~ b
return ((q | -q) >> 31) ~ 1
}
@(optimization_mode="none", require_results)
u64_eq :: proc "contextless" (a, b: u64) -> u64 {
_, borrow := bits.sub_u64(0, a ~ b, 0)
return (~borrow) & 1
q := a ~ b
return ((q | -q) >> 63) ~ 1
}
// eq returns 1 if and only if (⟺) a == b, 0 otherwise.
eq :: proc {
byte_eq,
u32_eq,
u64_eq,
}
// u64_is_zero returns 1 if and only if (⟺) a == 0, 0 otherwise.
@(optimization_mode="none")
u64_is_zero :: proc "contextless" (a: u64) -> u64 {
_, borrow := bits.sub_u64(a, 1, 0)
return borrow
@(require_results)
byte_neq :: proc "contextless" (a, b: byte) -> byte {
return #force_inline byte_eq(a, b) ~ 1
}
// u64_is_non_zero returns 1 if and only if (⟺) a != 0, 0 otherwise.
@(optimization_mode="none")
u64_is_non_zero :: proc "contextless" (a: u64) -> u64 {
is_zero := u64_is_zero(a)
return (~is_zero) & 1
@(optimization_mode="none", require_results)
u32_neq :: proc "contextless" (a, b: u32) -> u32 {
q := a ~ b
return (q | -q) >> 31
}
@(optimization_mode="none")
cmov_bytes :: proc "contextless" (dst, src: []byte, ctrl: int) {
@(optimization_mode="none", require_results)
u64_neq :: proc "contextless" (a, b: u64) -> u64 {
q := a ~ b
return (q | -q) >> 63
}
// neq returns 1 if and only if (⟺) a != b, 0 otherwise.
neq :: proc {
byte_neq,
u32_neq,
u64_neq,
}
@(optimization_mode="none", require_results)
u32_gt :: proc "contextless" (x, y: u32) -> u32 {
/*
* If both x < 2^31 and y < 2^31, then y-x will have its high
* bit set if x > y, cleared otherwise.
*
* If either x >= 2^31 or y >= 2^31 (but not both), then the
* result is the high bit of x.
*
* If both x >= 2^31 and y >= 2^31, then we can virtually
* subtract 2^31 from both, and we are back to the first case.
* Since (y-2^31)-(x-2^31) = y-x, the subtraction is already
* fine.
*/
z := y - x
return (z ~ ((x ~ y) & (x ~ z))) >> 31
}
@(optimization_mode="none", require_results)
u64_gt :: proc "contextless" (x, y: u64) -> u64 {
z := y - x
return (z ~ ((x ~ y) & (x ~ z))) >> 63
}
// gt returns 1 if x > y, 0 otherwise.
gt :: proc {
u32_gt,
u64_gt,
}
// gt returns 1 if x >= y, 0 otherwise.
@(require_results)
ge :: proc "contextless" (x, y: $T) -> T where T == u32 || T == u64 {
return #force_inline(gt(y, x)) ~ 1
}
// lt returns 1 if x < y, 0 otherwise.
@(require_results)
lt :: proc "contextless" (x, y: $T) -> T where T == u32 || T == u64 {
return #force_inline(gt(y, x))
}
// le returns 1 if x <= y, 0 otherwise.
@(require_results)
le :: proc "contextless" (x, y: $T) -> T where T == u32 || T == u64 {
return #force_inline(gt(x, y)) ~ 1
}
@(require_results)
u32_cmp :: proc "contextless" (x, y: u32) -> i32 {
return i32(#force_inline gt(x, y)) | -i32(#force_inline gt(y, x))
}
@(require_results)
u64_cmp :: proc "contextless" (x, y: u64) -> i64 {
return i64(#force_inline gt(x, y)) | -i64(#force_inline gt(y, x))
}
// cmp returns -1, 0, or 1, depending on wheter x is lower than, equal
// to, or greater than y.
cmp :: proc {
u32_cmp,
u64_cmp,
}
// eq0 returns 1 if and only if (⟺) a == 0, 0 otherwise.
@(require_results)
eq0 :: proc "contextless" (a: $T) -> T where T == u32 || T == u64 {
return #force_inline eq(a, 0)
}
// neq0 returns 1 if and only if (⟺) a != 0, 0 otherwise.
@(require_results)
neq0 :: proc "contextless" (a: $T) -> T where T == u32 || T == u64 {
return #force_inline eq(a, 0) ~ 1
}
cmov_bytes :: proc "contextless" (dst, src: []byte, #any_int ctrl: int) {
ensure_contextless(len(src) == len(dst), "crypto: cmov length mismatch")
cmov_impl(dst, src, ctrl)
}
cmov_u32s :: proc "contextless" (dst, src: []u32, #any_int ctrl: int) {
ensure_contextless(len(src) == len(dst), "crypto: cmov length mismatch")
cmov_impl(dst, src, ctrl)
}
@(private="file", optimization_mode="none")
cmov_impl :: proc "contextless"(dst, src: []$T, ctrl: int) {
s_len := len(src)
ensure_contextless(s_len == len(dst), "crypto: cmov length mismatch")
c := -(byte)(ctrl)
c := -(T)(ctrl)
for i in 0..<s_len {
dst[i] ~= c & (dst[i] ~ src[i])
}
}
@(optimization_mode="none")
csel_i16 :: proc "contextless" (a, b: i16, ctrl: int) -> i16 {
c := -(u16)(ctrl)
// cmov copies `src` into `dst` if and only if (⟺) ctrl == 1. `dst` and
// `src` may overlap completely (but not partially).
cmov :: proc {
cmov_bytes,
cmov_u32s,
}
@(optimization_mode="none", require_results)
csel_i16 :: proc "contextless" (a, b: i16, #any_int ctrl: u16) -> i16 {
c := -ctrl
return a ~ i16(c & u16(a ~ b))
}
@(optimization_mode="none")
csel_u16 :: proc "contextless" (a, b: u16, ctrl: int) -> u16 {
c := -(u16)(ctrl)
@(optimization_mode="none", require_results)
csel_u16 :: proc "contextless" (a, b: u16, #any_int ctrl: u16) -> u16 {
c := -ctrl
return a ~ (c & (a ~ b))
}
csel_u32 :: proc "contextless" (a, b: u32, ctrl: int) -> u32 {
return _fiat.cmovznz_u32(_fiat.u1(ctrl), a, b)
@(optimization_mode="none", require_results)
csel_u32 :: proc "contextless" (a, b: u32, #any_int ctrl: u32) -> u32 {
c := -ctrl
return a ~ (c & (a ~ b))
}
csel_u64 :: proc "contextless" (a, b: u64, ctrl: int) -> u64 {
return _fiat.cmovznz_u64(_fiat.u1(ctrl), a, b)
@(optimization_mode="none", require_results)
csel_u64 :: proc "contextless" (a, b: u64, #any_int ctrl: u64) -> u64 {
c := -ctrl
return a ~ (c & (a ~ b))
}
// csel returns `a` if ctl == `0`, `b` if ctl == `1`.
csel :: proc {
csel_i16,
csel_u16,
+2 -2
View File
@@ -196,10 +196,10 @@ fe_gen_y_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) {
@(require_results)
fe_is_zero_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) -> int {
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
return int(subtle.eq0(p256r1.fe_non_zero(fe)))
}
@(require_results)
fe_is_zero_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) -> int {
return int(subtle.u64_is_zero(p384r1.fe_non_zero(fe)))
return int(subtle.eq0(p384r1.fe_non_zero(fe)))
}
+2 -2
View File
@@ -133,10 +133,10 @@ sc_is_zero :: proc {
@(require_results)
sc_is_zero_p256r1 :: proc "contextless" (fe: ^Scalar_p256r1) -> int {
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
return int(subtle.eq0(p256r1.fe_non_zero(fe)))
}
@(require_results)
sc_is_zero_p384r1 :: proc "contextless" (fe: ^Scalar_p384r1) -> int {
return int(subtle.u64_is_zero(p384r1.fe_non_zero(fe)))
return int(subtle.eq0(p384r1.fe_non_zero(fe)))
}
+1 -1
View File
@@ -293,7 +293,7 @@ when crypto.COMPACT_IMPLS == false {
// conditionally select the right result.
pt_add_mixed(tmp, point, &tmp.x, &tmp.y)
ctrl := subtle.u64_is_non_zero(idx)
ctrl := subtle.neq0(idx)
pt_cond_select(point, point, tmp, int(ctrl))
}
}
+2 -2
View File
@@ -49,7 +49,7 @@ compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> i
// After the loop, v == 0 if and only if (⟺) a == b. The subtraction will underflow
// if and only if (⟺) v == 0, setting the sign-bit, which gets returned.
return subtle.eq(0, v)
return int(subtle.eq(0, v))
}
// is_zero_constant_time returns 1 if and only if (⟺) b is all 0s, 0 otherwise.
@@ -59,7 +59,7 @@ is_zero_constant_time :: proc "contextless" (b: []byte) -> int {
v |= b_
}
return subtle.byte_eq(0, v)
return int(subtle.byte_eq(0, v))
}
/*
+6 -3
View File
@@ -50,6 +50,7 @@ Public_Key :: struct {
// private_key_generate uses the system entropy source to generate a new
// Private_Key. This will only fail if and only if (⟺) the system entropy source is
// missing or broken.
@(require_results)
private_key_generate :: proc(priv_key: ^Private_Key) -> bool {
private_key_clear(priv_key)
@@ -61,13 +62,12 @@ private_key_generate :: proc(priv_key: ^Private_Key) -> bool {
defer crypto.zero_explicit(&b, size_of(b))
crypto.rand_bytes(b[:])
private_key_set_bytes(priv_key, b[:])
return true
return private_key_set_bytes(priv_key, b[:])
}
// private_key_set_bytes decodes a byte-encoded private key, and returns
// true if and only if (⟺) the operation was successful.
@(require_results)
private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
if len(b) != PRIVATE_KEY_SIZE {
return false
@@ -189,6 +189,7 @@ sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
// public_key_set_bytes decodes a byte-encoded public key, and returns
// true if and only if (⟺) the operation was successful.
@(require_results)
public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) -> bool {
if len(b) != PUBLIC_KEY_SIZE {
return false
@@ -237,6 +238,7 @@ public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
}
// public_key_equal returns true if and only if (⟺) pub_key is equal to other.
@(require_results)
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
ensure(pub_key._is_initialized && other._is_initialized, "crypto/ed25519: uninitialized public key")
@@ -254,6 +256,7 @@ public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
// implementation strictly compatible with FIPS 186-5, at the expense of
// SBS-security. Doing so is NOT recommended, and the disallowed
// public keys all have a known discrete-log.
@(require_results)
verify :: proc(pub_key: ^Public_Key, msg, sig: []byte, allow_small_order_A := false) -> bool {
switch {
case !pub_key._is_initialized:
+7
View File
@@ -0,0 +1,7 @@
/*
RSA (RivestShamirAdleman) cryptosystem.
See:
- [[ https://www.rfc-editor.org/info/rfc8017/ ]]
*/
package rsa
+444
View File
@@ -0,0 +1,444 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:bytes"
import "core:crypto"
import subtle "core:crypto/_subtle"
import "core:encoding/endian"
// Minimum size for a RSA modulus (in bits).
//
// Note: 1024-bits is arguably insufficient as of this writing, with
// 2048-bits being a more sensible value, however 1024-bits is likely
// still in frequent enough use.
//
// Note: CA signed TLS certificates have a strict requirement of a modulus
// size that is at least 2048-bits [[ https://cabforum.org/working-groups/server/baseline-requirements/documents/]].
MODULUS_MIN_SIZE :: 1024
// Maximum size for a RSA modulus (in bits).
//
// This value MUST be a multiple of 64. This value MUST NOT exceed 47666
// (some computations in RSA key generation rely on the factor size being
// no more than 23833 bits). RSA key sizes beyond 3072 bits don't make a
// lot of sense anyway.
MODULUS_MAX_SIZE :: 4096
// Maxmimum size for a RSA public exponent (in bits).
//
// Note: This implementation supports arbitrary size exponents, however
// limit it to something sensible (some implementations are known to
// choke on exponents >= 2^32), with the most common choice being
// `65537`.
EXPONENT_MAX_SIZE :: 32
// Maximum size for a RSA factor (in bits). This is for RSA private-key
// operations. Default is to support factors up to a bit more than half
// the maximum modulus size.
//
// This value MUST be a multiple of 32.
FACTOR_MAX_SIZE :: (MODULUS_MAX_SIZE + 64) >> 1
// Default size for a RSA key (in bits).
DEFAULT_MODULUS_SIZE :: 2048
// RSA public exponent used for key generation. This MUST be a prime
// number greater than 2.
@(private)
PUBLIC_EXPONENT :: 65537
#assert(EXPONENT_MAX_SIZE <= 32)
// Private_Key is a RSA private key.
Private_Key :: struct {
_pub_key: Public_Key,
_d: Modulus, // Private exponent has the same size as n.
_p: Factor,
_q: Factor,
// CRT coefficients.
_dp: Factor, // d % (p - 1)
_dq: Factor, // d % (q - 1)
_iq: Factor, // q^(-1) mod p
_is_initialized: bool,
}
// Public_Key is a RSA public key.
Public_Key :: struct {
_n: Modulus,
_e: u32,
_is_initialized: bool,
}
// private_key_generate uses the system entropy source to generate a new
// Private_Key. The key size is specified in bits, and must be a multiple
// of 8.
@(require_results)
private_key_generate :: proc(priv_key: ^Private_Key, key_size := DEFAULT_MODULUS_SIZE) -> bool {
if !crypto.HAS_RAND_BYTES {
return false
}
if key_size < MODULUS_MIN_SIZE || key_size > MODULUS_MAX_SIZE {
return false
}
if key_size % 8 != 0 {
return false
}
private_key_clear(priv_key)
defer if !priv_key._is_initialized {
private_key_clear(priv_key)
}
for {
// The only way this can fail is if we get extremely unlucky
// and we fail to derive `iq` (1/d mod p).
if keygen_inner(priv_key, key_size) == 1 {
break
}
}
priv_key._is_initialized = true
priv_key._pub_key._is_initialized = true
// Self-test the key.
priv_key._is_initialized = pkcs1_sig_selftest(priv_key)
return priv_key._is_initialized
}
// private_key_n copies the private key's public modulus to dst if dst is
// non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with `dst = nil` gets the required
// size).
@(require_results)
private_key_n :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return public_key_n(&priv_key._pub_key, dst)
}
// private_key_e returns the private key's public exponent as a u32.
@(require_results)
private_key_e :: proc(priv_key: ^Private_Key) -> u32 {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return public_key_e(&priv_key._pub_key)
}
// private_key_d copies the private key's private exponent `d` to dst if
// dst is non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with `dst = nil` gets the required
// size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_d :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return modulus_copyout(&priv_key._d, dst)
}
// private_key_p copies the private key's first prime factor `p` to dst
// if dst is non-nil and of sufficient size, and returns the number of
// bytes copied/would be copied (ie: calling with `dst = nil` gets the
// required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_p :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._p, dst)
}
// private_key_q copies the private key's second prime factor `q` to dst
// if dst is non-nil and of sufficient size, and returns the number of
// bytes copied/would be copied (ie: calling with `dst = nil` gets the
// required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_q :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._q, dst)
}
// private_key_dp copies the private key's first reduced exponent
// `d % (p-1)` to dst if dst is non-nil and of sufficient size, and
// returns the number of bytes copied/would be copied (ie: calling with
//`dst = nil` gets the required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_dp :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._dp, dst)
}
// private_key_dq copies the private key's second reduced exponent
// `d % (q-1)` to dst if dst is non-nil and of sufficient size, and
// returns the number of bytes copied/would be copied (ie: calling with
//`dst = nil` gets the required size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_dq :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._dq, dst)
}
// private_key_iq copies the private key's CRT coefficient `iq` to dst if
// dst is non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with`dst = nil` gets the required
// size).
//
// Note: The data returned MUST be kept confidential.
@(require_results)
private_key_iq :: proc(priv_key: ^Private_Key, dst: []byte) -> (n_len: int) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return factor_copyout(&priv_key._iq, dst)
}
// private_key_size returns the size of the private key's public modulus
// in bytes. All ciphertexts and signatures will also be this size.
@(require_results)
private_key_size :: proc(priv_key: ^Private_Key) -> int {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
return priv_key._pub_key._n.v_len
}
// private_key_set_bytes sets a private key from byte-encoded components,
// and returns true if and only if (⟺) the operation was successful.
//
// Note: All values are mandatory, and match the values included in the
// PKCS private key format.
//
// WARNING: This routine validates that it is possible to sign/verify with
// the deserialized values, however d is not checked at all, nor is the
// primality of p and q.
@(require_results)
private_key_set_bytes :: proc(
priv_key: ^Private_Key,
n: []byte,
e: []byte,
d: []byte,
p: []byte,
q: []byte,
dp: []byte,
dq: []byte,
iq: []byte,
) -> bool {
private_key_clear(priv_key)
defer if !priv_key._is_initialized {
private_key_clear(priv_key)
}
if !public_key_set_bytes(&priv_key._pub_key, n, e) {
return false
}
if !modulus_set_bytes(&priv_key._d, d) {
return false
}
if !factor_set_bytes(&priv_key._p, p) {
return false
}
if !factor_set_bytes(&priv_key._q, q) {
return false
}
if !factor_set_bytes(&priv_key._dp, dp) {
return false
}
if !factor_set_bytes(&priv_key._dq, dq) {
return false
}
if !factor_set_bytes(&priv_key._iq, iq) {
return false
}
priv_key._is_initialized = true
// Test the key.
//
// Note: This DOES NOT check that p/q are prime and if d is
// consistent (as it is not used by our implementation).
priv_key._is_initialized = pkcs1_sig_selftest(priv_key)
return priv_key._is_initialized
}
// private_key_set sets priv_key to src.
private_key_set :: proc(priv_key, src: ^Private_Key) {
if src == nil || !src._is_initialized {
private_key_clear(priv_key)
return
}
public_key_set(&priv_key._pub_key, &src._pub_key)
modulus_set(&priv_key._d, &src._d)
factor_set(&priv_key._p, &src._p)
factor_set(&priv_key._q, &src._q)
factor_set(&priv_key._dp, &src._dp)
factor_set(&priv_key._dq, &src._dq)
factor_set(&priv_key._iq, &src._iq)
priv_key._is_initialized = true
}
// private_key_equal returns true if and only if (⟺) priv_key is equal to other.
@(require_results)
private_key_equal :: proc(priv_key, other: ^Private_Key) -> bool {
ensure(priv_key._is_initialized && other._is_initialized, "crypto/rsa: uninitialized private key")
pk_eq := public_key_equal(&priv_key._pub_key, &other._pub_key)
eq := crypto.compare_constant_time(modulus_bytes(&priv_key._d), modulus_bytes(&other._d))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._p), factor_bytes(&other._p))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._q), factor_bytes(&other._q))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._dp), factor_bytes(&other._dp))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._dq), factor_bytes(&other._dq))
eq &= crypto.compare_constant_time(factor_bytes(&priv_key._iq), factor_bytes(&other._iq))
return pk_eq & (eq == 1)
}
// private_key_clear clears priv_key to the uninitialized state.
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
crypto.zero_explicit(priv_key, size_of(Private_Key))
}
// public_key_n copies the public key's modulus `n` to dst if dst is
// non-nil and of sufficient size, and returns the number of bytes
// copied/would be copied (ie: calling with `dst = nil` gets the
// required size).
@(require_results)
public_key_n :: proc(pub_key: ^Public_Key, dst: []byte) -> (n_len: int) {
ensure(pub_key._is_initialized, "crypto/rsa: uninitialized public key")
return modulus_copyout(&pub_key._n, dst)
}
// public_key_e returns the public key's exponent `e` as a u32.
@(require_results)
public_key_e :: proc(pub_key: ^Public_Key) -> u32 {
ensure(pub_key._is_initialized, "crypto/rsa: uninitialized public key")
return pub_key._e
}
// public_key_size returns the size of the public key's modulus in bytes.
// All ciphertexts and signatures will also be this size.
@(require_results)
public_key_size :: proc(pub_key: ^Public_Key) -> int {
ensure(pub_key._is_initialized, "crypto/rsa: uninitialized public key")
return pub_key._n.v_len
}
// public_key_set_bytes sets a public key from byte-encoded components,
// and returns true if and only if (⟺) the operation was successful.
@(require_results)
public_key_set_bytes :: proc(pub_key: ^Public_Key, n, e: []byte) -> bool {
public_key_clear(pub_key)
defer if !pub_key._is_initialized {
public_key_clear(pub_key)
}
ok := modulus_set_bytes(&pub_key._n, n)
if !ok {
return false
}
if modulus_len(&pub_key._n) < MODULUS_MIN_SIZE >> 3 {
return false
}
if !modulus_is_odd(&pub_key._n) {
return false
}
e_ := bytes.trim_left(e, []byte{0x00})
e_len := len(e_)
if e_len > EXPONENT_MAX_SIZE >> 3 {
return false
}
e_buf: [4]byte
copy(e_buf[4 - e_len:], e)
e_u32 := endian.unchecked_get_u32be(e_buf[:])
if e_u32 < 3 || e_u32 & 1 == 0 {
return false
}
pub_key._e = e_u32
pub_key._is_initialized = true
return true
}
// public_key_set sets pub_key to src.
public_key_set :: proc(pub_key, src: ^Public_Key) {
if src == nil || !src._is_initialized {
public_key_clear(pub_key)
return
}
modulus_set(&pub_key._n, &src._n)
pub_key._e = src._e
pub_key._is_initialized = true
}
// public_key_set_priv sets pub_key to the public component of priv_key.
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
ensure(priv_key._is_initialized, "crypto/rsa: uninitialized private key")
pub_key^ = priv_key._pub_key
}
// public_key_equal returns true if and only if (⟺) pub_key is equal to other.
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
ensure(pub_key._is_initialized && other._is_initialized, "crypto/rsa: uninitialized public key")
eq := crypto.compare_constant_time(modulus_bytes(&pub_key._n), modulus_bytes(&other._n))
eq &= int(subtle.eq(pub_key._e, other._e))
return eq == 1
}
// public_key_clear clears pub_key to the uninitialized state.
public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
crypto.zero_explicit(pub_key, size_of(Public_Key))
}
// size returns the size of the key's public modulus in bytes.
// All ciphertexts and signatures will also be this size.
size :: proc "contextless" (key: ^$T) -> int where T == Private_Key || T == Private_Key {
when T == Private_Key {
return private_key_size(key)
} else {
return public_key_size(key)
}
}
+197
View File
@@ -0,0 +1,197 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto"
import subtle "core:crypto/_subtle"
import "core:crypto/hash"
// decrypt_oaep returns the plaintext and true if and only if (⟺) it
// successfully decrypts the ciphertext with OAEP parameterized by
// label, hash_algo, and mgf1_algo, and writes the plaintext into dst.
// If mgf1_algo is unspecified, hash_algo will be used.
//
// Note: dst MUST be large enough to contain the plaintext.
@(require_results)
decrypt_oaep :: proc(
priv_key: ^Private_Key,
hash_algo: hash.Algorithm,
ciphertext: []byte,
dst: []byte,
label: []byte = nil,
mgf1_algo := hash.Algorithm.Invalid,
) -> (plaintext: []byte, ok: bool) {
if !priv_key._is_initialized {
return
}
ct_len := len(ciphertext)
if ct_len != modulus_len(&priv_key._pub_key._n) {
return
}
if hash_algo == .Invalid {
return
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
tmp: [MODULUS_MAX_SIZE >> 3]byte
pt_buf := tmp[:ct_len]
defer crypto.zero_explicit(raw_data(pt_buf), ct_len)
copy(pt_buf, ciphertext)
r := private_modpow(pt_buf, priv_key)
r_, l := oaep_dec_unpad(hash_algo, mgf1_algo_, label, pt_buf)
// Conditional branches are ok as we are past the padding
// verification.
if ok = r & r_ == 1; ok {
if l <= len(dst) {
copy(dst, pt_buf[:l])
plaintext = dst[:l]
} else {
ok = false
}
}
return
}
// oaep_max_plaintext_size returns the maximum supported plaintext size
// for a given key, with OAEP parameterized by hash_algo and mgf1_algo.
// If mgf1_algo is unspecified, hash_algo will be used.
@(require_results)
oaep_max_plaintext_size :: proc(
k: ^$T,
hash_algo: hash.Algorithm,
mgf1_algo := hash.Algorithm.Invalid,
) -> int where T == Private_Key || T == Public_Key {
if !k._is_initialized {
return 0
}
if hash_algo == .Invalid {
return 0
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
overhead := 2 + hash.DIGEST_SIZES[hash_algo] + hash.DIGEST_SIZES[mgf1_algo_]
pub_key: ^Public_Key
when T == Private_Key {
pub_keyk = &k._pub_key
} else {
pub_key = k
}
return modulus_len(&k._n) - overhead
}
@(private="file")
xor_hash_data :: proc(hash_algo: hash.Algorithm, dst: []byte, src: []byte) {
tmp: [hash.MAX_DIGEST_SIZE]byte = ---
hash_len := hash.DIGEST_SIZES[hash_algo]
digest := tmp[:hash_len]
defer crypto.zero_explicit(raw_data(digest), hash_len)
hash.hash_bytes_to_buffer(hash_algo, src, digest)
for v, u in digest {
dst[u] ~= v
}
}
@(private="file")
oaep_dec_unpad :: proc(
hash_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
label: []byte,
data: []byte,
) -> (u32, int) {
hash_len := hash.DIGEST_SIZES[hash_algo]
k := len(data)
buf := data
// There must be room for the padding.
if k < (hash_len << 1) + 2 {
return 0, 0
}
// Unmask the seed, then the DB value.
seed, db := buf[1:1+hash_len], buf[1+hash_len:]
mgf1_xor(seed, mgf1_algo, db)
mgf1_xor(db, mgf1_algo, seed)
// Hash the label and XOR it with the value in the array; if
// they are equal then these should yield only zeros.
xor_hash_data(hash_algo, db, label)
// At that point, if the padding was correct, when we should
// have: 0x00 || seed || 0x00 ... 0x00 0x01 || M
// Padding is valid as long as:
// - There is at least hlen+1 leading bytes of value 0x00.
// - There is at least one non-zero byte.
// - The first (leftmost) non-zero byte has value 0x01.
//
// Ultimately, we may leak the resulting message length, i.e.
// the position of the byte of value 0x01, but we must take care
// to do so only if the number of zero bytes has been verified
// to be at least hlen+1.
//
// The loop below counts the number of bytes of value 0x00, and
// checks that the next byte has value 0x01, in constant-time.
//
// - If the initial byte (before the seed) is not 0x00, then
// r and s are set to 0, and stay there.
// - Value r is 1 until the first non-zero byte is reached
// (after the seed); it switches to 0 at that point.
// - Value s is set to 1 if and only if the data encountered
// at the time of the transition of r from 1 to 0 has value
// exactly 0x01.
// - Value zlen counts the number of leading bytes of value zero
// (after the seed).
r := u32(subtle.eq(buf[0], 0))
s, zlen: u32
for u in hash_len + 1..<k {
w := u32(buf[u])
// nz == 1 only for the first non-zero byte.
nz := r & ((w + 0xFF) >> 8)
s |= nz & subtle.eq(w, 0x01)
r &= subtle.not(nz)
zlen += r
}
// Padding is correct only if s == 1, _and_ zlen >= hlen.
s &= subtle.ge(zlen, u32(hash_len))
// At that point, padding was verified, and we are now allowed
// to make conditional jumps.
if s != 0 {
plen := 2 + hash_len + int(zlen)
k -= plen
copy(buf[:k], buf[plen:])
}
return s, k
}
+55
View File
@@ -0,0 +1,55 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import subtle "core:crypto/_subtle"
// unsafe_decrypt_tls_pms decrypts a TLS RSA-Encrypted Premaster Secret
// Message, unconditionally moves the decrypted plaintext to `data[:48]`,
// and returns 1 if and only if (⟺) the operation was successful.
//
// WARNING: This routine MUST only be used when implementing server-side
// support for TLS 1.2's Client Key Exchange message, and extreme care
// MUST be taken when handling failures. This key exchange scheme was
// removed in TLS 1.3, and not implementing support in the first place
// is strongly RECOMMENDED even for TLS 1.2 servers.
@(require_results)
unsafe_decrypt_tls_pms :: proc(priv_key: ^Private_Key, data: []byte) -> u32 {
// A first check on length. Since this test works only on the
// buffer length, it needs not (and cannot) be constant-time.
_len := len(data)
if _len < 59 || _len != priv_key._pub_key._n.v_len {
return 0
}
x := private_modpow(data, priv_key)
x &= u32(subtle.eq(data[0], 0x00))
x &= u32(subtle.eq(data[1], 0x02))
for u in 2..<(_len-49) {
x &= u32(subtle.neq(data[u], 0))
}
x &= u32(subtle.eq(data[_len - 49], 0x00))
copy(data[:48], data[_len - 48:])
return x
}
+105
View File
@@ -0,0 +1,105 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:crypto"
import "core:crypto/hash"
// encrypt_oaep returns true if and only if (⟺) it successfully
// encrypts the plaintext with OAEP parameterized by label, hash_algo,
// and mgf1_algo, and writes the cipherttext into dst. If mgf1_algo is
// unspecified, hash_algo will be used.
//
// This routine will fail if the system entropy source is unavailable.
encrypt_oaep :: proc(
pub_key: ^Public_Key,
hash_algo: hash.Algorithm,
plaintext: []byte,
dst: []byte,
label: []byte = nil,
mgf1_algo := hash.Algorithm.Invalid,
) -> bool {
if !pub_key._is_initialized {
return false
}
if hash_algo == .Invalid {
return false
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
if len(dst) != modulus_len(&pub_key._n) {
return false
}
if len(plaintext) > oaep_max_plaintext_size(pub_key, hash_algo, mgf1_algo_) {
return false
}
if oaep_enc_pad(hash_algo, mgf1_algo_, label, dst, plaintext) != 1 {
return false
}
return public_modpow(dst, pub_key) == 1
}
@(private="file")
oaep_enc_pad :: proc(
hash_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
label: []byte,
dst: []byte,
src: []byte,
) -> u32 {
hash_len := hash.DIGEST_SIZES[hash_algo]
src_len := len(src)
k := len(dst)
// Note: Length checks are handled by the caller.
// Apply padding. At this point, things cannot fail.
buf := dst
// Assemble: DB = lHash || PS || 0x01 || M
// We first place the source message M with copy(), so that
// overlaps between source and destination buffers are supported.
copy(buf[k - src_len:], src)
hash.hash_bytes_to_buffer(hash_algo, label, buf[1+hash_len:1+hash_len << 1])
intrinsics.mem_zero(raw_data(buf[1 + hash_len << 1:]), k - src_len - (hash_len << 1) - 2)
buf[k - src_len - 1] = 0x01
// Make the random seed.
seed, db := buf[1:1+hash_len], buf[1+hash_len:]
crypto.rand_bytes(seed)
// Mask DB with the mask generated from the seed.
mgf1_xor(db, mgf1_algo, seed)
// Mask the seed with the mask generated from the masked DB.
mgf1_xor(seed, mgf1_algo, db)
// Padding result: EM = 0x00 || maskedSeed || maskedDB.
buf[0] = 0x00
return 1
}
+110
View File
@@ -0,0 +1,110 @@
#+private
package rsa
import "core:bytes"
Big_Int :: struct($N: int) {
v: [N]byte,
v_len: int,
}
Modulus :: Big_Int(MODULUS_MAX_SIZE >> 3)
Factor :: Big_Int(FACTOR_MAX_SIZE >> 3)
@(require_results)
modulus_set_bytes :: proc(n: ^Modulus, b: []byte) -> bool {
b_ := bytes.trim_left(b, []byte{0x00})
b_len := len(b_)
if b_len > size_of(n.v) || b_len == 0 {
return false
}
copy(n.v[:], b_)
n.v_len = b_len
return true
}
modulus_set :: proc "contextless" (n, other: ^Modulus) {
// Copy the full thing.
copy(n.v[:], other.v[:])
n.v_len = other.v_len
}
@(require_results)
modulus_bytes :: #force_inline proc "contextless" (n: ^Modulus) -> []byte {
return n.v[:n.v_len]
}
@(require_results)
modulus_len :: #force_inline proc "contextless" (n: ^Modulus) -> int {
return n.v_len
}
@(require_results)
modulus_copyout :: proc(n: ^Modulus, dst: []byte) -> (n_len: int) {
if n_len = modulus_len(n); n_len == 0 {
return
}
if len(dst) > 0 {
ensure(len(dst) >= n_len, "crypto/rsa: insufficent buffer size")
copy(dst, modulus_bytes(n))
}
return
}
@(require_results)
modulus_is_odd :: proc "contextless" (n: ^Modulus) -> bool {
if n.v_len == 0 || n.v[n.v_len-1] & 1 == 0 {
return false
}
return true
}
@(require_results)
factor_set_bytes :: proc(n: ^Factor, b: []byte) -> bool {
b_ := bytes.trim_left(b, []byte{0x00})
b_len := len(b_)
if b_len > size_of(n.v) || b_len == 0 {
return false
}
copy(n.v[:], b_)
n.v_len = b_len
return true
}
factor_set :: proc "contextless" (n, other: ^Factor) {
// Copy the full thing.
copy(n.v[:], other.v[:])
n.v_len = other.v_len
}
@(require_results)
factor_bytes :: #force_inline proc "contextless" (n: ^Factor) -> []byte {
return n.v[:n.v_len]
}
@(require_results)
factor_len :: #force_inline proc "contextless" (n: ^Factor) -> int {
return n.v_len
}
@(require_results)
factor_copyout :: proc(n: ^Factor, dst: []byte) -> (n_len: int) {
if n_len = factor_len(n); n_len == 0 {
return
}
if len(dst) > 0 {
ensure(len(dst) >= n_len, "crypto/rsa: insufficent buffer size")
copy(dst, factor_bytes(n))
}
return
}
+367
View File
@@ -0,0 +1,367 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto"
import bigint "core:crypto/_bigint"
import subtle "core:crypto/_subtle"
import "core:slice"
// Swap two buffers in RAM. They must be disjoint.
@(private="file")
bufswap_u32 :: proc "contextless" (b1, b2: []u32) {
l := len(b1)
for u in 0..<l {
b1[u], b2[u] = b2[u], b1[u]
}
}
@(private, require_results)
keygen_inner :: proc(sk: ^Private_Key, key_size: int) -> u32 {
// We need temporary values for at least 7 integers of the same size
// as a factor (including header word); more space helps with performance
// (in modular exponentiations), but we much prefer to remain under
// 2 kilobytes in total, to save stack space. The macro TEMPS below
// exceeds 512 (which is a count in 32-bit words) when MODULUS_MAX_SIZE
// is greater than 4464 (default value is 4096, so the 2-kB limit is
// maintained unless MODULUS_MAX_SIZE was modified).
TEMPS :: max(512, ((((7 * ((((MODULUS_MAX_SIZE + 1) >> 1) + 61) / 31))) + 1) >> 1) << 1)
assert(key_size >= MODULUS_MIN_SIZE && key_size <= MODULUS_MAX_SIZE)
t64: [TEMPS >> 1]u64
t32 := slice.reinterpret([]u32, t64[:])
defer crypto.zero_explicit(&t64, size_of(t64))
esize_p := u32(key_size + 1) >> 1
esize_q := u32(key_size) - esize_p
sk._p.v_len = int((esize_p + 7) >> 3)
sk._q.v_len = int((esize_q + 7) >> 3)
sk._dp.v_len = sk._p.v_len
sk._dq.v_len = sk._q.v_len
sk._iq.v_len = sk._p.v_len
pk := &sk._pub_key
pk._n.v_len = (key_size + 7) >> 3
pk._e = PUBLIC_EXPONENT
sk._d.v_len = pk._n.v_len // Private exponent length is that of the modulus.
// We now switch to encoded sizes.
//
// floor((x * 16913) / (2^19)) is equal to floor(x/31) for all
// integers x from 0 to 34966; the intermediate product fits on
// 30 bits, thus we can use MUL31().
esize_p += u32(bigint._mul31(esize_p, 16913) >> 19)
esize_q += u32(bigint._mul31(esize_q, 16913) >> 19)
plen := (esize_p + 31) >> 5
qlen := (esize_q + 31) >> 5
p := t32
q := p[1 + plen:]
t := q[1 + qlen:]
// Since we use a prime exponent, when searching for candidate primes,
// checking if `GCD(e, prime - 1) = 1` is a simple matter of euclidian
// division.
for {
bigint.i62_mkprime(p, esize_p, PUBLIC_EXPONENT, t)
p[1] -= 1
if bigint.i31_rem(p, PUBLIC_EXPONENT) != 0 {
p[1] += 1
break
}
}
for {
bigint.i62_mkprime(q, esize_q, PUBLIC_EXPONENT, t)
q[1] -= 1
if bigint.i31_rem(q, PUBLIC_EXPONENT) != 0 {
q[1] += 1
break
}
}
// If p and q have the same size, then it is possible that q > p
// (when the target modulus size is odd, we generate p with a
// greater bit length than q). If q > p, we want to swap p and q
// for two reasons:
// - The final step below (inversion of q modulo p) is easier if
// p > q.
// - While BearSSL's RSA code is perfectly happy with RSA keys such
// that p < q, some other implementations have restrictions and
// require p > q.
//
// Note that we can do a simple non-constant-time swap here,
// because the only information we leak here is that we insist on
// returning p and q such that p > q, which is not a secret.
if esize_p == esize_q && bigint.i31_sub(p, q, 0) == 1 {
bufswap_u32(p[:1+plen], q)
}
sk_p, sk_q := factor_bytes(&sk._p), factor_bytes(&sk._q)
bigint.i31_encode(sk_p, p)
bigint.i31_encode(sk_q, q)
// The odds of this happening are infinitesimally small, however
// checking for it is cheap.
if crypto.compare_constant_time(sk_p, sk_q) == 1 {
return 0
}
// Compute the public modulus too.
bigint.i31_zero(t, p[0])
bigint.i31_mulacc(t, p, q)
bigint.i31_encode(modulus_bytes(&pk._n), t)
// Compute the private exponent.
//
// Computing p - 1 and q - 1 this way is safe as p and q
// are guaranteed to be odd, thus the LSB will always be
// set.
p[1], q[1] = p[1] - 1, q[1] - 1 // p = p - 1, q = q - 1
if compute_privexp(sk, p, q, pk._e, t) != 1 {
return 0
}
// Compute `d % (p - 1)`.
d_mod := t[:1+plen]
bigint.i31_decode_reduce(d_mod, modulus_bytes(&sk._d), p)
bigint.i31_encode(factor_bytes(&sk._dp), d_mod)
// Compute `d % (q - 1)`.
bigint.i31_decode_reduce(d_mod, modulus_bytes(&sk._d), q)
bigint.i31_encode(factor_bytes(&sk._dq), d_mod)
// Compute `q^(-1) mod p`.
p[1], q[1] = p[1] + 1, q[1] + 1 // Restore p, q.
return compute_qinv(sk, p, q, plen, t)
}
@(private="file")
compute_qinv :: proc "contextless" (sk: ^Private_Key, p, q: []u32, plen: u32, t: []u32) -> u32 {
// Per Fermat's Little Theorem, `q^(-1) mod p = q^(p-2) mod p`.
//
// Note: p is guaranteed to be odd as it is a large prime.
// Compute and encode `p-2`.
p_minus_two := t[:1+plen]
copy(p_minus_two, p[:1+plen])
two := t[1+plen:] // Temporarily use this for 2.
bigint.i31_zero(two, p[0])
bigint.i31_decode(two, []byte{2})
_ = bigint.i31_sub(p_minus_two, two, 1)
iq := factor_bytes(&sk._iq) // Temporarily use this for p - 2.
bigint.i31_encode(iq, p_minus_two)
// Enforce 64-bit alignment.
t_ := t
if len(t_) & 1 != 0 {
t_ = t_[1:]
}
m0i := bigint.i31_ninv31(p[1])
ret := bigint.i62_modpow_opt_as_i31(q, iq, p, m0i, t)
if ret != 0 {
bigint.i31_encode(iq, q)
}
return ret
}
@(private="file")
compute_privexp :: proc "contextless" (sk: ^Private_Key, p_minus_one, q_minus_one: []u32, e: u32, tmp: []u32) -> u32 {
// Compute phi = (p-1)*(q-1). The mulacc function sets the announced
// bit length of t to be the sum of the announced bit lengths of
// p-1 and q-1, which is usually exact but may overshoot by one 1
// bit in some cases; we readjust it to its true length.
phi := tmp
bigint.i31_zero(phi, p_minus_one[0])
bigint.i31_mulacc(phi, p_minus_one, q_minus_one)
_len := (phi[0] + 31) >> 5
phi[0] = bigint.i31_bit_length(phi[1:1+_len])
_len = (phi[0] + 31) >> 5
// Divide phi by public exponent e. The final remainder r must be
// non-zero (otherwise, the key is invalid). The quotient is k,
// which we write over phi, since we don't need phi after that.
r: u32
for u := _len; u >= 1; u -= 1 {
// Upon entry, r < e, and phi[u] < 2^31; hence,
// hi:lo < e*2^31. Thus, the produced word k[u]
// must be lower than 2^31, and the new remainder r
// is lower than e.
hi := r >> 1
lo := (r << 31) + phi[u]
phi[u], r = bigint.div_rem_u32(hi, lo, e)
}
if r == 0 {
return 0
}
k := phi
// Compute u and v such that u*e - v*r = GCD(e,r). We use
// a binary GCD algorithm, with 6 extra integers a, b,
// u0, u1, v0 and v1. Initial values are:
// a = e u0 = 1 v0 = 0
// b = r u1 = r v1 = e-1
// The following invariants are maintained:
// a = u0*e - v0*r
// b = u1*e - v1*r
// 0 < a <= e
// 0 < b <= r
// 0 <= u0 <= r
// 0 <= v0 <= e
// 0 <= u1 <= r
// 0 <= v1 <= e
//
// At each iteration, we reduce either a or b by one bit, and
// adjust u0, u1, v0 and v1 to maintain the invariants:
// - if a is even, then a <- a/2
// - otherwise, if b is even, then b <- b/2
// - otherwise, if a > b, then a <- (a-b)/2
// - otherwise, if b > a, then b <- (b-a)/2
// Algorithm stops when a = b. At that point, the common value
// is the GCD of e and r; it must be 1 (otherwise, the private
// key or public exponent is not valid). The (u0,v0) or (u1,v1)
// pairs are the solution we are looking for.
//
// Since either a or b is reduced by at least 1 bit at each
// iteration, 62 iterations are enough to reach the end
// condition.
//
// To maintain the invariants, we must compute the same operations
// on the u* and v* values that we do on a and b:
// - When a is divided by 2, u0 and v0 must be divided by 2.
// - When b is divided by 2, u1 and v1 must be divided by 2.
// - When b is subtracted from a, u1 and v1 are subtracted from
// u0 and v0, respectively.
// - When a is subtracted from b, u0 and v0 are subtracted from
// u1 and v1, respectively.
//
// However, we want to keep the u* and v* values in their proper
// ranges. The following remarks apply:
//
// - When a is divided by 2, then a is even. Therefore:
//
// * If r is odd, then u0 and v0 must have the same parity;
// if they are both odd, then adding r to u0 and e to v0
// makes them both even, and the division by 2 brings them
// back to the proper range.
//
// * If r is even, then u0 must be even; if v0 is odd, then
// adding r to u0 and e to v0 makes them both even, and the
// division by 2 brings them back to the proper range.
//
// Thus, all we need to do is to look at the parity of v0,
// and add (r,e) to (u0,v0) when v0 is odd. In order to avoid
// a 32-bit overflow, we can add ((r+1)/2,(e/2)+1) after the
// division (r+1 does not overflow since r < e; and (e/2)+1
// is equal to (e+1)/2 since e is odd).
//
// - When we subtract b from a, three cases may occur:
//
// * u1 <= u0 and v1 <= v0: just do the subtractions
//
// * u1 > u0 and v1 > v0: compute:
// (u0, v0) <- (u0 + r - u1, v0 + e - v1)
//
// * u1 <= u0 and v1 > v0: compute:
// (u0, v0) <- (u0 + r - u1, v0 + e - v1)
//
// The fourth case (u1 > u0 and v1 <= v0) is not possible
// because it would contradict "b < a" (which is the reason
// why we subtract b from a).
//
// The tricky case is the third one: from the equations, it
// seems that u0 may go out of range. However, the invariants
// and ranges of other values imply that, in that case, the
// new u0 does not actually exceed the range.
//
// We can thus handle the subtraction by adding (r,e) based
// solely on the comparison between v0 and v1.
a, b: u32 = e, r
u0, v0: u32 = 1, 0
u1, v1: u32 = r, e - 1
hr, he := (r + 1) >> 1, (e >> 1) + 1
for _ in 0..<62 {
oa := a & 1 // 1 if a is odd
ob := b & 1 // 1 if b is odd
agtb := subtle.gt(a, b) // 1 if a > b
bgta := subtle.gt(b, a) // 1 if b > a
sab := oa & ob & agtb // 1 if a <- a-b
sba := oa & ob & bgta // 1 if b <- b-a
// a <- a-b, u0 <- u0-u1, v0 <- v0-v1
ctl := subtle.gt(v1, v0)
a -= b & -sab
u0 -= (u1 - (r & -ctl)) & -sab
v0 -= (v1 - (e & -ctl)) & -sab
// b <- b-a, u1 <- u1-u0 mod r, v1 <- v1-v0 mod e
ctl = subtle.gt(v0, v1)
b -= a & -sba
u1 -= (u0 - (r & -ctl)) & -sba
v1 -= (v0 - (e & -ctl)) & -sba
da := subtle.not(oa) | sab // 1 if a <- a/2
db := (oa & subtle.not(ob)) | sba // 1 if b <- b/2
// a <- a/2, u0 <- u0/2, v0 <- v0/2
ctl = v0 & 1
a ~= (a ~ (a >> 1)) & -da
u0 ~= (u0 ~ ((u0 >> 1) + (hr & -ctl))) & -da
v0 ~= (v0 ~ ((v0 >> 1) + (he & -ctl))) & -da
// b <- b/2, u1 <- u1/2 mod r, v1 <- v1/2 mod e
ctl = v1 & 1
b ~= (b ~ (b >> 1)) & -db
u1 ~= (u1 ~ ((u1 >> 1) + (hr & -ctl))) & -db
v1 ~= (v1 ~ ((v1 >> 1) + (he & -ctl))) & -db
}
// Check that the GCD is indeed 1. If not, then the key is invalid
// (and there's no harm in leaking that piece of information).
if (a != 1) {
return 0
}
// Now we have u0*e - v0*r = 1. Let's compute the result as:
// d = u0 + v0*k
// We still have k in the tmp[] array, and its announced bit
// length is that of phi.
m := k[1+_len:]
m[0] = (1 << 5) + 1 // bit length is 32 bits, encoded
m[1] = v0 & bigint.I31_MASK
m[2] = v0 >> 31
z := m[3:]
bigint.i31_zero(z, k[0])
z[1] = u0 & bigint.I31_MASK
z[2] = u0 >> 31
bigint.i31_mulacc(z, k, m)
// Encode the result.
bigint.i31_encode(modulus_bytes(&sk._d), z)
return 1
}
+49
View File
@@ -0,0 +1,49 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto/hash"
import "core:encoding/endian"
@(private)
mgf1_xor :: proc(data: []byte, hash_algo: hash.Algorithm, seed: []byte) {
tmp: [hash.MAX_DIGEST_SIZE]byte = ---
ctx: hash.Context = ---
buf, blen := data, len(data)
hlen := hash.DIGEST_SIZES[hash_algo]
digest := tmp[:hlen]
for u, c := int(0), u32(0); u < blen; u, c = u + hlen, c + 1 {
hash.init(&ctx, hash_algo)
hash.update(&ctx, seed)
endian.unchecked_put_u32be(tmp[:], c)
hash.update(&ctx, tmp[:4])
hash.final(&ctx, digest)
for v in 0..<hlen {
if u + v >= blen {
break
}
buf[u + v] ~= digest[v]
}
}
}
+165
View File
@@ -0,0 +1,165 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:crypto"
import bigint "core:crypto/_bigint"
import "core:slice"
@(private, require_results)
private_modpow :: proc(x: []byte, sk: ^Private_Key) -> u32 {
U :: (2 + ((FACTOR_MAX_SIZE + 30) / 31))
TLEN :: (4 * U) // TLEN is counted in 64-bit words
ensure(sk._is_initialized, "crypto/rsa: uninitialized private key")
// Compute the actual lengths of p and q, in bytes.
// These lengths are not considered secret (we cannot really hide
// them anyway in constant-time code).
//
// Note/yawning: The factors should already be the correct size,
// with leading `0x00`s stripped.
p := factor_bytes(&sk._p)
plen := len(p)
for plen > 0 && p[0] == 0 {
p = p[1:]
plen -= 1
}
q := factor_bytes(&sk._q)
qlen := len(q)
for qlen > 0 && q[0] == 0 {
q = q[1:]
qlen -= 1
}
// Compute the maximum factor length, in 31-bit words.
z := max(plen, qlen) << 3
fwlen := 1
for z > 0 {
z -= 31
fwlen += 1
}
// Convert size to 62-bit words.
fwlen = (fwlen + 1) >> 1
// We need to fit at least 6 values in the stack buffer.
if 6 * fwlen > TLEN {
return 0
}
// Compute signature length (in bytes).
xlen := modulus_len(&sk._pub_key._n)
tmp_: [TLEN]u64 // WARNING: This must be zeroed out.
defer crypto.zero_explicit(&tmp_, size_of(tmp_))
tmp := tmp_[:]
// Decode q.
mq := slice.reinterpret([]u32, tmp)
bigint.i31_decode(mq, q)
// Decode p.
t1 := slice.reinterpret([]u32, tmp[fwlen:])
bigint.i31_decode(t1, p)
// Upstream recomputes the public modulus n, but we can just
// decode it as our key representation stores all PKCS#1
// private key values,
t2 := slice.reinterpret([]u32, tmp[2*fwlen:])
bigint.i31_decode(t2, modulus_bytes(&sk._pub_key._n))
// We encode the modulus into bytes, to perform the comparison
// with bytes. We know that the product length, in bytes, is
// exactly xlen.
// The comparison actually computes the carry when subtracting
// the modulus from the source value; that carry must be 1 for
// a value in the correct range. We keep it in r, which is our
// accumulator for the error code.
m_buf := slice.reinterpret([]byte, tmp[4*fwlen:])
bigint.i31_encode(m_buf[:xlen], t2)
u := xlen
r: u32
for u > 0 {
u -= 1
wn := u32(m_buf[u])
wx := u32(x[u])
r = ((wx - (wn + r)) >> 8) & 1
}
// Move the decoded p to another temporary buffer.
mp := t2
copy(mp, t1[:2*fwlen])
// Compute s2 = x^dq mod q.
q0i := bigint.i31_ninv31(mq[1])
s2 := t1
bigint.i31_decode_reduce(s2, x, mq)
r &= bigint.i62_modpow_opt(s2, factor_bytes(&sk._dq), mq, q0i, tmp[3*fwlen:])
// Compute s1 = x^dp mod p.
p0i := bigint.i31_ninv31(mp[1])
s1 := slice.reinterpret([]u32, tmp[3*fwlen:])
bigint.i31_decode_reduce(s1, x, mp)
r &= bigint.i62_modpow_opt(s1, factor_bytes(&sk._dp), mp, p0i, tmp[4*fwlen:])
// Compute:
// h = (s1 - s2)*(1/q) mod p
// s1 is an integer modulo p, but s2 is modulo q. PKCS#1 is
// unclear about whether p may be lower than q (some existing,
// widely deployed implementations of RSA don't tolerate p < q),
// but we want to support that occurrence, so we need to use the
// reduction function.
//
// Since we use br_i31_decode_reduce() for iq (purportedly, the
// inverse of q modulo p), we also tolerate improperly large
// values for this parameter.
t1 = slice.reinterpret([]u32, tmp[4*fwlen:])
t2 = slice.reinterpret([]u32, tmp[5*fwlen:])
bigint.i31_reduce(t2, s2, mp)
_ = bigint.i31_add(s1, mp, bigint.i31_sub(s1, t2, 1))
bigint.i31_to_monty(s1, mp)
bigint.i31_decode_reduce(t1, factor_bytes(&sk._iq), mp)
bigint.i31_montymul(t2, s1, t1, mp, p0i)
// h is now in t2. We compute the final result:
// s = s2 + q*h
// All these operations are non-modular.
//
// We need mq, s2 and t2. We use the t3 buffer as destination.
// The buffers mp, s1 and t1 are no longer needed, so we can
// reuse them for t3. Moreover, the first step of the computation
// is to copy s2 into t3, after which s2 is not needed. Right
// now, mq is in slot 0, s2 is in slot 1, and t2 is in slot 5.
// Therefore, we have ample room for t3 by simply using s2.
t3 := s2
bigint.i31_mulacc(t3, mq, t2)
// Encode the result. Since we already checked the value of xlen,
// we can just use it right away.
bigint.i31_encode(x, t3)
// The only error conditions remaining at that point are invalid
// values for p and q (even integers).
return p0i & q0i & r
}
+89
View File
@@ -0,0 +1,89 @@
package rsa
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import bigint "core:crypto/_bigint"
import "core:encoding/endian"
import "core:slice"
@(private, require_results)
public_modpow :: proc(x: []byte, pk: ^Public_Key) -> u32 {
TLEN :: (2 * (2 + ((MODULUS_MAX_SIZE + 30) / 31)))
ensure(pk._is_initialized, "crypto/rsa: uninitialized public key")
// Get the actual length of the modulus, and see if it fits within
// our stack buffer. We also check that the length of x[] is valid.
//
// Note/yawning: The modulus should already be the correct size,
// with leading `0x00`s stripped.
n := modulus_bytes(&pk._n)
nlen := modulus_len(&pk._n)
for nlen > 0 && n[0] == 0 {
n = n[1:]
nlen -= 1
}
if nlen == 0 || nlen > (MODULUS_MAX_SIZE >> 3) || len(x) != nlen {
return 0
}
z := nlen << 3
fwlen := 1
for z > 0 {
z -= 31
fwlen += 1
}
// Convert fwlen to a count in 62-bit words.
fwlen = (fwlen + 1) >> 1
// The modulus gets decoded into m[].
// The value to exponentiate goes into a[].
tmp: [TLEN]u64 // WARNING: This must be zeroed out.
m := slice.reinterpret([]u32, tmp[:fwlen])
a := slice.reinterpret([]u32, tmp[fwlen:2*fwlen])
// Decode the modulus.
bigint.i31_decode(m, n)
m0i := bigint.i31_ninv31(m[1])
// Note: if m[] is even, then m0i == 0. Otherwise, m0i must be
// an odd integer.
r := m0i & 1
// Decode x[] into a[]; we also check that its value is proper.
r &= bigint.i31_decode_mod(a, x, m)
// Compute the modular exponentiation.
e_: [EXPONENT_MAX_SIZE >> 3]byte
e_off: int
endian.unchecked_put_u32be(e_[:], pk._e)
if e_[0] == 0 {
// `e = 65537` is the most common and sensible value, so this
// is the most sensible value.
e_off = 1
}
bigint.i62_modpow_opt(a, e_[e_off:], m, m0i, tmp[2*fwlen:])
// Encode the result.
bigint.i31_encode(x, a)
return r
}
+233
View File
@@ -0,0 +1,233 @@
package rsa
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "core:bytes"
import "core:crypto"
import "core:crypto/hash"
// PKCS1_HASH_OIDS maps common hash algorithms to the OIDs for
// use with PKCS#1 signatures.
@(rodata)
PKCS1_HASH_OIDS := #partial [hash.Algorithm][]byte {
// WARNING: Legacy verification ONLY.
.Insecure_SHA1 = []byte{
0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A,
},
.SHA224 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
},
.SHA256 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
},
.SHA384 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
},
.SHA512 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
},
.SHA512_256 = []byte{
0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06,
},
}
@(private="file", rodata)
PKCS1_SELFTEST_DIGEST_SHA256 := []byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
}
// verify_pkcs1 returns true if and only if (⟺) sig is a valid PKCS#1
// signature by pub_key over msg, hased using hash_algo. If pre_hashed
// is set to true, it is assumed that msg is already hashed.
@(require_results)
verify_pkcs1 :: proc(pub_key: ^Public_Key, hash_algo: hash.Algorithm, msg, sig: []byte, is_prehashed := false) -> bool {
if !pub_key._is_initialized {
return false
}
if len(sig) != modulus_len(&pub_key._n) {
return false
}
// Lookup the OID.
oid := PKCS1_HASH_OIDS[hash_algo]
if oid == nil {
return false
}
hash_len := hash.DIGEST_SIZES[hash_algo]
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash_len {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
// PKCS #1 V2.2 (RFC 8017) 8.2.2 specifies this as computing
// and comparing the padded hash, with unpadding and extracting
// the hash being an alternative. Upstream BearSSL implements
// the latter, which is not a problem if done correctly (which
// it does), however we will opt to go for implementing this
// as specified as it is more robust against implementation
// errors.
// Compute the expected hash.
sig_buf, padded_hash_buf: [MODULUS_MAX_SIZE >> 3]byte = ---, ---
if len(sig) > len(sig_buf) {
return false
}
padded_hash_ := padded_hash_buf[:len(sig)]
if pkcs1_sig_pad(oid, msg_hash, padded_hash_) != 1 {
return false
}
// Compute the signature's padded hash.
sig_ := sig_buf[:len(sig)]
copy(sig_, sig)
if public_modpow(sig_, pub_key) != 1 {
return false
}
return bytes.equal(sig_, padded_hash_)
}
// sign_pkcs1 returns true if and only if (⟺) it successfully writes
// the PKCS#1 signature by priv_key over msg, hashed using hash_algo.
// If pre_hashed is set to true, it is assumed that msg is already hashed.
@(require_results)
sign_pkcs1 :: proc(priv_key: ^Private_Key, hash_algo: hash.Algorithm, msg, sig: []byte, is_prehashed := false) -> bool {
if !priv_key._is_initialized {
return false
}
if len(sig) != modulus_len(&priv_key._pub_key._n) {
return false
}
// Lookup the OID.
oid := PKCS1_HASH_OIDS[hash_algo]
if oid == nil {
return false
}
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash.DIGEST_SIZES[hash_algo] {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
if pkcs1_sig_pad(oid, msg_hash, sig) != 1 {
return false
}
return private_modpow(sig, priv_key) == 1
}
@(private="file", require_results)
pkcs1_sig_pad :: proc "contextless" (hash_oid, hash, x: []byte) -> u32 {
// Padded hash value has format:
// 00 01 FF .. FF 00 30 x1 30 x2 06 x3 OID 05 00 04 x4 HASH
//
// with the following rules:
//
// -- Total length is equal to the modulus length (unsigned
// encoding).
//
// -- There must be at least eight bytes of value 0xFF.
//
// -- x4 is equal to the hash length (hash_len).
//
// -- x3 is equal to the encoded OID value length (hash_oid[0]).
//
// -- x2 = x3 + 4.
//
// -- x1 = x2 + x4 + 4 = x3 + x4 + 8.
//
// Note: the "05 00" is optional (signatures with and without
// that sequence exist in practice), but notes in PKCS#1 seem to
// indicate that the presence of that sequence (specifically,
// an ASN.1 NULL value for the hash parameters) may be slightly
// more "standard" than the opposite.
xlen, hash_len := len(x), len(hash)
// Note/yawning: The hash OID is mandatory, as is the "05 00".
x3 := hash_oid[0]
// Check that there is enough room for all the elements,
// including at least eight bytes of value 0xFF.
if xlen < int(x3) + hash_len + 21 {
return 0
}
x[0] = 0x00
x[1] = 0x01
u := xlen - int(x3) - hash_len - 11
for i in 2..< u {
x[i] = 0xff
}
x[u] = 0x00
x[u + 1] = 0x30
x[u + 2] = x3 + byte(hash_len) + 8
x[u + 3] = 0x30
x[u + 4] = x3 + 4
x[u + 5] = 0x06
copy(x[u+6:], hash_oid)
u += int(x3) + 7
x[u] = 0x05
u += 1
x[u] = 0x00
u += 1
x[u] = 0x04
u += 1
x[u] = byte(hash_len)
u += 1
copy(x[u:], hash)
return 1
}
@(private)
pkcs1_sig_selftest :: proc(priv_key: ^Private_Key) -> bool {
sig_buf: [MODULUS_MAX_SIZE >> 3]byte = ---
defer crypto.zero_explicit(&sig_buf, size_of(sig_buf))
sig := sig_buf[:private_key_size(priv_key)]
if !sign_pkcs1(priv_key, .SHA256, PKCS1_SELFTEST_DIGEST_SHA256, sig, true) {
return false
}
return verify_pkcs1(&priv_key._pub_key, .SHA256, PKCS1_SELFTEST_DIGEST_SHA256, sig, true)
}
+293
View File
@@ -0,0 +1,293 @@
package rsa
// Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import "base:intrinsics"
import "core:crypto"
import bigint "core:crypto/_bigint"
import subtle "core:crypto/_subtle"
import "core:crypto/hash"
// verify_pss returns true if and only if (⟺) sig is a valid PSS
// signature by pub_key over msg, hashed using hash_algo, and MGF1
// parameterized by mgf1_algo and salt_len. If mgf1_algo is
// unspecified, hash_algo will be used. If pre_hashed is set
// to true, it is assumed that msg is already hashed.
@(require_results)
verify_pss :: proc(
pub_key: ^Public_Key,
hash_algo: hash.Algorithm,
salt_len: int,
msg: []byte,
sig: []byte,
is_prehashed := false,
mgf1_algo := hash.Algorithm.Invalid,
) -> bool {
if !pub_key._is_initialized {
return false
}
if hash_algo == .Invalid {
return false
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
if len(sig) != modulus_len(&pub_key._n) {
return false
}
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
hash_len := hash.DIGEST_SIZES[hash_algo]
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash_len {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
sig_buf: [MODULUS_MAX_SIZE >> 3]byte = ---
sig_ := sig_buf[:len(sig)]
copy(sig_, sig)
if public_modpow(sig_, pub_key) != 1 {
return false
}
return pss_sig_unpad(hash_algo, mgf1_algo_, msg_hash, salt_len, pub_key, sig_) == 1
}
// sign_pss returns true if and only if (⟺) it successfully writes
// the PKCS#1 signature by priv_key over msg, hashed using hash_algo, and
// MGF1 parameterized by mgf1_algo and salt_len. If mgf1_algo is
// unspecified, hash_algo will be used. If pre_hashed is set to true,
// it is assumed that msg is already hashed. A reasonable choice for
// salt_len is the digest size of hash_algo, and FIPS 140-3 mandates
// that as the maximum permissible size.
//
// This routine will fail if the system entropy source is unavailable.
@(require_results)
sign_pss :: proc(
priv_key: ^Private_Key,
hash_algo: hash.Algorithm,
salt_len: int,
msg: []byte,
sig: []byte,
is_prehashed := false,
mgf1_algo := hash.Algorithm.Invalid,
) -> bool {
if !priv_key._is_initialized {
return false
}
if len(sig) != modulus_len(&priv_key._pub_key._n) {
return false
}
if hash_algo == .Invalid {
return false
}
mgf1_algo_ := mgf1_algo
if mgf1_algo == .Invalid {
mgf1_algo_ = hash_algo
}
if !crypto.HAS_RAND_BYTES && salt_len != 0 {
return false
}
// Compute the message hash.
msg_hash_buf: [hash.MAX_DIGEST_SIZE]byte = ---
hash_len := hash.DIGEST_SIZES[hash_algo]
msg_hash: []byte
switch is_prehashed {
case true:
if len(msg) != hash_len {
return false
}
msg_hash = msg
case false:
msg_hash = hash.hash_bytes_to_buffer(hash_algo, msg, msg_hash_buf[:])
}
// Work out the exact length of n in bits.
n := modulus_bytes(&priv_key._pub_key._n)
assert(len(n) > 0 && n[0] != 0)
n_bitlen := int(bigint._u32_bit_length(u32(n[0]))) + (len(n) - 1) * 8
if pss_sig_pad(hash_algo, mgf1_algo_, msg_hash, salt_len, n_bitlen, sig) != 1 {
return false
}
return private_modpow(sig, priv_key) == 1
}
@(private="file", require_results)
pss_sig_unpad :: proc(
data_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
digest: []byte,
salt_len: int,
pk: ^Public_Key,
sig: []byte,
) -> u32 {
hash_len := hash.DIGEST_SIZES[data_algo]
x := sig
// Value r will be set to a non-zero value is any test fails.
r: u32
// The value bit length (as an integer) must be strictly less than
// that of the modulus.
//
// Note/yawning: The modulus should already be the correct size,
// with leading `0x00`s stripped.
n := modulus_bytes(&pk._n)
nlen := modulus_len(&pk._n)
u: int
for u = 0; u < nlen; u += 1 {
if n[u] != 0 {
break
}
}
if u == nlen {
return 0
}
n_bitlen := bigint._u32_bit_length(u32(n[u])) + (u32(nlen - u - 1) << 3)
n_bitlen -= 1
if (n_bitlen & 7) == 0 {
r |= u32(x[0])
x = x[1:]
} else {
r |= u32(x[0] & (0xFF << (n_bitlen & 7)))
}
xlen := int((n_bitlen + 7) >> 3)
// Check that the modulus is large enough for the hash value
// length combined with the intended salt length.
if hash_len > xlen || salt_len > xlen || (hash_len + salt_len + 2) > xlen {
return 0
}
// Check value of rightmost byte.
r |= u32(x[xlen - 1] ~ 0xBC)
// Generate the mask and XOR it into the first bytes to reveal PS;
// we must also mask out the leading bits.
seed := x[xlen - hash_len - 1:]
mgf1_xor(x[:xlen - hash_len - 1], mgf1_algo, seed[:hash_len])
if (n_bitlen & 7) != 0 {
x[0] &= 0xFF >> (8 - (n_bitlen & 7))
}
// Check that all padding bytes have the expected value.
for u = 0; u < (xlen - hash_len - salt_len - 2); u += 1 {
r |= u32(x[u])
}
r |= u32(x[xlen - hash_len - salt_len - 2] ~ 0x01)
// Recompute H.
salt := x[xlen - hash_len - salt_len - 1:]
tmp: [hash.MAX_DIGEST_SIZE]byte
h := tmp[:hash_len]
ctx: hash.Context = ---
hash.init(&ctx, data_algo)
hash.update(&ctx, tmp[:8])
hash.update(&ctx, digest)
hash.update(&ctx, salt[:salt_len])
hash.final(&ctx, h)
// Check that the recomputed H value matches the one appearing
// in the string.
x = x[xlen - hash_len - 1:]
r |= subtle.eq0(u32(crypto.compare_constant_time(h, x[:hash_len])))
return subtle.eq0(r)
}
@(private="file", require_results)
pss_sig_pad :: proc(
data_algo: hash.Algorithm,
mgf1_algo: hash.Algorithm,
digest: []byte,
salt_len: int,
n_bitlen_: int,
sig: []byte,
) -> u32 {
x, n_bitlen := sig, n_bitlen_
hash_len := hash.DIGEST_SIZES[data_algo]
// The padded string is one bit smaller than the modulus;
// notably, if the modulus length is equal to 1 modulo 8, then
// the padded string will be one _byte_ smaller, and the first
// byte will be set to 0. We apply these transformations here.
n_bitlen -= 1
if (n_bitlen & 7) == 0 {
x[0] = 0
x = x[1:]
}
xlen := int((n_bitlen + 7) >> 3)
// Check that the modulus is large enough for the hash value
// length combined with the intended salt length.
if hash_len > xlen || salt_len > xlen || (hash_len + salt_len + 2) > xlen {
return 0
}
// Produce a random salt.
salt := x[xlen - hash_len - salt_len - 1:]
salt = salt[:salt_len]
if salt_len != 0 {
crypto.rand_bytes(salt)
}
// Compute the seed for MGF1.
seed := x[xlen - hash_len - 1:]
seed = seed[:hash_len]
ctx: hash.Context = ---
hash.init(&ctx, data_algo)
intrinsics.mem_zero(raw_data(seed), 8)
hash.update(&ctx, seed[:8])
hash.update(&ctx, digest)
hash.update(&ctx, salt)
hash.final(&ctx, seed)
// Prepare string PS (padded salt). The salt is already at the
// right place.
intrinsics.mem_zero(raw_data(x), xlen - salt_len - hash_len - 2)
x[xlen - salt_len - hash_len - 2] = 0x01
// Generate the mask and XOR it into PS.
mgf1_xor(x[:xlen - hash_len - 1], mgf1_algo, seed)
// Clear the top bits to ensure the value is lower than the
// modulus.
x[0] &= 0xFF >> ((u32(xlen) << 3) - u32(n_bitlen))
// The seed (H) is already in the right place. We just set the
// last byte.
x[xlen - 1] = 0xBC
return 1
}
+180
View File
@@ -0,0 +1,180 @@
package rsa
// private_key_set_insecure_test sets the private key to the
// pregenerated INSECURE test key "testRSA2048" from RFC 9500 2.1.
//
// WARNING: This key MUST only be used for testing purposes.
@(require_results)
private_key_set_insecure_test :: proc(priv_key: ^Private_Key) -> bool {
// RFC 9500 2.1 "testRSA2048"
return private_key_set_bytes(
priv_key,
// n
[]byte{
0xB0, 0xF9, 0xE8, 0x19, 0x43, 0xA7, 0xAE, 0x98,
0x92, 0xAA, 0xDE, 0x17, 0xCA, 0x7C, 0x40, 0xF8,
0x74, 0x4F, 0xED, 0x2F, 0x81, 0x48, 0xE6, 0xC8,
0xEA, 0xA2, 0x7B, 0x7D, 0x00, 0x15, 0x48, 0xFB,
0x51, 0x92, 0xAB, 0x28, 0xB5, 0x6C, 0x50, 0x60,
0xB1, 0x18, 0xCC, 0xD1, 0x31, 0xE5, 0x94, 0x87,
0x4C, 0x6C, 0xA9, 0x89, 0xB5, 0x6C, 0x27, 0x29,
0x6F, 0x09, 0xFB, 0x93, 0xA0, 0x34, 0xDF, 0x32,
0xE9, 0x7C, 0x6F, 0xF0, 0x99, 0x8C, 0xFD, 0x8E,
0x6F, 0x42, 0xDD, 0xA5, 0x8A, 0xCD, 0x1F, 0xA9,
0x79, 0x86, 0xF1, 0x44, 0xF3, 0xD1, 0x54, 0xD6,
0x76, 0x50, 0x17, 0x5E, 0x68, 0x54, 0xB3, 0xA9,
0x52, 0x00, 0x3B, 0xC0, 0x68, 0x87, 0xB8, 0x45,
0x5A, 0xC2, 0xB1, 0x9F, 0x7B, 0x2F, 0x76, 0x50,
0x4E, 0xBC, 0x98, 0xEC, 0x94, 0x55, 0x71, 0xB0,
0x78, 0x92, 0x15, 0x0D, 0xDC, 0x6A, 0x74, 0xCA,
0x0F, 0xBC, 0xD3, 0x54, 0x97, 0xCE, 0x81, 0x53,
0x4D, 0xAF, 0x94, 0x18, 0x84, 0x4B, 0x13, 0xAE,
0xA3, 0x1F, 0x9D, 0x5A, 0x6B, 0x95, 0x57, 0xBB,
0xDF, 0x61, 0x9E, 0xFD, 0x4E, 0x88, 0x7F, 0x2D,
0x42, 0xB8, 0xDD, 0x8B, 0xC9, 0x87, 0xEA, 0xE1,
0xBF, 0x89, 0xCA, 0xB8, 0x5E, 0xE2, 0x1E, 0x35,
0x63, 0x05, 0xDF, 0x6C, 0x07, 0xA8, 0x83, 0x8E,
0x3E, 0xF4, 0x1C, 0x59, 0x5D, 0xCC, 0xE4, 0x3D,
0xAF, 0xC4, 0x91, 0x23, 0xEF, 0x4D, 0x8A, 0xBB,
0xA9, 0x3D, 0x39, 0x05, 0xE4, 0x02, 0x8D, 0x7B,
0xA9, 0x14, 0x84, 0xA2, 0x75, 0x96, 0xE0, 0x7B,
0x4B, 0x6E, 0xD9, 0x92, 0xF0, 0x77, 0xB5, 0x24,
0xD3, 0xDC, 0xFE, 0x7D, 0xDD, 0x55, 0x49, 0xBE,
0x7C, 0xCE, 0x8D, 0xA0, 0x35, 0xCF, 0xA0, 0xB3,
0xFB, 0x8F, 0x9E, 0x46, 0xF7, 0x32, 0xB2, 0xA8,
0x6B, 0x46, 0x01, 0x65, 0xC0, 0x8F, 0x53, 0x13,
},
// e
[]byte{0x01, 0x00, 0x01},
// d
[]byte{
0x41, 0x18, 0x8B, 0x20, 0xCF, 0xDB, 0xDB, 0xC2,
0xCF, 0x1F, 0xFE, 0x75, 0x2D, 0xCB, 0xAA, 0x72,
0x39, 0x06, 0x35, 0x2E, 0x26, 0x15, 0xD4, 0x9D,
0xCE, 0x80, 0x59, 0x7F, 0xCF, 0x0A, 0x05, 0x40,
0x3B, 0xEF, 0x00, 0xFA, 0x06, 0x51, 0x82, 0xF7,
0x2D, 0xEC, 0xFB, 0x59, 0x6F, 0x4B, 0x0C, 0xE8,
0xFF, 0x59, 0x70, 0xBA, 0xF0, 0x7A, 0x89, 0xA5,
0x19, 0xEC, 0xC8, 0x16, 0xB2, 0xF4, 0xFF, 0xAC,
0x50, 0x69, 0xAF, 0x1B, 0x06, 0xBF, 0xEF, 0x7B,
0xF6, 0xBC, 0xD7, 0x9E, 0x4E, 0x81, 0xC8, 0xC5,
0xA3, 0xA7, 0xD9, 0x13, 0x0D, 0xC3, 0xCF, 0xBA,
0xDA, 0xE5, 0xF6, 0xD2, 0x88, 0xF9, 0xAE, 0xE3,
0xF6, 0xFF, 0x92, 0xFA, 0xE0, 0xF8, 0x1A, 0xF5,
0x97, 0xBE, 0xC9, 0x6A, 0xE9, 0xFA, 0xB9, 0x40,
0x2C, 0xD5, 0xFE, 0x41, 0xF7, 0x05, 0xBE, 0xBD,
0xB4, 0x7B, 0xB7, 0x36, 0xD3, 0xFE, 0x6C, 0x5A,
0x51, 0xE0, 0xE2, 0x07, 0x32, 0xA9, 0x7B, 0x5E,
0x46, 0xC1, 0xCB, 0xDB, 0x26, 0xD7, 0x48, 0x54,
0xC6, 0xB6, 0x60, 0x4A, 0xED, 0x46, 0x37, 0x35,
0xFF, 0x90, 0x76, 0x04, 0x65, 0x57, 0xCA, 0xF9,
0x49, 0xBF, 0x44, 0x88, 0x95, 0xC2, 0x04, 0x32,
0xC1, 0xE0, 0x9C, 0x01, 0x4E, 0xA7, 0x56, 0x60,
0x43, 0x4F, 0x1A, 0x0F, 0x3B, 0xE2, 0x94, 0xBA,
0xBC, 0x5D, 0x53, 0x0E, 0x6A, 0x10, 0x21, 0x3F,
0x53, 0xB6, 0x03, 0x75, 0xFC, 0x84, 0xA7, 0x57,
0x3F, 0x2A, 0xF1, 0x21, 0x55, 0x84, 0xF5, 0xB4,
0xBD, 0xA6, 0xD4, 0xE8, 0xF9, 0xE1, 0x7A, 0x78,
0xD9, 0x7E, 0x77, 0xB8, 0x6D, 0xA4, 0xA1, 0x84,
0x64, 0x75, 0x31, 0x8A, 0x7A, 0x10, 0xA5, 0x61,
0x01, 0x4E, 0xFF, 0xA2, 0x3A, 0x81, 0xEC, 0x56,
0xE9, 0xE4, 0x10, 0x9D, 0xEF, 0x8C, 0xB3, 0xF7,
0x97, 0x22, 0x3F, 0x7D, 0x8D, 0x0D, 0x43, 0x51,
},
// p
[]byte{
0xDD, 0x10, 0x57, 0x02, 0x38, 0x2F, 0x23, 0x2B,
0x36, 0x81, 0xF5, 0x37, 0x91, 0xE2, 0x26, 0x17,
0xC7, 0xBF, 0x4E, 0x9A, 0xCB, 0x81, 0xED, 0x48,
0xDA, 0xF6, 0xD6, 0x99, 0x5D, 0xA3, 0xEA, 0xB6,
0x42, 0x83, 0x9A, 0xFF, 0x01, 0x2D, 0x2E, 0xA6,
0x28, 0xB9, 0x0A, 0xF2, 0x79, 0xFD, 0x3E, 0x6F,
0x7C, 0x93, 0xCD, 0x80, 0xF0, 0x72, 0xF0, 0x1F,
0xF2, 0x44, 0x3B, 0x3E, 0xE8, 0xF2, 0x4E, 0xD4,
0x69, 0xA7, 0x96, 0x13, 0xA4, 0x1B, 0xD2, 0x40,
0x20, 0xF9, 0x2F, 0xD1, 0x10, 0x59, 0xBD, 0x1D,
0x0F, 0x30, 0x1B, 0x5B, 0xA7, 0xA9, 0xD3, 0x63,
0x7C, 0xA8, 0xD6, 0x5C, 0x1A, 0x98, 0x15, 0x41,
0x7D, 0x8E, 0xAB, 0x73, 0x4B, 0x0B, 0x4F, 0x3A,
0x2C, 0x66, 0x1D, 0x9A, 0x1A, 0x82, 0xF3, 0xAC,
0x73, 0x4C, 0x40, 0x53, 0x06, 0x69, 0xAB, 0x8E,
0x47, 0x30, 0x45, 0xA5, 0x8E, 0x65, 0x53, 0x9D,
},
// q
[]byte{
0xCC, 0xF1, 0xE5, 0xBB, 0x90, 0xC8, 0xE9, 0x78,
0x1E, 0xA7, 0x5B, 0xEB, 0xF1, 0x0B, 0xC2, 0x52,
0xE1, 0x1E, 0xB0, 0x23, 0xA0, 0x26, 0x0F, 0x18,
0x87, 0x55, 0x2A, 0x56, 0x86, 0x3F, 0x4A, 0x64,
0x21, 0xE8, 0xC6, 0x00, 0xBF, 0x52, 0x3D, 0x6C,
0xB1, 0xB0, 0xAD, 0xBD, 0xD6, 0x5B, 0xFE, 0xE4,
0xA8, 0x8A, 0x03, 0x7E, 0x3D, 0x1A, 0x41, 0x5E,
0x5B, 0xB9, 0x56, 0x48, 0xDA, 0x5A, 0x0C, 0xA2,
0x6B, 0x54, 0xF4, 0xA6, 0x39, 0x48, 0x52, 0x2C,
0x3D, 0x5F, 0x89, 0xB9, 0x4A, 0x72, 0xEF, 0xFF,
0x95, 0x13, 0x4D, 0x59, 0x40, 0xCE, 0x45, 0x75,
0x8F, 0x30, 0x89, 0x80, 0x90, 0x89, 0x56, 0x58,
0x8E, 0xEF, 0x57, 0x5B, 0x3E, 0x4B, 0xC4, 0xC3,
0x68, 0xCF, 0xE8, 0x13, 0xEE, 0x9C, 0x25, 0x2C,
0x2B, 0x02, 0xE0, 0xDF, 0x91, 0xF1, 0xAA, 0x01,
0x93, 0x8D, 0x38, 0x68, 0x5D, 0x60, 0xBA, 0x6F,
},
// dp
[]byte{
0x09, 0xED, 0x54, 0xEA, 0xED, 0x98, 0xF8, 0x4C,
0x55, 0x7B, 0x4A, 0x86, 0xBF, 0x4F, 0x57, 0x84,
0x93, 0xDC, 0xBC, 0x6B, 0xE9, 0x1D, 0xA1, 0x89,
0x37, 0x04, 0x04, 0xA9, 0x08, 0x72, 0x76, 0xF4,
0xCE, 0x51, 0xD8, 0xA1, 0x00, 0xED, 0x85, 0x7D,
0xC2, 0xB0, 0x64, 0x94, 0x74, 0xF3, 0xF1, 0x5C,
0xD2, 0x4C, 0x54, 0xDB, 0x28, 0x71, 0x10, 0xE5,
0x6E, 0x5C, 0xB0, 0x08, 0x68, 0x2F, 0x91, 0x68,
0xAA, 0x81, 0xF3, 0x14, 0x58, 0xB7, 0x43, 0x1E,
0xCC, 0x1C, 0x44, 0x90, 0x6F, 0xDA, 0x87, 0xCA,
0x89, 0x47, 0x10, 0xC3, 0x71, 0xE9, 0x07, 0x6C,
0x1D, 0x49, 0xFB, 0xAE, 0x51, 0x27, 0x69, 0x34,
0xF2, 0xAD, 0x78, 0x77, 0x89, 0xF4, 0x2D, 0x0F,
0xA0, 0xB4, 0xC9, 0x39, 0x85, 0x5D, 0x42, 0x12,
0x09, 0x6F, 0x70, 0x28, 0x0A, 0x4E, 0xAE, 0x7C,
0x8A, 0x27, 0xD9, 0xC8, 0xD0, 0x77, 0x2E, 0x65,
},
// dq
[]byte{
0x8C, 0xB6, 0x85, 0x7A, 0x7B, 0xD5, 0x46, 0x5F,
0x80, 0x04, 0x7E, 0x9B, 0x87, 0xBC, 0x00, 0x27,
0x31, 0x84, 0x05, 0x81, 0xE0, 0x62, 0x61, 0x39,
0x01, 0x2A, 0x5B, 0x50, 0x5F, 0x0A, 0x33, 0x84,
0x7E, 0xB7, 0xB8, 0xC3, 0x28, 0x99, 0x49, 0xAD,
0x48, 0x6F, 0x3B, 0x4B, 0x3D, 0x53, 0x9A, 0xB5,
0xDA, 0x76, 0x30, 0x21, 0xCB, 0xC8, 0x2C, 0x1B,
0xA2, 0x34, 0xA5, 0x66, 0x8D, 0xED, 0x08, 0x01,
0xB8, 0x59, 0xF3, 0x43, 0xF1, 0xCE, 0x93, 0x04,
0xE6, 0xFA, 0xA2, 0xB0, 0x02, 0xCA, 0xD9, 0xB7,
0x8C, 0xDE, 0x5C, 0xDC, 0x2C, 0x1F, 0xB4, 0x17,
0x1C, 0x42, 0x42, 0x16, 0x70, 0xA6, 0xAB, 0x0F,
0x50, 0xCC, 0x4A, 0x19, 0x4E, 0xB3, 0x6D, 0x1C,
0x91, 0xE9, 0x35, 0xBA, 0x01, 0xB9, 0x59, 0xD8,
0x72, 0x8B, 0x9E, 0x64, 0x42, 0x6B, 0x3F, 0xC3,
0xA7, 0x50, 0x6D, 0xEB, 0x52, 0x39, 0xA8, 0xA7,
},
// iq (aka u)
[]byte{
0x0A, 0x81, 0xD8, 0xA6, 0x18, 0x31, 0x4A, 0x80,
0x3A, 0xF6, 0x1C, 0x06, 0x71, 0x1F, 0x2C, 0x39,
0xB2, 0x66, 0xFF, 0x41, 0x4D, 0x53, 0x47, 0x6D,
0x1D, 0xA5, 0x2A, 0x43, 0x18, 0xAA, 0xFE, 0x4B,
0x96, 0xF0, 0xDA, 0x07, 0x15, 0x5F, 0x8A, 0x51,
0x34, 0xDA, 0xB8, 0x8E, 0xE2, 0x9E, 0x81, 0x68,
0x07, 0x6F, 0xCD, 0x78, 0xCA, 0x79, 0x1A, 0xC6,
0x34, 0x42, 0xA8, 0x1C, 0xD0, 0x69, 0x39, 0x27,
0xD8, 0x08, 0xE3, 0x35, 0xE8, 0xD8, 0xCB, 0xF2,
0x12, 0x19, 0x07, 0x50, 0x9A, 0x57, 0x75, 0x9B,
0x4F, 0x9A, 0x18, 0xFA, 0x3A, 0x7B, 0x33, 0x37,
0x79, 0xED, 0xDE, 0x7A, 0x45, 0x93, 0x84, 0xF8,
0x44, 0x4A, 0xDA, 0xEC, 0xFF, 0xEC, 0x95, 0xFD,
0x55, 0x2B, 0x0C, 0xFC, 0xB6, 0xC7, 0xF6, 0x92,
0x62, 0x6D, 0xDE, 0x1E, 0xF2, 0x68, 0xA4, 0x0D,
0x2F, 0x67, 0xB5, 0xC8, 0xAA, 0x38, 0x7F, 0xF7,
},
)
}
+4 -3
View File
@@ -411,12 +411,13 @@ _decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := c
io.copy_n(buf_stream, d.reader, i64(n)) or_return
}
v = buf.buf[:]
// Write zero byte so this can be converted to cstring.
strings.write_byte(&buf, 0)
if .Shrink_Excess in d.flags { shrink(&buf.buf) }
v = buf.buf[:len(buf.buf)-1]
return
}
@@ -884,4 +885,4 @@ _encode_deterministic_f64 :: proc(w: io.Writer, v: f64) -> io.Error {
}
return _encode_f64_exact(w, v)
}
}
+2 -2
View File
@@ -509,7 +509,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Slice:
length, scap := err_conv(_decode_len_container(d, add)) or_return
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, allocator=allocator, loc=loc) or_return
data := mem.alloc_bytes(t.elem.size * scap, t.elem.align, allocator=allocator, loc=loc) or_return
defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
da := mem.Raw_Dynamic_Array{raw_data(data), 0, scap, context.allocator }
@@ -529,7 +529,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Dynamic_Array:
length, scap := err_conv(_decode_len_container(d, add)) or_return
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, loc=loc) or_return
data := mem.alloc_bytes(t.elem.size * scap, t.elem.align, loc=loc) or_return
defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
raw := (^mem.Raw_Dynamic_Array)(v.data)
+1
View File
@@ -146,6 +146,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
for i in 0..<count {
write_rune(&builder, decoded[i])
}
prev = decoded[count - 1]
continue
}
}
+1
View File
@@ -134,6 +134,7 @@ register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> Regist
return .None
}
@(require_results)
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator, loc)
defer if err != nil {
+68
View File
@@ -0,0 +1,68 @@
package encoding_json
Match_Key_Variant :: union #no_nil {
int, // Index
string, // Key
}
Match_Error :: enum {
None,
Invalid_Argument,
Invalid_Type_For_Index,
Invalid_Type_For_Key,
Key_Not_Found,
Out_Of_Bounds_Index,
}
Match_Flags :: distinct bit_set[Match_Flag]
Match_Flag :: enum {
Ignore_Key_Not_Found,
Allow_String_Indexing_By_Byte,
}
@(require_results)
match :: proc(value: Value, args: ..Match_Key_Variant, flags: Match_Flags = nil) -> (found: Value, err: Match_Error) {
found = value
arg_loop: for arg in args {
switch k in arg {
case int:
#partial switch v in found {
case Array:
if 0 <= k && k < len(v) {
found = v[k]
continue arg_loop
}
err = .Out_Of_Bounds_Index
return
case String:
if .Allow_String_Indexing_By_Byte in flags {
if 0 <= k && k < len(v) {
found = Integer(v[k])
continue arg_loop
}
err = .Out_Of_Bounds_Index
return
}
}
err = .Invalid_Type_For_Index
return
case string:
v, ok := found.(Object)
if !ok {
err = .Invalid_Type_For_Key
return
}
vfound, vok := v[k]
if vok || (.Ignore_Key_Not_Found in flags) {
found = vfound
continue arg_loop
}
err = .Key_Not_Found
return
case:
err = .Invalid_Argument
return
}
}
return
}
+29 -3
View File
@@ -14,9 +14,16 @@ Parser :: struct {
parse_integers: bool,
}
make_parser :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
make_parser :: proc{
make_parser_from_bytes,
make_parser_from_string,
}
@(require_results)
make_parser_from_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
return make_parser_from_string(string(data), spec, parse_integers, allocator)
}
@(require_results)
make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
p: Parser
p.tok = make_tokenizer(data, spec, parse_integers)
@@ -27,11 +34,18 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par
return p
}
parse :: proc{
parse_bytes,
parse_string,
}
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
@(require_results)
parse_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
return parse_string(string(data), spec, parse_integers, allocator, loc)
}
@(require_results)
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
context.allocator = allocator
p := make_parser_from_string(data, spec, parse_integers, allocator)
@@ -51,6 +65,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
return parse_object(&p, loc)
}
@(require_results)
token_end_pos :: proc(tok: Token) -> Pos {
end := tok.pos
end.offset += len(tok.text)
@@ -65,6 +80,7 @@ advance_token :: proc(p: ^Parser) -> (Token, Error) {
}
@(require_results)
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
if p.curr_token.kind == kind {
advance_token(p)
@@ -73,6 +89,7 @@ allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
return false
}
@(require_results)
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
prev := p.curr_token
advance_token(p)
@@ -83,6 +100,7 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
}
@(require_results)
parse_colon :: proc(p: ^Parser) -> (err: Error) {
colon_err := expect_token(p, .Colon)
if colon_err == nil {
@@ -91,6 +109,7 @@ parse_colon :: proc(p: ^Parser) -> (err: Error) {
return .Expected_Colon_After_Key
}
@(require_results)
parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
switch p.spec {
case .JSON5, .MJSON:
@@ -106,6 +125,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
return false
}
@(require_results)
parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None
token := p.curr_token
@@ -176,6 +196,7 @@ parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
return
}
@(require_results)
parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None
expect_token(p, .Open_Bracket) or_return
@@ -203,7 +224,7 @@ parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
return
}
@(private)
@(private, require_results)
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) {
b, berr := mem.alloc_bytes(size, alignment, allocator, loc)
if berr != nil {
@@ -217,6 +238,7 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #calle
return
}
@(require_results)
clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) {
n := len(s)
b := bytes_make(n+1, 1, allocator, loc) or_return
@@ -228,6 +250,7 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio
return
}
@(require_results)
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) {
tok := p.curr_token
if p.spec != .JSON {
@@ -242,6 +265,7 @@ parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #calle
return unquote_string(tok, p.spec, key_allocator, loc)
}
@(require_results)
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) {
obj = make(Object, allocator=p.allocator, loc=loc)
@@ -282,6 +306,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_loca
return obj, .None
}
@(require_results)
parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
expect_token(p, .Open_Brace) or_return
obj := parse_object_body(p, .Close_Brace, loc) or_return
@@ -291,6 +316,7 @@ parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
@(require_results)
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) {
get_u2_rune :: proc(s: string) -> rune {
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
+11 -8
View File
@@ -49,11 +49,12 @@ Tokenizer :: struct {
curr_line_offset: int,
spec: Specification,
parse_integers: bool,
insert_comma: bool,
insert_comma: bool,
}
@(require_results)
make_tokenizer :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> Tokenizer {
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers}
next_rune(&t)
@@ -78,6 +79,7 @@ next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
}
@(require_results)
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
skip_digits :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
@@ -101,6 +103,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
}
}
@(require_results)
scan_escape :: proc(t: ^Tokenizer) -> bool {
switch t.r {
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
@@ -125,7 +128,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
return false
}
skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) -> rune {
skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) {
loop: for t.offset < len(t.data) {
switch t.r {
case ' ', '\t', '\v', '\f', '\r':
@@ -149,7 +152,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
break loop
}
}
return t.r
return
}
skip_to_next_line :: proc(t: ^Tokenizer) {
@@ -310,7 +313,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
break
}
if r == '\\' {
scan_escape(t)
_ = scan_escape(t)
}
}
@@ -347,10 +350,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
case '*':
// None-nested multi-line comments
for t.offset < len(t.data) {
next_rune(t)
if t.r == '*' {
next_rune(t)
if t.r == '/' {
if next_rune(t) == '*' {
if next_rune(t) == '/' {
next_rune(t)
return get_token(t)
}
@@ -385,6 +386,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
@(require_results)
is_valid_number :: proc(str: string, spec: Specification) -> bool {
s := str
if s == "" {
@@ -473,6 +475,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
return s == ""
}
@(require_results)
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
s := str
if len(s) < 2 {
+1
View File
@@ -112,6 +112,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call
}
}
@(require_results)
clone_value :: proc(value: Value, allocator := context.allocator) -> Value {
value := value
context.allocator = allocator
+1 -1
View File
@@ -635,7 +635,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
defer p.allocator = allocator
p.allocator = mem.nil_allocator()
parse_value(p) or_return
_ = parse_value(p) or_return
if parse_comma(p) {
break struct_loop
}
+6
View File
@@ -3,6 +3,7 @@ package encoding_json
import "core:mem"
// NOTE(bill): is_valid will not check for duplicate keys
@(require_results)
is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool {
p := make_parser(data, spec, parse_integers, mem.nil_allocator())
@@ -21,6 +22,7 @@ is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers :=
return validate_object(&p)
}
@(require_results)
validate_object_key :: proc(p: ^Parser) -> bool {
if p.spec != .JSON {
if allow_token(p, .Ident) {
@@ -31,6 +33,7 @@ validate_object_key :: proc(p: ^Parser) -> bool {
return err == .None
}
@(require_results)
validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
for p.curr_token.kind != end_token {
if !validate_object_key(p) {
@@ -48,6 +51,7 @@ validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
return true
}
@(require_results)
validate_object :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Brace); err != .None {
return false
@@ -61,6 +65,7 @@ validate_object :: proc(p: ^Parser) -> bool {
return true
}
@(require_results)
validate_array :: proc(p: ^Parser) -> bool {
if err := expect_token(p, .Open_Bracket); err != .None {
return false
@@ -83,6 +88,7 @@ validate_array :: proc(p: ^Parser) -> bool {
return true
}
@(require_results)
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token
+6 -15
View File
@@ -244,27 +244,18 @@ quaternion_mul_quaternion :: proc "contextless" (q1, q2: $Q) -> Q where IS_QUATE
@(require_results)
quaternion64_mul_vector3 :: proc "contextless" (q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
q := transmute(runtime.Raw_Quaternion64_Vector_Scalar)q
v := v
t := cross(2*q.vector, v)
return V(v + q.scalar*t + cross(q.vector, t))
t := cross(2*q.xyz, v)
return V(v + q.w*t + cross(q.xyz, t))
}
@(require_results)
quaternion128_mul_vector3 :: proc "contextless" (q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
q := transmute(runtime.Raw_Quaternion128_Vector_Scalar)q
v := v
t := cross(2*q.vector, v)
return V(v + q.scalar*t + cross(q.vector, t))
t := cross(2*q.xyz, v)
return V(v + q.w*t + cross(q.xyz, t))
}
@(require_results)
quaternion256_mul_vector3 :: proc "contextless" (q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
q := transmute(runtime.Raw_Quaternion256_Vector_Scalar)q
v := v
t := cross(2*q.vector, v)
return V(v + q.scalar*t + cross(q.vector, t))
t := cross(2*q.xyz, v)
return V(v + q.w*t + cross(q.xyz, t))
}
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3}
+2 -2
View File
@@ -1044,8 +1044,8 @@ Example:
Possible Output:
[7201011, 3, 9123, 231131]
[19578, 910081, 131, 7]
[3, 2, 0, 1]
[1, 0, 2, 3]
*/
@(require_results)
+6 -1
View File
@@ -381,6 +381,9 @@ advance_token :: proc(p: ^Parser) -> tokenizer.Token {
#partial switch p.curr_tok.kind {
case .Comment:
consume_comment_groups(p, prev)
if p.curr_tok.kind == .Semicolon && p.expr_level > 0 && p.curr_tok.text == "\n" {
advance_token(p)
}
case .Semicolon:
if p.expr_level > 0 && p.curr_tok.text == "\n" {
advance_token(p)
@@ -3622,7 +3625,9 @@ parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr {
case .If, .When:
if p.prev_tok.pos.line < op.pos.line {
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
break loop
if p.expr_level <= 0 {
break loop
}
}
}
+1 -1
View File
@@ -536,7 +536,7 @@ is_directory :: proc(path: string) -> bool {
}
/*
`copy_file` copies a file from `src_path` to `dst_path` and returns an error if any was encountered.
`is_tty` returns `true` if `f` is a TTY, `false` if not.
*/
@(require_results)
is_tty :: proc "contextless" (f: ^File) -> bool {
+2 -2
View File
@@ -263,8 +263,8 @@ _ram_stats :: proc "contextless" () -> (total_ram, free_ram, total_swap, free_sw
total_ram = i64(state.ullTotalPhys)
free_ram = i64(state.ullAvailPhys)
total_swap = i64(state.ullTotalPageFil)
free_swap = i64(state.ullAvailPageFil)
total_swap = i64(state.ullTotalPageFile)
free_swap = i64(state.ullAvailPageFile)
ok = true
return
+2 -2
View File
@@ -4802,8 +4802,8 @@ MEMORYSTATUSEX :: struct {
dwMemoryLoad: DWORD,
ullTotalPhys: DWORDLONG,
ullAvailPhys: DWORDLONG,
ullTotalPageFil: DWORDLONG,
ullAvailPageFil: DWORDLONG,
ullTotalPageFile: DWORDLONG,
ullAvailPageFile: DWORDLONG,
ullTotalVirtual: DWORDLONG,
ullAvailVirtual: DWORDLONG,
ullAvailExtendedVirtual: DWORDLONG,
+1 -1
View File
@@ -65,7 +65,7 @@ Capable of representing any time within the following range:
- `max: 2262-04-11 23:47:16.854775807 +0000 UTC`
*/
Time :: struct {
_nsec: i64, // Measured in UNIX nanonseconds
_nsec: i64, // Measured in UNIX nanoseconds
}
/*
+1
View File
@@ -49,6 +49,7 @@ package all
@(require) import "core:crypto/pbkdf2"
@(require) import "core:crypto/poly1305"
@(require) import "core:crypto/ristretto255"
@(require) import "core:crypto/rsa"
@(require) import "core:crypto/sha2"
@(require) import "core:crypto/sha3"
@(require) import "core:crypto/shake"
+1
View File
@@ -54,6 +54,7 @@ package all
@(require) import "core:crypto/pbkdf2"
@(require) import "core:crypto/poly1305"
@(require) import "core:crypto/ristretto255"
@(require) import "core:crypto/rsa"
@(require) import "core:crypto/sha2"
@(require) import "core:crypto/sha3"
@(require) import "core:crypto/shake"
+1
View File
@@ -6,5 +6,6 @@ package all
@(require) import "vendor:wgpu/sdl2glue"
@(require) import "vendor:wgpu"
@(require) import "vendor:box2d"
@(require) import "vendor:box3d"
@(require) import "vendor:windows/GameInput"
@(require) import "vendor:windows/XAudio2"
+3 -3
View File
@@ -670,7 +670,7 @@ union_type :: proc() {
{
// NOTE(bill): A union can be used to achieve something similar. Instead
// of embedding the base data into the derived types, the derived data
// in embedded into the base type. Below is the same example of the
// is embedded into the base type. Below is the same example of the
// basic game Entity but using an union.
Entity :: struct {
@@ -757,7 +757,7 @@ using_statement :: proc() {
// `using` as a struct field modifier remains available always
fmt.println("\n# using statement")
// using can used to bring entities declared in a scope/namespace
// using can be used to bring entities declared in a scope/namespace
// into the current scope. This can be applied to import names, struct
// fields, procedure fields, and struct values.
@@ -768,7 +768,7 @@ using_statement :: proc() {
orientation: quaternion128,
}
// It can used like this:
// It can be used like this:
foo0 :: proc(entity: ^Entity) {
fmt.println(entity.position.x, entity.position.y, entity.position.z)
}
+9 -3
View File
@@ -353,9 +353,15 @@ gb_internal void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) {
gb_internal void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) {
u32 yy = mp_get_u32(y);
BigInt d = {};
mp_div_2d(x, yy, dst, &d);
big_int_dealloc(&d);
BigInt rem = {};
defer (mp_clear(&rem));
mp_div_2d(x, yy, dst, &rem);
if (mp_isneg(x) && !mp_iszero(&rem)) {
mp_sub_d(dst, 1, dst);
}
}
gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
+43 -7
View File
@@ -171,6 +171,7 @@ enum Subtarget : u32 {
Subtarget_iPhone,
Subtarget_iPhoneSimulator,
Subtarget_Android,
Subtarget_Playdate,
Subtarget_COUNT,
Subtarget_Invalid, // NOTE(harold): Must appear after _COUNT as this is not a real subtarget
@@ -181,6 +182,7 @@ gb_global String subtarget_strings[Subtarget_COUNT] = {
str_lit("iphone"),
str_lit("iphonesimulator"),
str_lit("android"),
str_lit("playdate"),
};
@@ -879,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,
@@ -1957,6 +1959,16 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->no_plt = LLVM_VERSION_MAJOR >= 19;
break;
}
} else if (subtarget == Subtarget_Playdate) {
// Playdate uses the cortex-m7 as well and an FPU, requiring thumb instructions and hard floats.
bc->microarch = str_lit("cortex-m7");
bc->metrics.target_triplet = str_lit("thumbv7em-none-eabihf");
// Remove MOVW/MOVT instructions as playdate only handles R_ARM_ABS32 relocations.
if(bc->target_features_string.len > 0) {
bc->target_features_string = concatenate_strings(permanent_allocator(), bc->target_features_string, str_lit(",no-movt"));
} else {
bc->target_features_string = str_lit("no-movt");
}
}
if (metrics->os == TargetOs_windows ||
@@ -2397,12 +2409,32 @@ gb_internal bool init_build_paths(String init_filename) {
GB_PANIC("Unhandled build mode/target combination.\n");
}
bool output_should_be_directory = false;
if (bc->out_filepath.len > 0) {
bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
if (build_context.metrics.os == TargetOs_windows) {
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text));
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
bool output_is_directory = path_is_directory(bc->build_paths[BuildPath_Output]);
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text));
// NOTE(Jeroen): For LLVM-IR, we want `-out` to specify a directory.
// For other outputs we expect it to be a file path.
if (build_context.build_mode == BuildMode_LLVM_IR) {
if (!output_is_directory) {
gb_printf_err("Output path %.*s should be a directory for LLVM-IR output.\n", LIT(output_file));
return false;
}
output_should_be_directory = true;
} else if (build_context.build_mode == BuildMode_Object) {
// Both directory or filename prefix allowed
} else if (build_context.build_mode == BuildMode_Assembly) {
// Both directory or filename prefix allowed
} else if (build_context.metrics.os == TargetOs_windows) {
if (output_is_directory) {
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
return false;
} else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
@@ -2513,7 +2545,11 @@ gb_internal bool init_build_paths(String init_filename) {
// Do we have an extension? We might not if the output filename was supplied.
if (bc->build_paths[BuildPath_Output].ext.len == 0) {
if (build_context.metrics.os == TargetOs_windows || is_arch_wasm() || build_context.build_mode != BuildMode_Executable) {
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
// NOTE(Jeroen): If build mode is LLVM_IR and a custom output was set
if (!output_should_be_directory) {
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
}
}
}
@@ -2521,7 +2557,7 @@ gb_internal bool init_build_paths(String init_filename) {
defer (gb_free(ha, output_file.text));
// Check if output path is a directory.
if (path_is_directory(bc->build_paths[BuildPath_Output])) {
if (!output_should_be_directory && path_is_directory(bc->build_paths[BuildPath_Output])) {
gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
return false;
}
+2
View File
@@ -765,8 +765,10 @@ gb_internal bool check_builtin_c_procedure(CheckerContext *c, Operand *operand,
return false;
}
c->allow_c_vararg_param = true;
Operand args = {};
check_expr(c, &args, ce->args[1]);
c->allow_c_vararg_param = false;
if (list.mode == Addressing_Invalid) {
return false;
}
+30 -13
View File
@@ -1142,7 +1142,7 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
if (operand->mode == Addressing_Type && is_type_typeid(type)) {
add_type_info_type(c, operand->type);
add_type_and_value(c, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type));
add_type_and_value(c, operand->expr, Addressing_Constant, type, exact_value_typeid(operand->type));
return;
}
@@ -1968,6 +1968,14 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
break;
case Entity_Variable:
if ((e->flags & EntityFlag_CVarArg) && !c->allow_c_vararg_param) {
ERROR_BLOCK();
error(o->expr, "'#c_vararg' parameter '%.*s' cannot be used directly", LIT(name));
error_line("\tSuggestion: use c_va_start to convert C varargs to c_va_list\n");
o->mode = Addressing_Invalid;
o->type = t_invalid;
return e;
}
e->flags |= EntityFlag_Used;
if (type == t_invalid) {
o->type = t_invalid;
@@ -3926,6 +3934,11 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type, bool forb
}
}
// In this case, the cast involves array programming
// so the operand needs to be a computed value
if (!is_type_array_like(x->type) && is_type_array_like(type)) {
x->mode = Addressing_Value;
}
x->type = type;
}
@@ -5161,16 +5174,12 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
if (valid_count == 1) {
Type *new_type = t->Union.variants[first_success_index];
target_type = new_type;
if (is_type_union(new_type)) {
convert_to_typed(c, operand, new_type);
break;
}
operand->type = new_type;
if (operand->mode != Addressing_Constant ||
!elem_type_can_be_constant(operand->type)) {
!elem_type_can_be_constant(new_type)) {
operand->mode = Addressing_Value;
}
convert_to_typed(c, operand, new_type);
target_type = new_type;
break;
} else if (valid_count > 1) {
ERROR_BLOCK();
@@ -5204,7 +5213,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type, true);
if (count > 0) {
error_line("'%s' is a union which only excepts the following types:\n", type_str);
error_line("'%s' is a union which only accepts the following types:\n", type_str);
error_line("\t");
for (i32 i = 0; i < count; i++) {
@@ -5363,7 +5372,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
TEMPORARY_ALLOCATOR_GUARD();
String idx_str = big_int_to_string(temporary_allocator(), &i);
gbString expr_str = expr_to_string(operand.expr, temporary_allocator());
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str));
char range_type = open_range ? '=' : '<';
error(operand.expr, "Index '%s' is out of bounds range 0..%c%lld, got %.*s", expr_str, range_type, max_count, LIT(idx_str));
return false;
}
@@ -5819,7 +5829,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
if (e != nullptr && (e->kind == Entity_Procedure || e->kind == Entity_ProcGroup) && selector->kind == Ast_Ident) {
gbString sel_str = expr_to_string(selector);
error(node, "'%s' is not declared by by '%.*s'", sel_str, LIT(e->token.string));
error(node, "'%s' is not declared by '%.*s'", sel_str, LIT(e->token.string));
gb_string_free(sel_str);
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -6774,7 +6784,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) {
bool ok = false;
if (e && (e->flags & EntityFlag_AnyInt)) {
if (is_type_integer(param_type) && (is_type_integer(o->type) || is_type_enum(o->type))) {
if (o->mode != Addressing_Type && is_type_integer(param_type) && (is_type_integer(o->type) || is_type_enum(o->type))) {
ok = check_is_castable_to(c, o, param_type);
}
}
@@ -9898,6 +9908,7 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
if (is_diverging_expr(y.expr)) {
// Allow
y.mode = Addressing_Value;
y.type = x.type;
y_is_diverging = true;
} else {
error_operand_no_value(&y);
@@ -10217,7 +10228,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
Ast *ident = fv->field;
if (ident->kind == Ast_ImplicitSelectorExpr) {
gbString expr_str = expr_to_string(ident);
error(ident, "Field names do not start with a '.', remove the '.' in %.*s", expr_str, LIT(assignment_str));
error(ident, "Field names do not start with a '.', remove the '.' from '%s' in %.*s", expr_str, LIT(assignment_str));
gb_string_free(expr_str);
ident = ident->ImplicitSelectorExpr.selector;
@@ -12832,6 +12843,12 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
} else {
str = gb_string_appendc(str, " ---");
}
// NOTE(tf2spi):
// Two proc literals with the same signature output the same expr above
// which poses challenges for name canonicalization. Include the below
// discriminator with the file ID and offset to help with this.
TokenPos pos = ast_token(node).pos;
str = gb_string_append_fmt(str, " /* %d!%d */", pos.file_id, pos.offset);
case_end;
case_ast_node(cl, CompoundLit, node);
+17 -4
View File
@@ -432,11 +432,24 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
// NOTE(bill): Ignore assignments to '_'
if (is_blank_ident(node)) {
check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier"));
if (rhs->mode == Addressing_Invalid) {
return nullptr;
String context_name = str_lit("assignment to '_' identifier");
check_assignment(ctx, rhs, nullptr, context_name);
switch (rhs->mode) {
case Addressing_ProcGroup: {
gbString expr_str = expr_to_string(rhs->expr);
defer (gb_string_free(expr_str));
error(rhs->expr,
"Cannot assign procedure group '%s' in %.*s",
expr_str,
LIT(context_name));
rhs->mode = Addressing_Invalid;
}
case Addressing_Invalid:
return nullptr;
default:
return rhs->type;
}
return rhs->type;
}
Entity *e = nullptr;
+11 -3
View File
@@ -1764,7 +1764,10 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
} else {
if (in_type) {
expr = unparen_expr(expr);
if (expr && expr->kind == Ast_Uninit) {
error(expr, "Default parameter cannot be ---");
} else if (in_type) {
check_expr_with_type_hint(ctx, &o, expr, in_type);
} else {
check_expr(ctx, &o, expr);
@@ -2174,6 +2177,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (!valid) {
if (op.mode == Addressing_Constant) {
poly_const = op.value;
if (poly_const.kind == ExactValue_Integer && is_type_float(type)) {
poly_const.kind = ExactValue_Float;
poly_const.value_float = big_int_to_f64(&poly_const.value_integer);
}
} else {
if (!ctx->in_proc_group) {
error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
@@ -2221,8 +2228,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
}
if (p->flags&FieldFlag_no_alias) {
if (!is_type_pointer(type) && !is_type_multi_pointer(type)) {
error(name, "'#no_alias' can only be applied pointer or multi-pointer typed parameters");
bool ok = is_type_internally_pointer_like(type);
if (!ok) {
error(name, "'#no_alias' can only be applied pointer-like type parameters");
p->flags &= ~FieldFlag_no_alias; // Remove the flag
}
}
+14 -3
View File
@@ -1109,7 +1109,6 @@ gb_internal i64 odin_compile_timestamp(void) {
return ns_after_1970;
}
gb_internal bool lb_use_new_pass_system(void);
gb_internal void init_universal(void) {
BuildContext *bc = &build_context;
@@ -1247,6 +1246,7 @@ gb_internal void init_universal(void) {
{"iPhone", Subtarget_iPhone},
{"iPhoneSimulator", Subtarget_iPhoneSimulator},
{"Android", Subtarget_Android},
{"Playdate", Subtarget_Playdate},
};
auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values));
@@ -1382,7 +1382,8 @@ gb_internal void init_universal(void) {
}
{
bool f16_supported = lb_use_new_pass_system();
// Available since LLVM 17 / new pass system, which is the minimum now.
bool f16_supported = true;
if (is_arch_wasm()) {
f16_supported = false;
} else if (build_context.metrics.os == TargetOs_darwin && build_context.metrics.arch == TargetArch_amd64) {
@@ -6883,6 +6884,13 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
if (entity_has_deferred_procedure(dst)) {
error(src->token,
"Deferred procedure '%.*s' cannot be used as the target of '%.*s' because it has a deferred procedure itself (deferred procedure chaining is not allowed)",
LIT(dst->token.string), LIT(src->token.string));
continue;
}
if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) {
error(src->token, "'%s' cannot be used with a polymorphic procedure", attribute);
continue;
@@ -6894,7 +6902,10 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
GB_ASSERT(is_type_proc(src->type));
if (!is_type_proc(src->type)) {
error(src->token, "Invalid procedure type found during deferred procedure checking");
continue;
}
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
Type *src_results = base_type(src->type)->Proc.results;
+1
View File
@@ -834,6 +834,7 @@ struct CheckerContext {
bool hide_polymorphic_errors;
bool in_polymorphic_specialization;
bool allow_arrow_right_selector_expr;
bool allow_c_vararg_param;
u8 bit_field_bit_size;
Scope * polymorphic_scope;
+6 -5
View File
@@ -1067,8 +1067,8 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Procedure:
switch (op) {
case Token_CmpEq: return x.value_typeid == y.value_typeid;
case Token_NotEq: return x.value_typeid != y.value_typeid;
case Token_CmpEq: return x.value_procedure == y.value_procedure;
case Token_NotEq: return x.value_procedure != y.value_procedure;
}
break;
@@ -1132,12 +1132,13 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const
gb_free(heap_allocator(), s.text);
return str;
}
// NOTE(tf2spi): %.17g is specific enough to canonically serialize f64
case ExactValue_Float:
return gb_string_append_fmt(str, "%f", v.value_float);
return gb_string_append_fmt(str, "%.17g", v.value_float);
case ExactValue_Complex:
return gb_string_append_fmt(str, "%f+%fi", v.value_complex->real, v.value_complex->imag);
return gb_string_append_fmt(str, "%.17g+%.17gi", v.value_complex->real, v.value_complex->imag);
case ExactValue_Quaternion:
return gb_string_append_fmt(str, "%f+%fi+%fj+%fk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag);
return gb_string_append_fmt(str, "%.17g+%.17gi+%.17gj+%.17gk", v.value_quaternion->real, v.value_quaternion->imag, v.value_quaternion->jmag, v.value_quaternion->kmag);
case ExactValue_Pointer:
return str;
+1 -1
View File
@@ -5613,7 +5613,7 @@ gb_inline b32 gb_path_is_absolute(char const *path) {
b32 result = false;
GB_ASSERT(path != NULL);
#if defined(GB_SYSTEM_WINDOWS)
result == (gb_strlen(path) > 2) &&
result = (gb_strlen(path) > 2) &&
gb_char_is_alpha(path[0]) &&
(path[1] == ':' && path[2] == GB_PATH_SEPARATOR);
#else
+1
View File
@@ -13,6 +13,7 @@ struct LinkerData {
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output);
// No longer required not that LLVM 14 is removed(?)
gb_internal void linker_enable_system_library_linking(LinkerData *ld) {
ld->needs_system_library_linked = true;
}
+14 -6
View File
@@ -186,9 +186,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType
lbCallingConventionKind cc_kind = lbCallingConvention_C;
// TODO(bill): Clean up this logic
if (!is_arch_wasm()) {
if (selected_subtarget == Subtarget_Playdate) {
cc_kind = lbCallingConvention_ARM_AAPCS_VFP;
} else if (!is_arch_wasm()) {
cc_kind = lb_calling_convention_map[calling_convention];
}
}
// if (build_context.metrics.arch == TargetArch_amd64) {
// if (build_context.metrics.os == TargetOs_windows) {
// if (cc_kind == lbCallingConvention_C) {
@@ -1554,14 +1556,14 @@ namespace lbAbiWasm {
namespace lbAbiArm32 {
gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention);
gb_internal LB_ABI_INFO(abi_info) {
LLVMContextRef c = m->ctx;
lbFunctionType *ft = permanent_alloc_item<lbFunctionType>();
ft->ctx = c;
ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
ft->ret = compute_return_type(c, return_type, return_is_defined);
ft->ret = compute_return_type(c, return_type, return_is_defined, calling_convention);
ft->calling_convention = calling_convention;
return ft;
}
@@ -1604,7 +1606,10 @@ namespace lbAbiArm32 {
} else {
i64 sz = lb_sizeof(t);
i64 a = lb_alignof(t);
if (is_calling_convention_odin(calling_convention) && sz > 8) {
// Added to support hard floats included in the playdates cortex-m7.
if (calling_convention == ProcCC_CDecl && selected_subtarget == Subtarget_Playdate) {
args[i] = lb_arg_type_direct(t);
} else if (is_calling_convention_odin(calling_convention) && sz > 8) {
// Minor change to improve performance using the Odin calling conventions
args[i] = lb_arg_type_indirect(t, nullptr);
} else if (a <= 4) {
@@ -1619,10 +1624,13 @@ namespace lbAbiArm32 {
return args;
}
gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (!is_register(return_type, true)) {
if (calling_convention == ProcCC_CDecl && selected_subtarget == Subtarget_Playdate) {
return lb_arg_type_direct(return_type);
}
switch (lb_sizeof(return_type)) {
case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr);
case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr);
+19 -19
View File
@@ -1571,7 +1571,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) {
GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation);
lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type);
error(e->token, "Objective-C related things are not allowed with '-bedrock'");
if (build_context.bedrock) {
error(e->token, "Objective-C related things are not allowed with '-bedrock'");
}
}
// Ensure classes that have been implicitly referenced through
@@ -1684,7 +1687,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
Type *superclass = tn.objc_superclass;
if (superclass != nullptr) {
auto& superclass_global = string_map_must_get(&global_class_map, tn.objc_class_name);
auto &superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name);
superclass_value = superclass_global.class_value;
}
@@ -2408,10 +2411,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
LLVMInitializeFunctionPassManager(m->function_pass_managers[i]);
}
lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default], false, build_context.optimization_level);
lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default_without_memcpy], true, build_context.optimization_level);
lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_none], -1);
for (i32 i = 0; i < lbFunctionPassManager_COUNT; i++) {
LLVMFinalizeFunctionPassManager(m->function_pass_managers[i]);
}
@@ -2480,11 +2479,8 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
auto wd = cast(lbLLVMModulePassWorkerData *)data;
LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level);
LLVMRunPassManager(module_pass_manager, wd->m->mod);
#if LB_USE_NEW_PASS_SYSTEM
auto passes = array_make<char const *>(heap_allocator(), 0, 64);
defer (array_free(&passes));
@@ -2560,7 +2556,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
exit_with_errors();
return 1;
}
#endif
if (LLVM_IGNORE_VERIFICATION) {
return 0;
@@ -2713,9 +2708,12 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) {
s.len -= prefix.len;
}
path = concatenate_strings(permanent_allocator(), path, s);
path = concatenate_strings(permanent_allocator(), s, STR_LIT(".ll"));
if (build_context.out_filepath.len > 0) {
path = concatenate_strings(permanent_allocator(), path, s);
path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll"));
} else {
path = concatenate_strings(permanent_allocator(), s, STR_LIT(".ll"));
}
return path;
}
@@ -2736,6 +2734,8 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
gbString path = gb_string_make_length(heap_allocator(), basename.text, basename.len);
path = gb_string_appendc(path, "/");
bool output_is_directory = path_is_directory(make_string_c(path));
if (USE_SEPARATE_MODULES) {
GB_ASSERT(m->module_name != nullptr);
String s = make_string_c(m->module_name);
@@ -2760,10 +2760,14 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
if (build_context.lto_kind != LTO_None) {
ext = STR_LIT("bc");
} else if (build_context.build_mode == BuildMode_Assembly) {
ext = STR_LIT("S");
// Allow a user override for the asm extension.
// If that's a directory, we force the `.S` extension
ext = output_is_directory ? STR_LIT("S") : build_context.build_paths[BuildPath_Output].ext;
} else if (build_context.build_mode == BuildMode_Object) {
// Allow a user override for the object extension.
ext = build_context.build_paths[BuildPath_Output].ext;
// If that's a directory, we force the `.obj` extension
ext = output_is_directory ? STR_LIT("obj") : build_context.build_paths[BuildPath_Output].ext;
} else {
ext = infer_object_extension_from_build_context();
}
@@ -2855,7 +2859,6 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading)
gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime, lbProcedure *cleanup_runtime) {
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
Type *params = alloc_type_tuple();
@@ -3164,9 +3167,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone;
if (!LB_USE_NEW_PASS_SYSTEM) {
build_context.optimization_level = gb_clamp(build_context.optimization_level, -1, 2);
}
switch (build_context.optimization_level) {
default:/*fallthrough*/
case 0: code_gen_level = LLVMCodeGenLevelNone; break;
+5 -44
View File
@@ -4,6 +4,10 @@
#include <llvm/Config/llvm-config.h>
#endif
#if LLVM_VERSION_MAJOR < 17
#error "LLVM Version 17 is the minimum required"
#endif
#include <llvm-c/Core.h>
#include <llvm-c/ExecutionEngine.h>
#include <llvm-c/Target.h>
@@ -11,46 +15,14 @@
#include <llvm-c/Object.h>
#include <llvm-c/BitWriter.h>
#include <llvm-c/DebugInfo.h>
#if LLVM_VERSION_MAJOR >= 17
#include <llvm-c/Transforms/PassBuilder.h>
#else
#include <llvm-c/Transforms/AggressiveInstCombine.h>
#include <llvm-c/Transforms/InstCombine.h>
#include <llvm-c/Transforms/IPO.h>
#include <llvm-c/Transforms/PassManagerBuilder.h>
#include <llvm-c/Transforms/Scalar.h>
#include <llvm-c/Transforms/Utils.h>
#include <llvm-c/Transforms/Vectorize.h>
#endif
#if LLVM_VERSION_MAJOR < 14
#error "LLVM Version 14 is the minimum required"
#endif
#if LLVM_VERSION_MAJOR > 14 || (LLVM_VERSION_MAJOR == 14 && LLVM_VERSION_MINOR >= 0 && LLVM_VERSION_PATCH > 0)
#define ODIN_LLVM_MINIMUM_VERSION_14 1
#else
#define ODIN_LLVM_MINIMUM_VERSION_14 0
#endif
#if LLVM_VERSION_MAJOR == 15 || LLVM_VERSION_MAJOR == 16
#error "LLVM versions 15 and 16 are not supported"
#endif
#if LLVM_VERSION_MAJOR >= 17
#define LB_USE_NEW_PASS_SYSTEM 1
#else
#define LB_USE_NEW_PASS_SYSTEM 0
#endif
#if LLVM_VERSION_MAJOR >= 19
#define LLVMDIBuilderInsertDeclareAtEnd(...) LLVMDIBuilderInsertDeclareRecordAtEnd(__VA_ARGS__)
#endif
gb_internal bool lb_use_new_pass_system(void) {
return LB_USE_NEW_PASS_SYSTEM;
}
struct lbProcedure;
struct lbValue {
@@ -408,13 +380,6 @@ struct lbProcedure {
#define ABI_PKG_NAME_SEPARATOR "."
#endif
#if !ODIN_LLVM_MINIMUM_VERSION_14
#define LLVMConstGEP2(Ty__, ConstantVal__, ConstantIndices__, NumIndices__) LLVMConstGEP(ConstantVal__, ConstantIndices__, NumIndices__)
#define LLVMConstInBoundsGEP2(Ty__, ConstantVal__, ConstantIndices__, NumIndices__) LLVMConstInBoundsGEP(ConstantVal__, ConstantIndices__, NumIndices__)
#define LLVMBuildPtrDiff2(Builder__, Ty__, LHS__, RHS__, Name__) LLVMBuildPtrDiff(Builder__, LHS__, RHS__, Name__)
#endif
gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c);
gb_internal String lb_mangle_name(Entity *e);
@@ -595,7 +560,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named=false);
gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
@@ -641,11 +606,7 @@ gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, S
gb_internal LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos);
gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) {
#if LB_USE_NEW_PASS_SYSTEM
return LLVMArrayType2(ElementType, ElementCount);
#else
return LLVMArrayType(ElementType, cast(unsigned)ElementCount);
#endif
}
gb_internal lbValue lb_emit_struct_iv(lbProcedure *p, lbValue agg, lbValue field, i32 index);
+16 -176
View File
@@ -109,68 +109,7 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType
return LLVMConstPointerCast(val, dst);
}
case LLVMStructTypeKind: {
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src),
cast(long long)lb_sizeof(dst),
cast(long long)lb_sizeof(src));
unsigned src_n = LLVMCountStructElementTypes(src);
unsigned dst_n = LLVMCountStructElementTypes(dst);
if (src_n != dst_n) goto failure;
// bool skip_cast = true;
// for (unsigned i = 0; i < dst_n; i++) {
// LLVMTypeKind dt = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(dst, i));
// LLVMTypeKind st = LLVMGetTypeKind(LLVMStructGetTypeAtIndex(src, i));
// if (dt != st) {
// skip_cast = false;
// }
// if (dt == LLVMIntegerTypeKind) {
// continue;
// }
// if (dt != LLVMArrayTypeKind) {
// skip_cast = false;
// break;
// }
// LLVMValueRef field_val = llvm_const_extract_value(m, val, i);
// if (field_val == nullptr) goto failure;
// LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i);
// LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val);
// if (lb_sizeof(dst_elem_ty) > lb_sizeof(src_elem_ty)) {
// skip_cast = true;
// continue;
// }
// }
// if (skip_cast) {
// return val;
// }
LLVMValueRef *field_vals = temporary_alloc_array<LLVMValueRef>(dst_n);
for (unsigned i = 0; i < dst_n; i++) {
LLVMValueRef field_val = llvm_const_extract_value(m, val, i);
if (field_val == nullptr) goto failure;
LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i);
LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val);
GB_ASSERT_MSG(lb_sizeof(dst_elem_ty) == lb_sizeof(src_elem_ty), "dst:%s vs src:%s (dst:%lld vs src:%lld) to %s from %s", LLVMPrintTypeToString(dst_elem_ty), LLVMPrintTypeToString(src_elem_ty),
cast(long long)lb_sizeof(dst_elem_ty),
cast(long long)lb_sizeof(src_elem_ty),
LLVMPrintTypeToString(dst),
LLVMPrintTypeToString(src)
);
field_vals[i] = llvm_const_cast(m, field_val, dst_elem_ty, failure_);
if (failure_ && *failure_) goto failure;
}
if (!LLVMIsLiteralStruct(dst)) {
return LLVMConstNamedStruct(dst, field_vals, dst_n);
} else {
return LLVMConstStructInContext(m->ctx, field_vals, dst_n, LLVMIsPackedStruct(dst));
}
goto failure;
}
case LLVMArrayTypeKind: {
goto failure;
@@ -261,10 +200,15 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count);
}
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named) {
unsigned value_count = cast(unsigned)value_count_;
unsigned elem_count = LLVMCountStructElementTypes(t);
GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
if (force_non_named) {
return LLVMConstStructInContext(m->ctx, values, value_count, true);
}
bool failure = false;
for (unsigned i = 0; i < elem_count; i++) {
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
@@ -638,104 +582,6 @@ gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel,
return lb_is_elem_const(elem, ft);
}
#if LLVM_VERSION_MAJOR == 14
LLVMValueRef llvm_const_pad_to_size(lbModule *m, LLVMValueRef val, LLVMTypeRef dst_ty) {
LLVMContextRef ctx = m->ctx;
LLVMTargetDataRef td = LLVMGetModuleDataLayout(m->mod);
LLVMTypeRef src_ty = LLVMTypeOf(val);
unsigned src_bits = (unsigned)LLVMSizeOfTypeInBits(td, src_ty);
unsigned dst_bits = (unsigned)LLVMSizeOfTypeInBits(td, dst_ty);
LLVMValueRef as_int = nullptr;
LLVMTypeKind src_kind = LLVMGetTypeKind(src_ty);
if (src_kind == LLVMIntegerTypeKind ||
src_kind == LLVMFloatTypeKind ||
src_kind == LLVMDoubleTypeKind ||
src_kind == LLVMPointerTypeKind ||
src_kind == LLVMVectorTypeKind) {
LLVMTypeRef src_int_ty = LLVMIntTypeInContext(ctx, src_bits);
as_int = LLVMConstBitCast(val, src_int_ty);
} else if (src_kind == LLVMArrayTypeKind) {
unsigned elem_count = LLVMGetArrayLength(src_ty);
LLVMTypeRef elem_ty = LLVMGetElementType(src_ty);
unsigned elem_bits = (unsigned)LLVMSizeOfTypeInBits(td, elem_ty);
LLVMTypeRef src_int_ty = LLVMIntTypeInContext(ctx, src_bits);
as_int = LLVMConstInt(src_int_ty, 0, false);
for (unsigned i = 0; i < elem_count; i++) {
LLVMValueRef elem = llvm_const_extract_value(m, val, i);
LLVMTypeRef elem_int_ty = LLVMIntTypeInContext(ctx, elem_bits);
LLVMValueRef elem_int = llvm_const_pad_to_size(m, elem, elem_int_ty);
LLVMValueRef shifted = LLVMConstShl(LLVMConstZExt(elem_int, src_int_ty), LLVMConstInt(src_int_ty, i * elem_bits, false));
as_int = LLVMConstOr(as_int, shifted);
}
} else if (src_kind == LLVMStructTypeKind) {
unsigned field_count = LLVMCountStructElementTypes(src_ty);
LLVMTypeRef src_int_ty = LLVMIntTypeInContext(ctx, src_bits);
as_int = LLVMConstInt(src_int_ty, 0, false);
for (unsigned i = 0; i < field_count; i++) {
LLVMTypeRef field_ty = LLVMStructGetTypeAtIndex(src_ty, i);
unsigned field_bits = (unsigned)LLVMSizeOfTypeInBits(td, field_ty);
LLVMValueRef field = llvm_const_extract_value(m, val, i);
LLVMTypeRef field_int_ty = LLVMIntTypeInContext(ctx, field_bits);
LLVMValueRef field_int = llvm_const_pad_to_size(m, field, field_int_ty);
uint64_t field_offset_bytes = LLVMOffsetOfElement(td, src_ty, i);
uint64_t field_offset_bits = field_offset_bytes * 8;
LLVMValueRef shifted = LLVMConstShl(LLVMConstZExt(field_int, src_int_ty), LLVMConstInt(src_int_ty, field_offset_bits, false));
as_int = LLVMConstOr(as_int, shifted);
}
} else {
gb_printf_err("unsupported const_pad source type: %s\n", LLVMPrintTypeToString(src_ty));
return nullptr;
}
if (src_bits != dst_bits) {
LLVMTypeRef dst_int_ty = LLVMIntTypeInContext(ctx, dst_bits);
if (src_bits < dst_bits) {
as_int = LLVMConstZExt(as_int, dst_int_ty);
} else {
as_int = LLVMConstTrunc(as_int, dst_int_ty);
}
}
LLVMTypeKind dst_kind = LLVMGetTypeKind(dst_ty);
if (dst_kind == LLVMIntegerTypeKind ||
dst_kind == LLVMFloatTypeKind ||
dst_kind == LLVMDoubleTypeKind ||
dst_kind == LLVMPointerTypeKind ||
dst_kind == LLVMVectorTypeKind) {
return LLVMConstBitCast(as_int, dst_ty);
} else if (dst_kind == LLVMArrayTypeKind) {
unsigned elem_count = LLVMGetArrayLength(dst_ty);
LLVMTypeRef elem_ty = LLVMGetElementType(dst_ty);
unsigned elem_bits = (unsigned)LLVMSizeOfTypeInBits(td, elem_ty);
LLVMValueRef *elems = temporary_alloc_array<LLVMValueRef>(elem_count);
LLVMTypeRef as_int_ty = LLVMTypeOf(as_int);
for (unsigned i = 0; i < elem_count; i++) {
LLVMValueRef shifted = LLVMConstLShr(as_int, LLVMConstInt(as_int_ty, i * elem_bits, false));
LLVMTypeRef elem_int_ty = LLVMIntTypeInContext(ctx, elem_bits);
LLVMValueRef trunc = LLVMConstTrunc(shifted, elem_int_ty);
elems[i] = llvm_const_pad_to_size(m, trunc, elem_ty);
}
return LLVMConstArray(elem_ty, elems, elem_count);
}
gb_printf_err("unsupported const_pad destination type: %s\n", LLVMPrintTypeToString(dst_ty));
return nullptr;
}
#endif
gb_internal void lb_const_array_spread(lbModule *m, lbConstContext cc, Type *array, ExactValue value, lbValue *res, Type *value_type) {
GB_ASSERT(array->kind == Type_Array);
@@ -1070,16 +916,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
LLVMValueRef values[4] = {};
unsigned value_count = 0;
#if LLVM_VERSION_MAJOR == 14
LLVMTypeRef block_type = lb_type_internal_union_block_type(m, bt);
values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type);
#else
values[value_count++] = cv.value;
if (block_size != type_size_of(variant_type)) {
LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1);
values[value_count++] = LLVMConstNull(padding_type);
}
#endif
Type *tag_type = union_tag_type(bt);
LLVMTypeRef llvm_tag_type = lb_type(m, tag_type);
@@ -2101,6 +1942,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
LLVMTypeRef struct_type = lb_type(m, original_type);
bool force_non_named = false;
auto field_remapping = lb_get_struct_remapping(m, type);
unsigned value_count = LLVMCountStructElementTypes(struct_type);
@@ -2137,7 +1980,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
visited[index] = true;
}
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
@@ -2175,7 +2017,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
if (is_constant) {
LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, tav.type, cc).value;
if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
if (is_type_union(cv_type) || is_type_raw_union(cv_type)) {
force_non_named = true;
values[index] = llvm_const_insert_value_with_rebuild(m, values[index], elem_value, idx_list, idx_list_len);
} else {
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
}
} else if (is_local) {
#if 1
lbProcedure *p = m->curr_procedure;
@@ -2222,13 +2069,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
Entity *f = type->Struct.fields[i+multiple_return_offset];
TypeAndValue tav = cl->elems[i]->tav;
// INFO: dead code:
// ExactValue val = {};
// if (tav.mode != Addressing_Invalid) {
// val = tav.value;
// }
if (is_type_tuple(tav.type)){
multiple_return_offset += tav.type->Tuple.variables.count-1;
}
@@ -2268,7 +2108,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
}
if (is_constant) {
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count);
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count, force_non_named);
LLVMTypeRef res_type = LLVMTypeOf(res.value);
GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type));
return res;
+7 -5
View File
@@ -1982,19 +1982,20 @@ handle_op:;
LLVMValueRef lhsval = lhs.value;
LLVMValueRef bits = rhs.value;
bool is_unsigned = is_type_unsigned(integral_type);
LLVMValueRef bit_size = LLVMConstInt(lb_type(p->module, rhs.type), 8*type_size_of(lhs.type), false);
LLVMValueRef width_test = LLVMBuildICmp(p->builder, LLVMIntULT, bits, bit_size, "");
if (is_unsigned) {
res.value = LLVMBuildLShr(p->builder, lhsval, bits, "");
LLVMValueRef zero = LLVMConstNull(lb_type(p->module, lhs.type));
res.value = LLVMBuildSelect(p->builder, width_test, res.value, zero, "");
} else {
LLVMValueRef bit_size_minus_one = LLVMConstInt(lb_type(p->module, rhs.type), 8*type_size_of(lhs.type)-1, false);
bits = LLVMBuildSelect(p->builder, width_test, bits, bit_size_minus_one, "");
res.value = LLVMBuildAShr(p->builder, lhsval, bits, "");
}
LLVMValueRef zero = LLVMConstNull(lb_type(p->module, lhs.type));
res.value = LLVMBuildSelect(p->builder, width_test, res.value, zero, "");
return res;
}
case Token_AndNot:
@@ -3878,6 +3879,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
break;
}
GB_ASSERT(res.value != nullptr);
return res;
} else if (is_type_soa_pointer(a)) {
+33 -13
View File
@@ -166,10 +166,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
String init_fullpath = c->parser->init_fullpath;
linker_data_init(gen, &c->info, init_fullpath);
#if defined(GB_SYSTEM_OSX) && (LLVM_VERSION_MAJOR < 14)
linker_enable_system_library_linking(gen);
#endif
gen->info = &c->info;
map_init(&gen->modules, gen->info->packages.count*2);
@@ -461,6 +457,27 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg,
return extracted_value;
}
gb_internal LLVMValueRef llvm_const_insert_value_with_rebuild(lbModule *m, LLVMValueRef agg, LLVMValueRef val, unsigned *indices, isize count) {
GB_ASSERT(LLVMIsConstant(agg));
GB_ASSERT(LLVMIsConstant(val));
GB_ASSERT(count > 0);
unsigned value_count = LLVMCountStructElementTypes(LLVMTypeOf(agg));
LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, count);
defer (gb_free(heap_allocator(), values));
for (unsigned i = 0; i < value_count; i++) {
values[i] = llvm_const_extract_value(m, agg, i);
}
if (count == 1) {
values[indices[0]] = val;
} else {
values[indices[0]] = llvm_const_insert_value_with_rebuild(m, values[indices[0]], val, indices+1, count-1);
}
return LLVMConstStructInContext(m->ctx, values, value_count, true);
}
@@ -1647,12 +1664,14 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
unsigned element_count = LLVMCountStructElementTypes(uvt);
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
LLVMValueRef ptr = u.value;
ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), "");
lbValue tag_ptr = {};
tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, "");
tag_ptr.type = alloc_type_pointer(tag_type);
tag_ptr.value = LLVMBuildPointerCast(p->builder, tag_ptr.value, lb_type(p->module, tag_ptr.type), "");
return tag_ptr;
}
@@ -2402,8 +2421,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
i64 full_type_align = type_align_of(type);
GB_ASSERT(full_type_size % full_type_align == 0);
if (type->Struct.is_raw_union) {
bool requires_packing = type->Struct.is_packed;
if (type->Struct.is_raw_union) {
lbStructFieldRemapping field_remapping = {};
slice_init(&field_remapping, permanent_allocator(), 1);
@@ -2411,7 +2431,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align);
field_remapping[0] = 0;
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), requires_packing);
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
return struct_type;
@@ -2431,7 +2451,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
}
i64 prev_offset = 0;
bool requires_packing = type->Struct.is_packed;
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
Entity *field = type->Struct.fields[field_index];
i64 offset = type->Struct.offsets[field_index];
@@ -2521,12 +2540,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
LLVMTypeRef tag_type = lb_type(m, union_tag_type(type));
array_add(&fields, block_type);
// #if LLVM_VERSION_MAJOR > 14
// { // NOTE(bill): always add a zero-byte pad to make inline constant unions work
// LLVMTypeRef padding_type = lb_type_padding_filler(m, 0, 1);
// array_add(&fields, padding_type);
// }
// #endif
array_add(&fields, tag_type);
i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type);
i64 padding = size - used_size;
@@ -2648,6 +2661,13 @@ gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type) {
m->internal_type_level += 1;
llvm_type = lb_type_internal(m, type);
m->internal_type_level -= 1;
// {
// i64 tsz = type_size_of(type);
// i64 lsz = lb_sizeof(llvm_type);
// GB_ASSERT_MSG(tsz == lsz, "%s %lld vs %lld %s", type_to_string(type), cast(long long)tsz, cast(long long)lsz, LLVMPrintTypeToString(llvm_type));
// }
if (m->internal_type_level == 0) {
map_set(&m->types, type, llvm_type);
}
-220
View File
@@ -32,11 +32,6 @@
**************************************************************************/
gb_internal void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
gb_internal void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
gb_internal void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
gb_internal void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level);
// gb_internal LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data) {
// lbModule *m = cast(lbModule *)user_data;
// if (m == nullptr) {
@@ -48,221 +43,6 @@ gb_internal void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPas
// return LLVMIsAAllocaInst(value) != nullptr;
// }
gb_internal bool lb_opt_ignore(i32 optimization_level) {
return optimization_level < 0;
}
gb_internal void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
if (lb_opt_ignore(optimization_level)) {
return;
}
#if !LB_USE_NEW_PASS_SYSTEM
if (false && optimization_level <= 0 && build_context.ODIN_DEBUG) {
LLVMAddMergedLoadStoreMotionPass(fpm);
} else {
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
if (!build_context.ODIN_DEBUG) {
LLVMAddEarlyCSEPass(fpm);
}
}
#endif
}
gb_internal void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
if (lb_opt_ignore(optimization_level)) {
return;
}
#if !LB_USE_NEW_PASS_SYSTEM
if (ignore_memcpy_pass) {
lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
} else if (optimization_level <= 0) {
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
#if 0
LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
#else
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
LLVMAddSCCPPass(fpm);
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddUnifyFunctionExitNodesPass(fpm);
LLVMAddCFGSimplificationPass(fpm);
LLVMAddEarlyCSEPass(fpm);
LLVMAddLowerExpectIntrinsicPass(fpm);
#endif
#endif
}
gb_internal void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
if (lb_opt_ignore(optimization_level)) {
return;
}
#if !LB_USE_NEW_PASS_SYSTEM
if (optimization_level <= 0) {
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
#if 1
LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level);
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
#else
LLVMAddMemCpyOptPass(fpm);
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
LLVMAddEarlyCSEPass(fpm);
LLVMAddMergedLoadStoreMotionPass(fpm);
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddCFGSimplificationPass(fpm);
LLVMAddSCCPPass(fpm);
LLVMAddPromoteMemoryToRegisterPass(fpm);
LLVMAddUnifyFunctionExitNodesPass(fpm);
LLVMAddCFGSimplificationPass(fpm);
LLVMAddEarlyCSEPass(fpm);
LLVMAddLowerExpectIntrinsicPass(fpm);
#endif
#endif
}
gb_internal void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level) {
#if !LB_USE_NEW_PASS_SYSTEM
LLVMAddCFGSimplificationPass(mpm);
LLVMAddJumpThreadingPass(mpm);
LLVMAddSimplifyLibCallsPass(mpm);
LLVMAddTailCallEliminationPass(mpm);
LLVMAddCFGSimplificationPass(mpm);
LLVMAddReassociatePass(mpm);
LLVMAddLoopRotatePass(mpm);
LLVMAddLICMPass(mpm);
LLVMAddLoopUnswitchPass(mpm);
LLVMAddCFGSimplificationPass(mpm);
LLVMAddLoopIdiomPass(mpm);
LLVMAddLoopDeletionPass(mpm);
LLVMAddMergedLoadStoreMotionPass(mpm);
LLVMAddMemCpyOptPass(mpm);
LLVMAddSCCPPass(mpm);
LLVMAddBitTrackingDCEPass(mpm);
LLVMAddJumpThreadingPass(mpm);
LLVMAddLICMPass(mpm);
LLVMAddLoopRerollPass(mpm);
LLVMAddAggressiveDCEPass(mpm);
LLVMAddCFGSimplificationPass(mpm);
#endif
}
gb_internal void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level) {
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
if (optimization_level <= 0 && build_context.ODIN_DEBUG) {
return;
}
#if !LB_USE_NEW_PASS_SYSTEM
LLVMAddAlwaysInlinerPass(mpm);
LLVMAddStripDeadPrototypesPass(mpm);
LLVMAddAnalysisPasses(target_machine, mpm);
LLVMAddPruneEHPass(mpm);
if (optimization_level <= 0) {
return;
}
LLVMAddGlobalDCEPass(mpm);
if (optimization_level >= 2) {
// NOTE(bill, 2021-03-29: use this causes invalid code generation)
// LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
// LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level);
// LLVMPassManagerBuilderPopulateModulePassManager(pmb, mpm);
// LLVMPassManagerBuilderPopulateLTOPassManager(pmb, mpm, false, true);
// return;
}
LLVMAddIPSCCPPass(mpm);
LLVMAddCalledValuePropagationPass(mpm);
LLVMAddGlobalOptimizerPass(mpm);
LLVMAddDeadArgEliminationPass(mpm);
LLVMAddCFGSimplificationPass(mpm);
LLVMAddPruneEHPass(mpm);
if (optimization_level < 2) {
return;
}
LLVMAddFunctionInliningPass(mpm);
lb_add_function_simplifcation_passes(mpm, optimization_level);
LLVMAddGlobalDCEPass(mpm);
LLVMAddGlobalOptimizerPass(mpm);
LLVMAddLoopRotatePass(mpm);
LLVMAddLoopVectorizePass(mpm);
if (optimization_level >= 2) {
LLVMAddEarlyCSEPass(mpm);
LLVMAddLICMPass(mpm);
LLVMAddLoopUnswitchPass(mpm);
LLVMAddCFGSimplificationPass(mpm);
}
LLVMAddCFGSimplificationPass(mpm);
LLVMAddSLPVectorizePass(mpm);
LLVMAddLICMPass(mpm);
LLVMAddAlignmentFromAssumptionsPass(mpm);
LLVMAddStripDeadPrototypesPass(mpm);
if (optimization_level >= 2) {
LLVMAddGlobalDCEPass(mpm);
LLVMAddConstantMergePass(mpm);
}
LLVMAddCFGSimplificationPass(mpm);
#endif
}
/**************************************************************************
IMPORTANT NOTE(bill, 2021-11-06): Custom Passes
+29 -14
View File
@@ -452,7 +452,9 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
Type *pt = p->type;
lbCallingConventionKind cc_kind = lbCallingConvention_C;
if (!is_arch_wasm()) {
if (selected_subtarget == Subtarget_Playdate) {
cc_kind = lbCallingConvention_ARM_AAPCS_VFP;
} else if (!is_arch_wasm()) {
cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
}
LLVMSetFunctionCallConv(p->value, cc_kind);
@@ -846,8 +848,9 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
GB_ASSERT(pd->body != nullptr);
lbModule *m = p->module;
if (e->min_dep_count.load(std::memory_order_relaxed) == 0) {
if (e->min_dep_count.load(std::memory_order_relaxed) == 0 || e->code_gen_procedure != nullptr) {
// NOTE(bill): Nothing depends upon it so doesn't need to be built
// NOTE(tf2spi): Code may already be generated (i.e. loop unrolling), don't regen it
return;
}
@@ -965,8 +968,7 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
for (unsigned i = 0; i < param_count; i++) {
LLVMTypeRef param_type = param_types[i];
LLVMTypeRef arg_type = LLVMTypeOf(args[i]);
if (LB_USE_NEW_PASS_SYSTEM &&
arg_type != param_type) {
if (arg_type != param_type) {
LLVMTypeKind arg_kind = LLVMGetTypeKind(arg_type);
LLVMTypeKind param_kind = LLVMGetTypeKind(param_type);
if (arg_kind == param_kind) {
@@ -994,6 +996,9 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, "");
auto llvm_cc = lb_calling_convention_map[proc_type->Proc.calling_convention];
if (selected_subtarget == Subtarget_Playdate) {
llvm_cc = lbCallingConvention_ARM_AAPCS_VFP;
}
LLVMSetInstructionCallConv(ret, llvm_cc);
LLVMAttributeIndex param_offset = LLVMAttributeIndex_FirstArgIndex;
@@ -1693,7 +1698,8 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
case BuiltinProc_simd_min:
if (is_float) {
return lb_emit_min(p, res.type, arg0, arg1);
LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -1701,7 +1707,8 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
return res;
case BuiltinProc_simd_max:
if (is_float) {
return lb_emit_max(p, res.type, arg0, arg1);
LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
} else {
LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, "");
res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
@@ -4762,14 +4769,22 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr, lbValue *sret_
return res;
}
gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value) {
gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value, Type *c_vararg_type = nullptr) {
if (is_type_tuple(value.type)) {
for_array(i, value.type->Tuple.variables) {
lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i);
array_add(args, sub_value);
if (c_vararg_type) {
array_add(args, lb_emit_c_vararg(p, sub_value, c_vararg_type));
} else {
array_add(args, sub_value);
}
}
} else {
array_add(args, value);
if (c_vararg_type) {
array_add(args, lb_emit_c_vararg(p, value, c_vararg_type));
} else {
array_add(args, value);
}
}
}
@@ -4890,9 +4905,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
if (is_type_untyped_nil(arg.type)) {
arg = lb_const_nil(p->module, t_rawptr);
}
array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
lb_add_values_to_array(p, &args, arg, arg.type);
} else {
array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
lb_add_values_to_array(p, &args, arg, elem_type);
}
}
break;
@@ -5018,15 +5033,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
if (is_type_untyped_nil(arg.type)) {
arg = lb_const_nil(p->module, t_rawptr);
}
array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
lb_add_values_to_array(p, &args, arg, arg.type);
} else {
array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
lb_add_values_to_array(p, &args, arg, elem_type);
}
}
} else {
lbValue value = lb_build_expr(p, fv->value);
GB_ASSERT(!is_type_tuple(value.type));
array_add(&args, lb_emit_c_vararg(p, value, value.type));
lb_add_values_to_array(p, &args, value, value.type);
}
} else {
lbValue value = lb_build_expr(p, fv->value);
+24 -6
View File
@@ -153,7 +153,7 @@ gb_internal lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbVa
gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t);
bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
bool use_llvm_intrinsic = !is_arch_wasm() && is_type_float(t);
if (use_llvm_intrinsic) {
LLVMValueRef args[2] = {x.value, y.value};
LLVMTypeRef types[1] = {lb_type(p->module, t)};
@@ -169,7 +169,7 @@ gb_internal lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
gb_internal lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
x = lb_emit_conv(p, x, t);
y = lb_emit_conv(p, y, t);
bool use_llvm_intrinsic = !is_arch_wasm() && (is_type_float(t) || (is_type_simd_vector(t) && is_type_float(base_array_type(t))));
bool use_llvm_intrinsic = !is_arch_wasm() && is_type_float(t);
if (use_llvm_intrinsic) {
LLVMValueRef args[2] = {x.value, y.value};
LLVMTypeRef types[1] = {lb_type(p->module, t)};
@@ -1206,18 +1206,19 @@ gb_internal lbValue lb_emit_struct_ep_internal(lbProcedure *p, lbValue s, i32 in
i32 original_index = index;
index = lb_convert_struct_index(p->module, t, index);
lbModule *m = p->module;
if (lb_is_const(s)) {
// NOTE(bill): this cannot be replaced with lb_emit_epi
lbModule *m = p->module;
lbValue res = {};
LLVMValueRef indices[2] = {llvm_zero(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
res.type = alloc_type_pointer(result_type);
res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
res.value = LLVMConstPointerCast(res.value, lb_type(m, res.type));
return res;
} else {
lbValue res = {};
LLVMTypeRef st = lb_type(p->module, type_deref(s.type));
LLVMTypeRef st = lb_type(m, type_deref(s.type));
// gb_printf_err("%s\n", type_to_string(s.type));
// gb_printf_err("%s\n", LLVMPrintTypeToString(LLVMTypeOf(s.value)));
// gb_printf_err("%d\n", index);
@@ -1225,8 +1226,9 @@ gb_internal lbValue lb_emit_struct_ep_internal(lbProcedure *p, lbValue s, i32 in
unsigned count = LLVMCountStructElementTypes(st);
GB_ASSERT_MSG(count >= cast(unsigned)index, "%u %d %d", count, index, original_index);
res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
res.type = alloc_type_pointer(result_type);
res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
res.value = LLVMBuildPointerCast(p->builder, res.value, lb_type(m, res.type), "");
return res;
}
}
@@ -1274,6 +1276,17 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
case 1: result_type = ft; break;
case 2: result_type = ft; break;
case 3: result_type = ft; break;
case -1:
{
lbValue res = {};
res.type = alloc_type_pointer(alloc_type_array(ft, 3));
if (lb_is_const(s)) {
res.value = LLVMConstPointerCast(s.value, lb_type(p->module, res.type));
} else {
res.value = LLVMBuildPointerCast(p->builder, s.value, lb_type(p->module, res.type), "");
}
return res;
}
}
} else if (is_type_slice(t)) {
switch (index) {
@@ -1443,6 +1456,11 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
case 1: result_type = ft; break;
case 2: result_type = ft; break;
case 3: result_type = ft; break;
case -1: {
lbValue ptr_q = lb_address_from_load_or_generate_local(p, s);
lbValue ptr_xyz = lb_emit_struct_ep(p, ptr_q, -1);
return lb_emit_load(p, ptr_xyz);
}
}
break;
}
+9 -17
View File
@@ -875,7 +875,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
} else if (value.value_string == "speed") {
build_context.custom_optimization_level = true;
build_context.optimization_level = 2;
} else if (value.value_string == "aggressive" && LB_USE_NEW_PASS_SYSTEM) {
} else if (value.value_string == "aggressive") {
build_context.custom_optimization_level = true;
build_context.optimization_level = 3;
} else {
@@ -884,9 +884,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
gb_printf_err("\tminimal\n");
gb_printf_err("\tsize\n");
gb_printf_err("\tspeed\n");
if (LB_USE_NEW_PASS_SYSTEM) {
gb_printf_err("\taggressive\n");
}
gb_printf_err("\taggressive\n");
gb_printf_err("\tnone (useful for -debug builds)\n");
bad_flags = true;
}
@@ -1199,8 +1197,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
bool found = false;
if (selected_target_metrics->metrics->os != TargetOs_darwin &&
selected_target_metrics->metrics->os != TargetOs_linux ) {
gb_printf_err("-subtarget can only be used with darwin and linux based targets at the moment\n");
selected_target_metrics->metrics->os != TargetOs_linux &&
(selected_target_metrics->metrics->os != TargetOs_freestanding ||
selected_target_metrics->metrics->arch != TargetArch_arm32)) {
gb_printf_err("-subtarget can only be used with darwin, linux or freestanding_arm32 based targets at the moment\n");
bad_flags = true;
break;
}
@@ -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>")) {
@@ -3041,9 +3042,7 @@ gb_internal int print_show_help(String const arg0, String command, String option
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
if (LB_USE_NEW_PASS_SYSTEM) {
print_usage_line(3, "-o:aggressive (use this with caution)");
}
print_usage_line(2, "The default is -o:minimal. If -debug is set, the default is -o:none.");
}
@@ -3102,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");
@@ -3202,7 +3200,7 @@ gb_internal int print_show_help(String const arg0, String command, String option
if (build) {
if (print_flag("-subtarget:<subtarget>")) {
print_usage_line(2, "[Darwin and Linux only]");
print_usage_line(2, "[Darwin, Linux and Freestanding ARM32 only]");
print_usage_line(2, "Available subtargets:");
String prefix = str_lit("-subtarget:");
for (u32 i = 1; i < Subtarget_COUNT; i++) {
@@ -4219,12 +4217,6 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf_err("missing required target feature: \"%.*s\", enable it by setting a different -microarch or explicitly adding it through -target-features\n", LIT(disabled));
gb_exit(1);
}
// NOTE(laytan): some weird errors on LLVM 14 that LLVM 17 fixes.
if (LLVM_VERSION_MAJOR < 17) {
gb_printf_err("Invalid LLVM version %s, RISC-V targets require at least LLVM 17\n", LLVM_VERSION_STRING);
gb_exit(1);
}
}
if (build_context.show_debug_messages) {
+15 -5
View File
@@ -761,17 +761,27 @@ gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) {
gb_internal Find_Result find_visual_studio_and_windows_sdk() {
Find_Result r = {};
find_windows_kit_paths(&r);
find_visual_studio_by_fighting_through_microsoft_craziness(&r);
// Prefer the toolset exported into the environment (e.g. by vsdevcmd) so the
// linker uses the same MSVC toolset of other externally compiled C++ dependencies.
// The COM autodetection grabs the first installed Visual Studio, which may be an older
// toolset whose runtime libraries lack symbols that the newer-built libraries
// reference. Fall back to autodetection when the environment is bare.
find_visual_studio_paths_from_env_vars(&r);
bool vs_found =
r.vs_exe_path.len &&
r.vs_library_path.len ;
if (!vs_found) {
find_visual_studio_by_fighting_through_microsoft_craziness(&r);
}
bool sdk_found =
r.windows_sdk_bin_path.len &&
r.windows_sdk_um_library_path.len &&
r.windows_sdk_ucrt_library_path.len ;
bool vs_found =
r.vs_exe_path.len &&
r.vs_library_path.len ;
if (!sdk_found) {
find_windows_kit_paths_from_env_vars(&r);
}
+16 -2
View File
@@ -516,9 +516,17 @@ gb_internal u64 type_hash_canonical_type(Type *type) {
return prev_hash;
}
// NOTE(tf2spi): Unwrap type aliases similar to are_types_identical*
Type *type_unaliased = type;
if (type->kind == Type_Named) {
Entity *e = type->Named.type_name;
if (e->TypeName.is_type_alias) {
type_unaliased = type->Named.base;
}
}
TypeWriter w = {};
type_writer_make_hasher(&w, &w.hash_ctx);
write_type_to_canonical_string(&w, type);
write_type_to_canonical_string(&w, type_unaliased);
u64 hash = typeid_hash_context_fini(&w.hash_ctx);
if (build_context.webkit_switch_workaround) {
// Clear the top bit so every `typeid` is in [1, 2^63). A `switch` over a
@@ -972,6 +980,12 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
type_writer_appendc(w, "->");
write_canonical_params(w, type->Proc.results);
}
if (type->Proc.diverging) {
type_writer_appendc(w, "!");
}
if (type->Proc.optional_ok) {
type_writer_appendc(w, "#optional_ok");
}
return;
case Type_Generic:
@@ -1010,4 +1024,4 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
}
return;
}
}
+45 -10
View File
@@ -1529,6 +1529,9 @@ gb_internal Token advance_token(AstFile *f) {
switch (f->curr_token.kind) {
case Token_Comment:
consume_comment_groups(f, prev);
if (f->curr_token.kind == Token_Semicolon && ignore_newlines(f) && f->curr_token.string == "\n") {
advance_token(f);
}
break;
case Token_Semicolon:
if (ignore_newlines(f) && f->curr_token.string == "\n") {
@@ -3582,7 +3585,9 @@ gb_internal Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
case Token_when:
if (prev.pos.line < op.pos.line) {
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
goto loop_end;
if (f->expr_level <= 0) {
goto loop_end;
}
}
break;
}
@@ -6049,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;
@@ -6111,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++) {
@@ -6129,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++) {
@@ -6157,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);
@@ -6229,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;
@@ -6282,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) {
@@ -6308,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 = {};
@@ -7095,4 +7131,3 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
return ParseFile_None;
}
+2 -2
View File
@@ -643,7 +643,7 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
thread_init(pool, t, idx);
isize stack_size = 0;
isize stack_size = 1 * 1024 * 1024; // 1 MiB (LLVM takes a lot of stack space)
#if defined(GB_SYSTEM_WINDOWS)
t->win32_handle = CreateThread(NULL, stack_size, internal_thread_proc, t, 0, NULL);
@@ -1067,4 +1067,4 @@ void atomic_freelist_put(std::atomic<AtomicFreelist<T> *> &head_list, AtomicFree
}
}
}
}
+26 -1
View File
@@ -1471,6 +1471,10 @@ gb_internal bool is_type_constant_type(Type *t) {
return is_type_constant_type(t->Array.elem);
case Type_EnumeratedArray:
return is_type_constant_type(t->EnumeratedArray.elem);
case Type_SimdVector:
return is_type_constant_type(t->SimdVector.elem);
case Type_Matrix:
return is_type_constant_type(t->Matrix.elem);
}
return false;
}
@@ -1525,7 +1529,7 @@ gb_internal bool is_type_multi_pointer(Type *t) {
return t->kind == Type_MultiPointer;
}
gb_internal bool is_type_internally_pointer_like(Type *t) {
return is_type_pointer(t) || is_type_multi_pointer(t) || is_type_cstring(t) || is_type_proc(t);
return is_type_pointer(t) || is_type_multi_pointer(t) || is_type_cstring(t) || is_type_cstring16(t) || is_type_proc(t);
}
gb_internal bool is_type_tuple(Type *t) {
@@ -3977,6 +3981,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f16, false, 1);
gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f16, false, 2);
gb_local_persist String xyz = str_lit("xyz");
gb_local_persist Entity *entity__xyz = alloc_entity_field(nullptr, make_token_ident(xyz), alloc_type_array(t_f16, 3), false, -1);
String n = field_name.string();
if (n == w) {
selection_add_index(&sel, 3);
@@ -3994,6 +4001,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
selection_add_index(&sel, 2);
sel.entity = entity__z;
return sel;
} else if (n == xyz) {
selection_add_index(&sel, -1);
sel.entity = entity__xyz;
return sel;
}
} break;
@@ -4008,6 +4019,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f32, false, 1);
gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f32, false, 2);
gb_local_persist String xyz = str_lit("xyz");
gb_local_persist Entity *entity__xyz = alloc_entity_field(nullptr, make_token_ident(xyz), alloc_type_array(t_f32, 3), false, -1);
String n = field_name.string();
if (n == w) {
selection_add_index(&sel, 3);
@@ -4025,6 +4039,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
selection_add_index(&sel, 2);
sel.entity = entity__z;
return sel;
} else if (n == xyz) {
selection_add_index(&sel, -1);
sel.entity = entity__xyz;
return sel;
}
} break;
@@ -4039,6 +4057,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f64, false, 1);
gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f64, false, 2);
gb_local_persist String xyz = str_lit("xyz");
gb_local_persist Entity *entity__xyz = alloc_entity_field(nullptr, make_token_ident(xyz), alloc_type_array(t_f64, 3), false, -1);
String n = field_name.string();
if (n == w) {
selection_add_index(&sel, 3);
@@ -4056,6 +4077,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
selection_add_index(&sel, 2);
sel.entity = entity__z;
return sel;
} else if (n == xyz) {
selection_add_index(&sel, -1);
sel.entity = entity__xyz;
return sel;
}
} break;
+1 -1
View File
@@ -127,7 +127,7 @@ gb_internal isize utf8_decode(u8 const *str, isize str_len, Rune *codepoint_out)
sz = x&7;
accept = global__utf8_accept_ranges[x>>4];
if (str_len < gb_size_of(sz))
if (str_len < sz)
goto invalid_codepoint;
b1 = str[1];
+160
View File
@@ -0,0 +1,160 @@
package benchmark_core_crypto
import "base:runtime"
import "core:log"
import "core:testing"
import "core:text/table"
import "core:time"
import "core:crypto"
import "core:crypto/rsa"
// RSA key generation is time consuming and high variance, so it takes
// an unreasonable amount of time to get a semi-sensible value, so this
// is skipped by default.
RSA_BENCH_KEYGEN: bool : #config(ODIN_BENCHMARK_RSA_KEYGEN, false)
@(private = "file")
KEYGEN_ITERS :: 100
@(private = "file")
SIGN_ITERS :: 5000
@(private = "file")
ENCRYPT_ITERS :: 5000
@(test)
benchmark_crypto_rsa :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tbl: table.Table
table.init(&tbl)
defer table.destroy(&tbl)
table.caption(&tbl, "RSA")
table.aligned_header_of_values(&tbl, .Right, "Operation", "Avg. Time")
if RSA_BENCH_KEYGEN {
bench_keygen_2048(&tbl)
table.row_of_values(&tbl)
}
bench_pkcs1_2048(&tbl)
table.row_of_values(&tbl)
bench_pss_2048(&tbl)
table.row_of_values(&tbl)
bench_oaep_2048(&tbl)
log_table(&tbl)
}
@(private="file")
bench_keygen_2048 :: proc(tbl: ^table.Table) {
if !crypto.HAS_RAND_BYTES {
log.warnf("rsa: keygen benchmarks skipped, no system entropy source")
}
priv_key: rsa.Private_Key
start := time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
ok := rsa.private_key_generate(&priv_key, 2048)
assert(ok, "keygen should succeed")
}
taken := time.tick_since(start) / KEYGEN_ITERS
append_tbl(tbl, "Keygen/2048", taken)
}
@(private="file")
bench_pkcs1_2048 :: proc(tbl: ^table.Table) {
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
msg_bytes := transmute([]byte)(SIG_MSG)
sig_bytes: [2048 >> 3]byte
start := time.tick_now()
for _ in 0 ..< SIGN_ITERS {
ok := rsa.sign_pkcs1(&priv_key, .SHA256, msg_bytes, sig_bytes[:])
assert(ok, "signing should succeed")
}
taken := time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PKCS1/2048/SHA256/sign", taken)
start = time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
ok := rsa.verify_pkcs1(&priv_key._pub_key, .SHA256, msg_bytes, sig_bytes[:])
assert(ok, "verify should succeed")
}
taken = time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PKCS1/2048/SHA256/verify", taken)
}
@(private="file")
bench_pss_2048 :: proc(tbl: ^table.Table) {
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
msg_bytes := transmute([]byte)(SIG_MSG)
sig_bytes: [2048 >> 3]byte
start := time.tick_now()
for _ in 0 ..< SIGN_ITERS {
ok := rsa.sign_pss(&priv_key, .SHA256, 32, msg_bytes, sig_bytes[:])
assert(ok, "signing should succeed")
}
taken := time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PSS/2048/SHA256/sign", taken)
start = time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
ok := rsa.verify_pss(&priv_key._pub_key, .SHA256, 32, msg_bytes, sig_bytes[:])
assert(ok, "verify should succeed")
}
taken = time.tick_since(start) / SIGN_ITERS
append_tbl(tbl, "PSS/2048/SHA256/verify", taken)
}
@(private="file")
bench_oaep_2048 :: proc(tbl: ^table.Table) {
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping")
return
}
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
msg_bytes := transmute([]byte)(SIG_MSG)
ciphertext_bytes: [2048 >> 3]byte
buf: [32]byte
start := time.tick_now()
for _ in 0 ..< SIGN_ITERS {
ok := rsa.encrypt_oaep(&priv_key._pub_key, .SHA256, msg_bytes, ciphertext_bytes[:])
assert(ok, "encryption should succeed")
}
taken := time.tick_since(start) / ENCRYPT_ITERS
append_tbl(tbl, "OAEP/2048/SHA256/encrypt", taken)
start = time.tick_now()
for _ in 0 ..< KEYGEN_ITERS {
_, ok := rsa.decrypt_oaep(&priv_key, .SHA256, ciphertext_bytes[:], buf[:])
assert(ok, "decrypt should succeed")
}
taken = time.tick_since(start) / ENCRYPT_ITERS
append_tbl(tbl, "OAEP/2048/SHA256/decrypt", taken)
}
@(private="file")
append_tbl :: proc(tbl: ^table.Table, op_name: string, avg_time: time.Duration) {
table.aligned_row_of_values(
tbl,
.Right,
op_name,
table.format(tbl, "%8M", avg_time),
)
}
+22 -1
View File
@@ -135,9 +135,30 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
testing.expect(t, rb.len(tree) == entry_count - 1, "iterator/remove: len should drop by 1")
rb.destroy(&tree)
testing.expect(t, rb.len(tree) == 0, "destroy: len should be 0")
testing.expect(t, rb.len(tree) == 0, "destroy: len should be 0")
testing.expectf(t, callback_count == 0, "remove: on_remove should've been called %v times, it was %v", entry_count, callback_count)
// Test upsert
rb.init(&tree)
clear(&inserted_map)
for i := 0; i < NR_INSERTS * 4; i += 1 {
k := Key(i) & 0x7
v := Value(i)
existing_node, in_map := inserted_map[k]
n, inserted, _ := rb.upsert(&tree, k, v)
testing.expect(t, in_map != inserted, "upsert: inserted should match inverse of map lookup")
if inserted {
inserted_map[k] = n
} else {
testing.expect(t, existing_node == n, "upsert: expecting existing node")
testing.expect_value(t, v, n.value) // And updated value
}
}
rb.destroy(&tree)
// print_tree_node(tree._root)
delete(inserted_map)
delete(inserted_keys)
+310
View File
@@ -0,0 +1,310 @@
// Tests for the constant time RSA primitives
package test_core_crypto_bigint
import "base:runtime"
import "core:crypto/_bigint"
import "core:log"
import "core:slice"
import "core:testing"
ROUNDS :: 100_000
i31_equal :: proc(a, b: []u32) -> bool {
if a[0] != b[0] { return false }
bits := uint(a[0])
idx := 1
for bits > 0 {
ex := min(bits, 31)
mask := u32(1<<ex) - 1
if a[idx] & mask != b[idx] & mask { return false }
bits -= ex
idx += 1
}
return true
}
@(test)
i31_is_zero :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
arr := make([][5]u32, ROUNDS, context.temp_allocator)
for i in 0..<len(arr) {
_bigint.i31_mkrand(arr[i][:], u32((len(arr[i])-1) * 32))
}
for &v in arr {
v[0] = _bigint.i31_bit_length(v[1:])
sum: u64
for w in v[1:] {
sum += u64(w)
}
testing.expect_value(t, _bigint.i31_is_zero(v[:]), 1 if sum == 0 else 0)
slice.zero(v[1:])
testing.expect_value(t, _bigint.i31_is_zero(v[:]), 1)
}
}
@(test)
i31_add :: proc(t: ^testing.T) {
N :: 5
res: [N]u32
for v in i31_add_test_vectors {
if len(v.a) > N || len(v.b) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
if !(len(v.a) == len(v.b) && len(v.b) == len(v.res)) {
log.infof("Skipped %v, expected `a`, `b` and `res` lengths to be equal", v)
continue
}
// Copy into writable memory
copy(res[:], v.a[:])
// Add b to "a" in place
cc := _bigint.i31_add(res[:], v.b[:], 1)
testing.expect(t, slice.equal(res[:len(v.res)], v.res))
testing.expect_value(t, cc, v.carry)
}
}
@(test)
i31_sub :: proc(t: ^testing.T) {
N :: 5
res: [N]u32
for v in i31_sub_test_vectors {
if len(v.a) > N || len(v.b) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
if !(len(v.a) == len(v.b) && len(v.b) == len(v.res)) {
log.infof("Skipped %v, expected `a`, `b` and `res` lengths to be equal", v)
continue
}
// Copy into writable memory
copy(res[:], v.a[:])
// Add b to "a" in place
cc := _bigint.i31_sub(res[:], v.b[:], 1)
testing.expect(t, slice.equal(res[:len(v.res)], v.res))
testing.expect_value(t, cc, v.carry)
}
}
@(test)
i31_bit_length :: proc (t: ^testing.T) {
for v in i31_add_test_vectors {
a_len := _bigint.i31_bit_length(v.a[1:])
b_len := _bigint.i31_bit_length(v.b[1:])
testing.expect_value(t, a_len, v.a[0])
testing.expect_value(t, b_len, v.b[0])
}
}
@(test)
i31_decode :: proc(t: ^testing.T) {
N :: 10
res: [N]u32
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_decode_test_vectors {
if len(v.decode) > N || len(v.mod) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
_bigint.i31_decode(res[:], v.src)
testing.expect(t, slice.equal(res[:len(v.decode)], v.decode))
slice.zero(res[:])
mod_res := _bigint.i31_decode_mod(res[:], v.src, mod)
testing.expect(t, slice.equal(res[:len(v.mod)], v.mod))
testing.expect_value(t, mod_res, v.mod_res)
encoded: [32]u8 = 0
_bigint.i31_encode(encoded[:], v.decode)
testing.expect(t, slice.equal(encoded[32 - len(v.src):], v.src))
}
}
@(test)
i31_rshift :: proc(t: ^testing.T) {
N :: 4
res: [N]u32
for v in i31_rshift_test_vectors {
if len(v.orig) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
if v.shift < 0 || v.shift > 31 {
log.infof("Skipped %v, invalid shift amount", v)
continue
}
copy(res[:], v.orig)
_bigint.i31_rshift(res[:], v.shift)
testing.expect(t, slice.equal(res[:len(v.res)], v.res))
}
}
@(test)
i31_reduce :: proc(t: ^testing.T) {
N :: 12
res: [N]u32 = ---
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_reduce_test_vectors {
if len(v.orig) > N || len(v.res) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
slice.zero(res[:])
_bigint.i31_reduce(res[:], v.orig, mod)
testing.expect(t, i31_equal(res[:], v.res))
}
}
@(test)
i31_decode_reduce :: proc(t: ^testing.T) {
N :: 4
res: [N]u32 = 0
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_decode_reduce_test_vectors {
if len(v.decode) > N {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
res = 0
_bigint.i31_decode_reduce(res[:], v.src, mod)
testing.expect(t, i31_equal(res[:], v.decode))
}
}
@(test)
i31_muladd_small :: proc(t: ^testing.T) {
mod := []u32{42, 0x7fff_fffe, 0x3ff, 0}
for v in i31_mul_add_test_vectors {
if len(v.orig) > len(mod) || len(v.res) > len(mod) {
log.infof("Skipped %v, not enough scratch space", v)
continue
}
res: [3]u32 = 0
copy(res[:], v.orig)
_bigint.i31_muladd_small(res[:], v.z, mod)
l := len(v.res)
testing.expect(t, slice.equal(res[:l], v.res[:l]))
}
}
@(test)
i31_encode :: proc(t: ^testing.T) {
for v in i31_encode_test_vectors {
decoded: [10]u32 = 0
_bigint.i31_decode(decoded[:], v.encoded)
l := len(v.orig)
testing.expect(t, slice.equal(decoded[:l], v.orig[:l]))
encoded: [32]u8 = 0
_bigint.i31_encode(encoded[:], v.orig)
testing.expect(t, slice.equal(encoded[:], v.encoded))
}
}
@(test)
i31_monty_mul :: proc(t: ^testing.T) {
for v in i31_monty_mul_test_vectors {
res: [6]u32 = 0
m0i := _bigint.i31_ninv31(v.m[1])
if m0i == 0 {
log.infof("Expected _bigint.i31_ninv31(%v) to not be 0, m[1] must be even. Skipped.", v.m[1])
continue
}
_bigint.i31_montymul(res[:], v.x, v.y, v.m, m0i)
testing.expect(t, slice.equal(res[:], v.res))
}
}
@(test)
i31_to_monty :: proc(t: ^testing.T) {
for v in i31_to_monty_test_vectors {
res: [6]u32 = 0
copy(res[:], v.orig)
_bigint.i31_to_monty(res[:], v.m)
testing.expect(t, slice.equal(res[:], v.x))
m0i := _bigint.i31_ninv31(v.m[1])
if m0i == 0 {
log.infof("Expected _bigint.i31_ninv31(%v) to not be 0, m[1] must be even. Skipped.", v.m[1])
continue
}
_bigint.i31_from_monty(res[:], v.m, m0i)
testing.expect(t, slice.equal(res[:], v.orig))
}
}
@(test)
i31_modpow :: proc(t: ^testing.T) {
for v in i31_mod_pow_test_vectors {
x_out: [6]u32
temp: [100]u32
copy(x_out[:], v.orig)
m0i := _bigint.i31_ninv31(v.m[1])
assert(m0i != 0)
_bigint.i31_modpow(x_out[:], v.e, v.m, m0i, temp[:6], temp[6:][:6])
testing.expect(t, slice.equal(x_out[:], v.x))
}
}
@(test)
i31_mulacc :: proc(t: ^testing.T) {
for v in i31_mul_acc_test_vectors {
res: [12]u32 = 0
copy(res[:], v.d)
assert(v.d[0] == v.a[0])
_bigint.i31_mulacc(res[:], v.a, v.b)
testing.expect(t, slice.equal(res[:], v.res))
}
}
@(test)
internal_div_rem_u32 :: proc(t: ^testing.T) {
for v in i31_div_rem_test_vectors {
den := u64(v.hi) << 32 + u64(v.lo)
res := u64(v.quo) * u64(v.div) + u64(v.rem)
assert(den == res)
quo, rem := _bigint.div_rem_u32(v.hi, v.lo, v.div)
testing.expect_value(t, quo, v.quo)
testing.expect_value(t, rem, v.rem)
}
}
+577
View File
@@ -0,0 +1,577 @@
package test_core_crypto_bigint
// Generated using BearSSL bindings.
I31_Test_Vector_Binary :: struct {
a: []u32,
b: []u32,
res: []u32,
carry: u32,
}
@(rodata)
i31_add_test_vectors := []I31_Test_Vector_Binary {
{a = {127, 0x051558fb, 0x578cb6cf, 0x3d2097aa, 0x7512fbf6}, b = {127, 0x6b2123f1, 0x4ad77a17, 0x705c1500, 0x508d965d}, res = {127, 0x70367cec, 0x226430e6, 0x2d7cacab, 0x45a09254}, carry = 1},
{a = {127, 0x0248fd90, 0x7825baae, 0x6de2183c, 0x4f5d0505}, b = {125, 0x492ba26a, 0x1bc7df54, 0x5114950e, 0x1ae989b2}, res = {127, 0x4b749ffa, 0x13ed9a02, 0x3ef6ad4b, 0x6a468eb8}, carry = 0},
{a = {126, 0x4833d6ef, 0x37a112d4, 0x2f71a920, 0x3093d1c3}, b = {127, 0x20574627, 0x39305532, 0x20b76825, 0x6e2eeea3}, res = {126, 0x688b1d16, 0x70d16806, 0x50291145, 0x1ec2c066}, carry = 1},
{a = {127, 0x55fb7b29, 0x21bef5c8, 0x67fe29e8, 0x798dabac}, b = {127, 0x274221db, 0x562cbe9c, 0x785bf5dc, 0x7058bbfa}, res = {127, 0x7d3d9d04, 0x77ebb464, 0x605a1fc4, 0x69e667a7}, carry = 1},
{a = {127, 0x2ffa533d, 0x59e20331, 0x59323704, 0x42cf6e75}, b = {127, 0x7ac812c0, 0x32ca1723, 0x15abc385, 0x410265b0}, res = {127, 0x2ac265fd, 0x0cac1a55, 0x6eddfa8a, 0x03d1d425}, carry = 1},
{a = {126, 0x789b52f9, 0x6535ff6a, 0x2744cf6e, 0x2e4342a6}, b = {127, 0x32dd2f41, 0x64d80309, 0x1ae80d4b, 0x405d1799}, res = {126, 0x2b78823a, 0x4a0e0274, 0x422cdcba, 0x6ea05a3f}, carry = 0},
{a = {127, 0x62003ba4, 0x2b58e836, 0x4587aced, 0x760bdb36}, b = {126, 0x0201e795, 0x50230e49, 0x30039ab5, 0x36f17032}, res = {127, 0x64022339, 0x7b7bf67f, 0x758b47a2, 0x2cfd4b68}, carry = 1},
{a = {124, 0x4ef8170f, 0x65e714cc, 0x5500f2a4, 0x090fac28}, b = {127, 0x0a03290f, 0x0d3fafb1, 0x74ebb1b5, 0x7a30e9c6}, res = {124, 0x58fb401e, 0x7326c47d, 0x49eca459, 0x034095ef}, carry = 1},
{a = {127, 0x34995a2f, 0x58f39080, 0x376ab5ef, 0x555b4fdf}, b = {122, 0x1c0928d9, 0x037fc1ea, 0x35c6d9b0, 0x0206ff68}, res = {127, 0x50a28308, 0x5c73526a, 0x6d318f9f, 0x57624f47}, carry = 0},
{a = {127, 0x7029eb02, 0x465899f5, 0x71ab22fc, 0x604f1ccf}, b = {124, 0x703e163c, 0x50f72eb1, 0x0a0f0bae, 0x0a2028fe}, res = {127, 0x6068013e, 0x174fc8a7, 0x7bba2eab, 0x6a6f45cd}, carry = 0},
{a = {125, 0x70cc03f6, 0x4d984b06, 0x4b990b9d, 0x16dd5097}, b = {125, 0x6eed2b72, 0x6001ddd2, 0x12c50807, 0x14f176cb}, res = {125, 0x5fb92f68, 0x2d9a28d9, 0x5e5e13a5, 0x2bcec762}, carry = 0},
{a = {127, 0x5e6a99af, 0x3ffc66eb, 0x31e47de6, 0x5bcd2f4b}, b = {124, 0x0f6f4335, 0x07d4bf78, 0x61e99233, 0x0fe21745}, res = {127, 0x6dd9dce4, 0x47d12663, 0x13ce1019, 0x6baf4691}, carry = 0},
{a = {127, 0x45de8b13, 0x010684b5, 0x01c77d90, 0x7464b4c8}, b = {127, 0x672a5a37, 0x33cb5670, 0x4ccc9e85, 0x7c0b88e1}, res = {127, 0x2d08e54a, 0x34d1db26, 0x4e941c15, 0x70703da9}, carry = 1},
{a = {127, 0x012fe1a4, 0x741e0733, 0x44d3fb4a, 0x79d78453}, b = {126, 0x66d849d4, 0x427b3ff0, 0x5b2b72e8, 0x3c88987d}, res = {127, 0x68082b78, 0x36994723, 0x1fff6e33, 0x36601cd1}, carry = 1},
{a = {125, 0x3ee54e09, 0x6bc71a68, 0x53cb44c8, 0x1f803699}, b = {124, 0x506ff07c, 0x63b155a0, 0x2c87590f, 0x0c7347e4}, res = {125, 0x0f553e85, 0x4f787009, 0x00529dd8, 0x2bf37e7e}, carry = 0},
{a = {125, 0x4847d126, 0x4c6af2f2, 0x663970b3, 0x11d97a24}, b = {126, 0x67c8e79a, 0x313d0fc2, 0x786fdac1, 0x23c16f5c}, res = {125, 0x3010b8c0, 0x7da802b5, 0x5ea94b74, 0x359ae981}, carry = 0},
{a = {127, 0x035a5f5c, 0x5fad2c0b, 0x6df6c2f3, 0x6cf28780}, b = {125, 0x09719328, 0x6adec2fa, 0x07d51f17, 0x1467d4f6}, res = {127, 0x0ccbf284, 0x4a8bef05, 0x75cbe20b, 0x015a5c76}, carry = 1},
{a = {127, 0x48a484fe, 0x4a2eb1e7, 0x04a04363, 0x52dd3789}, b = {126, 0x136b08b1, 0x4d63762d, 0x52072be9, 0x2f4ef42b}, res = {127, 0x5c0f8daf, 0x17922814, 0x56a76f4d, 0x022c2bb4}, carry = 1},
{a = {123, 0x119e8d05, 0x36510131, 0x38c3b084, 0x07bfe00d}, b = {127, 0x2ecdd4e9, 0x0afaddad, 0x77058092, 0x6b9f72dd}, res = {123, 0x406c61ee, 0x414bdede, 0x2fc93116, 0x735f52eb}, carry = 0},
{a = {126, 0x459b051a, 0x6cfaeee6, 0x461ccc17, 0x36a7eb37}, b = {126, 0x53726a35, 0x34443d10, 0x2d29d052, 0x27d37b9d}, res = {126, 0x190d6f4f, 0x213f2bf7, 0x73469c6a, 0x5e7b66d4}, carry = 0},
{a = {126, 0x73ccf104, 0x352c20d6, 0x580eeeca, 0x28ffb3bc}, b = {126, 0x1aba27fc, 0x31a85479, 0x05e9d689, 0x338a99a8}, res = {126, 0x0e871900, 0x66d47550, 0x5df8c553, 0x5c8a4d64}, carry = 0},
{a = {127, 0x0220ed88, 0x4ccecbb5, 0x31016917, 0x7bc75c14}, b = {127, 0x19d9b7b6, 0x763136a7, 0x783931cf, 0x58fec945}, res = {127, 0x1bfaa53e, 0x4300025c, 0x293a9ae7, 0x54c6255a}, carry = 1},
{a = {127, 0x14696b6a, 0x63348532, 0x71d09a50, 0x77ec660c}, b = {126, 0x67251ffd, 0x1f9606e1, 0x1205eb43, 0x32c3b9f7}, res = {127, 0x7b8e8b67, 0x02ca8c13, 0x03d68594, 0x2ab02004}, carry = 1},
{a = {127, 0x570637d9, 0x22723669, 0x77a7b284, 0x4e1e69ac}, b = {127, 0x32c70945, 0x072c2f80, 0x7ef86c2f, 0x595e375a}, res = {127, 0x09cd411e, 0x299e65ea, 0x76a01eb3, 0x277ca107}, carry = 1},
{a = {126, 0x47ab18d6, 0x4918a98b, 0x51c48283, 0x39a6cb59}, b = {127, 0x2430ac5b, 0x5328674a, 0x1fdffc7d, 0x69dced9b}, res = {126, 0x6bdbc531, 0x1c4110d5, 0x71a47f01, 0x2383b8f4}, carry = 1},
{a = {127, 0x30e85990, 0x65afbf0b, 0x3a20ce3d, 0x5a014c71}, b = {127, 0x27d0d3a9, 0x2483d56f, 0x7e96f8e9, 0x54c679f8}, res = {127, 0x58b92d39, 0x0a33947a, 0x38b7c727, 0x2ec7c66a}, carry = 1},
{a = {127, 0x6ec2ebc5, 0x2fe87e6e, 0x5c214f86, 0x46ce9354}, b = {127, 0x7b6b78e3, 0x0de8a5eb, 0x1cf27da3, 0x43d7077f}, res = {127, 0x6a2e64a8, 0x3dd1245a, 0x7913cd29, 0x0aa59ad3}, carry = 1},
{a = {127, 0x532479dc, 0x38cb5eb9, 0x6b8afdb8, 0x624a0f13}, b = {127, 0x74ab8b56, 0x27e5b9da, 0x62e7b771, 0x50f115a6}, res = {127, 0x47d00532, 0x60b11894, 0x4e72b529, 0x333b24ba}, carry = 1},
{a = {126, 0x7fe1b477, 0x4477a7c5, 0x04b0164d, 0x31d00838}, b = {125, 0x321ef997, 0x579ab7cf, 0x54fb3455, 0x133928f5}, res = {126, 0x3200ae0e, 0x1c125f95, 0x59ab4aa3, 0x4509312d}, carry = 0},
{a = {127, 0x0dd79881, 0x120bcfae, 0x73cc2c5d, 0x43b522e7}, b = {126, 0x0b81b7af, 0x3c00816c, 0x5658e7d3, 0x30483552}, res = {127, 0x19595030, 0x4e0c511a, 0x4a251430, 0x73fd583a}, carry = 0},
{a = {126, 0x0c52d45e, 0x698fd6df, 0x29a362fc, 0x3b6b5882}, b = {127, 0x5e851c19, 0x72428920, 0x417745c0, 0x437e8c53}, res = {126, 0x6ad7f077, 0x5bd25fff, 0x6b1aa8bd, 0x7ee9e4d5}, carry = 0},
{a = {126, 0x5d82d3a5, 0x328636c2, 0x175e0f86, 0x270f7afa}, b = {126, 0x7c06e395, 0x21b77837, 0x5fb98ab1, 0x225d6181}, res = {126, 0x5989b73a, 0x543daefa, 0x77179a37, 0x496cdc7b}, carry = 0},
{a = {127, 0x16be1fea, 0x48971493, 0x4a8f5617, 0x6b2a9968}, b = {127, 0x47387295, 0x2cecd451, 0x59abfdc3, 0x40028930}, res = {127, 0x5df6927f, 0x7583e8e4, 0x243b53da, 0x2b2d2299}, carry = 1},
{a = {127, 0x3cea792f, 0x62a42211, 0x18aa6284, 0x4ca7e07f}, b = {127, 0x5ea60937, 0x5e7c6b69, 0x5bd789bd, 0x774ddc8e}, res = {127, 0x1b908266, 0x41208d7b, 0x7481ec42, 0x43f5bd0d}, carry = 1},
{a = {127, 0x0e21cb57, 0x34087c35, 0x7bcad13b, 0x59c8dc35}, b = {125, 0x08a0ae9f, 0x145d8ebb, 0x1bcb683a, 0x1caefdbd}, res = {127, 0x16c279f6, 0x48660af0, 0x17963975, 0x7677d9f3}, carry = 0},
{a = {127, 0x0b0dd998, 0x1210d9cc, 0x01074e0a, 0x762996da}, b = {126, 0x3c80570a, 0x00fbed3c, 0x77bb5d04, 0x258d2213}, res = {127, 0x478e30a2, 0x130cc708, 0x78c2ab0e, 0x1bb6b8ed}, carry = 1},
{a = {127, 0x4e88ee76, 0x335e5d85, 0x634e3d26, 0x56cff5de}, b = {126, 0x15f3b966, 0x1c0a3f86, 0x1da6a59b, 0x35edda6f}, res = {127, 0x647ca7dc, 0x4f689d0b, 0x00f4e2c1, 0x0cbdd04e}, carry = 1},
{a = {127, 0x374f8369, 0x00230d40, 0x76f627a4, 0x45cb7dff}, b = {127, 0x0e437bcd, 0x5524b249, 0x6aa80d40, 0x578666e8}, res = {127, 0x4592ff36, 0x5547bf89, 0x619e34e4, 0x1d51e4e8}, carry = 1},
{a = {126, 0x7804142f, 0x66b7d326, 0x2f6fbe1b, 0x340e91e8}, b = {127, 0x5650e768, 0x22201fc5, 0x1a849718, 0x608b4950}, res = {126, 0x4e54fb97, 0x08d7f2ec, 0x49f45534, 0x1499db38}, carry = 1},
{a = {127, 0x409a9e5d, 0x6b53d3a4, 0x284aa18f, 0x60899852}, b = {126, 0x340a5074, 0x05dc9720, 0x6c400c75, 0x2c0a2e6d}, res = {127, 0x74a4eed1, 0x71306ac4, 0x148aae04, 0x0c93c6c0}, carry = 1},
{a = {127, 0x484f4d07, 0x45bf1a9a, 0x7679ef61, 0x58da55e9}, b = {125, 0x1672599a, 0x25b9fde9, 0x1dd52ce2, 0x1b792755}, res = {127, 0x5ec1a6a1, 0x6b791883, 0x144f1c43, 0x74537d3f}, carry = 0},
{a = {127, 0x46f0b919, 0x258efe9f, 0x25ac2909, 0x7ee35251}, b = {126, 0x26292ec9, 0x052799f6, 0x26e8cd9f, 0x2a91ea77}, res = {127, 0x6d19e7e2, 0x2ab69895, 0x4c94f6a8, 0x29753cc8}, carry = 1},
{a = {127, 0x662c1549, 0x7d1d8413, 0x2820005b, 0x640fa366}, b = {127, 0x1a1c703a, 0x7c960a8a, 0x0468ab46, 0x76636006}, res = {127, 0x00488583, 0x79b38e9e, 0x2c88aba2, 0x5a73036c}, carry = 1},
{a = {127, 0x32c078a6, 0x09144c2e, 0x7216eb1e, 0x575eb4a5}, b = {127, 0x014b7cd8, 0x7374bc3f, 0x79bad310, 0x41c309b3}, res = {127, 0x340bf57e, 0x7c89086d, 0x6bd1be2e, 0x1921be59}, carry = 1},
{a = {127, 0x5d152378, 0x0db3b779, 0x27cbd808, 0x729e2498}, b = {127, 0x639a2cac, 0x4e2dc8f3, 0x22846201, 0x538c5c6c}, res = {127, 0x40af5024, 0x5be1806d, 0x4a503a09, 0x462a8104}, carry = 1},
{a = {126, 0x4f52ebe5, 0x2e2ce1fe, 0x740060a5, 0x2b602c28}, b = {127, 0x23613e9d, 0x5236ec37, 0x744c1397, 0x6a0e964f}, res = {126, 0x72b42a82, 0x0063ce35, 0x684c743d, 0x156ec278}, carry = 1},
{a = {126, 0x7cab43a4, 0x7b2c3d9d, 0x44c551d5, 0x3e795426}, b = {127, 0x25f21ad6, 0x50b3905d, 0x4477f701, 0x64063b12}, res = {126, 0x229d5e7a, 0x4bdfcdfb, 0x093d48d7, 0x227f8f39}, carry = 1},
{a = {126, 0x54d5908a, 0x4db32799, 0x4e170b52, 0x32d0557d}, b = {126, 0x24f21022, 0x0e431da5, 0x52beb40c, 0x2e5eada4}, res = {126, 0x79c7a0ac, 0x5bf6453e, 0x20d5bf5e, 0x612f0322}, carry = 0},
{a = {127, 0x2fd0c679, 0x429d2164, 0x3b7d1340, 0x478c197e}, b = {126, 0x5b9f625b, 0x15cfdd9b, 0x0eb39653, 0x3f2a9e1a}, res = {127, 0x0b7028d4, 0x586cff00, 0x4a30a993, 0x06b6b798}, carry = 1},
{a = {127, 0x47e483cd, 0x635dad05, 0x334178a6, 0x5be2703c}, b = {127, 0x12487678, 0x0b47445a, 0x2b468da6, 0x5cf39115}, res = {127, 0x5a2cfa45, 0x6ea4f15f, 0x5e88064c, 0x38d60151}, carry = 1},
{a = {127, 0x16bd1190, 0x6f358b1b, 0x43fef060, 0x57246c18}, b = {125, 0x52f9efe3, 0x1d8e35d7, 0x45422cbc, 0x1e9a7d06}, res = {127, 0x69b70173, 0x0cc3c0f2, 0x09411d1d, 0x75bee91f}, carry = 0},
{a = {124, 0x300566bd, 0x4043d1da, 0x07083a42, 0x0ac4490a}, b = {126, 0x7bc7aaa4, 0x089b207d, 0x7d73159e, 0x20475102}, res = {124, 0x2bcd1161, 0x48def258, 0x047b4fe0, 0x2b0b9a0d}, carry = 0},
{a = {127, 0x1879a134, 0x2d5dce4b, 0x4fb09e72, 0x6f409165}, b = {127, 0x224335a0, 0x20c2b717, 0x748b2f65, 0x738d9650}, res = {127, 0x3abcd6d4, 0x4e208562, 0x443bcdd7, 0x62ce27b6}, carry = 1},
{a = {127, 0x49d61045, 0x5b207515, 0x2e191bd8, 0x64903107}, b = {127, 0x08359044, 0x2f1aff57, 0x730d9fb4, 0x7f03816e}, res = {127, 0x520ba089, 0x0a3b746c, 0x2126bb8d, 0x6393b276}, carry = 1},
{a = {127, 0x623eb7a3, 0x4d52e07d, 0x4a90b87c, 0x4641403a}, b = {125, 0x08f857d7, 0x3761ae9d, 0x46ead119, 0x13a35973}, res = {127, 0x6b370f7a, 0x04b48f1a, 0x117b8996, 0x59e499ae}, carry = 0},
{a = {126, 0x71b46b67, 0x1edc966c, 0x42558b0c, 0x29be2808}, b = {127, 0x666102d3, 0x573e7bfe, 0x65d18c72, 0x67b4562e}, res = {126, 0x58156e3a, 0x761b126b, 0x2827177e, 0x11727e37}, carry = 1},
{a = {127, 0x50500966, 0x0c5234da, 0x3e8cf39e, 0x62fbe6ce}, b = {127, 0x6c96a29c, 0x0ffb4fc4, 0x5f3a488f, 0x60d3a913}, res = {127, 0x3ce6ac02, 0x1c4d849f, 0x1dc73c2d, 0x43cf8fe2}, carry = 1},
{a = {125, 0x508f7e5a, 0x2a6c24f8, 0x7cb4ae59, 0x1816c2cd}, b = {126, 0x1e8cc9df, 0x16ed73e1, 0x5910fa15, 0x3a24b307}, res = {125, 0x6f1c4839, 0x415998d9, 0x55c5a86e, 0x523b75d5}, carry = 0},
{a = {127, 0x510325a0, 0x1ded9a89, 0x51e31db4, 0x6ddc474b}, b = {127, 0x22f4ab1e, 0x6fb69633, 0x51a5308e, 0x6eb15ef6}, res = {127, 0x73f7d0be, 0x0da430bc, 0x23884e43, 0x5c8da642}, carry = 1},
{a = {127, 0x12108d0c, 0x386688ec, 0x69d02125, 0x628e00f3}, b = {124, 0x11f211b8, 0x2a6ff08f, 0x33daa305, 0x0c610086}, res = {127, 0x24029ec4, 0x62d6797b, 0x1daac42a, 0x6eef017a}, carry = 0},
{a = {126, 0x0c264fba, 0x0884a2d3, 0x20569035, 0x304b267d}, b = {126, 0x6ff360e6, 0x12c6ad2b, 0x33f44806, 0x21d94783}, res = {126, 0x7c19b0a0, 0x1b4b4ffe, 0x544ad83b, 0x52246e00}, carry = 0},
{a = {127, 0x43ab14a8, 0x7c15842c, 0x636094f1, 0x53d57eea}, b = {126, 0x15682230, 0x35fc075d, 0x59910014, 0x23e605cb}, res = {127, 0x591336d8, 0x32118b89, 0x3cf19506, 0x77bb84b6}, carry = 0},
{a = {126, 0x5a7805ff, 0x32dd0387, 0x75f7a085, 0x3ea23d06}, b = {127, 0x4c55c285, 0x26e33530, 0x03d565c7, 0x724d2fcb}, res = {126, 0x26cdc884, 0x59c038b8, 0x79cd064c, 0x30ef6cd1}, carry = 1},
{a = {125, 0x1a9e1657, 0x1af68cda, 0x4602b940, 0x17acd817}, b = {127, 0x539ac7fe, 0x54adc300, 0x1fe07458, 0x403f5f12}, res = {125, 0x6e38de55, 0x6fa44fda, 0x65e32d98, 0x57ec3729}, carry = 0},
{a = {127, 0x62b5821b, 0x2c747aa2, 0x49572e10, 0x691ea596}, b = {127, 0x2e8da32f, 0x1325dd52, 0x08bf290a, 0x7b103bf2}, res = {127, 0x1143254a, 0x3f9a57f5, 0x5216571a, 0x642ee188}, carry = 1},
{a = {127, 0x215fb007, 0x0dfc51a6, 0x7014c1ee, 0x5e30b9d9}, b = {125, 0x52f164a9, 0x300a98d7, 0x2cab73e3, 0x132af275}, res = {127, 0x745114b0, 0x3e06ea7d, 0x1cc035d1, 0x715bac4f}, carry = 0},
{a = {127, 0x4dfcc7e5, 0x10d63d11, 0x443f7365, 0x7eb2535a}, b = {127, 0x543fd063, 0x041977bb, 0x294f5562, 0x7fabe576}, res = {127, 0x223c9848, 0x14efb4cd, 0x6d8ec8c7, 0x7e5e38d0}, carry = 1},
{a = {127, 0x1a8c810b, 0x49c32b54, 0x5dfbd7fe, 0x49ce1a98}, b = {127, 0x5166f071, 0x0bfea258, 0x3325555d, 0x7ebb7c16}, res = {127, 0x6bf3717c, 0x55c1cdac, 0x11212d5b, 0x488996af}, carry = 1},
{a = {124, 0x4b8a34ea, 0x5b0935d4, 0x3568d516, 0x0d16387a}, b = {126, 0x620a0226, 0x65058ce5, 0x6dd1444f, 0x2f49abb0}, res = {124, 0x2d943710, 0x400ec2ba, 0x233a1966, 0x3c5fe42b}, carry = 0},
{a = {126, 0x5e834973, 0x4242f578, 0x03a1e339, 0x2c61fc03}, b = {127, 0x04a255ce, 0x01ec5f4b, 0x4e56d825, 0x5d998057}, res = {126, 0x63259f41, 0x442f54c3, 0x51f8bb5e, 0x09fb7c5a}, carry = 1},
{a = {127, 0x6cb09c4b, 0x293baae1, 0x7e2830b9, 0x4edf0b6b}, b = {127, 0x4a59137b, 0x4b516bec, 0x0e882e9a, 0x67fd68cb}, res = {127, 0x3709afc6, 0x748d16ce, 0x0cb05f53, 0x36dc7437}, carry = 1},
{a = {126, 0x49d63bd1, 0x20f7d1ab, 0x78eec602, 0x21eea324}, b = {127, 0x4bf2e9c3, 0x55004234, 0x012195b5, 0x725e1f50}, res = {126, 0x15c92594, 0x75f813e0, 0x7a105bb7, 0x144cc274}, carry = 1},
{a = {118, 0x009708e5, 0x0f6eae6a, 0x22717ea3, 0x0027f75e}, b = {125, 0x73b69ce9, 0x05c9d5e7, 0x079ce44c, 0x1e0e4b57}, res = {118, 0x744da5ce, 0x15388451, 0x2a0e62ef, 0x1e3642b5}, carry = 0},
{a = {127, 0x7d5fcca1, 0x703581e8, 0x5ebea294, 0x6d59d7b6}, b = {127, 0x072dc186, 0x2556d525, 0x6550936e, 0x483050d7}, res = {127, 0x048d8e27, 0x158c570e, 0x440f3603, 0x358a288e}, carry = 1},
{a = {125, 0x1ee5e478, 0x6cbc0fd8, 0x5a08ca0d, 0x160799a6}, b = {124, 0x20c5baa0, 0x6893f6b8, 0x2e615746, 0x08b55361}, res = {125, 0x3fab9f18, 0x55500690, 0x086a2154, 0x1ebced08}, carry = 0},
{a = {126, 0x5ba7f924, 0x69e6da01, 0x7012307c, 0x257f31e9}, b = {124, 0x4f354878, 0x5c6c44df, 0x580de770, 0x088dc09c}, res = {126, 0x2add419c, 0x46531ee1, 0x482017ed, 0x2e0cf286}, carry = 0},
{a = {126, 0x53c283ee, 0x19ddd382, 0x401fa681, 0x39b9198a}, b = {126, 0x3e9c90df, 0x3091f3aa, 0x1ec070d3, 0x2f2473fa}, res = {126, 0x125f14cd, 0x4a6fc72d, 0x5ee01754, 0x68dd8d84}, carry = 0},
{a = {125, 0x2aab90f9, 0x0ee5a976, 0x4b5b0203, 0x1672068b}, b = {125, 0x53c43b94, 0x020d4782, 0x260b9c3a, 0x1faca98d}, res = {125, 0x7e6fcc8d, 0x10f2f0f8, 0x71669e3d, 0x361eb018}, carry = 0},
{a = {127, 0x1741cb5d, 0x42adb37f, 0x16f4e290, 0x7c26149a}, b = {127, 0x5c12de2e, 0x77a67be5, 0x2ef8e62e, 0x4a9fdb63}, res = {127, 0x7354a98b, 0x3a542f64, 0x45edc8bf, 0x46c5effd}, carry = 1},
{a = {125, 0x21437747, 0x41ce24dd, 0x70efb0ac, 0x1d4aac67}, b = {126, 0x1235886f, 0x7bc15966, 0x1097debf, 0x29249284}, res = {125, 0x3378ffb6, 0x3d8f7e43, 0x01878f6c, 0x466f3eec}, carry = 0},
{a = {126, 0x0e70b5d5, 0x43bd3fd6, 0x1bdbe98b, 0x2033fc0b}, b = {127, 0x7ce4daf7, 0x16ace9f7, 0x073703bc, 0x5704964f}, res = {126, 0x0b5590cc, 0x5a6a29ce, 0x2312ed47, 0x7738925a}, carry = 0},
{a = {120, 0x62a94a7e, 0x604154fa, 0x3deeb80c, 0x00df6634}, b = {125, 0x6fd3f3b5, 0x3cfc6cef, 0x63fd704a, 0x1678e57f}, res = {120, 0x527d3e33, 0x1d3dc1ea, 0x21ec2857, 0x17584bb4}, carry = 0},
{a = {123, 0x7978a2ad, 0x5d491b8a, 0x71574e8a, 0x0663444b}, b = {126, 0x0bc05c34, 0x7c0f280f, 0x015a49fc, 0x38502a7f}, res = {123, 0x0538fee1, 0x5958439a, 0x72b19887, 0x3eb36eca}, carry = 0},
{a = {125, 0x51d6b6f2, 0x079bd814, 0x6ce81e2d, 0x18c4c981}, b = {127, 0x54f420d0, 0x2dc663dc, 0x13bc81b9, 0x4060575c}, res = {125, 0x26cad7c2, 0x35623bf1, 0x00a49fe6, 0x592520de}, carry = 0},
{a = {127, 0x36485bbe, 0x09796514, 0x63ee7f58, 0x5625e4e2}, b = {127, 0x21458fe6, 0x7c9fa294, 0x54b76a7e, 0x6d695c7f}, res = {127, 0x578deba4, 0x061907a8, 0x38a5e9d7, 0x438f4162}, carry = 1},
{a = {127, 0x33d70ec2, 0x1f127a0b, 0x2717bb82, 0x5fd43cd2}, b = {125, 0x3f0797eb, 0x0d5c370b, 0x3d594b1d, 0x1752300b}, res = {127, 0x72dea6ad, 0x2c6eb116, 0x6471069f, 0x77266cdd}, carry = 0},
{a = {127, 0x37eae763, 0x19004adb, 0x38b7eff2, 0x688fe1c5}, b = {127, 0x2cac471c, 0x37811110, 0x1f3987d5, 0x7fe15712}, res = {127, 0x64972e7f, 0x50815beb, 0x57f177c7, 0x687138d7}, carry = 1},
{a = {126, 0x6c225971, 0x0437a4c8, 0x1bcb8b3e, 0x28ceb662}, b = {127, 0x48cd5436, 0x7e421cca, 0x29d44db3, 0x55711f42}, res = {126, 0x34efada7, 0x0279c193, 0x459fd8f2, 0x7e3fd5a4}, carry = 0},
{a = {126, 0x2574a51d, 0x5de32cbd, 0x6c67c9cc, 0x376694a2}, b = {126, 0x138f84b7, 0x46f2b3f1, 0x47583471, 0x3b15472c}, res = {126, 0x390429d4, 0x24d5e0ae, 0x33bffe3e, 0x727bdbcf}, carry = 0},
{a = {127, 0x588e6a4d, 0x03204eb0, 0x400a06ba, 0x469c62fd}, b = {127, 0x33609fc4, 0x65a624e4, 0x6111951d, 0x7648f1ae}, res = {127, 0x0bef0a11, 0x68c67395, 0x211b9bd7, 0x3ce554ac}, carry = 1},
{a = {127, 0x03178062, 0x2c0793b9, 0x6b91ed2a, 0x436a75d5}, b = {127, 0x364e9acd, 0x20881e30, 0x609f019a, 0x5d437f92}, res = {127, 0x39661b2f, 0x4c8fb1e9, 0x4c30eec4, 0x20adf568}, carry = 1},
{a = {126, 0x582502a7, 0x76d9bbd7, 0x6ec18736, 0x3d15fed5}, b = {127, 0x45e8a9e7, 0x0cfae275, 0x4c164029, 0x641d8e58}, res = {126, 0x1e0dac8e, 0x03d49e4d, 0x3ad7c760, 0x21338d2e}, carry = 1},
{a = {127, 0x31a44c73, 0x4c970755, 0x433f481c, 0x63704395}, b = {127, 0x1a3979dc, 0x0e9d5437, 0x592a1251, 0x5cb4e212}, res = {127, 0x4bddc64f, 0x5b345b8c, 0x1c695a6d, 0x402525a8}, carry = 1},
{a = {126, 0x31499a0a, 0x2df3b6ac, 0x4cf3e9b4, 0x3fdaa87e}, b = {126, 0x6749f6f2, 0x28c62023, 0x3e54e11b, 0x30a577bf}, res = {126, 0x189390fc, 0x56b9d6d0, 0x0b48cacf, 0x7080203e}, carry = 0},
{a = {126, 0x2d6e6ed4, 0x3c9f5183, 0x27326fac, 0x35b95ad6}, b = {125, 0x28a46b0d, 0x4143c903, 0x182ab95c, 0x17b09437}, res = {126, 0x5612d9e1, 0x7de31a86, 0x3f5d2908, 0x4d69ef0d}, carry = 0},
{a = {126, 0x26f20408, 0x2af94d52, 0x440bf192, 0x360df26a}, b = {127, 0x7b2198cb, 0x763bc4c5, 0x78ac7d5a, 0x5de9efc9}, res = {126, 0x22139cd3, 0x21351218, 0x3cb86eed, 0x13f7e234}, carry = 1},
{a = {127, 0x47128b80, 0x670a3dbb, 0x63efbba7, 0x74a10d1e}, b = {124, 0x7f481747, 0x59187cd8, 0x51e741bc, 0x0a8ea0cd}, res = {127, 0x465aa2c7, 0x4022ba94, 0x35d6fd64, 0x7f2fadec}, carry = 0},
{a = {127, 0x5a47d043, 0x052b9ec0, 0x401cbe7c, 0x49104294}, b = {127, 0x7427cee8, 0x06334bc8, 0x4528bd14, 0x741e6df6}, res = {127, 0x4e6f9f2b, 0x0b5eea89, 0x05457b90, 0x3d2eb08b}, carry = 1},
{a = {127, 0x6520f6a0, 0x5b37902f, 0x31d3c910, 0x5f9fe4e6}, b = {127, 0x6aa5d0dc, 0x61ad0b30, 0x3b782608, 0x7dd0cd5f}, res = {127, 0x4fc6c77c, 0x3ce49b60, 0x6d4bef19, 0x5d70b245}, carry = 1},
{a = {126, 0x34bdc731, 0x2de09f84, 0x3896e197, 0x3fee6ac9}, b = {127, 0x5b5d8627, 0x3c22bdb9, 0x5e7b4aaf, 0x555e4e2a}, res = {126, 0x101b4d58, 0x6a035d3e, 0x17122c46, 0x154cb8f4}, carry = 1},
}
@(rodata)
i31_sub_test_vectors := []I31_Test_Vector_Binary {
{a = {126, 0x75832201, 0x72b1ddb3, 0x3e8d7744, 0x325d7cb5}, b = {127, 0x52a23cfc, 0x51a47476, 0x19fd5fa3, 0x5a5e5d48}, res = {126, 0x22e0e505, 0x210d693d, 0x249017a1, 0x57ff1f6d}, carry = 1},
{a = {127, 0x02585014, 0x15b77a75, 0x66fa4e0f, 0x5368a27c}, b = {126, 0x1aaa3973, 0x288030e7, 0x51ede1ff, 0x37bbad71}, res = {127, 0x67ae16a1, 0x6d37498d, 0x150c6c0f, 0x1bacf50b}, carry = 0},
{a = {125, 0x3b43d5aa, 0x3de09ea7, 0x18004f89, 0x113b0b5e}, b = {127, 0x3a318699, 0x10ee38f1, 0x6d8c06d8, 0x7ac6ef8f}, res = {125, 0x01124f11, 0x2cf265b6, 0x2a7448b1, 0x16741bce}, carry = 1},
{a = {127, 0x047e69b6, 0x5455ee19, 0x012e5e13, 0x73a56c35}, b = {127, 0x10606c28, 0x33f1e053, 0x18f48327, 0x69bc2be6}, res = {127, 0x741dfd8e, 0x20640dc5, 0x6839daec, 0x09e9404e}, carry = 0},
{a = {127, 0x02c391a7, 0x07feb24a, 0x3a32b686, 0x5ee14ddd}, b = {126, 0x3f3b74ff, 0x2c1596d7, 0x677a7df4, 0x2ce6395d}, res = {127, 0x43881ca8, 0x5be91b72, 0x52b83891, 0x31fb147f}, carry = 0},
{a = {125, 0x4b26152d, 0x24f5c3a3, 0x6f1d3b6f, 0x1586f40a}, b = {125, 0x7380829e, 0x1bd90254, 0x382393b6, 0x1b85a0c5}, res = {125, 0x57a5928f, 0x091cc14e, 0x36f9a7b9, 0x7a015345}, carry = 1},
{a = {126, 0x4fa59569, 0x643c2005, 0x72e9a332, 0x2e315696}, b = {121, 0x6de61d40, 0x6b932a39, 0x52568d6d, 0x01e8488c}, res = {126, 0x61bf7829, 0x78a8f5cb, 0x209315c4, 0x2c490e0a}, carry = 0},
{a = {127, 0x5379b430, 0x467ac863, 0x297696ab, 0x70622b6f}, b = {127, 0x37aac8dc, 0x1adda229, 0x58df1d01, 0x505ee996}, res = {127, 0x1bceeb54, 0x2b9d263a, 0x509779aa, 0x200341d8}, carry = 0},
{a = {127, 0x1ccf6e2c, 0x11dc002c, 0x561b6361, 0x46990123}, b = {127, 0x3e9f8554, 0x42397834, 0x4fdd5416, 0x7e39420f}, res = {127, 0x5e2fe8d8, 0x4fa287f7, 0x063e0f4a, 0x485fbf14}, carry = 1},
{a = {126, 0x2150add9, 0x26e965d1, 0x5e4ac34d, 0x2a55c9de}, b = {127, 0x6669738d, 0x3d3ff599, 0x582b736f, 0x4dd82557}, res = {126, 0x3ae73a4c, 0x69a97037, 0x061f4fdd, 0x5c7da487}, carry = 1},
{a = {127, 0x61a3eb42, 0x2796bb5e, 0x5b90dcd6, 0x78085440}, b = {127, 0x51c6a80f, 0x0a65c487, 0x6179dc58, 0x562966ef}, res = {127, 0x0fdd4333, 0x1d30f6d7, 0x7a17007e, 0x21deed50}, carry = 0},
{a = {127, 0x51d4085f, 0x72b1daa2, 0x7560d03e, 0x47df6842}, b = {125, 0x27993d6b, 0x1b36ba5a, 0x78a56068, 0x1811037d}, res = {127, 0x2a3acaf4, 0x577b2048, 0x7cbb6fd6, 0x2fce64c4}, carry = 0},
{a = {125, 0x2cb4937a, 0x48c3af52, 0x7c4e70a7, 0x1578b00c}, b = {127, 0x20263089, 0x3feb52a0, 0x1b877614, 0x4f5677bc}, res = {125, 0x0c8e62f1, 0x08d85cb2, 0x60c6fa93, 0x46223850}, carry = 1},
{a = {126, 0x338493e9, 0x4ff90c99, 0x5dc3827f, 0x2a9898da}, b = {122, 0x588232b4, 0x1c53bb05, 0x17ef86ab, 0x03bce2b4}, res = {126, 0x5b026135, 0x33a55193, 0x45d3fbd4, 0x26dbb626}, carry = 0},
{a = {124, 0x40625bc9, 0x204b059d, 0x48deea64, 0x0a019e61}, b = {127, 0x661bd15a, 0x48ead2c5, 0x2a0e651f, 0x7999457b}, res = {124, 0x5a468a6f, 0x576032d7, 0x1ed08544, 0x106858e6}, carry = 1},
{a = {127, 0x5874a542, 0x71947250, 0x054ff642, 0x7d66c0f6}, b = {125, 0x780fcb4e, 0x290dc73d, 0x2687ab55, 0x107555db}, res = {127, 0x6064d9f4, 0x4886ab12, 0x5ec84aed, 0x6cf16b1a}, carry = 0},
{a = {126, 0x6fd19206, 0x50f14417, 0x7f82f1ed, 0x2bbb502b}, b = {127, 0x0468cf8a, 0x67942b1c, 0x7c399be0, 0x6306501c}, res = {126, 0x6b68c27c, 0x695d18fb, 0x0349560c, 0x48b5000f}, carry = 1},
{a = {124, 0x51a0623b, 0x7b984ebd, 0x0fb05efe, 0x0fd13b63}, b = {127, 0x2a6b91ec, 0x68eaf51b, 0x3265f0fd, 0x4a4f4fb3}, res = {124, 0x2734d04f, 0x12ad59a2, 0x5d4a6e01, 0x4581ebaf}, carry = 1},
{a = {127, 0x0f1be1af, 0x76ef0418, 0x489dab08, 0x4998335f}, b = {127, 0x24c297f4, 0x6fabbbe8, 0x01529fff, 0x6c5b3c15}, res = {127, 0x6a5949bb, 0x0743482f, 0x474b0b09, 0x5d3cf74a}, carry = 1},
{a = {125, 0x53a96664, 0x6e05147a, 0x1017a26d, 0x19d02282}, b = {127, 0x46f7e29b, 0x466f513b, 0x1c9bb1fc, 0x56817f94}, res = {125, 0x0cb183c9, 0x2795c33f, 0x737bf071, 0x434ea2ed}, carry = 1},
{a = {127, 0x2fcad8be, 0x60aea5d7, 0x63f1ddde, 0x5e392547}, b = {127, 0x3006e08d, 0x560753cd, 0x7e2304f9, 0x48c18d08}, res = {127, 0x7fc3f831, 0x0aa75209, 0x65ced8e5, 0x1577983e}, carry = 0},
{a = {125, 0x140a0bd3, 0x0af4e07b, 0x1d4d97a3, 0x12530f8a}, b = {124, 0x66378508, 0x26164d79, 0x0b3ff9ba, 0x0b94a13a}, res = {125, 0x2dd286cb, 0x64de9301, 0x120d9de8, 0x06be6e50}, carry = 0},
{a = {127, 0x16d0c935, 0x6e1afbd1, 0x08ef273a, 0x694e551c}, b = {125, 0x0f34a643, 0x167cbfb7, 0x2c1977e7, 0x1bd4f3cc}, res = {127, 0x079c22f2, 0x579e3c1a, 0x5cd5af53, 0x4d79614f}, carry = 0},
{a = {127, 0x3d62a534, 0x3c284b2d, 0x53524377, 0x6016b0bd}, b = {125, 0x7cf4d88b, 0x68fb44c3, 0x61c8f0ad, 0x10e57b36}, res = {127, 0x406dcca9, 0x532d0669, 0x718952c9, 0x4f313586}, carry = 0},
{a = {127, 0x6f015d33, 0x536fbab1, 0x26915f4b, 0x562f74ee}, b = {122, 0x63e75212, 0x68694786, 0x3f82de9a, 0x02f4382a}, res = {127, 0x0b1a0b21, 0x6b06732b, 0x670e80b0, 0x533b3cc3}, carry = 0},
{a = {127, 0x66a82756, 0x4025f122, 0x42ad0938, 0x58fbe956}, b = {123, 0x150d6a84, 0x23439905, 0x7f8687ac, 0x057badf7}, res = {127, 0x519abcd2, 0x1ce2581d, 0x4326818c, 0x53803b5e}, carry = 0},
{a = {127, 0x1b0466bd, 0x69accc1e, 0x55da7183, 0x71f64927}, b = {126, 0x12a653b6, 0x4be3b926, 0x174ecdc5, 0x2e28e368}, res = {127, 0x085e1307, 0x1dc912f8, 0x3e8ba3be, 0x43cd65bf}, carry = 0},
{a = {119, 0x119a6115, 0x130e1918, 0x4e2c62c0, 0x00586779}, b = {127, 0x7042f5f4, 0x6c596133, 0x0b8e23ed, 0x4fded5b4}, res = {119, 0x21576b21, 0x26b4b7e4, 0x429e3ed2, 0x307991c5}, carry = 1},
{a = {127, 0x05b5cfd3, 0x2ae55120, 0x46abe106, 0x70b46cd4}, b = {127, 0x4a8e2790, 0x5557a5e9, 0x3e14dfb6, 0x62ff997b}, res = {127, 0x3b27a843, 0x558dab36, 0x0897014f, 0x0db4d359}, carry = 0},
{a = {126, 0x40c5bd09, 0x1bc7d36e, 0x5787d3c1, 0x2dadafd4}, b = {127, 0x7d36195e, 0x67d20645, 0x384c8cf2, 0x7bc9c106}, res = {126, 0x438fa3ab, 0x33f5cd28, 0x1f3b46ce, 0x31e3eece}, carry = 1},
{a = {121, 0x582c3425, 0x5e591a55, 0x64bc368c, 0x01171496}, b = {127, 0x7ff455d7, 0x1d082b60, 0x69a595c6, 0x7506f22f}, res = {121, 0x5837de4e, 0x4150eef4, 0x7b16a0c6, 0x0c102266}, carry = 1},
{a = {126, 0x76e444c8, 0x5d162950, 0x0b4c9cb8, 0x350172ce}, b = {126, 0x6fae6b50, 0x457e2833, 0x22e790fd, 0x222f32e8}, res = {126, 0x0735d978, 0x1798011d, 0x68650bbb, 0x12d23fe5}, carry = 0},
{a = {126, 0x0a94f6cb, 0x7e659900, 0x21e27381, 0x20898f13}, b = {125, 0x6c0e1d51, 0x26db9379, 0x4aa78374, 0x13e8525f}, res = {126, 0x1e86d97a, 0x578a0586, 0x573af00d, 0x0ca13cb3}, carry = 0},
{a = {126, 0x2df85daa, 0x089e925f, 0x372ad1cd, 0x282da3da}, b = {126, 0x5f6eb57d, 0x666b94e8, 0x72523b11, 0x2e872674}, res = {126, 0x4e89a82d, 0x2232fd76, 0x44d896bb, 0x79a67d65}, carry = 1},
{a = {127, 0x59c5b936, 0x14069767, 0x00e21797, 0x7ab2135d}, b = {127, 0x4a74250d, 0x65c5e230, 0x5562bf62, 0x5c25dec5}, res = {127, 0x0f519429, 0x2e40b537, 0x2b7f5834, 0x1e8c3497}, carry = 0},
{a = {125, 0x74ce4c1e, 0x59cb9a18, 0x5e0560c1, 0x142bdb97}, b = {126, 0x0e08a60d, 0x4c39a45f, 0x0929589f, 0x22096dd5}, res = {125, 0x66c5a611, 0x0d91f5b9, 0x54dc0822, 0x72226dc2}, carry = 1},
{a = {127, 0x65273636, 0x653e4647, 0x2cf1b1f4, 0x74f9fddd}, b = {122, 0x6e960ace, 0x7ea2dfda, 0x35a317bd, 0x0202a322}, res = {127, 0x76912b68, 0x669b666c, 0x774e9a36, 0x72f75aba}, carry = 0},
{a = {126, 0x45582ae7, 0x6996c2bb, 0x09380ea2, 0x30b4897c}, b = {127, 0x246ce79f, 0x758393b5, 0x00926c53, 0x40dd24dc}, res = {126, 0x20eb4348, 0x74132f06, 0x08a5a24e, 0x6fd764a0}, carry = 1},
{a = {127, 0x7face3b5, 0x4e099393, 0x1e3f7fe6, 0x50946a47}, b = {126, 0x50b06b20, 0x36db7586, 0x40ec226f, 0x33e64381}, res = {127, 0x2efc7895, 0x172e1e0d, 0x5d535d77, 0x1cae26c5}, carry = 0},
{a = {124, 0x5bae9309, 0x23a74c8d, 0x4c7f0704, 0x0fd4a29c}, b = {127, 0x07e40cc7, 0x31e632d3, 0x1ae2cbd2, 0x6f8f930a}, res = {124, 0x53ca8642, 0x71c119ba, 0x319c3b31, 0x20450f92}, carry = 1},
{a = {126, 0x69c3634b, 0x0c4ffbaa, 0x390a326e, 0x25e998b2}, b = {123, 0x4d164145, 0x6cf31f2c, 0x60273791, 0x05ea1210}, res = {126, 0x1cad2206, 0x1f5cdc7e, 0x58e2fadc, 0x1fff86a1}, carry = 0},
{a = {124, 0x25a0c384, 0x38438ee6, 0x67b92d68, 0x0b8d4052}, b = {127, 0x65152a5c, 0x6af4d890, 0x78f3790f, 0x516ebf7b}, res = {124, 0x408b9928, 0x4d4eb655, 0x6ec5b458, 0x3a1e80d6}, carry = 1},
{a = {120, 0x27e60366, 0x1158cd09, 0x7f574484, 0x00eb60e2}, b = {126, 0x5ee2ec9b, 0x17d9507a, 0x283fcdf8, 0x3d95a50e}, res = {120, 0x490316cb, 0x797f7c8e, 0x5717768b, 0x4355bbd4}, carry = 1},
{a = {126, 0x7a6cfbf1, 0x3b47fde4, 0x1f96a1b0, 0x3415815d}, b = {127, 0x6db4865c, 0x68d15d67, 0x29492ffd, 0x55b34d59}, res = {126, 0x0cb87595, 0x5276a07d, 0x764d71b2, 0x5e623403}, carry = 1},
{a = {126, 0x78bc926b, 0x06872ef9, 0x77dc59ee, 0x341e1fe5}, b = {127, 0x4aafbaf4, 0x0f29aa6c, 0x6e2dcd74, 0x5ae5ab56}, res = {126, 0x2e0cd777, 0x775d848d, 0x09ae8c79, 0x5938748f}, carry = 1},
{a = {127, 0x5339599f, 0x6d388285, 0x1fb30e96, 0x653d70d1}, b = {127, 0x26e44c38, 0x360bdfc0, 0x317ed433, 0x4e44f71e}, res = {127, 0x2c550d67, 0x372ca2c5, 0x6e343a63, 0x16f879b2}, carry = 0},
{a = {126, 0x5f57436e, 0x5d7b8fc4, 0x3b913a49, 0x289c171d}, b = {127, 0x63106f0e, 0x181ab5a1, 0x1a35ecdc, 0x4f6fd3de}, res = {126, 0x7c46d460, 0x4560da22, 0x215b4d6d, 0x592c433f}, carry = 1},
{a = {125, 0x0a9e7523, 0x4011ed51, 0x573e92c1, 0x1465e918}, b = {125, 0x190d6359, 0x595c3a46, 0x76ee73b0, 0x1163ef5f}, res = {125, 0x719111ca, 0x66b5b30a, 0x60501f10, 0x0301f9b8}, carry = 0},
{a = {126, 0x5b4c4e1c, 0x368e93d7, 0x1d7f383f, 0x3893cc55}, b = {126, 0x15361741, 0x192e05a0, 0x69808f62, 0x3d0b8de0}, res = {126, 0x461636db, 0x1d608e37, 0x33fea8dd, 0x7b883e74}, carry = 1},
{a = {127, 0x711129f3, 0x1221b384, 0x75a6ce65, 0x75cc9dda}, b = {125, 0x05242048, 0x29449c5e, 0x299b4ec4, 0x1179eca2}, res = {127, 0x6bed09ab, 0x68dd1726, 0x4c0b7fa0, 0x6452b138}, carry = 0},
{a = {126, 0x23d8928a, 0x6bc1710b, 0x4f1a2853, 0x3920c780}, b = {127, 0x3ed60b0f, 0x639f16e2, 0x26d5472a, 0x7a6d17fb}, res = {126, 0x6502877b, 0x08225a28, 0x2844e129, 0x3eb3af85}, carry = 1},
{a = {127, 0x0d9cfe5d, 0x619328e7, 0x542c700d, 0x6997d292}, b = {127, 0x1cedd3b7, 0x5edfbdf6, 0x45d76cf4, 0x7357a00a}, res = {127, 0x70af2aa6, 0x02b36af0, 0x0e550319, 0x76403288}, carry = 1},
{a = {127, 0x03e30b55, 0x4013c48f, 0x729f2a80, 0x58b486a3}, b = {126, 0x786328fe, 0x37cc20dc, 0x25a645e1, 0x27cf6abb}, res = {127, 0x0b7fe257, 0x0847a3b2, 0x4cf8e49f, 0x30e51be8}, carry = 0},
{a = {125, 0x7ac1580f, 0x2c93cbec, 0x527026f9, 0x124b767e}, b = {127, 0x68ba663a, 0x4df0c1d0, 0x4a5c9da6, 0x5da272db}, res = {125, 0x1206f1d5, 0x5ea30a1c, 0x08138952, 0x34a903a3}, carry = 1},
{a = {127, 0x56b6b76a, 0x3ece11a5, 0x296c5e68, 0x59ab3003}, b = {127, 0x6e69a755, 0x7099639a, 0x357c87d6, 0x4c9f33fd}, res = {127, 0x684d1015, 0x4e34ae0a, 0x73efd691, 0x0d0bfc05}, carry = 0},
{a = {127, 0x4404fa8a, 0x5866a5c4, 0x0c22add4, 0x5ef97523}, b = {127, 0x1c21bae1, 0x256e56eb, 0x77662e5f, 0x7376f17a}, res = {127, 0x27e33fa9, 0x32f84ed9, 0x14bc7f75, 0x6b8283a8}, carry = 1},
{a = {126, 0x2c0d6109, 0x3509e87e, 0x53519777, 0x3c09bbc0}, b = {127, 0x3a60646e, 0x3f868a8e, 0x461edbdd, 0x777a0c2c}, res = {126, 0x71acfc9b, 0x75835def, 0x0d32bb99, 0x448faf94}, carry = 1},
{a = {126, 0x5f8c1f7a, 0x40041120, 0x463c5e66, 0x35a4cd6e}, b = {127, 0x08cf40d3, 0x2edbcf34, 0x784ab86d, 0x4de2eb71}, res = {126, 0x56bcdea7, 0x112841ec, 0x4df1a5f9, 0x67c1e1fc}, carry = 1},
{a = {127, 0x2266e65d, 0x0553ee53, 0x0c667741, 0x4fd147e2}, b = {127, 0x3fb1e4a2, 0x7cb1c12a, 0x535dbe75, 0x67bfcc87}, res = {127, 0x62b501bb, 0x08a22d28, 0x3908b8cb, 0x68117b5a}, carry = 1},
{a = {127, 0x76bfd2fb, 0x40e3235f, 0x7110f948, 0x55262313}, b = {126, 0x56f35745, 0x288792fc, 0x3759df43, 0x36acfcf9}, res = {127, 0x1fcc7bb6, 0x185b9063, 0x39b71a05, 0x1e79261a}, carry = 0},
{a = {125, 0x79452908, 0x28cb7d35, 0x033fb36e, 0x1e3d1dc2}, b = {126, 0x625452fe, 0x6f0e270e, 0x4a783375, 0x21ed361b}, res = {125, 0x16f0d60a, 0x39bd5627, 0x38c77ff8, 0x7c4fe7a6}, carry = 1},
{a = {126, 0x7372a659, 0x3cd43111, 0x79c40547, 0x2d1afece}, b = {127, 0x7f8726fd, 0x24d0aa04, 0x1f6ecb14, 0x4e053ba9}, res = {126, 0x73eb7f5c, 0x1803870c, 0x5a553a33, 0x5f15c325}, carry = 1},
{a = {126, 0x2f15690a, 0x4de81324, 0x3e9f55fe, 0x373cfdd3}, b = {125, 0x02bb7d7f, 0x3f2d94bb, 0x769d851b, 0x10f5260a}, res = {126, 0x2c59eb8b, 0x0eba7e69, 0x4801d0e3, 0x2647d7c8}, carry = 0},
{a = {125, 0x54d7b5c6, 0x3932181a, 0x53521bf5, 0x1c5cf5ee}, b = {125, 0x03d8109e, 0x4d5cbcea, 0x49faabc0, 0x1d3e3e9a}, res = {125, 0x50ffa528, 0x6bd55b30, 0x09577034, 0x7f1eb754}, carry = 1},
{a = {125, 0x43482a38, 0x1ce74ab0, 0x624445b9, 0x176cc140}, b = {125, 0x202ec3d7, 0x1cdd0bba, 0x4493150c, 0x13241d43}, res = {125, 0x23196661, 0x000a3ef6, 0x1db130ad, 0x0448a3fd}, carry = 0},
{a = {126, 0x63a9b9c1, 0x5557f9c9, 0x4a536191, 0x3c4994c8}, b = {127, 0x5ee2af7f, 0x7dda218e, 0x53e931bb, 0x4270e93f}, res = {126, 0x04c70a42, 0x577dd83b, 0x766a2fd5, 0x79d8ab88}, carry = 1},
{a = {127, 0x4950f74f, 0x6aad0a67, 0x43e36683, 0x76afb633}, b = {127, 0x2213721e, 0x76557c01, 0x299aae91, 0x44c18e55}, res = {127, 0x273d8531, 0x74578e66, 0x1a48b7f1, 0x31ee27de}, carry = 0},
{a = {126, 0x0935ca8f, 0x1200cc80, 0x2f3661fb, 0x23960e06}, b = {126, 0x4624b76e, 0x14091fd6, 0x6286cb7a, 0x3e1aa872}, res = {126, 0x43111321, 0x7df7aca9, 0x4caf9680, 0x657b6593}, carry = 1},
{a = {124, 0x52ac128b, 0x45fa9e02, 0x6427aa53, 0x0feaf8b5}, b = {127, 0x18aa3cc4, 0x19e96b87, 0x6c0e4dcc, 0x45da3929}, res = {124, 0x3a01d5c7, 0x2c11327b, 0x78195c87, 0x4a10bf8b}, carry = 1},
{a = {126, 0x4111b645, 0x1c825213, 0x2a8b75b3, 0x213c3a3e}, b = {126, 0x23f60c22, 0x490b2da9, 0x353a3844, 0x29d5dd47}, res = {126, 0x1d1baa23, 0x5377246a, 0x75513d6e, 0x77665cf6}, carry = 1},
{a = {127, 0x09c853b4, 0x7541862d, 0x36e91f14, 0x6e89e67d}, b = {127, 0x21d3bfe2, 0x61403f04, 0x554b57fc, 0x76d413df}, res = {127, 0x67f493d2, 0x14014728, 0x619dc718, 0x77b5d29d}, carry = 1},
{a = {127, 0x67761ecf, 0x3837a139, 0x335e0875, 0x79923e83}, b = {127, 0x76813044, 0x7c0f5491, 0x59acc858, 0x5a7a5795}, res = {127, 0x70f4ee8b, 0x3c284ca7, 0x59b1401c, 0x1f17e6ed}, carry = 0},
{a = {126, 0x6429a8c8, 0x635056e0, 0x76e76b1d, 0x3837df4c}, b = {127, 0x531005db, 0x115acaa3, 0x69231eee, 0x43d19db2}, res = {126, 0x1119a2ed, 0x51f58c3d, 0x0dc44c2f, 0x7466419a}, carry = 1},
{a = {127, 0x5014a598, 0x068dbb0f, 0x22fdc5bf, 0x6a670715}, b = {126, 0x7c4f94e9, 0x2e78116b, 0x224d3768, 0x2b95833a}, res = {127, 0x53c510af, 0x5815a9a3, 0x00b08e56, 0x3ed183db}, carry = 0},
{a = {126, 0x75b94a38, 0x71d62d52, 0x6c557c79, 0x2d8cfb9f}, b = {127, 0x743c6840, 0x3b04bcff, 0x50744d2c, 0x608611a3}, res = {126, 0x017ce1f8, 0x36d17053, 0x1be12f4d, 0x4d06e9fc}, carry = 1},
{a = {126, 0x04d24ee0, 0x37e1c03c, 0x50704c96, 0x2577ba6b}, b = {127, 0x6dd14735, 0x03c4f523, 0x379aa43a, 0x4fbfbf80}, res = {126, 0x170107ab, 0x341ccb18, 0x18d5a85c, 0x55b7faeb}, carry = 1},
{a = {123, 0x572d0f53, 0x732fb14c, 0x7975e187, 0x05706d99}, b = {127, 0x51a90823, 0x707dd091, 0x2f6c63b4, 0x789adddf}, res = {123, 0x05840730, 0x02b1e0bb, 0x4a097dd3, 0x0cd58fba}, carry = 1},
{a = {126, 0x68306d82, 0x51d883c8, 0x52a45e7f, 0x339e6fc7}, b = {127, 0x57da71c8, 0x72d5dd1c, 0x21a11ff7, 0x5d6ea34b}, res = {126, 0x1055fbba, 0x5f02a6ac, 0x31033e87, 0x562fcc7c}, carry = 1},
{a = {125, 0x47b20e6e, 0x39411f59, 0x0324640b, 0x19996204}, b = {124, 0x4e55588b, 0x64846ee8, 0x57d16f9e, 0x0a3db8bc}, res = {125, 0x795cb5e3, 0x54bcb070, 0x2b52f46c, 0x0f5ba947}, carry = 0},
{a = {127, 0x5a6ab51b, 0x0051b988, 0x1a192e89, 0x42b3d43a}, b = {125, 0x2daa3aa4, 0x063f37e9, 0x7ba7850f, 0x13dcea8c}, res = {127, 0x2cc07a77, 0x7a12819f, 0x1e71a979, 0x2ed6e9ad}, carry = 0},
{a = {125, 0x46813953, 0x6920da78, 0x5b6db2f9, 0x12219a9c}, b = {126, 0x5666e4fb, 0x5458ccf2, 0x097952e7, 0x38f4e37f}, res = {125, 0x701a5458, 0x14c80d85, 0x51f46012, 0x592cb71d}, carry = 1},
{a = {127, 0x47633dea, 0x6405989b, 0x1d019f24, 0x7dab4fa6}, b = {127, 0x686dbe43, 0x5715424a, 0x2027ac93, 0x70c8d29c}, res = {127, 0x5ef57fa7, 0x0cf05650, 0x7cd9f291, 0x0ce27d09}, carry = 0},
{a = {127, 0x22705ca5, 0x2c180e47, 0x393c2083, 0x40d0fea1}, b = {124, 0x37ce1e5a, 0x17b2b88d, 0x49bd2a90, 0x0d5d9087}, res = {127, 0x6aa23e4b, 0x146555b9, 0x6f7ef5f3, 0x33736e19}, carry = 0},
{a = {126, 0x5d61d089, 0x1391e7a4, 0x780aeb70, 0x3eb6e8e2}, b = {127, 0x7a54a013, 0x28f31254, 0x3d63ef25, 0x5d2cf7ff}, res = {126, 0x630d3076, 0x6a9ed54f, 0x3aa6fc4a, 0x6189f0e3}, carry = 1},
{a = {126, 0x505f4672, 0x275eb845, 0x665c7a55, 0x31c26f48}, b = {127, 0x3af113f0, 0x6a0358aa, 0x009cd3d5, 0x551cd2f6}, res = {126, 0x156e3282, 0x3d5b5f9b, 0x65bfa67f, 0x5ca59c52}, carry = 1},
{a = {126, 0x1a313b22, 0x04332012, 0x4cfb19fe, 0x3aa25e6c}, b = {127, 0x0525c494, 0x1a177649, 0x10bd52f2, 0x5d114ab1}, res = {126, 0x150b768e, 0x6a1ba9c9, 0x3c3dc70b, 0x5d9113bb}, carry = 1},
{a = {126, 0x52981c2d, 0x4c46b130, 0x37b3c3cf, 0x231f6fc4}, b = {126, 0x76da8c93, 0x5e52130f, 0x7efb6a4f, 0x2e3c4907}, res = {126, 0x5bbd8f9a, 0x6df49e20, 0x38b8597f, 0x74e326bc}, carry = 1},
{a = {127, 0x05600262, 0x0bb65331, 0x19f8d9cb, 0x5ddc2c62}, b = {127, 0x687b227e, 0x27348976, 0x072f2464, 0x4fbe7ebe}, res = {127, 0x1ce4dfe4, 0x6481c9ba, 0x12c9b566, 0x0e1dada4}, carry = 0},
{a = {127, 0x6a2d2e4a, 0x1b8b56dd, 0x50946d73, 0x64b0886d}, b = {122, 0x68ceaba5, 0x5344cea7, 0x7aeae02f, 0x03444737}, res = {127, 0x015e82a5, 0x48468836, 0x55a98d43, 0x616c4135}, carry = 0},
{a = {126, 0x2be80971, 0x7a1ffbd6, 0x7d5cd745, 0x310e6aa8}, b = {126, 0x31c62e38, 0x08a3a28d, 0x4d67f9a3, 0x30493db3}, res = {126, 0x7a21db39, 0x717c5948, 0x2ff4dda2, 0x00c52cf5}, carry = 0},
{a = {125, 0x030ba3d3, 0x162c1b78, 0x3f878cb3, 0x1a5108ff}, b = {127, 0x1f37aec7, 0x01e1153a, 0x325aab1e, 0x4b9a6619}, res = {125, 0x63d3f50c, 0x144b063d, 0x0d2ce195, 0x4eb6a2e6}, carry = 1},
{a = {126, 0x15a0528b, 0x29cbf0ad, 0x3048884b, 0x2c03d1c4}, b = {127, 0x5c32f40c, 0x192de971, 0x21a7f87d, 0x558c0542}, res = {126, 0x396d5e7f, 0x109e073b, 0x0ea08fce, 0x5677cc82}, carry = 1},
{a = {126, 0x0b48d92b, 0x5b09cd3a, 0x73960e62, 0x3f0ed1cd}, b = {124, 0x7a3d70fe, 0x5ff9c6b1, 0x4bd0e555, 0x0cc51487}, res = {126, 0x110b682d, 0x7b100688, 0x27c5290c, 0x3249bd46}, carry = 0},
{a = {127, 0x1bf5bd1c, 0x54104aef, 0x0c7ce316, 0x420fe407}, b = {126, 0x409409a2, 0x3d04fecd, 0x3e06417a, 0x3078c80b}, res = {127, 0x5b61b37a, 0x170b4c21, 0x4e76a19c, 0x11971bfb}, carry = 0},
{a = {125, 0x33d00217, 0x1c394fa4, 0x574f8331, 0x1d57d174}, b = {127, 0x6630d00d, 0x666f444c, 0x1a90af22, 0x484d8ca0}, res = {125, 0x4d9f320a, 0x35ca0b57, 0x3cbed40e, 0x550a44d4}, carry = 1},
{a = {127, 0x166e17c2, 0x1ecc3694, 0x1d8ad0ad, 0x5cfd9dd8}, b = {125, 0x130183de, 0x537699d8, 0x353fa45d, 0x114feeeb}, res = {127, 0x036c93e4, 0x4b559cbc, 0x684b2c4f, 0x4badaeec}, carry = 0},
{a = {127, 0x2736ea4c, 0x34a77f3b, 0x5d62185c, 0x49f65789}, b = {126, 0x10ef92f4, 0x6d50c8cd, 0x3b2c8da0, 0x26d91a49}, res = {127, 0x16475758, 0x4756b66e, 0x22358abb, 0x231d3d40}, carry = 0},
{a = {127, 0x7c90f6d6, 0x6e8c2441, 0x56303ec7, 0x648b71e2}, b = {127, 0x5efcffab, 0x35738e3f, 0x6a0010b5, 0x54fd6ef0}, res = {127, 0x1d93f72b, 0x39189602, 0x6c302e12, 0x0f8e02f1}, carry = 0},
{a = {127, 0x09d4aa13, 0x39bee362, 0x48f85dfd, 0x54d4270d}, b = {121, 0x75609829, 0x5a20f480, 0x2a01c588, 0x0146be72}, res = {127, 0x147411ea, 0x5f9deee1, 0x1ef69874, 0x538d689b}, carry = 0},
{a = {123, 0x5ca62e2f, 0x5bd68bd9, 0x009238b5, 0x04524739}, b = {126, 0x0f74d025, 0x68d0ff9b, 0x3cd174c2, 0x23b408c2}, res = {123, 0x4d315e0a, 0x73058c3e, 0x43c0c3f2, 0x609e3e76}, carry = 1},
}
I31_Test_Vector_Decode :: struct {
src: []byte,
decode: []u32,
mod: []u32,
mod_res: u32,
}
@(rodata)
i31_decode_test_vectors := []I31_Test_Vector_Decode {
{src = {0x49}, decode = {7, 0x00000049}, mod = {42, 0x00000049}, mod_res = 1},
{src = {0x27, 0xdb}, decode = {14, 0x000027db}, mod = {42, 0x000027db}, mod_res = 1},
{src = {0xb8, 0xd7, 0x72}, decode = {24, 0x00b8d772}, mod = {42, 0x00b8d772}, mod_res = 1},
{src = {0xa0, 0x94, 0x6e, 0xac}, decode = {33, 0x20946eac, 0x00000001}, mod = {42, 0x20946eac, 0x00000001}, mod_res = 1},
{src = {0xed, 0x8a, 0xc2, 0xe4, 0x45}, decode = {41, 0x0ac2e445, 0x000001db}, mod = {42, 0x0ac2e445, 0x000001db}, mod_res = 1},
{src = {0xfb, 0x37, 0x36, 0x46, 0x28, 0x4e}, decode = {49, 0x3646284e, 0x0001f66e}, mod = {42, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd8, 0xcc, 0xbb, 0xf2, 0x13, 0x66, 0xdf}, decode = {57, 0x721366df, 0x01b19977}, mod = {42, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xcb, 0xc3, 0xe2, 0xa8, 0x4c, 0xfd, 0x9e, 0x8a}, decode = {66, 0x4cfd9e8a, 0x1787c550, 0x00000003}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x0d, 0xd5, 0x76, 0xc1, 0xb4, 0x02, 0xdb, 0xb4, 0xb9}, decode = {70, 0x02dbb4b9, 0x2aed8368, 0x00000037}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xf6, 0xb6, 0x08, 0xb3, 0xfb, 0x0c, 0x7e, 0x8e, 0x58, 0xa7}, decode = {82, 0x7e8e58a7, 0x1167f618, 0x0003dad8}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xf9, 0x51, 0xde, 0x6a, 0x29, 0x54, 0x48, 0xf5, 0x3c, 0x78, 0x85}, decode = {90, 0x753c7885, 0x5452a891, 0x03e54779}, mod = {42, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xb5, 0x5a, 0x92, 0x18, 0x67, 0x09, 0x6e, 0x21, 0x5d, 0x84, 0x10, 0xce}, decode = {99, 0x5d8410ce, 0x4e12dc42, 0x556a4861, 0x00000005}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd3, 0xa4, 0x7b, 0xa0, 0x90, 0x26, 0x36, 0x73, 0xa6, 0x7f, 0x28, 0x66, 0x4e}, decode = {107, 0x7f28664e, 0x4c6ce74c, 0x11ee8240, 0x0000069d}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x23, 0xfa, 0xe7, 0x0c, 0x6a, 0x07, 0x00, 0x86, 0xa2, 0xcf, 0x22, 0xa1, 0x1f, 0x62}, decode = {113, 0x22a11f62, 0x010d459e, 0x1c31a81c, 0x00011fd7}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x9c, 0x39, 0xf8, 0x67, 0x8a, 0x9f, 0x85, 0xb4, 0xdb, 0x92, 0x4b, 0xe6, 0xdc, 0x12, 0xa5}, decode = {123, 0x66dc12a5, 0x69b72497, 0x1e2a7e16, 0x04e1cfc3}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x8f, 0xc7, 0xcc, 0xda, 0x43, 0x21, 0x0b, 0x39, 0x0e, 0xc3, 0x51, 0xb0, 0xbd, 0xce, 0xbb, 0x73}, decode = {132, 0x3dcebb73, 0x1d86a361, 0x0c842ce4, 0x7e3e66d2, 0x00000008}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xc5, 0x8d, 0xd3, 0x1b, 0xda, 0x0d, 0x21, 0xfd, 0xb2, 0x69, 0xca, 0x12, 0xd4, 0x47, 0x44, 0x4e, 0x61}, decode = {140, 0x47444e61, 0x539425a8, 0x3487f6c9, 0x6e98ded0, 0x00000c58}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x4f, 0x00, 0x7f, 0x9c, 0x00, 0x11, 0x29, 0x8d, 0x94, 0xd6, 0x7e, 0x1c, 0xc3, 0x26, 0x78, 0xb7, 0xfb, 0xbb}, decode = {147, 0x78b7fbbb, 0x7c39864c, 0x26365359, 0x7ce00089, 0x0004f007}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xb8, 0x85, 0x3f, 0xe0, 0x03, 0x89, 0x20, 0x72, 0xf8, 0xa6, 0x66, 0x48, 0x0f, 0xdb, 0x63, 0x19, 0x8e, 0xbb, 0x00}, decode = {156, 0x198ebb00, 0x101fb6c6, 0x4be29999, 0x001c4903, 0x0b8853fe}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x3c, 0x88, 0x99, 0xf2, 0x23, 0xc8, 0x65, 0x6d, 0x6a, 0x93, 0xa0, 0xf6, 0xef, 0x8d, 0x28, 0xd8, 0x85, 0x9e, 0x8f, 0x85}, decode = {163, 0x059e8f85, 0x5f1a51b1, 0x2a4e83db, 0x1e432b6b, 0x48899f22, 0x00000007}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd8, 0x28, 0x9e, 0xae, 0x7c, 0xc2, 0xf6, 0x9c, 0x72, 0xe7, 0x25, 0xf6, 0x0c, 0xec, 0xcc, 0x78, 0x5d, 0xf9, 0xb0, 0xe9, 0x9f}, decode = {173, 0x79b0e99f, 0x5998f0bb, 0x1c97d833, 0x17b4e397, 0x09eae7cc, 0x00001b05}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xec, 0x5f, 0x68, 0xa7, 0xb6, 0xa4, 0x7d, 0x01, 0x88, 0x39, 0xeb, 0x88, 0x3a, 0x33, 0x60, 0x8a, 0x59, 0x67, 0xaa, 0xfe, 0x67, 0xc2}, decode = {181, 0x2afe67c2, 0x4114b2cf, 0x2e20e8cd, 0x680c41cf, 0x0a7b6a47, 0x001d8bed}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x34, 0x8a, 0x87, 0xdb, 0xbf, 0x60, 0xa6, 0x58, 0x23, 0xe3, 0x99, 0x9a, 0x43, 0x5c, 0x43, 0x9e, 0xfe, 0xb2, 0xbd, 0xd7, 0xd2, 0xa3, 0x74}, decode = {187, 0x57d2a374, 0x3dfd657b, 0x690d710e, 0x411f1ccc, 0x3bf60a65, 0x069150fb}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xe6, 0xe7, 0x48, 0x95, 0x31, 0x9e, 0xdb, 0x97, 0xd7, 0x37, 0x17, 0x1a, 0x9a, 0x51, 0xde, 0x02, 0xa4, 0x7f, 0x84, 0xbd, 0x25, 0x02, 0xc7, 0xe1}, decode = {198, 0x2502c7e1, 0x48ff097a, 0x6947780a, 0x39b8b8d4, 0x19edb97d, 0x5ce912a6, 0x00000039}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x5c, 0xd5, 0xa2, 0x66, 0xf4, 0x01, 0x12, 0xba, 0xe8, 0x05, 0x63, 0xa0, 0xc0, 0xfa, 0xaf, 0xe1, 0x3d, 0xe4, 0xc5, 0x21, 0xab, 0xa9, 0x52, 0x99, 0x3f}, decode = {205, 0x2952993f, 0x498a4357, 0x6abf84f7, 0x2b1d0607, 0x112bae80, 0x344cde80, 0x00001735}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x8f, 0x93, 0xfc, 0x0c, 0x73, 0x29, 0x7d, 0x28, 0xdc, 0x71, 0x7b, 0x01, 0x53, 0x4f, 0x87, 0xdd, 0xf5, 0x43, 0xe2, 0xb0, 0x97, 0x1d, 0xe1, 0xf7, 0x7f, 0xcf}, decode = {214, 0x61f77fcf, 0x45612e3b, 0x1f77d50f, 0x580a9a7c, 0x528dc717, 0x018e652f, 0x0023e4ff}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xd7, 0x86, 0x9b, 0x8d, 0x91, 0x97, 0xff, 0x98, 0xbe, 0xa7, 0x67, 0x73, 0xda, 0x7a, 0x07, 0xe7, 0x07, 0xf3, 0x61, 0xc4, 0x2d, 0x01, 0x96, 0x68, 0xbf, 0xd6, 0x36}, decode = {222, 0x68bfd636, 0x085a032c, 0x1c1fcd87, 0x1ed3d03f, 0x0bea7677, 0x3232fff3, 0x35e1a6e3}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xf7, 0xec, 0xfa, 0x44, 0x18, 0x5e, 0x6a, 0x51, 0x98, 0x3d, 0x3b, 0xc9, 0xcf, 0xb5, 0x9f, 0x97, 0xa8, 0x7d, 0xdd, 0xda, 0xf3, 0xf3, 0x1c, 0x02, 0x9c, 0x40, 0x5c, 0x8a}, decode = {231, 0x1c405c8a, 0x67e63805, 0x21f7776b, 0x7dacfcbd, 0x03d3bc9c, 0x0bcd4a33, 0x7b3e9106, 0x0000007b}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0xca, 0x32, 0x04, 0xef, 0xc8, 0x40, 0x55, 0xc9, 0x67, 0x2e, 0x3b, 0x1a, 0xbe, 0x2c, 0xf1, 0xdd, 0xe0, 0xaa, 0x46, 0x31, 0x40, 0xc5, 0xf6, 0xaa, 0x25, 0x88, 0x0d, 0x0f, 0x30}, decode = {239, 0x080d0f30, 0x0bed544b, 0x2918c503, 0x678eef05, 0x63b1abe2, 0x0ab92ce5, 0x013bf210, 0x00006519}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x96, 0xb5, 0x62, 0x03, 0x50, 0x9f, 0x04, 0xfc, 0xc3, 0x06, 0x25, 0xc1, 0xa7, 0x21, 0x6d, 0xeb, 0x60, 0x8d, 0xb0, 0x02, 0x34, 0x18, 0xaf, 0xd1, 0xb0, 0x23, 0x0f, 0x6a, 0xe6, 0x63}, decode = {247, 0x0f6ae663, 0x5fa36046, 0x4008d062, 0x6f5b046d, 0x5c1a7216, 0x1f9860c4, 0x00d427c1, 0x004b5ab1}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
{src = {0x5c, 0x00, 0x1a, 0x67, 0x07, 0x8e, 0x49, 0x72, 0x30, 0x6f, 0x2b, 0xa6, 0x93, 0x37, 0x6a, 0x2b, 0x5a, 0x8f, 0xb4, 0x30, 0x65, 0x89, 0xa1, 0x7b, 0xbc, 0xf9, 0x85, 0x3f, 0x44, 0x64, 0x48}, decode = {254, 0x3f446448, 0x7779f30a, 0x41962685, 0x5ad47da1, 0x693376a2, 0x460de574, 0x41e3925c, 0x2e000d33, 0x00000000}, mod = {42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, mod_res = 0},
}
I31_Test_RShift :: struct {
orig: []u32,
res: []u32,
shift: i32,
}
@(rodata)
i31_rshift_test_vectors := []I31_Test_RShift {
{orig = {92, 0x4b451fff, 0x2874869d, 0x0d1b97a7}, res = {92, 0x4b451fff, 0x2874869d, 0x0d1b97a7}, shift = 0},
{orig = {94, 0x44709cd4, 0x60d36574, 0x3a587e31}, res = {94, 0x22384e6a, 0x7069b2ba, 0x1d2c3f18}, shift = 1},
{orig = {94, 0x77ea5771, 0x78324ed5, 0x3d9ad39d}, res = {94, 0x3dfa95dc, 0x3e0c93b5, 0x0f66b4e7}, shift = 2},
{orig = {94, 0x631db445, 0x248c243a, 0x3f1cc589}, res = {94, 0x2c63b688, 0x14918487, 0x07e398b1}, shift = 3},
{orig = {95, 0x6f23a8ed, 0x7d5a3c91, 0x7e913a13}, res = {95, 0x0ef23a8e, 0x1fd5a3c9, 0x07e913a1}, shift = 4},
{orig = {94, 0x03dd8abf, 0x5f02ee65, 0x249f3f58}, res = {94, 0x141eec55, 0x62f81773, 0x0124f9fa}, shift = 5},
{orig = {94, 0x2927bee6, 0x4f308116, 0x3279228a}, res = {94, 0x2ca49efb, 0x153cc204, 0x00c9e48a}, shift = 6},
{orig = {95, 0x22687024, 0x73cb5fd3, 0x52d71799}, res = {95, 0x5344d0e0, 0x19e796bf, 0x00a5ae2f}, shift = 7},
{orig = {94, 0x71a3bca0, 0x27642d52, 0x23e2059f}, res = {94, 0x2971a3bc, 0x4fa7642d, 0x0023e205}, shift = 8},
{orig = {95, 0x180d85aa, 0x12a8516d, 0x4797741e}, res = {95, 0x5b4c06c2, 0x07895428, 0x0023cbba}, shift = 9},
{orig = {95, 0x7b30aeeb, 0x3ababf4f, 0x6a446aff}, res = {95, 0x69fecc2b, 0x5feeaeaf, 0x001a911a}, shift = 10},
{orig = {94, 0x3b2d5f09, 0x7bde80e9, 0x33ed8ce0}, res = {94, 0x0e9765ab, 0x4e0f7bd0, 0x00067db1}, shift = 11},
{orig = {93, 0x62c89828, 0x20d78d11, 0x14bbd081}, res = {93, 0x688e2c89, 0x040a0d78, 0x00014bbd}, shift = 12},
{orig = {95, 0x102c28e6, 0x31f2a99f, 0x6c337ed1}, res = {95, 0x267c8161, 0x7b458f95, 0x0003619b}, shift = 13},
{orig = {94, 0x78097c9b, 0x6b3f99c8, 0x2ea10fbb}, res = {94, 0x3391e025, 0x1f77acfe, 0x0000ba84}, shift = 14},
{orig = {92, 0x4684f360, 0x56d54074, 0x0dd7bfa7}, res = {92, 0x40748d09, 0x3fa7adaa, 0x00001baf}, shift = 15},
{orig = {93, 0x59e9b272, 0x1792ca1d, 0x134ce6d8}, res = {93, 0x650ed9e9, 0x736c1792, 0x0000134c}, shift = 16},
{orig = {94, 0x2be2d592, 0x566508a1, 0x3a8622ab}, res = {94, 0x422855f1, 0x08aaeb32, 0x00001d43}, shift = 17},
{orig = {95, 0x3082be20, 0x118cde6a, 0x57a1832c}, res = {95, 0x1bcd4c20, 0x30658463, 0x000015e8}, shift = 18},
{orig = {95, 0x7b7ec9fc, 0x30304a84, 0x7cb524f7}, res = {95, 0x04a84f6f, 0x524f7606, 0x00000f96}, shift = 19},
{orig = {94, 0x7a038786, 0x5902ae15, 0x2ee36b72}, res = {94, 0x1570afa0, 0x1b5b9590, 0x000002ee}, shift = 20},
{orig = {95, 0x2a127f63, 0x5d1b323f, 0x7dc23bd7}, res = {95, 0x6cc8fd50, 0x08ef5ee8, 0x000003ee}, shift = 21},
{orig = {95, 0x3cf76a47, 0x30310973, 0x69bc9430}, res = {95, 0x6212e6f3, 0x792860c0, 0x000001a6}, shift = 22},
{orig = {94, 0x3ca70baa, 0x07747e80, 0x3a710be4}, res = {94, 0x747e8079, 0x710be40e, 0x00000074}, shift = 23},
{orig = {95, 0x6bc0e3b9, 0x77b2972f, 0x5b9f893c}, res = {95, 0x594b97eb, 0x4fc49e77, 0x0000005b}, shift = 24},
{orig = {90, 0x49e59ccc, 0x7d67d332, 0x02b4f127}, res = {90, 0x59f4cca4, 0x2d3c49fe, 0x00000001}, shift = 25},
{orig = {94, 0x55e1b1d7, 0x7a1c0bdb, 0x217e8547}, res = {94, 0x43817b75, 0x2fd0a8fe, 0x00000008}, shift = 26},
{orig = {94, 0x67fb980c, 0x20a9410b, 0x25de2ffd}, res = {94, 0x0a9410bc, 0x5de2ffd4, 0x00000004}, shift = 27},
{orig = {92, 0x18363569, 0x0b492aba, 0x0a37f42c}, res = {92, 0x5a4955d1, 0x51bfa160, 0x00000000}, shift = 28},
{orig = {95, 0x23bb0e53, 0x4bb6c3aa, 0x77932915}, res = {95, 0x2edb0ea9, 0x5e4ca456, 0x00000003}, shift = 29},
{orig = {95, 0x725acb18, 0x1da81862, 0x6d6c306b}, res = {95, 0x3b5030c5, 0x5ad860d6, 0x00000001}, shift = 30},
{orig = {95, 0x70d51c12, 0x44e8b652, 0x4a0d2a6b}, res = {95, 0x44e8b652, 0x4a0d2a6b, 0x00000000}, shift = 31},
}
I31_Test_Reduce :: struct {
orig: []u32,
res: []u32,
}
@(rodata)
i31_reduce_test_vectors := []I31_Test_Reduce {
{orig = {62, 0x27da8fd9, 0x2fea2339}, res = {42, 0x27f284e9, 0x00000339}},
{orig = {95, 0x37856cc1, 0x54ad3e73, 0x718777f1}, res = {42, 0x33efdfc1, 0x0000022f, 0x718777f1}},
{orig = {123, 0x4787b519, 0x47fa51cf, 0x11ef98ae, 0x058b1a99}, res = {42, 0x567ed6bd, 0x000002fe, 0x11ef98ae, 0x058b1a99}},
{orig = {158, 0x328fbfb6, 0x6e9ec225, 0x0241df84, 0x4a3627d0, 0x3a79e4a4}, res = {42, 0x005f59f0, 0x000001aa, 0x0241df84, 0x4a3627d0, 0x3a79e4a4}},
{orig = {191, 0x451321f5, 0x45f677b9, 0x2c43b0b8, 0x7cb722c1, 0x70e594e6, 0x4c81757c}, res = {42, 0x684ba25d, 0x0000008d, 0x2c43b0b8, 0x7cb722c1, 0x70e594e6, 0x4c81757c}},
{orig = {222, 0x0749fe0b, 0x3c45795b, 0x21e6f14c, 0x265b264d, 0x37def307, 0x35019b6b, 0x25ab0a3e}, res = {42, 0x36c99d3d, 0x00000173, 0x21e6f14c, 0x265b264d, 0x37def307, 0x35019b6b, 0x25ab0a3e}},
{orig = {255, 0x1c2630cd, 0x0c0d9a7e, 0x375154d0, 0x67249adf, 0x0df9ec39, 0x73b5ad9e, 0x396d0f52, 0x4ed9b56a}, res = {42, 0x79997cc9, 0x000002b4, 0x375154d0, 0x67249adf, 0x0df9ec39, 0x73b5ad9e, 0x396d0f52, 0x4ed9b56a}},
{orig = {286, 0x35b7eae2, 0x033002a6, 0x149766aa, 0x5d4a5a16, 0x7d09704c, 0x380c8cf2, 0x249df2ff, 0x03caabad, 0x214fc645}, res = {42, 0x20f488ba, 0x000003e1, 0x149766aa, 0x5d4a5a16, 0x7d09704c, 0x380c8cf2, 0x249df2ff, 0x03caabad, 0x214fc645}},
{orig = {315, 0x7a5813dc, 0x76309cbf, 0x03e432bc, 0x218df9a1, 0x1a0d0525, 0x793ad550, 0x280cbf6e, 0x18356492, 0x4b6f39a1, 0x04adeb0d}, res = {42, 0x0016ad0c, 0x000000a8, 0x03e432bc, 0x218df9a1, 0x1a0d0525, 0x793ad550, 0x280cbf6e, 0x18356492, 0x4b6f39a1, 0x04adeb0d}},
{orig = {351, 0x0ded15c8, 0x3875535e, 0x4627ebc1, 0x101a8369, 0x7300acd4, 0x22113509, 0x2a441bc0, 0x25902fec, 0x230133c0, 0x7ecaa587, 0x439bfdcd}, res = {42, 0x60547c04, 0x00000144, 0x4627ebc1, 0x101a8369, 0x7300acd4, 0x22113509, 0x2a441bc0, 0x25902fec, 0x230133c0, 0x7ecaa587, 0x439bfdcd}},
}
@(rodata)
i31_decode_reduce_test_vectors := []I31_Test_Vector_Decode {
{src = {171, 54, 46}, decode = {42, 0x00ab362e, 0x00000000, 0x00000000}},
{src = {87, 80, 187, 242}, decode = {42, 0x5750bbf2, 0x00000000, 0x00000000}},
{src = {181, 9, 43, 65, 203}, decode = {42, 0x092b41cb, 0x0000016a, 0x00000000}},
{src = {196, 160, 88, 214, 25, 234}, decode = {42, 0x58d61aae, 0x00000140, 0x00000000}},
{src = {223, 248, 213, 188, 56, 226, 125}, decode = {42, 0x3c39c275, 0x000001ab, 0x00000000}},
{src = {252, 152, 213, 96, 59, 205, 38, 68}, decode = {42, 0x3cc9bf18, 0x000002c0, 0x00000000}},
{src = {61, 151, 139, 227, 56, 129, 245, 7, 245}, decode = {42, 0x3f8c93d7, 0x00000271, 0x00000000}},
{src = {50, 242, 36, 43, 28, 76, 75, 80, 191, 213}, decode = {42, 0x3d74eaf1, 0x000000fe, 0x00000000}},
{src = {45, 174, 63, 171, 108, 66, 180, 223, 196, 24, 202}, decode = {42, 0x1f6f853a, 0x000000c6, 0x00000000}},
{src = {106, 140, 119, 25, 145, 178, 50, 134, 118, 181, 241, 205}, decode = {42, 0x10480e8b, 0x000001fb, 0x00000000}},
}
I31_Test_Mul_Add_Small :: struct {
orig: []u32,
res: []u32,
z: u32,
}
@(rodata)
i31_mul_add_test_vectors := []I31_Test_Mul_Add_Small {
{orig = {63, 0x157df37a, 0x5e97b10e}, res = {63, 0x438abf24, 0x7fffff7a}, z = 42},
{orig = {63, 0x6c11d92a, 0x74b6b943}, res = {63, 0x50f60940, 0x0000012a}, z = 84},
{orig = {63, 0x0bf17f5a, 0x756c85bb}, res = {63, 0x6ec5f93e, 0x7fffff5a}, z = 126},
{orig = {63, 0x0d24e4eb, 0x5891203e}, res = {63, 0x0f86931a, 0x000000eb}, z = 168},
{orig = {63, 0x41dda071, 0x6deb6411}, res = {63, 0x0460efa2, 0x00000071}, z = 210},
{orig = {63, 0x29793d56, 0x4d5204e5}, res = {63, 0x3954bd9a, 0x00000156}, z = 252},
{orig = {58, 0x40e72062, 0x039ba033}, res = {58, 0x0ce074b6, 0x00000062}, z = 294},
{orig = {63, 0x6e9b147b, 0x60dcf3ef}, res = {63, 0x7bf74edc, 0x7ffffc7c}, z = 336},
{orig = {62, 0x74572099, 0x3af8369a}, res = {62, 0x26ba2d0a, 0x0000009a}, z = 378},
}
I31_Test_Vector_Encode :: struct {
orig: []u32,
encoded: []u8,
}
@(rodata)
i31_encode_test_vectors := []I31_Test_Vector_Encode {
{orig = {30, 0x2003ca33}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 3, 202, 51}},
{orig = {61, 0x13d86a42, 0x1c0fc55a}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 7, 226, 173, 19, 216, 106, 66}},
{orig = {93, 0x0adffd1b, 0x3a03c1bc, 0x19682bb9}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 90, 10, 238, 93, 1, 224, 222, 10, 223, 253, 27}},
{orig = {127, 0x193841a2, 0x5cf0aa2f, 0x57594f6d, 0x4fc77899}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 248, 239, 19, 53, 214, 83, 219, 110, 120, 85, 23, 153, 56, 65, 162}},
{orig = {158, 0x219d0fd8, 0x623e21ae, 0x5c9ad413, 0x6dc292d4, 0x2b2f85fe}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 178, 248, 95, 237, 184, 82, 90, 151, 38, 181, 4, 241, 31, 16, 215, 33, 157, 15, 216}},
{orig = {189, 0x69363092, 0x4b751e5d, 0x663b4745, 0x2e93e19e, 0x1a3dabce, 0x1d4caa3f}, encoded = {0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 101, 81, 249, 163, 218, 188, 229, 210, 124, 51, 217, 142, 209, 209, 101, 186, 143, 46, 233, 54, 48, 146}},
{orig = {222, 0x016b86ed, 0x29c31304, 0x15cca452, 0x6abefa4a, 0x7410f562, 0x450559df, 0x28ad9252}, encoded = {0, 0, 0, 0, 0, 162, 182, 73, 74, 40, 42, 206, 255, 65, 15, 86, 45, 87, 223, 73, 69, 115, 41, 20, 148, 225, 137, 130, 1, 107, 134, 237}},
{orig = {253, 0x4b6d0cd3, 0x357f8b7d, 0x4bd86436, 0x5838a86c, 0x10280aa0, 0x22f9b902, 0x4bdb5c1c, 0x19a20f9e}, encoded = {0, 51, 68, 31, 61, 47, 109, 112, 113, 23, 205, 200, 17, 2, 128, 170, 11, 7, 21, 13, 146, 246, 25, 13, 154, 191, 197, 190, 203, 109, 12, 211}},
}
I31_Test_Ninv :: struct {
orig: []u32,
ninv: []u32,
}
@(rodata)
i31_ninv_test_vectors := I31_Test_Ninv {
orig = {0x00000c5c, 0x47faf728, 0x69a8e9d5, 0x49f5015c, 0x4ea9aea5, 0x164bcf32, 0x3fc395b4, 0x1c0a908a, 0x795f47f2, 0x79aa0c9a, 0x1a37680f, 0x2834cf17, 0x499235c8, 0x6d239632, 0x0560438b, 0x2cf4b82b, 0x5133fd25, 0x3b63a8b9, 0x45b66eca, 0x0213c13c, 0x6cd589f3, 0x4ed03f68, 0x722eb913, 0x670c76f4, 0x444d31d0, 0x1809da41, 0x3656a4af, 0x2ab43d09, 0x281c0e85, 0x426b3fd3, 0x680413c0, 0x065884c2, 0x55e4db17, 0x3839a23f, 0x3cc508b0, 0x3c492cda, 0x43325992, 0x5bc31283, 0x3891deaa, 0x5ddddd43, 0x64314225, 0x43a1c73b, 0x5e0ec431, 0x27f583bf, 0x491a73dc, 0x377982b8, 0x0b760791, 0x7835c61d, 0x0192b0f7, 0x129ca7ca, 0x16333e2d, 0x39c07864, 0x4d4b0d1b, 0x252fdb31, 0x2ca1bef8, 0x0358e216, 0x6aeda289, 0x201bca8f, 0x6ded1451, 0x5fbb04bb, 0x03d86c3b, 0x2f402c96, 0x4567a0ad, 0x6159d4de, 0x494f89b9, 0x7035fbdf, 0x5c3cfbd6, 0x40bfdbc8, 0x5e27b49c, 0x4fee508e, 0x76bd2f29, 0x45b49e47, 0x7da53921, 0x3d9380aa, 0x5d5bce86, 0x212b2912, 0x2e3e0a61, 0x218cfe04, 0x22ebe499, 0x70713a1c, 0x5b690632, 0x3f5c8dc1, 0x323815a5, 0x1f9f4c8f, 0x56a54a21, 0x469a2122, 0x6fb2beea, 0x643ac847, 0x01d2c5bc, 0x7dc51683, 0x0b876ebe, 0x214a069a, 0x535258ed, 0x529b3dce, 0x2760b2c2, 0x78cf5cd3, 0x19d1d3f2, 0x572ebc02, 0x1ab91604, 0x0b5f09a0},
ninv = {0x00000000, 0x00000000, 0x66e2f883, 0x00000000, 0x210176d3, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x40ad7911, 0x3954a759, 0x00000000, 0x00000000, 0x657333dd, 0x11c1b97d, 0x1c122953, 0x40642277, 0x00000000, 0x00000000, 0x7f3dc8c5, 0x00000000, 0x166a06e5, 0x00000000, 0x00000000, 0x605cca3f, 0x3548cdb1, 0x35b19ec7, 0x681845b3, 0x1c438fa5, 0x00000000, 0x00000000, 0x2beff359, 0x79f2b241, 0x00000000, 0x00000000, 0x00000000, 0x6040b3d5, 0x00000000, 0x1ee96895, 0x1f18f653, 0x7f80860d, 0x3901eb2f, 0x56ff93c1, 0x00000000, 0x00000000, 0x562e668f, 0x72bad3cb, 0x3de0ef39, 0x00000000, 0x6be23e5b, 0x00000000, 0x3a327aed, 0x0fae622f, 0x00000000, 0x00000000, 0x004d8c47, 0x5a1feb91, 0x76f34b4f, 0x68675f8d, 0x7dec730d, 0x00000000, 0x5a953cdb, 0x00000000, 0x65605377, 0x7aa67fe1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4eaf22e7, 0x4a585489, 0x3fa2751f, 0x00000000, 0x00000000, 0x00000000, 0x7107e65f, 0x00000000, 0x5f81d057, 0x00000000, 0x00000000, 0x2d7b7dbf, 0x0e2f35d3, 0x75e7ad91, 0x4ed7461f, 0x00000000, 0x00000000, 0x4a199e89, 0x00000000, 0x45bf97d5, 0x00000000, 0x00000000, 0x4ada3b1b, 0x00000000, 0x00000000, 0x7456a4a5, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
}
I31_Test_Monty_Mul :: struct {
x: []u32,
y: []u32,
m: []u32,
res: []u32,
}
@(rodata)
i31_monty_mul_test_vectors := []I31_Test_Monty_Mul {
{x = {127, 0x7c5a766e, 0x6ce75a35, 0x1c64cb75, 0x51263ee0, 0x00000000}, y = {127, 0x75063ae3, 0x3f506a6f, 0x1d1ad6d5, 0x47a85d53, 0x00000000}, m = {159, 0x7e0a5a33, 0x41c36cc5, 0x065bfbbd, 0x4608e748, 0x69ac6740, 0x00000000}, res = {159, 0x3e2b1a07, 0x4990ba63, 0x15f1a1ea, 0x6e8ff678, 0x13ba5b66}},
{x = {124, 0x5232b140, 0x341f2854, 0x3e6bde54, 0x092324e1, 0x00000000}, y = {127, 0x6c66ba58, 0x51577b9e, 0x72a600d4, 0x53bd0450, 0x00000000}, m = {159, 0x153010a5, 0x7d0ddec2, 0x13c7f180, 0x1cd216b3, 0x5818bdd7, 0x00000000}, res = {159, 0x6de73758, 0x21d8ff38, 0x69b18e1e, 0x5d1c7d47, 0x41c75523}},
{x = {126, 0x176e0f5b, 0x0a98100a, 0x475e0a72, 0x348f9141, 0x00000000}, y = {127, 0x334e2dfe, 0x2c988363, 0x73e23cf8, 0x606dcc5f, 0x00000000}, m = {159, 0x7023de77, 0x54103e3b, 0x338cd291, 0x0e2c1e77, 0x6fa41147, 0x00000000}, res = {159, 0x488db6ba, 0x3ec6b26b, 0x0dddf8b5, 0x4e0334db, 0x2125e61c}},
{x = {127, 0x1018738e, 0x233c04d7, 0x46a665c0, 0x79c84e0a, 0x00000000}, y = {127, 0x783e44fe, 0x14a1ea17, 0x31bea107, 0x66c49111, 0x00000000}, m = {158, 0x580ca6c1, 0x61ad5aad, 0x238b9bdb, 0x0ce14c93, 0x2749fc5c, 0x00000000}, res = {158, 0x37557af5, 0x5ca2f6fb, 0x7fdca455, 0x4d436f9b, 0x18d60f4f}},
{x = {126, 0x45da293a, 0x1665982a, 0x0e8fc303, 0x231c2772, 0x00000000}, y = {126, 0x542485c1, 0x3f2f7d3f, 0x684047a6, 0x3abaed33, 0x00000000}, m = {159, 0x03d31fd1, 0x6f0c8ff3, 0x12825696, 0x199fda81, 0x76457daf, 0x00000000}, res = {159, 0x6c6852a3, 0x0d1fd2b0, 0x67e96590, 0x768c4fca, 0x3ad66382}},
{x = {126, 0x21d23a0e, 0x5ba5d464, 0x29697487, 0x338d2cff, 0x00000000}, y = {125, 0x29e49a2c, 0x144564ae, 0x3d17958d, 0x101f7e41, 0x00000000}, m = {158, 0x4545452b, 0x6e68f705, 0x2302e752, 0x07b5d5d6, 0x3657a220, 0x00000000}, res = {158, 0x3e90a0ed, 0x457cb17b, 0x53b4f9f5, 0x7a6324ce, 0x35d2cdd7}},
{x = {125, 0x4732d8b6, 0x45573e7d, 0x07f4963f, 0x12a290dd, 0x00000000}, y = {127, 0x39baf88d, 0x5d858830, 0x4580eb58, 0x6d196a63, 0x00000000}, m = {159, 0x0cb6812b, 0x482b08af, 0x4f1b863c, 0x3cef2e92, 0x50f7c7f1, 0x00000000}, res = {159, 0x6ad503c6, 0x27c67766, 0x5fca2845, 0x15bbcb6b, 0x46c3cb6c}},
{x = {127, 0x354a4328, 0x651d0b7f, 0x43a452fb, 0x7bd74c98, 0x00000000}, y = {126, 0x3cfc2842, 0x71256a54, 0x3719176f, 0x34407271, 0x00000000}, m = {159, 0x306eba87, 0x772cb87d, 0x4907c954, 0x5dc5e91b, 0x74d5eb17, 0x00000000}, res = {159, 0x49e12a70, 0x7e89a281, 0x1ea3548a, 0x54711053, 0x713bfe04}},
{x = {126, 0x4affb101, 0x52a6864d, 0x7152422e, 0x3cca5f4e, 0x00000000}, y = {126, 0x3f4b1e18, 0x32aaa89a, 0x635dcdc7, 0x3c44b1de, 0x00000000}, m = {158, 0x01432ccf, 0x2bcf7f6d, 0x7561291c, 0x7064f0cb, 0x3381e843, 0x00000000}, res = {158, 0x03895437, 0x6792e4bb, 0x68391714, 0x5a6104d6, 0x16bbc670}},
{x = {127, 0x56c42650, 0x2723c136, 0x4d64beac, 0x5e656e91, 0x00000000}, y = {126, 0x1aa1d10f, 0x01fffab5, 0x44fd097e, 0x3483b271, 0x00000000}, m = {159, 0x2f77d067, 0x1f191f5b, 0x1a1a8568, 0x52026c09, 0x4692f6e6, 0x00000000}, res = {159, 0x1b1bfa15, 0x3726bd88, 0x1af57d8e, 0x2de30fb3, 0x3f96e86f}},
}
I31_Test_To_Monty :: struct {
orig: []u32,
x: []u32,
m: []u32,
}
@(rodata)
i31_to_monty_test_vectors := []I31_Test_To_Monty {
{orig = {123, 0x3f3aff0e, 0x459fe053, 0x4bba523d, 0x04b91a78, 0x00000000}, x = {123, 0x2eab2260, 0x4d3d8e5d, 0x2943569d, 0x01d1c5f3, 0x00000000}, m = {123, 0x3f3aff1b, 0x459fe061, 0x4bba524b, 0x04b91a78, 0x00000000}},
{orig = {127, 0x111641a2, 0x4fcfb2b4, 0x1cf1576b, 0x6b088658, 0x00000000}, x = {127, 0x25c08f99, 0x3bdc976a, 0x3a808f23, 0x5be48931, 0x00000000}, m = {127, 0x111641af, 0x4fcfb2c1, 0x1cf15779, 0x6b088658, 0x00000000}},
{orig = {126, 0x03accd4a, 0x34a7e2d7, 0x749af3f3, 0x25febe05, 0x00000000}, x = {126, 0x763fca85, 0x61a5498c, 0x28eb0856, 0x1dab3aee, 0x00000000}, m = {126, 0x03accd57, 0x34a7e2e5, 0x749af401, 0x25febe05, 0x00000000}},
{orig = {127, 0x6d8c4617, 0x01cffa24, 0x3475f32e, 0x7c9ba743, 0x00000000}, x = {127, 0x7304a0f6, 0x653ea916, 0x6c412db9, 0x427e57a8, 0x00000000}, m = {127, 0x6d8c4625, 0x01cffa31, 0x3475f33b, 0x7c9ba743, 0x00000000}},
{orig = {127, 0x2da2a286, 0x152f4795, 0x3f383173, 0x6622cb58, 0x00000000}, x = {127, 0x1e118541, 0x63461082, 0x57a7e060, 0x3529988e, 0x00000000}, m = {127, 0x2da2a293, 0x152f47a3, 0x3f383181, 0x6622cb58, 0x00000000}},
{orig = {127, 0x2a17a287, 0x52b707dd, 0x30d2b30f, 0x6334fa45, 0x00000000}, x = {127, 0x06823214, 0x2c2ddf09, 0x286443db, 0x231af948, 0x00000000}, m = {127, 0x2a17a295, 0x52b707eb, 0x30d2b31d, 0x6334fa45, 0x00000000}},
{orig = {127, 0x41152b43, 0x0a898780, 0x5a517da6, 0x72eb605e, 0x00000000}, x = {127, 0x2761082a, 0x1e1ff23b, 0x3f2516a3, 0x0b165b25, 0x00000000}, m = {127, 0x41152b51, 0x0a89878d, 0x5a517db3, 0x72eb605e, 0x00000000}},
{orig = {127, 0x6b29c7ff, 0x6297d9f9, 0x3225bebb, 0x71bc4932, 0x00000000}, x = {127, 0x4bb7368d, 0x12320331, 0x5cc9ce0e, 0x1eb2ad59, 0x00000000}, m = {127, 0x6b29c80d, 0x6297da07, 0x3225bec9, 0x71bc4932, 0x00000000}},
{orig = {127, 0x6cb514c0, 0x3ff230d8, 0x6a6915f5, 0x73655603, 0x00000000}, x = {127, 0x1ebae285, 0x28f81b38, 0x37f80ed8, 0x21dc723e, 0x00000000}, m = {127, 0x6cb514cd, 0x3ff230e5, 0x6a691603, 0x73655603, 0x00000000}},
{orig = {127, 0x632339fa, 0x30508879, 0x038d4f85, 0x4bff3ced, 0x00000000}, x = {127, 0x3a3502a9, 0x4364df08, 0x3c8b1fdc, 0x20b2fa04, 0x00000000}, m = {127, 0x63233a07, 0x30508887, 0x038d4f93, 0x4bff3ced, 0x00000000}},
}
I31_Test_Mod_Pow :: struct {
orig: []u32,
x: []u32,
e: []u8,
m: []u32,
}
@(rodata)
i31_mod_pow_test_vectors := []I31_Test_Mod_Pow {
{orig = {123, 0x2eab2260, 0x4d3d8e5d, 0x2943569d, 0x01d1c5f3, 0x00000000}, x = {123, 0x030692d4, 0x14836f1f, 0x7c343636, 0x014fdd77, 0x00000000}, e = {81, 188, 252, 55, 213, 144, 254, 197, 116, 215, 162}, m = {123, 0x3f3aff1b, 0x459fe061, 0x4bba524b, 0x04b91a78, 0x00000000}},
{orig = {127, 0x25c08f99, 0x3bdc976a, 0x3a808f23, 0x5be48931, 0x00000000}, x = {127, 0x3cd4569e, 0x6474f743, 0x23fc7461, 0x05e72909, 0x00000000}, e = {145, 145, 164, 213, 254, 94, 63, 192, 72, 205, 16}, m = {127, 0x111641af, 0x4fcfb2c1, 0x1cf15779, 0x6b088658, 0x00000000}},
{orig = {126, 0x763fca85, 0x61a5498c, 0x28eb0856, 0x1dab3aee, 0x00000000}, x = {126, 0x0eb01d51, 0x178d8313, 0x044cbdfa, 0x022d38b4, 0x00000000}, e = {254, 224, 156, 88, 224, 176, 213, 125, 112, 43, 72}, m = {126, 0x03accd57, 0x34a7e2e5, 0x749af401, 0x25febe05, 0x00000000}},
{orig = {127, 0x7304a0f6, 0x653ea916, 0x6c412db9, 0x427e57a8, 0x00000000}, x = {127, 0x3b1cad0e, 0x15e996b3, 0x6ccc145e, 0x63b7aa04, 0x00000000}, e = {250, 142, 147, 53, 107, 204, 201, 168, 122, 147, 143}, m = {127, 0x6d8c4625, 0x01cffa31, 0x3475f33b, 0x7c9ba743, 0x00000000}},
{orig = {127, 0x1e118541, 0x63461082, 0x57a7e060, 0x3529988e, 0x00000000}, x = {127, 0x3ed716f7, 0x09f828bb, 0x00840e9d, 0x19c59712, 0x00000000}, e = {244, 245, 26, 174, 174, 246, 9, 106, 176, 161, 120}, m = {127, 0x2da2a293, 0x152f47a3, 0x3f383181, 0x6622cb58, 0x00000000}},
{orig = {127, 0x06823214, 0x2c2ddf09, 0x286443db, 0x231af948, 0x00000000}, x = {127, 0x79062be3, 0x5787887e, 0x59a8999f, 0x3f1c71f1, 0x00000000}, e = {212, 118, 203, 181, 125, 193, 238, 33, 133, 239, 12}, m = {127, 0x2a17a295, 0x52b707eb, 0x30d2b31d, 0x6334fa45, 0x00000000}},
{orig = {127, 0x2761082a, 0x1e1ff23b, 0x3f2516a3, 0x0b165b25, 0x00000000}, x = {127, 0x7a632df1, 0x1775c47f, 0x30b8030b, 0x562da059, 0x00000000}, e = {139, 161, 176, 68, 175, 27, 137, 101, 59, 37, 210}, m = {127, 0x41152b51, 0x0a89878d, 0x5a517db3, 0x72eb605e, 0x00000000}},
{orig = {127, 0x4bb7368d, 0x12320331, 0x5cc9ce0e, 0x1eb2ad59, 0x00000000}, x = {127, 0x00db078c, 0x078b7daf, 0x2c19dc27, 0x5e45284f, 0x00000000}, e = {154, 81, 208, 248, 214, 247, 12, 230, 127, 200, 184}, m = {127, 0x6b29c80d, 0x6297da07, 0x3225bec9, 0x71bc4932, 0x00000000}},
{orig = {127, 0x1ebae285, 0x28f81b38, 0x37f80ed8, 0x21dc723e, 0x00000000}, x = {127, 0x1f004d80, 0x15576afe, 0x7dccb09d, 0x31928c9a, 0x00000000}, e = {164, 131, 3, 178, 189, 171, 249, 186, 241, 118, 179}, m = {127, 0x6cb514cd, 0x3ff230e5, 0x6a691603, 0x73655603, 0x00000000}},
{orig = {127, 0x3a3502a9, 0x4364df08, 0x3c8b1fdc, 0x20b2fa04, 0x00000000}, x = {127, 0x13718bdd, 0x3ea119db, 0x1d2488e5, 0x02324bed, 0x00000000}, e = {107, 45, 140, 104, 190, 245, 72, 158, 102, 160, 168}, m = {127, 0x63233a07, 0x30508887, 0x038d4f93, 0x4bff3ced, 0x00000000}},
}
I31_Test_Mul_Acc :: struct {
res: []u32,
d: []u32,
a: []u32,
b: []u32,
}
@(rodata)
i31_mul_acc_test_vectors := []I31_Test_Mul_Acc {
{res = {317, 0x535a1b12, 0x44ccd6ec, 0x1fa7b212, 0x584b46e2, 0x222bf023, 0x4580794b, 0x67411ea6, 0x47b4ac1d, 0x229d50a9, 0x1818f1a8, 0x00000000}, d = {157, 0x79ef7e23, 0x4387243c, 0x0f107f29, 0x3567d945, 0x24e08c78, 0x00000000}, a = {157, 0x06deb1d1, 0x7bba988f, 0x46c80867, 0x490dafbc, 0x1f998ea4, 0x00000000}, b = {159, 0x1c1f52bf, 0x56cb5e69, 0x4f743315, 0x56bd7776, 0x619c427b, 0x00000000}},
{res = {317, 0x3ef33eed, 0x50471631, 0x244d5b99, 0x3aace6d9, 0x3152bb58, 0x13b7c355, 0x0e321425, 0x567217ce, 0x31b3e5b9, 0x0d74845e, 0x00000000}, d = {157, 0x736962be, 0x7712398c, 0x7b098d1f, 0x43804b77, 0x3bd3f8bf, 0x00000000}, a = {157, 0x12a5d163, 0x4ae379f1, 0x1387fd7f, 0x1dec4842, 0x195d1d4c, 0x00000000}, b = {159, 0x6023c9c5, 0x3740c9c3, 0x612c755b, 0x6f3d0941, 0x43e704ff, 0x00000000}},
{res = {318, 0x5c85ea39, 0x54f32864, 0x507dd4d4, 0x6250b7ce, 0x1334f373, 0x445b332d, 0x14557fc2, 0x7e1738be, 0x21bab745, 0x2f0aa313, 0x00000000}, d = {158, 0x77f935c9, 0x1e13e902, 0x43c028fd, 0x4937d854, 0x7e23bf79, 0x00000000}, a = {158, 0x1d618801, 0x79681bf4, 0x7f92559f, 0x2f1cccaf, 0x3faefa11, 0x00000000}, b = {159, 0x32413470, 0x5aff5bf1, 0x05f98105, 0x53055486, 0x5e8cf955, 0x00000000}},
{res = {319, 0x3e013eac, 0x07fe4a8a, 0x65782090, 0x5b880ea5, 0x7408456c, 0x5bf575a0, 0x2a3cd440, 0x23d148fa, 0x418bc6d7, 0x31cc34a1, 0x00000000}, d = {159, 0x102ef9f4, 0x1bc81608, 0x02f901f1, 0x668b0bd6, 0x283c9d2b, 0x00000000}, a = {159, 0x7da4773e, 0x360df7cb, 0x2b3be4f2, 0x79faa607, 0x5e09cc20, 0x00000000}, b = {159, 0x57ef4024, 0x32c96c31, 0x4559d5d3, 0x3b4d08ae, 0x43c831a5, 0x00000000}},
{res = {317, 0x2588c09a, 0x7a89223b, 0x41126c82, 0x321cdef0, 0x3faf4f56, 0x54b95fd5, 0x44abcdbd, 0x4c9acfbb, 0x5d732ab6, 0x1e44b843, 0x00000000}, d = {158, 0x524131a8, 0x6388c83c, 0x4a50e580, 0x0df9f5b8, 0x4fb833b3, 0x00000000}, a = {158, 0x523a18b9, 0x465a2251, 0x2dd0ebec, 0x7a14c3c3, 0x3caa78eb, 0x00000000}, b = {158, 0x30ec8982, 0x37707644, 0x7f244b46, 0x6f958529, 0x3fdd26db, 0x00000000}},
{res = {318, 0x61bad587, 0x761aa039, 0x6ab9a7d4, 0x41f18ba1, 0x0f77af87, 0x557292c8, 0x3428f9a7, 0x7c028945, 0x405a01c3, 0x1f3c11e1, 0x00000000}, d = {159, 0x62a6cc33, 0x07c52956, 0x4b78094d, 0x0210300b, 0x59a0cc24, 0x00000000}, a = {159, 0x6538ecfc, 0x666ac7d2, 0x62ada152, 0x1b6f0537, 0x40b88005, 0x00000000}, b = {158, 0x4f69016b, 0x2b90ac7e, 0x547c3f0e, 0x2f1de4c9, 0x3dc60ec7, 0x00000000}},
{res = {318, 0x7c1d72ed, 0x4c4d8e1f, 0x461bb74f, 0x38b75f85, 0x0c340951, 0x37882272, 0x5bb93d3e, 0x5f8ed31e, 0x7bd1add0, 0x258ae081, 0x00000000}, d = {158, 0x007d10b7, 0x7e03550f, 0x28189fea, 0x3407d2f0, 0x44f16726, 0x00000000}, a = {158, 0x152afd5f, 0x71ab1c22, 0x6fb41945, 0x522bd719, 0x3025871d, 0x00000000}, b = {159, 0x7272538a, 0x795fd146, 0x7111af44, 0x2b400ac9, 0x63cef8dc, 0x00000000}},
{res = {317, 0x6ac9a927, 0x37bf839e, 0x72166cf4, 0x66e97192, 0x0f1010a8, 0x475dd251, 0x730eb0ff, 0x295b1515, 0x340bb235, 0x14da57c9, 0x00000000}, d = {157, 0x026ce5ab, 0x49a7c219, 0x7d86a513, 0x47701297, 0x56e7b169, 0x00000000}, a = {157, 0x6f6a9a0e, 0x7e018fa4, 0x23b35254, 0x2566e0a9, 0x158799e8, 0x00000000}, b = {159, 0x12e6bed2, 0x3b186bb1, 0x3bddcfc9, 0x75ca6013, 0x7bf9ee6e, 0x00000000}},
{res = {317, 0x277fe2cd, 0x00e40ce6, 0x2c35b175, 0x59bd9a99, 0x2a53689e, 0x3972c269, 0x4db20926, 0x67d98c0d, 0x57bbbc3f, 0x0bf1a543, 0x00000000}, d = {159, 0x17d3efaf, 0x59a9c4ff, 0x69349a09, 0x535b0031, 0x677b2a0d, 0x00000000}, a = {159, 0x67e1ef0e, 0x7d1e406e, 0x5111e847, 0x321572bc, 0x547d3b3a, 0x00000000}, b = {157, 0x1c685fb9, 0x2f3d09a2, 0x6611e3c9, 0x25575675, 0x12184aec, 0x00000000}},
{res = {317, 0x11c8df23, 0x0d48a117, 0x0a6b8c50, 0x0fef02e9, 0x3eb1ae96, 0x32cd060b, 0x0cdb0651, 0x2dc87323, 0x16ea6bb3, 0x09cf6776, 0x00000000}, d = {159, 0x53b8638b, 0x4e180172, 0x3a3cdcac, 0x614f28ca, 0x0f644498, 0x00000000}, a = {159, 0x72ac2c95, 0x196ed06f, 0x43412b53, 0x59441827, 0x4364974f, 0x00000000}, b = {157, 0x0e220f38, 0x02785026, 0x6566e3fe, 0x1a270c17, 0x12a1eeaa, 0x00000000}},
}
I31_Test_Div_Rem :: struct {
hi: u32,
lo: u32,
div: u32,
quo: u32,
rem: u32,
}
@(rodata)
i31_div_rem_test_vectors := []I31_Test_Div_Rem {
{hi = 0x632c115c, lo = 0x4b2bf821, div = 0xb8481290, quo = 0x89c490d1, rem = 0x07a3d091},
{hi = 0x1cb7e3aa, lo = 0x63e1d659, div = 0xd94a9eb0, quo = 0x21d58fe1, rem = 0x02380da9},
{hi = 0xbe78690c, lo = 0x88cc1bd7, div = 0xbf1211a9, quo = 0xff32200f, rem = 0x4a85f2f0},
{hi = 0xa91488a1, lo = 0x2f8f64ac, div = 0xb5027f82, quo = 0xef20d04a, rem = 0x86fce918},
{hi = 0x074838b4, lo = 0xc1b7bbbe, div = 0x0cc1ef1e, quo = 0x92207a42, rem = 0x0c03ca02},
{hi = 0x415c651d, lo = 0xe696b3e5, div = 0x5a782289, quo = 0xb8f37d3e, rem = 0x149671b7},
{hi = 0x26454389, lo = 0x986fc51a, div = 0xebc02a27, quo = 0x298ec804, rem = 0x27dea47e},
{hi = 0x5a3b3e87, lo = 0xf475705b, div = 0x5e6f1d1a, quo = 0xf49b708c, rem = 0x4c382623},
{hi = 0x8b1580f2, lo = 0x4ca1db17, div = 0xafd69ac8, quo = 0xca7d730d, rem = 0x938c26ef},
{hi = 0x0dd6f07e, lo = 0xde23b70c, div = 0x5bd60fda, quo = 0x26943342, rem = 0x05c332d8},
{hi = 0xede68f1d, lo = 0x07813b19, div = 0xff2b167e, quo = 0xeead1023, rem = 0x1c0f47df},
{hi = 0x5c5ec31f, lo = 0xd985218f, div = 0xae020a42, quo = 0x87e50b9b, rem = 0x6cce1599},
{hi = 0x6639e3c0, lo = 0xe8690a35, div = 0x6c241512, quo = 0xf1ff7b0a, rem = 0x69f29181},
{hi = 0x0a45fb42, lo = 0x113dd8cc, div = 0x2ee12611, quo = 0x3819d4aa, rem = 0x0c8b7d82},
{hi = 0x3ea35d11, lo = 0x5e2a590f, div = 0xd465b470, quo = 0x4b7f3d2b, rem = 0x21865a3f},
{hi = 0xcb7cee11, lo = 0x25cea707, div = 0xe191debc, quo = 0xe6f0751f, rem = 0x7218c243},
{hi = 0x816b74f4, lo = 0xb66d7312, div = 0x83e048ef, quo = 0xfb3b4f1d, rem = 0x6b6e6eff},
{hi = 0x4863d8a6, lo = 0x7de42a8b, div = 0x588fc2d3, quo = 0xd140fa7c, rem = 0x3c3fbe57},
{hi = 0x2540246a, lo = 0xe24d9d05, div = 0x8320712a, quo = 0x48b98123, rem = 0x047dfa47},
{hi = 0xc074131e, lo = 0xdc4d465e, div = 0xe1dc958c, quo = 0xda22448b, rem = 0x8d36e35a},
{hi = 0xf6ef68bc, lo = 0xc22f3f3e, div = 0xfa5c4162, quo = 0xfc7f66e0, rem = 0x07cafd7e},
{hi = 0x061d173b, lo = 0xeccf58ec, div = 0xb792ff7a, quo = 0x08869175, rem = 0x3a107c2a},
{hi = 0xb2ee2b9d, lo = 0x7805c28d, div = 0xf610e48b, quo = 0xba276d3a, rem = 0xb7b5cc0f},
{hi = 0x38849ac0, lo = 0x67df2bd4, div = 0xa314f95e, quo = 0x58b84824, rem = 0x0739aa9c},
{hi = 0x32dc45d0, lo = 0x2c7f42e9, div = 0xfafc8a2a, quo = 0x33e05b19, rem = 0xa1f8d6cf},
{hi = 0x4240d91f, lo = 0xec3b2dd8, div = 0x7a1bc3fe, quo = 0x8ae65d59, rem = 0x602cc48a},
{hi = 0x3c0836b4, lo = 0x275cd2a6, div = 0x871c1085, quo = 0x71bf0a5e, rem = 0x6a2e8fd0},
{hi = 0x49de09c7, lo = 0xf62ac65b, div = 0x9b7ae8a1, quo = 0x799f9edf, rem = 0x1587c41c},
{hi = 0x3da17dbc, lo = 0xe4437bf3, div = 0xf64f9bca, quo = 0x400e1fa2, rem = 0x5cf9701f},
{hi = 0x4e22607b, lo = 0xa8798c83, div = 0xfe90aa77, quo = 0x4e931fa6, rem = 0xedb19a59},
{hi = 0x43dd8e64, lo = 0xd2aee2d3, div = 0xfa05bf14, quo = 0x457ceca2, rem = 0x5d35882b},
{hi = 0x74ae120e, lo = 0x4a567457, div = 0x80e12b3b, quo = 0xe7c46e8b, rem = 0x3954a14e},
{hi = 0x25c33e85, lo = 0xd4fe503d, div = 0xab5e1c7a, quo = 0x38698d75, rem = 0x4f421a7b},
{hi = 0xba818477, lo = 0xf1edb77a, div = 0xf5028225, quo = 0xc2df32d7, rem = 0x472c3067},
{hi = 0xce85c4d5, lo = 0xc1530b0c, div = 0xe5a6d9ab, quo = 0xe63795d9, rem = 0x94770219},
{hi = 0x08adb5bb, lo = 0xa277cb09, div = 0xab1ee4c6, quo = 0x0cfbb916, rem = 0x045b0c05},
{hi = 0x8dc1580e, lo = 0xe73d2e39, div = 0xadf26902, quo = 0xd09f833f, rem = 0x349b50bb},
{hi = 0x4435ddf6, lo = 0x42a05702, div = 0x594f5cd1, quo = 0xc3850b8d, rem = 0x3d583ce5},
{hi = 0x6cb4f9c6, lo = 0xcc9b6e48, div = 0xa7bbed72, quo = 0xa5e948ca, rem = 0x00c80254},
{hi = 0x3cb3b260, lo = 0xf2f7a398, div = 0xecf16edf, quo = 0x419586bc, rem = 0x6ad67dd4},
{hi = 0x1720b73c, lo = 0x347ff447, div = 0x1724839d, quo = 0xffd5fbb6, rem = 0x0ede73a9},
{hi = 0xe3003f2c, lo = 0xe1cb1bdf, div = 0xf2298637, quo = 0xeff8ef99, rem = 0x04648c00},
{hi = 0x558a5621, lo = 0x1f717ddc, div = 0xf03e2bca, quo = 0x5b269cff, rem = 0xa0d8c7a6},
{hi = 0x949fb36a, lo = 0xce76c920, div = 0xd215ba56, quo = 0xb51b3884, rem = 0x456de4c8},
{hi = 0xa0f2fc11, lo = 0xd0259ff1, div = 0xc8626cac, quo = 0xcd9ea15a, rem = 0xa90b3f79},
{hi = 0x70c3fa84, lo = 0x74d29bb9, div = 0xf9e5a764, quo = 0x7384fbe9, rem = 0x9c1e35b5},
{hi = 0x2da900de, lo = 0xcf74abff, div = 0x99abc1da, quo = 0x4c10ae3a, rem = 0x6b28949b},
{hi = 0x3664397e, lo = 0x2ba486dc, div = 0x7ea469c9, quo = 0x6df304d3, rem = 0x39af3231},
{hi = 0x095d67a6, lo = 0xe3b31742, div = 0xdbe7549c, quo = 0x0ae6ee14, rem = 0x88cf7312},
{hi = 0x9a402804, lo = 0x462e5dcd, div = 0xa736e128, quo = 0xec272359, rem = 0x76399ee5},
{hi = 0x12809274, lo = 0x09011d2f, div = 0xac4c1332, quo = 0x1b7d9f66, rem = 0x7d5b6943},
{hi = 0x6a897aed, lo = 0x1d121571, div = 0xef42fe4b, quo = 0x71fd7b8c, rem = 0xa921fb6d},
{hi = 0x55ed8b10, lo = 0x9090ea62, div = 0x9cd2c065, quo = 0x8c45083c, rem = 0x21efaab6},
{hi = 0x3930a3f2, lo = 0x541cf31f, div = 0xa7c1fef1, quo = 0x5745c1d4, rem = 0x884d228b},
{hi = 0xa7167582, lo = 0x0abb9b9f, div = 0xf63dc17e, quo = 0xadb5a66e, rem = 0x3ca4c37b},
{hi = 0x74b72666, lo = 0x4abb1c94, div = 0x7ee27c4a, quo = 0xeb7b903b, rem = 0x0110d786},
{hi = 0x88a89e33, lo = 0x2dada2d6, div = 0xe8725e91, quo = 0x9681856f, rem = 0x5be44cf7},
{hi = 0x5affad0b, lo = 0x68c7b744, div = 0x8bf402e7, quo = 0xa6740fb8, rem = 0x6a8e183c},
{hi = 0x18400a64, lo = 0x91c19fee, div = 0xececa37b, quo = 0x1a33df48, rem = 0xbd4a8056},
{hi = 0x0ca1d509, lo = 0x5ace5b38, div = 0x405fe35b, quo = 0x323c1088, rem = 0x1a53e2e0},
{hi = 0x49c3d579, lo = 0xb57e3052, div = 0x8fe8c087, quo = 0x833871ec, rem = 0x1b691cde},
{hi = 0x055e153e, lo = 0x876ba83b, div = 0xd0a7b0e1, quo = 0x0695deae, rem = 0x1119514d},
{hi = 0x1ab22cf1, lo = 0xc9c28af3, div = 0x626e63e5, quo = 0x456e54c0, rem = 0x18ca7b33},
{hi = 0x5ce6ba97, lo = 0x46dbc829, div = 0xbf4a582f, quo = 0x7c53ef3f, rem = 0x6fff3398},
{hi = 0x280dd6e5, lo = 0x13c19c79, div = 0xe67fd7e2, quo = 0x2c7c3e0b, rem = 0xcc8299c3},
{hi = 0x1c7c2a51, lo = 0x8aecb540, div = 0x3decec9d, quo = 0x75c1d104, rem = 0x02afd5cc},
{hi = 0x3a6e806b, lo = 0x142a6ad0, div = 0x56b24a8d, quo = 0xac89eeee, rem = 0x17a505ba},
{hi = 0x23a1b1d0, lo = 0xda933d16, div = 0x85938fe1, quo = 0x4449c850, rem = 0x08e57ec6},
{hi = 0x1f177f2e, lo = 0x107c53c1, div = 0xdef2389a, quo = 0x23b390c4, rem = 0x7d845dd9},
{hi = 0x735cb354, lo = 0xbcf96584, div = 0xacc0d360, quo = 0xaaf3fec8, rem = 0x107b0284},
{hi = 0x5ecb810e, lo = 0x51b4a6d1, div = 0x84db55d8, quo = 0xb6a8bd00, rem = 0x7d942ed1},
{hi = 0x4765b107, lo = 0xded18aa4, div = 0x540b7c9f, quo = 0xd979b39b, rem = 0x4592e95f},
{hi = 0xddb19fc7, lo = 0xe30727aa, div = 0xfaf579d9, quo = 0xe225a80a, rem = 0xcf1cfd30},
{hi = 0x85f9707c, lo = 0x5511a037, div = 0x8e318efd, quo = 0xf133d2a5, rem = 0x5e6ded26},
{hi = 0x0c2c41f1, lo = 0xbf357e4a, div = 0x62d254f1, quo = 0x1f88bf7d, rem = 0x421a359d},
{hi = 0x335d8188, lo = 0x00fd4b21, div = 0xfcf1e085, quo = 0x33fc54b7, rem = 0x507e280e},
{hi = 0x4eef80e4, lo = 0xfcd9e9de, div = 0xf2d2e5c0, quo = 0x53380265, rem = 0x07d9c51e},
{hi = 0x431aeca5, lo = 0x07963290, div = 0xe6ca948b, quo = 0x4a6f5427, rem = 0xcfb6f563},
{hi = 0x13638612, lo = 0x71f2880c, div = 0x25f44434, quo = 0x82c6db4c, rem = 0x0eddcc9c},
{hi = 0x24dd96bb, lo = 0xd6b8ffca, div = 0xb3697b2b, quo = 0x349a55fc, rem = 0x61207a76},
{hi = 0x3201b855, lo = 0x0e6b7026, div = 0x65a232ae, quo = 0x7df5a4a0, rem = 0x2a0e4b66},
{hi = 0x1e2536d5, lo = 0xfbef9964, div = 0x97c9027e, quo = 0x32d7cea9, rem = 0x54699036},
{hi = 0x03486ba3, lo = 0x4ae30d34, div = 0x5f768217, quo = 0x08cdbad7, rem = 0x1f6c15e3},
{hi = 0x7000b42d, lo = 0xd9357d65, div = 0xc0b7f01f, quo = 0x94c7bd20, rem = 0x61d79685},
{hi = 0x2f088bd1, lo = 0x098fe330, div = 0x3a2fc060, quo = 0xceee1d21, rem = 0x075d36d0},
{hi = 0x30ede94b, lo = 0x1200278e, div = 0x42fc5880, quo = 0xbafe65ba, rem = 0x08bd5a8e},
{hi = 0x5ed1fc0f, lo = 0x0537fbbf, div = 0x6ae246f5, quo = 0xe31b295f, rem = 0x630b69d4},
{hi = 0x1d2496a2, lo = 0xecca6436, div = 0xaa60d4ea, quo = 0x2bc9d3a0, rem = 0x270e73f6},
{hi = 0x97ae9a4e, lo = 0xba69862d, div = 0xea9984e9, quo = 0xa584c095, rem = 0x563c6a90},
{hi = 0x12cf1435, lo = 0x99a99414, div = 0x2644910d, quo = 0x7dd36379, rem = 0x1871fdef},
{hi = 0x7fc1828d, lo = 0x70c995ca, div = 0xdebea8f1, quo = 0x92d45c4d, rem = 0x9326294d},
{hi = 0x32a003d1, lo = 0x6c09aff4, div = 0xd460becc, quo = 0x3d05fb48, rem = 0x7fc60294},
{hi = 0x3add17a2, lo = 0xb4e2c499, div = 0x45b4eb3a, quo = 0xd82db19b, rem = 0x20833e7b},
{hi = 0xc4a6d715, lo = 0xb1576cc0, div = 0xcf15af86, quo = 0xf31a46cc, rem = 0xb874e9f8},
{hi = 0x2da93388, lo = 0x980b3f98, div = 0xf8e18784, quo = 0x2ef78f93, rem = 0x3b7bb2cc},
{hi = 0x5d77f50a, lo = 0xc8f5c09b, div = 0xaa3cc6c4, quo = 0x8c8e703a, rem = 0x2f82f833},
{hi = 0x4898d91b, lo = 0xe7956338, div = 0x4e4a2805, quo = 0xed62bc94, rem = 0x3c689454},
{hi = 0xb9620ae8, lo = 0x0e9ed51d, div = 0xc281fe4d, quo = 0xf3fd8e35, rem = 0x458d792c},
{hi = 0xc342d71a, lo = 0xecafbf43, div = 0xfbfa840c, quo = 0xc6609971, rem = 0x2db049f7},
{hi = 0x4e2d4c4b, lo = 0x4609de2e, div = 0xc737fab5, quo = 0x64757da5, rem = 0x40d1e685},
}
+11
View File
@@ -1,6 +1,7 @@
package test_crypto_common
import "core:bytes"
import "core:encoding/base64"
import "core:encoding/hex"
// Common helpers for cryptography tests.
@@ -23,3 +24,13 @@ hexbytes_decode :: proc(x: Hex_Bytes, allocator := context.allocator) -> []byte
return dst
}
Jwk_Bytes :: string
jwkbytes_decode :: proc(s: Jwk_Bytes, allocator := context.allocator) -> []byte {
dst, err := base64.decode(s, base64.DEC_URL_TABLE, allocator = allocator)
if err != nil {
panic("Jwk_Bytes: invalid hex encoding")
}
return dst
}
+419
View File
@@ -0,0 +1,419 @@
#+build !riscv64
package test_core_crypto
import "core:bytes"
import "core:encoding/hex"
import "core:log"
import "core:testing"
import "core:crypto"
import "core:crypto/rsa"
// BUG/yawning: RISC-V fails the PSS test in CI with a nonsensical
// bounds-checking error suggesting something spooky, as the failure
// was not reproducible on qemu whole system emulation.
@(private="file")
TEST_MSG: string : "don't let them immanentize the eschaton"
@(private="file", rodata)
TEST_MSG_SHA256 := []byte{
0x50, 0x95, 0x66, 0x8e, 0x7c, 0xd0, 0xd5, 0x8e,
0x9d, 0x59, 0xf8, 0x4a, 0x1a, 0x46, 0x5a, 0x8a,
0x2e, 0x69, 0xcc, 0xad, 0x6a, 0xca, 0x8b, 0xb7,
0x55, 0x55, 0x15, 0x71, 0x9b, 0xc7, 0x50, 0x88,
}
@(private="file", rodata)
TEST_TLS_PMS := []byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
}
@(test)
test_rsa_private_key :: proc(t: ^testing.T) {
priv_key: rsa.Private_Key
if !testing.expectf(
t,
rsa.private_key_set_insecure_test(&priv_key),
"rsa: failed to set test key",
) {
return
}
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping generate")
return
}
if !testing.expectf(
t,
rsa.private_key_generate(&priv_key),
"rsa: failed to generate private key",
) {
return
}
log.debugf("n=0x%s", hex.encode(priv_key._pub_key._n.v[:priv_key._pub_key._n.v_len], context.temp_allocator))
log.debugf("e=%d", priv_key._pub_key._e)
log.debugf("d=0x%s", hex.encode(priv_key._d.v[:priv_key._d.v_len], context.temp_allocator))
log.debugf("p=0x%s", hex.encode(priv_key._p.v[:priv_key._p.v_len], context.temp_allocator))
log.debugf("q=0x%s", hex.encode(priv_key._q.v[:priv_key._q.v_len], context.temp_allocator))
log.debugf("dp=0x%s", hex.encode(priv_key._dp.v[:priv_key._dp.v_len], context.temp_allocator))
log.debugf("dq=0x%s", hex.encode(priv_key._dq.v[:priv_key._dq.v_len], context.temp_allocator))
log.debugf("iq=0x%s", hex.encode(priv_key._iq.v[:priv_key._iq.v_len], context.temp_allocator))
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
if !testing.expectf(
t,
rsa.public_key_equal(&priv_key._pub_key, &pub_key),
"rsa: failed clone public key",
) {
return
}
}
@(test)
test_rsa_sign_pkcs1 :: proc(t: ^testing.T) {
test_msg := transmute([]byte)(TEST_MSG)
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
// Generated with Go's crypto code. Signatures are
// deterministic.
expected_sig := []byte{
0x65, 0x0e, 0xa9, 0xd1, 0x66, 0xb3, 0x61, 0x4c,
0x67, 0x8f, 0xd6, 0x9d, 0x2e, 0xc9, 0x24, 0xdd,
0xcc, 0xa1, 0x0b, 0xdc, 0xbb, 0x35, 0x60, 0xc5,
0x03, 0xb1, 0xa7, 0x10, 0x64, 0x53, 0x83, 0x02,
0x7a, 0x8b, 0xc2, 0x83, 0x7f, 0xd6, 0xfc, 0xc3,
0xe1, 0x4d, 0x33, 0x57, 0x90, 0x81, 0x52, 0xea,
0xcd, 0x7c, 0xaa, 0xa5, 0x98, 0x59, 0x90, 0xd1,
0x88, 0x21, 0x87, 0xc2, 0x9d, 0x51, 0x4d, 0x45,
0x18, 0x06, 0xa2, 0xde, 0x7a, 0xc9, 0xc9, 0x1b,
0x3d, 0x27, 0x07, 0xe4, 0xad, 0x46, 0xe8, 0x09,
0xe1, 0xd5, 0xbd, 0x33, 0x1a, 0x9c, 0x7f, 0x3e,
0x1b, 0x22, 0xc0, 0xfa, 0xa1, 0x30, 0xfb, 0xda,
0x3b, 0x09, 0x9f, 0x6f, 0x44, 0x0e, 0xa8, 0x9b,
0x65, 0x36, 0x48, 0x07, 0x95, 0xfc, 0xf7, 0x2b,
0x61, 0xa0, 0x95, 0x36, 0x94, 0x66, 0x01, 0x90,
0x5f, 0xf6, 0x72, 0x05, 0x64, 0x0c, 0x50, 0x8a,
0xce, 0xfc, 0x75, 0xc7, 0x1e, 0x05, 0x62, 0xd9,
0x8e, 0xdf, 0x85, 0x7e, 0x5d, 0x84, 0x8c, 0x0b,
0x18, 0x88, 0x71, 0x4a, 0x94, 0x11, 0x5e, 0x16,
0x53, 0xd0, 0xdc, 0x8e, 0x62, 0x73, 0x9d, 0x94,
0x66, 0x1a, 0xdf, 0xf4, 0x01, 0xaa, 0xa5, 0x33,
0xb3, 0x0c, 0x14, 0x53, 0x5d, 0xe7, 0xdb, 0x66,
0xf0, 0x3e, 0xf0, 0x08, 0x81, 0x77, 0x40, 0x79,
0xa5, 0xc6, 0x48, 0x68, 0xcc, 0xa2, 0xb6, 0xf3,
0x1f, 0xf9, 0xd5, 0xb5, 0x26, 0x04, 0x97, 0xbc,
0x93, 0xa3, 0x19, 0x29, 0x26, 0x86, 0x89, 0xf8,
0x11, 0xe5, 0xdd, 0xbf, 0x35, 0x6b, 0x96, 0x25,
0xa5, 0x78, 0xab, 0x02, 0x69, 0xdf, 0x54, 0x95,
0xc5, 0x6b, 0x95, 0xcc, 0x38, 0x7b, 0x11, 0xfa,
0x8c, 0x98, 0xa9, 0x95, 0x7e, 0x39, 0x7c, 0xf5,
0x2f, 0xea, 0x42, 0x6d, 0xf5, 0xaa, 0xb3, 0x16,
0x10, 0x2f, 0x29, 0xf3, 0x4a, 0xbc, 0x47, 0xfb,
}
// Note: The key generate/set routines use the prehashed
// mode.
sig: [2048 >> 3]byte
ok := rsa.sign_pkcs1(
&priv_key,
.SHA256,
test_msg,
sig[:],
)
if !testing.expectf(t, ok, "rsa/pkcs1: signing failed") {
return
}
if !testing.expectf(
t,
bytes.equal(sig[:], expected_sig),
"rsa/pkcs1: signature mismatch: %x (expected %x)",
sig[:],
expected_sig,
) {
return
}
ok = rsa.verify_pkcs1(
&pub_key,
.SHA256,
test_msg,
expected_sig,
)
if !testing.expectf(t, ok, "rsa/pkcs1: verify failed") {
return
}
}
@(test)
test_rsa_sign_pss :: proc(t: ^testing.T) {
test_msg := transmute([]byte)(TEST_MSG)
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
// Generated with Go's crypto code. Signatures are
// NOT deterministic.
test_sig := []byte{
0x46, 0xd3, 0x7e, 0xb2, 0x2c, 0x82, 0xe0, 0x68,
0x80, 0x58, 0x8c, 0x07, 0x0e, 0xe9, 0x41, 0x3f,
0xde, 0x45, 0x4a, 0xa0, 0x3f, 0xff, 0xa3, 0xa8,
0xc2, 0x76, 0x74, 0xf0, 0xe8, 0x44, 0x07, 0x2d,
0xdb, 0x12, 0xd5, 0x57, 0xcc, 0x28, 0x15, 0xfa,
0xeb, 0xb6, 0x14, 0x76, 0x10, 0x3f, 0xfc, 0xba,
0xb1, 0x6e, 0x7f, 0x65, 0x6c, 0x9b, 0x1a, 0x62,
0x60, 0xc4, 0xfa, 0xb0, 0x03, 0x07, 0x2b, 0x6b,
0x6a, 0x5a, 0x84, 0x10, 0xe4, 0xf5, 0x64, 0xad,
0xfa, 0xd3, 0x5f, 0xc9, 0xf4, 0xac, 0x70, 0xde,
0x54, 0x06, 0x14, 0x59, 0xf7, 0x60, 0x15, 0xc9,
0xa2, 0xe2, 0x54, 0xbc, 0x79, 0xa8, 0x02, 0x72,
0xba, 0x6a, 0x68, 0x08, 0x15, 0x6b, 0xcb, 0xc8,
0x55, 0x83, 0x63, 0x06, 0xe4, 0x28, 0x36, 0x6b,
0xe6, 0x15, 0x2a, 0x21, 0xc9, 0x4f, 0xc0, 0x0e,
0x30, 0x54, 0x3b, 0xf1, 0xa4, 0x79, 0x83, 0xb9,
0x02, 0xd9, 0x2e, 0xa1, 0x31, 0xf4, 0x5b, 0x1b,
0xbe, 0x44, 0x17, 0xd8, 0x42, 0x6d, 0xb3, 0x38,
0x3f, 0x23, 0x82, 0x9e, 0x76, 0x52, 0x0c, 0x5e,
0xa0, 0xcd, 0xd1, 0xcc, 0xe2, 0x5b, 0x71, 0xb5,
0xca, 0x28, 0x33, 0xc3, 0x03, 0x30, 0xc5, 0xa6,
0xdc, 0x6e, 0xfd, 0xd7, 0x34, 0x0a, 0xd2, 0x30,
0x2c, 0x80, 0x2d, 0x31, 0xea, 0xe9, 0x44, 0x2a,
0x7e, 0x1d, 0xde, 0x71, 0xa3, 0x7e, 0xe2, 0x5e,
0x8a, 0x91, 0x61, 0x23, 0x5b, 0x26, 0x6d, 0x3b,
0x44, 0xfc, 0x6b, 0x36, 0x40, 0xb1, 0xdb, 0xe7,
0xf9, 0xe7, 0x8a, 0x12, 0x7c, 0xba, 0xd8, 0x33,
0x1b, 0xac, 0x70, 0x59, 0x24, 0x83, 0x3a, 0x8b,
0x2a, 0x51, 0x1b, 0x97, 0xa3, 0x8e, 0x34, 0xd1,
0xbb, 0xc1, 0x4e, 0x00, 0xab, 0x21, 0x12, 0x53,
0xeb, 0xda, 0x36, 0x77, 0x30, 0xea, 0x82, 0x92,
0xb5, 0xfb, 0x07, 0xb1, 0x34, 0x37, 0x78, 0x69,
}
ok := rsa.verify_pss(
&pub_key,
.SHA256,
32,
test_msg,
test_sig,
)
if !testing.expectf(t, ok, "rsa/pss: verify (pregenerated) failed") {
return
}
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping round trip tests")
return
}
// Just do a simple round-trip test, as the failure
// cases are covered by wycheproof, and de-randomizing
// PSS is an API nightmare.
sig: [2048 >> 3]byte
ok = rsa.sign_pss(
&priv_key,
.SHA256,
32,
TEST_MSG_SHA256,
sig[:],
true,
)
if !testing.expectf(t, ok, "rsa/pss: signing failed") {
return
}
ok = rsa.verify_pss(
&pub_key,
.SHA256,
32,
TEST_MSG_SHA256,
sig[:],
true,
)
if !testing.expectf(t, ok, "rsa/pss: verify failed") {
return
}
}
@(test)
test_rsa_enc_dec_oaep :: proc(t: ^testing.T) {
test_msg := transmute([]byte)(TEST_MSG)
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
pub_key: rsa.Public_Key
rsa.public_key_set_priv(&pub_key, &priv_key)
// Generated with Go's crypto code. Ciphertexts are
// NOT deterministic.
test_ciphertext := []byte{
0x51, 0x7e, 0x55, 0xe8, 0xf8, 0x69, 0x9e, 0x68,
0x8e, 0x2f, 0x38, 0xec, 0x11, 0xfb, 0x5f, 0x1e,
0xf1, 0x9b, 0x2c, 0xea, 0x8a, 0xfb, 0x13, 0x04,
0x2a, 0xbd, 0x4f, 0x69, 0xca, 0x11, 0x31, 0x1b,
0xb0, 0x00, 0x18, 0x69, 0x32, 0x88, 0xe3, 0x07,
0x82, 0xe8, 0x1d, 0x34, 0x09, 0x26, 0xdf, 0x41,
0x1c, 0xc3, 0xf1, 0x39, 0x31, 0x45, 0xb6, 0x67,
0xa5, 0x7c, 0xa4, 0xaf, 0x48, 0x5e, 0x96, 0x26,
0x9b, 0x78, 0x76, 0x4e, 0xd2, 0x6e, 0x53, 0xd0,
0x51, 0xc9, 0x80, 0x71, 0xea, 0x67, 0x8a, 0x44,
0x1e, 0xb0, 0x81, 0x2e, 0xce, 0x43, 0x9a, 0xd9,
0x1c, 0xea, 0x5c, 0x8b, 0x94, 0x3e, 0x1e, 0x5c,
0xb0, 0x17, 0xb9, 0x50, 0x44, 0x22, 0xc9, 0x17,
0xd0, 0x73, 0x54, 0x2b, 0x15, 0x68, 0xe2, 0xcf,
0xbe, 0x0b, 0xef, 0x91, 0x11, 0xfc, 0xa6, 0x78,
0x14, 0xdd, 0x62, 0xf3, 0xba, 0x8c, 0x8d, 0x4b,
0x7f, 0x4b, 0xfa, 0x8b, 0x9c, 0x91, 0x08, 0x9f,
0x39, 0x47, 0x27, 0xba, 0x9a, 0xfd, 0x2a, 0xb8,
0x1e, 0x70, 0xa3, 0x9c, 0xe1, 0x23, 0x21, 0xc5,
0xca, 0x00, 0x2a, 0x9b, 0x23, 0x0f, 0x15, 0xe2,
0x9a, 0x62, 0xc2, 0x20, 0xb6, 0xe8, 0x85, 0x5f,
0x94, 0xba, 0x72, 0x06, 0x55, 0xcf, 0x5a, 0xd6,
0xc6, 0xc0, 0x89, 0xff, 0xd3, 0x72, 0xf9, 0x34,
0x7a, 0x12, 0xfc, 0xe3, 0x74, 0x64, 0x00, 0xfe,
0xa1, 0x35, 0x78, 0x66, 0x56, 0x1a, 0xde, 0x6a,
0x83, 0x6b, 0x20, 0x06, 0xe2, 0x51, 0xae, 0xc7,
0x27, 0x44, 0x5b, 0x21, 0x4f, 0xdf, 0xf6, 0x52,
0x8e, 0x3a, 0x84, 0x07, 0x26, 0xc8, 0xe3, 0x6a,
0x18, 0xd4, 0x49, 0x44, 0xd8, 0x24, 0x08, 0x94,
0xe1, 0x67, 0xde, 0x4a, 0x8e, 0x6a, 0x2a, 0x28,
0x72, 0x0e, 0x68, 0x9c, 0x7f, 0x55, 0x13, 0x54,
0x13, 0x32, 0xdb, 0xe7, 0x31, 0x84, 0x90, 0xaf,
}
buf: [2048 >> 3]byte
derived_msg, ok := rsa.decrypt_oaep(
&priv_key,
.SHA256,
test_ciphertext,
buf[:],
)
if !testing.expectf(t, ok, "rsa/oaep: decryption (pregenerated) failed") {
return
}
if !testing.expectf(
t,
bytes.equal(test_msg, derived_msg),
"rsa/oaep: unexpected plaintext: %x",
derived_msg,
) {
return
}
if !crypto.HAS_RAND_BYTES {
log.info("rand_bytes not supported - skipping")
return
}
// Just do a simple round-trip test, as the failure
// cases are covered by wycheproof, and de-randomizing
// OAEP is an API nightmare.
ok = rsa.encrypt_oaep(
&pub_key,
.SHA512,
test_msg,
buf[:],
)
if !testing.expectf(t, ok, "rsa/oaep: encryption failed") {
return
}
derived_msg, ok = rsa.decrypt_oaep(
&priv_key,
.SHA512,
buf[:],
buf[:],
)
if !testing.expectf(t, ok, "rsa/oaep: decryption failed") {
return
}
if !testing.expectf(
t,
bytes.equal(test_msg, derived_msg),
"rsa/oaep: unexpected plaintext: %x",
derived_msg,
) {
return
}
}
@(test)
test_rsa_dec_pms :: proc(t: ^testing.T) {
priv_key: rsa.Private_Key
_ = rsa.private_key_set_insecure_test(&priv_key)
// Generated with Go's crypto code. Go's PKCS1v15
// encryption output is NOT deterministic.
test_ciphertext := []byte{
0x0c, 0x18, 0x48, 0x23, 0x0b, 0x4a, 0xa9, 0x20,
0xbb, 0xa6, 0x38, 0xbf, 0x25, 0xda, 0x16, 0xd7,
0x92, 0x72, 0x3a, 0x81, 0xbc, 0xee, 0x74, 0x6b,
0xd8, 0x55, 0x26, 0x72, 0x81, 0x70, 0x73, 0x32,
0xd4, 0x30, 0x56, 0xe2, 0xeb, 0x1b, 0x57, 0xf3,
0xf5, 0x7d, 0xea, 0x3e, 0x3c, 0x62, 0x35, 0x78,
0x06, 0x88, 0x27, 0x4d, 0x6e, 0xa0, 0x45, 0x91,
0x62, 0x8c, 0xe9, 0x93, 0xd0, 0xde, 0x85, 0x71,
0x01, 0x24, 0x7a, 0x1a, 0x5d, 0x03, 0x49, 0x92,
0x73, 0x5a, 0x5b, 0x1e, 0x1e, 0x3b, 0xf0, 0xf2,
0xea, 0x04, 0x9a, 0x87, 0x32, 0x20, 0x52, 0xbd,
0x72, 0xc8, 0xa8, 0x38, 0xd1, 0x29, 0x97, 0x87,
0x0b, 0x76, 0xc1, 0x68, 0xe5, 0x05, 0x72, 0xb4,
0x4d, 0xd1, 0x95, 0xb2, 0xa8, 0x19, 0x3f, 0xc3,
0x1e, 0xee, 0x34, 0x19, 0x72, 0xac, 0x1e, 0x4b,
0x01, 0xd7, 0x60, 0xeb, 0x27, 0xf1, 0x12, 0x7c,
0xbc, 0x07, 0x5d, 0xf6, 0xb9, 0xac, 0xf9, 0xdb,
0x1a, 0xaf, 0x47, 0x13, 0x22, 0x16, 0xb5, 0x05,
0x5a, 0x9c, 0x45, 0x69, 0x0a, 0xf1, 0x36, 0x6b,
0xab, 0x96, 0xd7, 0x7f, 0x66, 0xff, 0x16, 0x3c,
0x29, 0xc4, 0x10, 0x03, 0xe1, 0x35, 0xcc, 0xae,
0x71, 0x08, 0x14, 0xff, 0x57, 0x4f, 0x3d, 0x79,
0x7a, 0xa7, 0x19, 0x2e, 0x23, 0x08, 0xad, 0xb2,
0xe5, 0xb1, 0xe8, 0x47, 0x6d, 0xe1, 0x24, 0x4a,
0xd8, 0x1f, 0xe4, 0x52, 0x21, 0x3e, 0xf1, 0xcd,
0x07, 0x72, 0xa4, 0xb8, 0x06, 0x98, 0x3a, 0x17,
0xfd, 0xca, 0x74, 0x93, 0xb1, 0x2b, 0xd8, 0x76,
0x7c, 0x6f, 0x71, 0xfc, 0x16, 0xef, 0x99, 0xa1,
0xf9, 0x13, 0xeb, 0xfc, 0x34, 0x90, 0xb5, 0x00,
0xbf, 0xdc, 0x19, 0x99, 0xb4, 0x12, 0x85, 0x25,
0x3a, 0x49, 0x70, 0x63, 0x2f, 0xfc, 0xbc, 0xca,
0x38, 0x2f, 0x7a, 0x0e, 0x78, 0x8d, 0x7b, 0x87,
}
ok := rsa.unsafe_decrypt_tls_pms(&priv_key, test_ciphertext)
if !testing.expectf(
t,
ok == 1,
"rsa/tls_pms: decryption failed: %x",
test_ciphertext[:48],
) {
return
}
if !testing.expectf(
t,
bytes.equal(test_ciphertext[:48], TEST_TLS_PMS),
"rsa/tls_pms: unexpected plaintext: %x",
test_ciphertext[:48],
) {
return
}
}
+39
View File
@@ -51,6 +51,45 @@ import "core:testing"
// - pbkdf2_hmacsha256_test.json
// - pbkdf2_hmacsha384_test.json
// - pbkdf2_hmacsha512_test.json
// - crypto/rsa
// - rsa_pkcs1_1024_sig_gen_test.json
// - rsa_pkcs1_1536_sig_gen_test.json
// - rsa_pkcs1_2048_sig_gen_test.json
// - rsa_pkcs1_3072_sig_gen_test.json
// - rsa_pkcs1_4096_sig_gen_test.json
// - rsa_pss_2048_sha1_mgf1_20_test.json
// - rsa_pss_2048_sha256_mgf1_0_test.json
// - rsa_pss_2048_sha256_mgf1_32_test.json
// - rsa_pss_2048_sha256_mgf1sha1_20_test.json
// - rsa_pss_2048_sha384_mgf1_48_test.json
// - rsa_pss_2048_sha512_256_mgf1_32_test.json
// - rsa_pss_3072_sha256_mgf1_32_test.json
// - rsa_pss_4096_sha256_mgf1_32_test.json
// - rsa_pss_4096_sha384_mgf1_48_test.json
// - rsa_pss_4096_sha512_mgf1_32_test.json
// - rsa_pss_4096_sha512_mgf1_64_test.json
// - rsa_pss_misc_test.json
// - rsa_oaep_2048_sha1_mgf1sha1_test.json
// - rsa_oaep_2048_sha224_mgf1sha1_test.json
// - rsa_oaep_2048_sha224_mgf1sha224_test.json
// - rsa_oaep_2048_sha256_mgf1sha1_test.json
// - rsa_oaep_2048_sha256_mgf1sha256_test.json
// - rsa_oaep_2048_sha384_mgf1sha1_test.json
// - rsa_oaep_2048_sha384_mgf1sha384_test.json
// - rsa_oaep_2048_sha512_224_mgf1sha1_test.json
// - rsa_oaep_2048_sha512_mgf1sha1_test.json
// - rsa_oaep_2048_sha512_mgf1sha512_test.json
// - rsa_oaep_3072_sha256_mgf1sha1_test.json
// - rsa_oaep_3072_sha256_mgf1sha256_test.json
// - rsa_oaep_3072_sha512_256_mgf1sha1_test.json
// - rsa_oaep_3072_sha512_256_mgf1sha512_256_test.json
// - rsa_oaep_3072_sha512_mgf1sha1_test.json
// - rsa_oaep_3072_sha512_mgf1sha512_test.json
// - rsa_oaep_4096_sha256_mgf1sha1_test.json
// - rsa_oaep_4096_sha256_mgf1sha256_test.json
// - rsa_oaep_4096_sha512_mgf1sha1_test.json
// - rsa_oaep_4096_sha512_mgf1sha512_test.json
// - rsa_oaep_misc_test.json
// - crypto/siphash
// - siphash_1_3_test.json
// - siphash_2_4_test.json
+541
View File
@@ -0,0 +1,541 @@
package test_wycheproof
import "core:encoding/hex"
import "core:fmt"
import "core:log"
import "core:mem"
import "core:os"
import "core:testing"
import "core:crypto/rsa"
import "../common"
@(test)
test_rsa_pkcs1_signature :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("rsa/pkcs1/signatures: starting")
files := []string {
"rsa_pkcs1_1024_sig_gen_test.json",
"rsa_pkcs1_1536_sig_gen_test.json",
"rsa_pkcs1_2048_sig_gen_test.json",
"rsa_pkcs1_3072_sig_gen_test.json",
"rsa_pkcs1_4096_sig_gen_test.json",
}
for f in files {
mem.free_all()
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Rsa_Pkcs1_Sig_Test_Group)
load_ok := load(&test_vectors, fn)
if !testing.expectf(t, load_ok, "Unable to load {}", f) {
continue
}
testing.expectf(t, test_rsa_pkcs1_sig(t, &test_vectors), "RSA PKCS1 signature failed")
}
}
test_rsa_pkcs1_sig :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Rsa_Pkcs1_Sig_Test_Group)) -> bool {
JWK_KTY :: "RSA"
params_str := fmt.aprintf("RSA-%d/PKCS1/Signature", test_vectors.test_groups[0].key_size)
log.debugf("%s: starting", params_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, tg_id in test_vectors.test_groups {
hash_str := test_group.sha
hash_alg, _ := hash_name_to_algorithm(hash_str)
if hash_alg == .Invalid {
log.infof("%s: unsupported hash: %s", params_str, hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
priv_key: rsa.Private_Key
pub_key := &priv_key._pub_key
ok, have_priv: bool
if test_group.private_key_jwk.kty == JWK_KTY {
ok = jwk_to_private_key(&test_group.private_key_jwk, &priv_key)
have_priv = true
} else {
ok = rsa.public_key_set_bytes(
pub_key,
common.hexbytes_decode(test_group.private_key.modulus),
common.hexbytes_decode(test_group.private_key.public_exponent),
)
}
if !testing.expectf(t, ok, "%s/%d: invalid RSA key: %v", params_str, tg_id, test_group.private_key) {
num_ran += len(test_group.tests)
num_failed += len(test_group.tests)
continue
}
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"%s/%s/%d/%d: %s: %+v",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("%s/%s/%d/%d: %+v", params_str, hash_str, tg_id, test_vector.tc_id, test_vector.flags)
}
msg := common.hexbytes_decode(test_vector.msg)
sig := common.hexbytes_decode(test_vector.sig)
verify_ok := rsa.verify_pkcs1(pub_key, hash_alg, msg, sig)
if !testing.expectf(
t,
result_check(test_vector.result, verify_ok, false),
"%s/%s/%d/%d: verify failed: expected %s actual %v",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
test_vector.result,
verify_ok,
) {
num_failed += 1
continue
}
if have_priv && verify_ok {
sign_ok := rsa.sign_pkcs1(&priv_key, hash_alg, msg, sig)
if !testing.expectf(
t,
sign_ok,
"%s/%s/%d/%d: sign failed",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
) {
num_failed += 1
continue
}
if !testing.expectf(
t,
common.hexbytes_compare(test_vector.sig, sig),
"%s/%s/%d/%d: sign failed: expected %s actual %s",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
test_vector.sig,
hex.encode(sig),
) {
num_failed += 1
continue
}
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"%s: ran %d, passed %d, failed %d, skipped %d",
params_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(test)
test_rsa_pss_signature :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("rsa/pss/signatures: starting")
files := []string {
"rsa_pss_2048_sha1_mgf1_20_test.json",
"rsa_pss_2048_sha256_mgf1_0_test.json",
"rsa_pss_2048_sha256_mgf1_32_test.json",
"rsa_pss_2048_sha256_mgf1sha1_20_test.json",
"rsa_pss_2048_sha384_mgf1_48_test.json",
"rsa_pss_2048_sha512_256_mgf1_32_test.json",
"rsa_pss_3072_sha256_mgf1_32_test.json",
"rsa_pss_4096_sha256_mgf1_32_test.json",
"rsa_pss_4096_sha384_mgf1_48_test.json",
"rsa_pss_4096_sha512_mgf1_32_test.json",
"rsa_pss_4096_sha512_mgf1_64_test.json",
"rsa_pss_misc_test.json",
// These tests include the MGF1 parameters in the public key,
// which we do not support with our existing API.
//
// "rsa_pss_2048_sha1_mgf1_20_params_test.json",
// "rsa_pss_2048_sha256_mgf1_0_params_test.json",
// "rsa_pss_2048_sha256_mgf1_32_params_test.json",
// "rsa_pss_2048_sha512_mgf1sha256_32_params_test.json",
// "rsa_pss_3072_sha256_mgf1_32_params_test.json",
// "rsa_pss_4096_sha512_mgf1_32_params_test.json",
// "rsa_pss_4096_sha512_mgf1_64_params_test.json",
// "rsa_pss_misc_params_test.json",
// Unsupported hash algorithm:
// "rsa_pss_2048_sha512_224_mgf1_28_test.json",
}
for f in files {
mem.free_all()
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Rsa_Pss_Sig_Test_Group)
load_ok := load(&test_vectors, fn)
if !testing.expectf(t, load_ok, "Unable to load {}", f) {
continue
}
testing.expectf(t, test_rsa_pss_sig(t, &test_vectors), "RSA PSS signature failed")
}
}
test_rsa_pss_sig :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Rsa_Pss_Sig_Test_Group)) -> bool {
MGF1 :: "MGF1"
params_str := fmt.aprintf("RSA-%d/PSS/Signature", test_vectors.test_groups[0].key_size)
log.debugf("%s: starting", params_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, tg_id in test_vectors.test_groups {
if test_group.mgf != MGF1 {
log.infof("%s: unsupported MGF: %s", params_str, test_group.mgf)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_str := test_group.sha
hash_alg, _ := hash_name_to_algorithm(hash_str)
if hash_alg == .Invalid {
log.infof("%s: unsupported hash: %s", params_str, hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
mgf_hash_str := test_group.mfg_sha
mgf_hash_alg, _ := hash_name_to_algorithm(mgf_hash_str)
if mgf_hash_alg == .Invalid {
log.infof("%s: unsupported MGF hash: %s", params_str, mgf_hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_params_str := fmt.aprintf("%s/%s(%s)", hash_str, test_group.mgf, mgf_hash_str)
pub_key: rsa.Public_Key
ok := rsa.public_key_set_bytes(
&pub_key,
common.hexbytes_decode(test_group.public_key.modulus),
common.hexbytes_decode(test_group.public_key.public_exponent),
)
if !testing.expectf(t, ok, "%s/%d: invalid RSA key: %v", params_str, tg_id, test_group.public_key) {
num_ran += len(test_group.tests)
num_failed += len(test_group.tests)
continue
}
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"%s/%s/%d/%d: %s: %+v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("%s/%s/%d/%d: %+v", params_str, hash_str, tg_id, test_vector.tc_id, test_vector.flags)
}
msg := common.hexbytes_decode(test_vector.msg)
sig := common.hexbytes_decode(test_vector.sig)
verify_ok := rsa.verify_pss(
&pub_key,
hash_alg,
test_group.s_len,
msg,
sig,
false,
mgf_hash_alg,
)
if !testing.expectf(
t,
result_check(test_vector.result, verify_ok, false),
"%s/%s/%d/%d: verify failed: expected %s actual %v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
test_vector.result,
verify_ok,
) {
num_failed += 1
continue
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"%s: ran %d, passed %d, failed %d, skipped %d",
params_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(test)
test_rsa_oaep_decryption :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("rsa/oaep/decryption: starting")
files := []string {
"rsa_oaep_2048_sha1_mgf1sha1_test.json",
"rsa_oaep_2048_sha224_mgf1sha1_test.json",
"rsa_oaep_2048_sha224_mgf1sha224_test.json",
"rsa_oaep_2048_sha256_mgf1sha1_test.json",
"rsa_oaep_2048_sha256_mgf1sha256_test.json",
"rsa_oaep_2048_sha384_mgf1sha1_test.json",
"rsa_oaep_2048_sha384_mgf1sha384_test.json",
"rsa_oaep_2048_sha512_224_mgf1sha1_test.json",
"rsa_oaep_2048_sha512_mgf1sha1_test.json",
"rsa_oaep_2048_sha512_mgf1sha512_test.json",
"rsa_oaep_3072_sha256_mgf1sha1_test.json",
"rsa_oaep_3072_sha256_mgf1sha256_test.json",
"rsa_oaep_3072_sha512_256_mgf1sha1_test.json",
"rsa_oaep_3072_sha512_256_mgf1sha512_256_test.json",
"rsa_oaep_3072_sha512_mgf1sha1_test.json",
"rsa_oaep_3072_sha512_mgf1sha512_test.json",
"rsa_oaep_4096_sha256_mgf1sha1_test.json",
"rsa_oaep_4096_sha256_mgf1sha256_test.json",
"rsa_oaep_4096_sha512_mgf1sha1_test.json",
"rsa_oaep_4096_sha512_mgf1sha512_test.json",
"rsa_oaep_misc_test.json",
// Unsupported hash algorithm:
// "rsa_oaep_2048_sha512_224_mgf1sha512_224_test.json",
}
for f in files {
mem.free_all()
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Rsa_Oaep_Dec_Test_Group)
load_ok := load(&test_vectors, fn)
if !testing.expectf(t, load_ok, "Unable to load {}", f) {
continue
}
testing.expectf(t, test_rsa_oaep_dec(t, &test_vectors), "RSA OAEP decryption failed")
}
}
test_rsa_oaep_dec :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Rsa_Oaep_Dec_Test_Group)) -> bool {
MGF1 :: "MGF1"
params_str := fmt.aprintf("RSA-%d/OAEP/Decryption", test_vectors.test_groups[0].key_size)
log.debugf("%s: starting", params_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, tg_id in test_vectors.test_groups {
if test_group.key_size > rsa.MODULUS_MAX_SIZE {
log.infof("%s: unsupported key size: %d", params_str, test_group.key_size)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
if test_group.mgf != MGF1 {
log.infof("%s: unsupported MGF: %s", params_str, test_group.mgf)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_str := test_group.sha
hash_alg, _ := hash_name_to_algorithm(hash_str)
if hash_alg == .Invalid {
log.infof("%s: unsupported hash: %s", params_str, hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
mgf_hash_str := test_group.mfg_sha
mgf_hash_alg, _ := hash_name_to_algorithm(mgf_hash_str)
if mgf_hash_alg == .Invalid {
log.infof("%s: unsupported MGF hash: %s", params_str, mgf_hash_str)
num_ran += len(test_group.tests)
num_skipped += len(test_group.tests)
continue
}
hash_params_str := fmt.aprintf("%s/%s(%s)", hash_str, test_group.mgf, mgf_hash_str)
priv_key: rsa.Private_Key
ok := json_to_private_key(&test_group.private_key, &priv_key)
if !testing.expectf(t, ok, "%s/%d: invalid RSA key: %v", params_str, tg_id, test_group.private_key) {
num_ran += len(test_group.tests)
num_failed += len(test_group.tests)
continue
}
dst: [rsa.MODULUS_MAX_SIZE >> 3]byte = ---
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"%s/%s/%d/%d: %s: %+v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("%s/%s/%d/%d: %+v", params_str, hash_str, tg_id, test_vector.tc_id, test_vector.flags)
}
label := common.hexbytes_decode(test_vector.label)
ct := common.hexbytes_decode(test_vector.ct)
pt, decrypt_ok := rsa.decrypt_oaep(
&priv_key,
hash_alg,
ct,
dst[:],
label,
mgf_hash_alg,
)
if !testing.expectf(
t,
result_check(test_vector.result, decrypt_ok, false),
"%s/%s/%d/%d: decrypt failed: expected %s actual %v",
params_str,
hash_params_str,
tg_id,
test_vector.tc_id,
test_vector.result,
decrypt_ok,
) {
num_failed += 1
continue
}
if decrypt_ok {
if !testing.expectf(
t,
common.hexbytes_compare(test_vector.msg, pt),
"%s/%s/%d/%d: decrypt failed: expected %s actual %s",
params_str,
hash_str,
tg_id,
test_vector.tc_id,
test_vector.msg,
hex.encode(pt),
) {
num_failed += 1
continue
}
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"%s: ran %d, passed %d, failed %d, skipped %d",
params_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(private="file")
jwk_to_private_key :: proc(jwk: ^Rsa_Jwk_Private_Key, priv_key: ^rsa.Private_Key) -> bool {
return rsa.private_key_set_bytes(
priv_key,
common.jwkbytes_decode(jwk.n),
common.jwkbytes_decode(jwk.e),
common.jwkbytes_decode(jwk.d),
common.jwkbytes_decode(jwk.p),
common.jwkbytes_decode(jwk.q),
common.jwkbytes_decode(jwk.dp),
common.jwkbytes_decode(jwk.dq),
common.jwkbytes_decode(jwk.qi),
)
}
@(private="file")
json_to_private_key :: proc(json: ^Rsa_Private_Key, priv_key: ^rsa.Private_Key) -> bool {
return rsa.private_key_set_bytes(
priv_key,
common.hexbytes_decode(json.modulus),
common.hexbytes_decode(json.public_exponent),
common.hexbytes_decode(json.private_exponent),
common.hexbytes_decode(json.prime1),
common.hexbytes_decode(json.prime2),
common.hexbytes_decode(json.exponent1),
common.hexbytes_decode(json.exponent2),
common.hexbytes_decode(json.coefficient),
)
}
+97 -4
View File
@@ -150,10 +150,10 @@ Eddsa_Key :: struct {
}
Eddsa_Jwk :: struct {
kid: string `json:"kid"`,
crv: string `json:"crv"`,
kty: string `json:"kty"`,
x: string `json:"x"`,
kid: string `json:"kid"`,
crv: string `json:"crv"`,
kty: string `json:"kty"`,
x: common.Jwk_Bytes `json:"x"`,
}
Ecdsa_Key :: struct {
@@ -241,3 +241,96 @@ Mldsa_Test_Vector :: struct {
result: Result `json:"result"`,
flags: []string `json:"flags"`,
}
Rsa_Pkcs1_Sig_Test_Group :: struct {
private_key: Rsa_Private_Key `json:"privateKey"`,
key_asn: common.Hex_Bytes `json:"keyAsn"`,
key_der: common.Hex_Bytes `json:"keyDer"`,
key_pem: string `json:"keyPem"`,
key_size: int `json:"keySize"`,
private_key_jwk: Rsa_Jwk_Private_Key `json:"privateKeyJwk"`,
private_key_pem: string `json:"privateKeyPem"`,
private_key_pkcs8: common.Hex_Bytes `json:"privateKeyPkcs8"`,
sha: string `json:"sha"`,
type: string `json:"type"`,
source: Test_Group_Source `json:"source"`,
tests: []Rsa_Sig_Test_Vector `json:"tests"`,
}
Rsa_Sig_Test_Vector :: struct {
tc_id: int `json:"tcId"`,
comment: string `json:"comment"`,
msg: common.Hex_Bytes `json:"msg"`,
sig: common.Hex_Bytes `json:"sig"`,
result: Result `json:"result"`,
flags: []string `json:"flags"`,
}
Rsa_Pss_Sig_Test_Group :: struct {
public_key: Rsa_Public_Key `json:"publicKey"`,
public_key_asn: common.Hex_Bytes `json:"publicKeyAsn"`,
public_key_der: common.Hex_Bytes `json:"publicKeyDer"`,
public_key_pem: string `json:"publicKeyPem"`,
key_size: int `json:"keySize"`,
sha: string `json:"sha"`,
mgf: string `json:"mgf"`,
mfg_sha: string `json:"mgfSha"`,
s_len: int `json:"sLen"`,
type: string `json:"type"`,
source: Test_Group_Source `json:"source"`,
tests: []Rsa_Sig_Test_Vector `json:"tests"`,
}
Rsa_Oaep_Dec_Test_Group :: struct {
key_size: int `json:"keySize"`,
private_key: Rsa_Private_Key `json:"privateKey"`,
private_key_jwk: Rsa_Jwk_Private_Key `json:"privateKeyJwk"`,
private_key_pem: string `json:"privateKeyPem"`,
private_key_pkcs8: common.Hex_Bytes `json:"privateKeyPkcs8"`,
sha: string `json:"sha"`,
mgf: string `json:"mgf"`,
mfg_sha: string `json:"mgfSha"`,
type: string `json:"type"`,
source: Test_Group_Source `json:"source"`,
tests: []Rsa_Oaep_Test_Vector `json:"tests"`,
}
Rsa_Oaep_Test_Vector :: struct {
tc_id: int `json:"tcId"`,
comment: string `json:"comment"`,
msg: common.Hex_Bytes `json:"msg"`,
ct: common.Hex_Bytes `json:"ct"`,
label: common.Hex_Bytes `json:"label"`,
result: Result `json:"result"`,
flags: []string `json:"flags"`,
}
Rsa_Public_Key :: struct {
modulus: common.Hex_Bytes `json:"modulus"`,
public_exponent: common.Hex_Bytes `json:"publicExponent"`,
}
Rsa_Private_Key :: struct {
modulus: common.Hex_Bytes `json:"modulus"`,
private_exponent: common.Hex_Bytes `json:"privateExponent"`,
public_exponent: common.Hex_Bytes `json:"publicExponent"`,
prime1: common.Hex_Bytes `json:"prime1"`,
prime2: common.Hex_Bytes `json:"prime2"`,
exponent1: common.Hex_Bytes `json:"exponent1"`,
exponent2: common.Hex_Bytes `json:"exponent2"`,
coefficient: common.Hex_Bytes `json:"coefficient"`,
}
Rsa_Jwk_Private_Key :: struct {
akg: string `json:"alg"`,
kid: string `json:"kid"`,
kty: string `json:"kty"`,
d: common.Jwk_Bytes `json:"d"`,
dp: common.Jwk_Bytes `json:"dp"`,
dq: common.Jwk_Bytes `json:"dq"`,
e: common.Jwk_Bytes `json:"e"`,
n: common.Jwk_Bytes `json:"n"`,
p: common.Jwk_Bytes `json:"p"`,
q: common.Jwk_Bytes `json:"q"`,
qi: common.Jwk_Bytes `json:"qi"`,
}
+25 -1
View File
@@ -1,5 +1,6 @@
package test_core_xml
import "core:encoding/entity"
import "core:encoding/xml"
import "core:testing"
import "core:strings"
@@ -217,6 +218,29 @@ run_test :: proc(t: ^testing.T, test: TEST, loc := #caller_location) {
}
}
@(test)
test_normalize_whitespace :: proc(t: ^testing.T) {
s := "A &amp; B"
normalized_entity_decode, _ := entity.decode_xml(s, {.Normalize_Whitespace})
defer delete(normalized_entity_decode)
testing.expect_value(t, normalized_entity_decode, "A & B")
s = `<hellope attr="A &amp; B">A &amp; B</hellope>`
opts := xml.Options{
flags = {.Ignore_Unsupported, .Decode_SGML_Entities},
}
doc, err := xml.parse_bytes(transmute([]byte)s, opts)
defer xml.destroy(doc)
assert(err == .None)
testing.expect_value(t, doc.elements[0].value[0], "A & B")
attr := doc.elements[0].attribs
testing.expect_value(t, attr[0].val, "A & B")
}
@(private)
doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
/*
@@ -298,4 +322,4 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
print(strings.to_writer(&buf), doc)
return strings.clone(strings.to_string(buf))
}
}
+75
View File
@@ -131,3 +131,78 @@ my_func :: proc (cond: bool, a: string, b: string) -> string {
testing.expect(t, ok, "bad parse")
testing.expect(t, file.syntax_error_count == 0, "should contain zero errors")
}
@test
test_parse_multiline_ternary_infix_with_comment :: proc(t: ^testing.T) {
context.allocator = context.temp_allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
file := ast.File{
fullpath = "test.odin",
src = `
package main
my_func :: proc (cond: bool, a: string, b: string) -> string {
out := (
cond
? a // This is a comment!
: b
)
return out
}
`,
}
p := parser.default_parser()
p.err = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.errorf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
p.warn = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.warnf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
ok := parser.parse_file(&p, &file)
testing.expect(t, ok, "bad parse")
testing.expect(t, file.syntax_error_count == 0, "should contain zero errors")
}
@test
test_parse_ternary_if_statements_with_comment :: proc(t: ^testing.T) {
context.allocator = context.temp_allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
file := ast.File{
fullpath = "test.odin",
src = `
package main
my_func :: proc (cond: bool, a: string, b: string) -> string {
out := (
cond
if a // This is a comment!
else b
)
return out
}
`,
}
p := parser.default_parser()
p.err = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.errorf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
p.warn = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.warnf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
ok := parser.parse_file(&p, &file)
testing.expect(t, ok, "bad parse")
testing.expect(t, file.syntax_error_count == 0, "should contain zero errors")
}
+1
View File
@@ -2,6 +2,7 @@
package tests_core
@(require) import "crypto"
@(require) import "crypto/bigint"
@(require) import "hash"
@(require) import "image"
@(require) import "math/big"
+4 -4
View File
@@ -222,7 +222,7 @@ utf8_to_wstring_alloc_test :: proc(t : ^testing.T) {
buf : [^]u16
result = win32.utf8_to_wstring_alloc("Hello\x00, World!", allocator)
buf = transmute([^]u16)result
buf = cast([^]u16)result
testing.expect(t, result != nil)
testing.expect_value(t, buf[4], 'o')
testing.expect_value(t, buf[5], 0)
@@ -231,12 +231,12 @@ utf8_to_wstring_alloc_test :: proc(t : ^testing.T) {
testing.expect_value(t, buf[14], 0)
result = win32.utf8_to_wstring_alloc("H\x00\x00", allocator)
buf = transmute([^]u16)result
buf = cast([^]u16)result
testing.expect(t, result != nil)
testing.expect_value(t, buf[1], 0)
result = win32.utf8_to_wstring_alloc("你好,世界!", allocator)
buf = transmute([^]u16)result
buf = cast([^]u16)result
testing.expect(t, result != nil)
testing.expect_value(t, buf[0], 0x4F60)
testing.expect_value(t, buf[1], 0x597D)
@@ -247,7 +247,7 @@ utf8_to_wstring_alloc_test :: proc(t : ^testing.T) {
testing.expect_value(t, buf[6], 0)
result = win32.utf8_to_wstring_alloc("", allocator)
buf = transmute([^]u16)result
buf = cast([^]u16)result
// Valid, and distinguishable from an error.
testing.expect(t, result != nil)
testing.expect_value(t, buf[0], 0)
+1
View File
@@ -37,6 +37,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused
..\..\..\odin test ..\test_pr_6476.odin %COMMON% || exit /b
..\..\..\odin check ..\test_issue_6484.odin -no-entry-point %COMMON% || exit /b
..\..\..\odin check ..\test_issue_6874.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b
..\..\..\odin check ..\test_issue_6979.odin -no-entry-point %COMMON% || exit /b
@echo off
+1
View File
@@ -79,6 +79,7 @@ else
echo "SUCCESSFUL 0/1"
exit 1
fi
$ODIN check ../test_issue_6979.odin -no-entry-point $COMMON
set +x
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff. Show more