Compare commits

...

55 Commits

Author SHA1 Message Date
gingerBill aab122ede8 Remove ? 2024-04-11 15:59:22 +01:00
gingerBill 503964c769 Add @(static) check 2024-04-11 15:57:13 +01:00
gingerBill b2e887be36 Change stack overflow check to >256 KiB 2024-04-11 15:55:01 +01:00
gingerBill f36fb6d1ef Add nil checks 2024-04-11 15:41:01 +01:00
gingerBill 45d7a670ce Fix @(static) error message bug 2024-04-11 15:34:34 +01:00
gingerBill 86e82dc182 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-11 15:24:42 +01:00
gingerBill b6d2ac11b8 Add -vet-unused-variables and `-vet-unused-imports (-vet-unused is both) 2024-04-11 15:24:36 +01:00
gingerBill 6c38ae3658 Remove #optional_ok from docs 2024-04-11 15:24:08 +01:00
Jeroen van Rijn 532d477705 Merge pull request #3121 from RilleP/parsing-package-fixes
core:odin parsing fixes
2024-04-11 15:19:10 +02:00
gingerBill 0abbf3ba0a Fix #3412 2024-04-11 13:57:37 +01:00
RilleP 330c161625 remove semicolon 2024-04-11 09:36:28 +02:00
gingerBill 97db075e45 Add #panic to tell of moved package 2024-04-10 20:32:55 +01:00
RilleP 730f992bff fix indentation 2024-04-10 19:16:38 +02:00
RilleP 95a38d5a96 Merge branch 'master' into parsing-package-fixes 2024-04-10 19:10:33 +02:00
gingerBill 13e459980b Fix ptr_to_bit_field.field 2024-04-10 16:18:44 +01:00
gingerBill 3b5e515a22 Fix #3386 - fixed.to_string 2024-04-10 14:35:14 +01:00
gingerBill 3ad95d6be3 Add append_u128 2024-04-10 14:35:00 +01:00
gingerBill abd5fc606c Fix #3407 2024-04-10 14:12:41 +01:00
gingerBill 6678242280 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-10 13:40:31 +01:00
gingerBill fd487f66bc Add json:"name,omitempty" 2024-04-10 13:40:25 +01:00
gingerBill d0dc7395e9 Allow for comma-separate json names json:"name,flag" 2024-04-10 13:29:10 +01:00
Jeroen van Rijn 2ba2bc1fec Merge pull request #3288 from Yawning/feature/even-moar-crypto
core/crypto: Even more improvments
2024-04-10 14:19:25 +02:00
gingerBill 97e2d8916a Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-10 12:32:32 +01:00
gingerBill 3dfd61dd4f Make intrinsics.overflow_* NOT #optional_ok 2024-04-10 12:32:26 +01:00
gingerBill 0f39b9ef22 Merge pull request #3403 from iciuperca/master
Avoid loop index shadowing in vendor:OpenGL
2024-04-10 12:01:08 +01:00
gingerBill af67cc7afe Merge pull request #3397 from wrapperup/add-movefile-flags
Add flags for windows `MoveFileEx`
2024-04-10 12:01:01 +01:00
gingerBill 53558313d8 Merge pull request #3402 from Chickenkeeper/using-param-error-typo-fix
Fix typo in using parameter error message
2024-04-10 11:53:08 +01:00
Jeroen van Rijn 8a6a3e883c Merge pull request #3406 from laytan/bit-field-core-parser
add bit_field to `core:odin`
2024-04-10 03:12:40 +02:00
Laytan Laats af6d2480fa add bit_field parsing to core:odin/parser
Also adds it to the core type thingy like it is in the compiler.
2024-04-10 01:01:32 +02:00
iciuperca c644f79573 Avoid loop index shadowing
The inner loop uses the same index variable name "i" as the parent.

This causes an error message with -vet -strict-style
2024-04-09 18:24:57 +01:00
Chris 219eb58c08 Fix typo in using parameter error message 2024-04-09 12:03:41 +01:00
gingerBill 2207a01494 Merge pull request #3400 from Chickenkeeper/datetime-using-parameter-fix
Remove using parameter from validate_datetime
2024-04-09 12:03:11 +01:00
Chris 60478c0e07 Remove using from validate_datetime 2024-04-09 10:57:51 +01:00
wrapperup 9b496e82f3 add movefile flags 2024-04-09 01:50:16 -04:00
Yawning Angel fa1d681e65 tests/core/crypto: Start adding comprehensive curve25519 tests 2024-04-09 14:37:59 +09:00
Yawning Angel 893c3bef9a core/crypto/ed25519: Initial import 2024-04-09 14:37:59 +09:00
Yawning Angel d96f8bb5c1 core/crypto/ristretto255: Initial import 2024-04-09 14:37:59 +09:00
Yawning Angel 563c527419 core/crypto/_edwards25519: Initial import 2024-04-09 14:37:59 +09:00
Yawning Angel fec42a6d74 core/crypto/_fiat/field_scalar25519: Initial import 2024-04-09 10:23:58 +09:00
Yawning Angel 4defe88dec core/crypto/_fiat/field_poly1305: Mark more functions contextless 2024-04-09 10:23:58 +09:00
Yawning Angel 36f3001d59 core/crypto/_fiat/field_poly1305: Use multiply to calculate the mask 2024-04-09 10:23:58 +09:00
Yawning Angel 1ce279e6a1 core/crypto/_fiat/field_curve25519: Mark more functions contextless 2024-04-09 10:23:58 +09:00
Yawning Angel c951cbdbbc core/crypto/_fiat: odinfmt (NFC) 2024-04-09 10:23:58 +09:00
Yawning Angel 31aba5a728 core/crypto/_fiat/field_poly1305: Move routines (NFC) 2024-04-09 10:23:58 +09:00
Yawning Angel 9a418fd27b core/crypto/_fiat/field_curve25519: Move routines (NFC) 2024-04-09 10:23:58 +09:00
Yawning Angel f9b9521bf0 core/crypto/_fiat/field_curve25519: Use multiply to calculate the mask
Largely for consistency with the generic code, either is valid with Odin
semantics, but this is easier to comprehend.
2024-04-09 10:23:58 +09:00
Yawning Angel b155fdf8c9 core/crypto: Add has_rand_bytes
This allows runtime detection as to if `rand_bytes` is supported or not,
and lets us enable the test-case on all of the supported targets.
2024-04-09 10:23:58 +09:00
Yawning Angel a43a5b053c core/crypto: Add more documentation about assumptions (NFC) 2024-04-09 10:23:58 +09:00
gingerBill a14f0d8f58 Merge pull request #3396 from laytan/fix-nasm-check
fix nasm check
2024-04-09 00:09:13 +01:00
gingerBill 25f781d64b Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-08 23:58:36 +01:00
gingerBill 9933ca8b56 Make map_total_allocation_size public; Add map_total_allocation_size_from_value 2024-04-08 23:58:30 +01:00
Laytan Laats 9c958ee66d fix nasm check 2024-04-08 20:43:52 +02:00
Rikard Petré 239d4e1076 odin/tokenizer: Reset insert_semicolon to false in tokenizer.init to fix bug when tokenizing multiple files. 2024-01-20 16:09:41 +01:00
Rikard Petré 99825a28d7 odin/parser: Allow semicolon after return statement for the case:
if x do return y;
else do return z;
2024-01-20 16:00:41 +01:00
Rikard Petré 144504a752 odin/parser: Fix parsing of struct literal/call expression when closing brace/paren is on a new line without a comma after the last argument. 2024-01-20 15:57:14 +01:00
60 changed files with 4088 additions and 318 deletions
+3 -3
View File
@@ -38,9 +38,9 @@ count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) --- byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_add :: proc(lhs, rhs: $T) -> (T, bool) ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) ---
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
+3 -2
View File
@@ -597,8 +597,9 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
base := info base := info
loop: for { loop: for {
#partial switch i in base.variant { #partial switch i in base.variant {
case Type_Info_Named: base = i.base case Type_Info_Named: base = i.base
case Type_Info_Enum: base = i.base case Type_Info_Enum: base = i.base
case Type_Info_Bit_Field: base = i.backing_type
case: break loop case: break loop
} }
} }
+7 -1
View File
@@ -333,7 +333,7 @@ map_kvh_data_values_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^
} }
@(private, require_results) @(require_results)
map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr { map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
round :: #force_inline proc "contextless" (value: uintptr) -> uintptr { round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1 CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
@@ -350,6 +350,12 @@ map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr
return size return size
} }
@(require_results)
map_total_allocation_size_from_value :: #force_inline proc "contextless" (m: $M/map[$K]$V) -> uintptr {
return map_total_allocation_size(uintptr(cap(m)), map_info(M))
}
// The only procedure which needs access to the context is the one which allocates the map. // The only procedure which needs access to the context is the one which allocates the map.
@(require_results) @(require_results)
map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) { map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
+8
View File
@@ -14,6 +14,14 @@ constant-time byte comparison.
- Best-effort is make to mitigate timing side-channels on reasonable - Best-effort is make to mitigate timing side-channels on reasonable
architectures. Architectures that are known to be unreasonable include 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, and WebAssembly.
- Implementations assume a 64-bit architecture (64-bit integer arithmetic
is fast, and includes 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.
- Fault injection attacks etc.
- Hardware vulnerabilities ("apply mitigations or buy a new CPU").
- The packages attempt to santize sensitive data, however this is, and - The packages attempt to santize sensitive data, however this is, and
will remain a "best-effort" implementation decision. As Thomas Pornin will remain a "best-effort" implementation decision. As Thomas Pornin
puts it "In general, such memory cleansing is a fool's quest." puts it "In general, such memory cleansing is a fool's quest."
+428
View File
@@ -0,0 +1,428 @@
package _edwards25519
/*
This implements the edwards25519 composite-order group, primarily for
the purpose of implementing X25519, Ed25519, and ristretto255. Use of
this package for other purposes is NOT RECOMMENDED.
See:
- https://eprint.iacr.org/2011/368.pdf
- https://datatracker.ietf.org/doc/html/rfc8032
- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
*/
import "base:intrinsics"
import "core:crypto"
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
// Group_Element is an edwards25519 group element, as extended homogenous
// coordinates, which represents the affine point `(x, y)` as `(X, Y, Z, T)`,
// with the relations `x = X/Z`, `y = Y/Z`, and `x * y = T/Z`.
//
// d = -121665/121666 = 37095705934669439343138083508754565189542113879843219016388785533085940283555
// a = -1
//
// Notes:
// - There is considerable scope for optimization, however that
// will not change the external API, and this is simple and reasonably
// performant.
// - The API delibarately makes it hard to create arbitrary group
// elements that are not on the curve.
// - The group element decoding routine takes the opinionated stance of
// rejecting non-canonical encodings.
FE_D := field.Tight_Field_Element {
929955233495203,
466365720129213,
1662059464998953,
2033849074728123,
1442794654840575,
}
@(private)
FE_A := field.Tight_Field_Element {
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
}
@(private)
FE_D2 := field.Tight_Field_Element {
1859910466990425,
932731440258426,
1072319116312658,
1815898335770999,
633789495995903,
}
@(private)
GE_BASEPOINT := Group_Element {
field.Tight_Field_Element {
1738742601995546,
1146398526822698,
2070867633025821,
562264141797630,
587772402128613,
},
field.Tight_Field_Element {
1801439850948184,
1351079888211148,
450359962737049,
900719925474099,
1801439850948198,
},
field.Tight_Field_Element{1, 0, 0, 0, 0},
field.Tight_Field_Element {
1841354044333475,
16398895984059,
755974180946558,
900171276175154,
1821297809914039,
},
}
GE_IDENTITY := Group_Element {
field.Tight_Field_Element{0, 0, 0, 0, 0},
field.Tight_Field_Element{1, 0, 0, 0, 0},
field.Tight_Field_Element{1, 0, 0, 0, 0},
field.Tight_Field_Element{0, 0, 0, 0, 0},
}
Group_Element :: struct {
x: field.Tight_Field_Element,
y: field.Tight_Field_Element,
z: field.Tight_Field_Element,
t: field.Tight_Field_Element,
}
ge_clear :: proc "contextless" (ge: ^Group_Element) {
mem.zero_explicit(ge, size_of(Group_Element))
}
ge_set :: proc "contextless" (ge, a: ^Group_Element) {
field.fe_set(&ge.x, &a.x)
field.fe_set(&ge.y, &a.y)
field.fe_set(&ge.z, &a.z)
field.fe_set(&ge.t, &a.t)
}
@(require_results)
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
// Do the work in a scratch element, so that ge is unchanged on
// failure.
tmp: Group_Element = ---
defer ge_clear(&tmp)
field.fe_one(&tmp.z) // Z = 1
// The encoding is the y-coordinate, with the x-coordinate polarity
// (odd/even) encoded in the MSB.
field.fe_from_bytes(&tmp.y, b_) // ignores high bit
// Recover the candidate x-coordinate via the curve equation:
// x^2 = (y^2 - 1) / (d * y^2 + 1) (mod p)
fe_tmp := &tmp.t // Use this to store intermediaries.
fe_one := &tmp.z
// x = num = y^2 - 1
field.fe_carry_square(fe_tmp, field.fe_relax_cast(&tmp.y)) // fe_tmp = y^2
field.fe_carry_sub(&tmp.x, fe_tmp, fe_one)
// den = d * y^2 + 1
field.fe_carry_mul(fe_tmp, field.fe_relax_cast(fe_tmp), field.fe_relax_cast(&FE_D))
field.fe_carry_add(fe_tmp, fe_tmp, fe_one)
// x = invsqrt(den/num)
is_square := field.fe_carry_sqrt_ratio_m1(
&tmp.x,
field.fe_relax_cast(&tmp.x),
field.fe_relax_cast(fe_tmp),
)
if is_square == 0 {
return false
}
// Pick the right x-coordinate.
field.fe_cond_negate(&tmp.x, &tmp.x, int(b[31] >> 7))
// t = x * y
field.fe_carry_mul(&tmp.t, field.fe_relax_cast(&tmp.x), field.fe_relax_cast(&tmp.y))
// Reject non-canonical encodings of ge.
buf: [32]byte = ---
field.fe_to_bytes(&buf, &tmp.y)
buf[31] |= byte(field.fe_is_negative(&tmp.x)) << 7
is_canonical := crypto.compare_constant_time(b, buf[:])
ge_cond_assign(ge, &tmp, is_canonical)
mem.zero_explicit(&buf, size_of(buf))
return is_canonical == 1
}
ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
if len(dst) != 32 {
intrinsics.trap()
}
dst_ := transmute(^[32]byte)(raw_data(dst))
// Convert the element to affine (x, y) representation.
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
field.fe_carry_inv(&z_inv, field.fe_relax_cast(&ge.z))
field.fe_carry_mul(&x, field.fe_relax_cast(&ge.x), field.fe_relax_cast(&z_inv))
field.fe_carry_mul(&y, field.fe_relax_cast(&ge.y), field.fe_relax_cast(&z_inv))
// Encode the y-coordinate.
field.fe_to_bytes(dst_, &y)
// Copy the least significant bit of the x-coordinate to the most
// significant bit of the encoded y-coordinate.
dst_[31] |= byte((x[0] & 1) << 7)
field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &z_inv})
}
ge_identity :: proc "contextless" (ge: ^Group_Element) {
field.fe_zero(&ge.x)
field.fe_one(&ge.y)
field.fe_one(&ge.z)
field.fe_zero(&ge.t)
}
ge_generator :: proc "contextless" (ge: ^Group_Element) {
ge_set(ge, &GE_BASEPOINT)
}
@(private)
Addend_Group_Element :: struct {
y2_minus_x2: field.Loose_Field_Element, // t1
y2_plus_x2: field.Loose_Field_Element, // t3
k_times_t2: field.Tight_Field_Element, // t4
two_times_z2: field.Loose_Field_Element, // t5
}
@(private)
ge_addend_set :: proc "contextless" (ge_a: ^Addend_Group_Element, ge: ^Group_Element) {
field.fe_sub(&ge_a.y2_minus_x2, &ge.y, &ge.x)
field.fe_add(&ge_a.y2_plus_x2, &ge.y, &ge.x)
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&FE_D2), field.fe_relax_cast(&ge.t))
field.fe_add(&ge_a.two_times_z2, &ge.z, &ge.z)
}
@(private)
ge_addend_conditional_assign :: proc "contextless" (ge_a, a: ^Addend_Group_Element, ctrl: int) {
field.fe_cond_select(&ge_a.y2_minus_x2, &ge_a.y2_minus_x2, &a.y2_minus_x2, ctrl)
field.fe_cond_select(&ge_a.y2_plus_x2, &ge_a.y2_plus_x2, &a.y2_plus_x2, ctrl)
field.fe_cond_select(&ge_a.k_times_t2, &ge_a.k_times_t2, &a.k_times_t2, ctrl)
field.fe_cond_select(&ge_a.two_times_z2, &ge_a.two_times_z2, &a.two_times_z2, ctrl)
}
@(private)
Add_Scratch :: struct {
A, B, C, D: field.Tight_Field_Element,
E, F, G, H: field.Loose_Field_Element,
t0, t2: field.Loose_Field_Element,
}
ge_add :: proc "contextless" (ge, a, b: ^Group_Element) {
b_: Addend_Group_Element = ---
ge_addend_set(&b_, b)
scratch: Add_Scratch = ---
ge_add_addend(ge, a, &b_, &scratch)
mem.zero_explicit(&b_, size_of(Addend_Group_Element))
mem.zero_explicit(&scratch, size_of(Add_Scratch))
}
@(private)
ge_add_addend :: proc "contextless" (
ge, a: ^Group_Element,
b: ^Addend_Group_Element,
scratch: ^Add_Scratch,
) {
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
// Assumptions: k=2*d.
//
// t0 = Y1-X1
// t1 = Y2-X2
// A = t0*t1
// t2 = Y1+X1
// t3 = Y2+X2
// B = t2*t3
// t4 = k*T2
// C = T1*t4
// t5 = 2*Z2
// D = Z1*t5
// E = B-A
// F = D-C
// G = D+C
// H = B+A
// X3 = E*F
// Y3 = G*H
// T3 = E*H
// Z3 = F*G
//
// In order to make the scalar multiply faster, the addend is provided
// as a `Addend_Group_Element` with t1, t3, t4, and t5 precomputed, as
// it is trivially obvious that those are the only values used by the
// formula that are directly dependent on `b`, and are only dependent
// on `b` and constants. This saves 1 sub, 2 adds, and 1 multiply,
// each time the intermediate representation can be reused.
A, B, C, D := &scratch.A, &scratch.B, &scratch.C, &scratch.D
E, F, G, H := &scratch.E, &scratch.F, &scratch.G, &scratch.H
t0, t2 := &scratch.t0, &scratch.t2
field.fe_sub(t0, &a.y, &a.x)
t1 := &b.y2_minus_x2
field.fe_carry_mul(A, t0, t1)
field.fe_add(t2, &a.y, &a.x)
t3 := &b.y2_plus_x2
field.fe_carry_mul(B, t2, t3)
t4 := &b.k_times_t2
field.fe_carry_mul(C, field.fe_relax_cast(&a.t), field.fe_relax_cast(t4))
t5 := &b.two_times_z2
field.fe_carry_mul(D, field.fe_relax_cast(&a.z), t5)
field.fe_sub(E, B, A)
field.fe_sub(F, D, C)
field.fe_add(G, D, C)
field.fe_add(H, B, A)
field.fe_carry_mul(&ge.x, E, F)
field.fe_carry_mul(&ge.y, G, H)
field.fe_carry_mul(&ge.t, E, H)
field.fe_carry_mul(&ge.z, F, G)
}
@(private)
Double_Scratch :: struct {
A, B, C, D, G: field.Tight_Field_Element,
t0, t2, t3: field.Tight_Field_Element,
E, F, H: field.Loose_Field_Element,
t1: field.Loose_Field_Element,
}
ge_double :: proc "contextless" (ge, a: ^Group_Element, scratch: ^Double_Scratch = nil) {
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd
//
// A = X1^2
// B = Y1^2
// t0 = Z1^2
// C = 2*t0
// D = a*A
// t1 = X1+Y1
// t2 = t1^2
// t3 = t2-A
// E = t3-B
// G = D+B
// F = G-C
// H = D-B
// X3 = E*F
// Y3 = G*H
// T3 = E*H
// Z3 = F*G
sanitize, scratch := scratch == nil, scratch
if sanitize {
tmp: Double_Scratch = ---
scratch = &tmp
}
A, B, C, D, G := &scratch.A, &scratch.B, &scratch.C, &scratch.D, &scratch.G
t0, t2, t3 := &scratch.t0, &scratch.t2, &scratch.t3
E, F, H := &scratch.E, &scratch.F, &scratch.H
t1 := &scratch.t1
field.fe_carry_square(A, field.fe_relax_cast(&a.x))
field.fe_carry_square(B, field.fe_relax_cast(&a.y))
field.fe_carry_square(t0, field.fe_relax_cast(&a.z))
field.fe_carry_add(C, t0, t0)
field.fe_carry_mul(D, field.fe_relax_cast(&FE_A), field.fe_relax_cast(A))
field.fe_add(t1, &a.x, &a.y)
field.fe_carry_square(t2, t1)
field.fe_carry_sub(t3, t2, A)
field.fe_sub(E, t3, B)
field.fe_carry_add(G, D, B)
field.fe_sub(F, G, C)
field.fe_sub(H, D, B)
G_ := field.fe_relax_cast(G)
field.fe_carry_mul(&ge.x, E, F)
field.fe_carry_mul(&ge.y, G_, H)
field.fe_carry_mul(&ge.t, E, H)
field.fe_carry_mul(&ge.z, F, G_)
if sanitize {
mem.zero_explicit(scratch, size_of(Double_Scratch))
}
}
ge_negate :: proc "contextless" (ge, a: ^Group_Element) {
field.fe_carry_opp(&ge.x, &a.x)
field.fe_set(&ge.y, &a.y)
field.fe_set(&ge.z, &a.z)
field.fe_carry_opp(&ge.t, &a.t)
}
ge_cond_negate :: proc "contextless" (ge, a: ^Group_Element, ctrl: int) {
tmp: Group_Element = ---
ge_negate(&tmp, a)
ge_cond_assign(ge, &tmp, ctrl)
ge_clear(&tmp)
}
ge_cond_assign :: proc "contextless" (ge, a: ^Group_Element, ctrl: int) {
field.fe_cond_assign(&ge.x, &a.x, ctrl)
field.fe_cond_assign(&ge.y, &a.y, ctrl)
field.fe_cond_assign(&ge.z, &a.z, ctrl)
field.fe_cond_assign(&ge.t, &a.t, ctrl)
}
ge_cond_select :: proc "contextless" (ge, a, b: ^Group_Element, ctrl: int) {
field.fe_cond_select(&ge.x, &a.x, &b.x, ctrl)
field.fe_cond_select(&ge.y, &a.y, &b.y, ctrl)
field.fe_cond_select(&ge.z, &a.z, &b.z, ctrl)
field.fe_cond_select(&ge.t, &a.t, &b.t, ctrl)
}
@(require_results)
ge_equal :: proc "contextless" (a, b: ^Group_Element) -> int {
// (x, y) ?= (x', y') -> (X/Z, Y/Z) ?= (X'/Z', Y'/Z')
// X/Z ?= X'/Z', Y/Z ?= Y'/Z' -> X*Z' ?= X'*Z, Y*Z' ?= Y'*Z
ax_bz, bx_az, ay_bz, by_az: field.Tight_Field_Element = ---, ---, ---, ---
field.fe_carry_mul(&ax_bz, field.fe_relax_cast(&a.x), field.fe_relax_cast(&b.z))
field.fe_carry_mul(&bx_az, field.fe_relax_cast(&b.x), field.fe_relax_cast(&a.z))
field.fe_carry_mul(&ay_bz, field.fe_relax_cast(&a.y), field.fe_relax_cast(&b.z))
field.fe_carry_mul(&by_az, field.fe_relax_cast(&b.y), field.fe_relax_cast(&a.z))
ret := field.fe_equal(&ax_bz, &bx_az) & field.fe_equal(&ay_bz, &by_az)
field.fe_clear_vec([]^field.Tight_Field_Element{&ax_bz, &ay_bz, &bx_az, &by_az})
return ret
}
@(require_results)
ge_is_small_order :: proc "contextless" (ge: ^Group_Element) -> bool {
tmp: Group_Element = ---
ge_double(&tmp, ge)
ge_double(&tmp, &tmp)
ge_double(&tmp, &tmp)
return ge_equal(&tmp, &GE_IDENTITY) == 1
}
@(require_results)
ge_in_prime_order_subgroup_vartime :: proc "contextless" (ge: ^Group_Element) -> bool {
// This is currently *very* expensive. The faster method would be
// something like (https://eprint.iacr.org/2022/1164.pdf), however
// that is a ~50% speedup, and a lot of added complexity for something
// that is better solved by "just use ristretto255".
tmp: Group_Element = ---
_ge_scalarmult(&tmp, ge, &SC_ELL, true)
return ge_equal(&tmp, &GE_IDENTITY) == 1
}
@@ -0,0 +1,61 @@
package _edwards25519
import "base:intrinsics"
import field "core:crypto/_fiat/field_scalar25519"
import "core:mem"
Scalar :: field.Montgomery_Domain_Field_Element
// WARNING: This is non-canonical and only to be used when checking if
// a group element is on the prime-order subgroup.
@(private)
SC_ELL := field.Non_Montgomery_Domain_Field_Element {
field.ELL[0],
field.ELL[1],
field.ELL[2],
field.ELL[3],
}
sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
tmp := field.Non_Montgomery_Domain_Field_Element{i, 0, 0, 0}
field.fe_to_montgomery(sc, &tmp)
mem.zero_explicit(&tmp, size_of(tmp))
}
@(require_results)
sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
return field.fe_from_bytes(sc, b_)
}
sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
field.fe_from_bytes_rfc8032(sc, b_)
}
sc_clear :: proc "contextless" (sc: ^Scalar) {
mem.zero_explicit(sc, size_of(Scalar))
}
sc_set :: field.fe_set
sc_set_bytes_wide :: field.fe_from_bytes_wide
sc_bytes :: field.fe_to_bytes
sc_zero :: field.fe_zero
sc_one :: field.fe_one
sc_add :: field.fe_add
sc_sub :: field.fe_sub
sc_negate :: field.fe_opp
sc_mul :: field.fe_mul
sc_square :: field.fe_square
sc_cond_assign :: field.fe_cond_assign
sc_equal :: field.fe_equal
@@ -0,0 +1,288 @@
package _edwards25519
import field "core:crypto/_fiat/field_scalar25519"
import "core:math/bits"
import "core:mem"
// GE_BASEPOINT_TABLE is 1 * G, ... 15 * G, in precomputed format.
//
// Note: When generating, the values were reduced to Tight_Field_Element
// ranges, even though that is not required.
@(private)
GE_BASEPOINT_TABLE := Multiply_Table {
{
{62697248952638, 204681361388450, 631292143396476, 338455783676468, 1213667448819585},
{1288382639258501, 245678601348599, 269427782077623, 1462984067271730, 137412439391563},
{301289933810280, 1259582250014073, 1422107436869536, 796239922652654, 1953934009299142},
{2, 0, 0, 0, 0},
},
{
{1519297034332653, 1098796920435767, 1823476547744119, 808144629470969, 2110930855619772},
{338005982828284, 1667856962156925, 100399270107451, 1604566703601691, 1950338038771369},
{1920505767731247, 1443759578976892, 1659852098357048, 1484431291070208, 275018744912646},
{763163817085987, 2195095074806923, 2167883174351839, 1868059999999762, 911071066608705},
},
{
{960627541894068, 1314966688943942, 1126875971034044, 2059608312958945, 605975666152586},
{1714478358025626, 2209607666607510, 1600912834284834, 496072478982142, 481970031861896},
{851735079403194, 1088965826757164, 141569479297499, 602804610059257, 2004026468601520},
{197585529552380, 324719066578543, 564481854250498, 1173818332764578, 35452976395676},
},
{
{1152980410747203, 2196804280851952, 25745194962557, 1915167295473129, 1266299690309224},
{809905889679060, 979732230071345, 1509972345538142, 188492426534402, 818965583123815},
{997685409185036, 1451818320876327, 2126681166774509, 2000509606057528, 235432372486854},
{887734189279642, 1460338685162044, 877378220074262, 102436391401299, 153369156847490},
},
{
{2056621900836770, 1821657694132497, 1627986892909426, 1163363868678833, 1108873376459226},
{1187697490593623, 1066539945237335, 885654531892000, 1357534489491782, 359370291392448},
{1509033452137525, 1305318174298508, 613642471748944, 1987256352550234, 1044283663101541},
{220105720697037, 387661783287620, 328296827867762, 360035589590664, 795213236824054},
},
{
{1820794733038396, 1612235121681074, 757405923441402, 1094031020892801, 231025333128907},
{1639067873254194, 1484176557946322, 300800382144789, 1329915446659183, 1211704578730455},
{641900794791527, 1711751746971612, 179044712319955, 576455585963824, 1852617592509865},
{743549047192397, 685091042550147, 1952415336873496, 1965124675654685, 513364998442917},
},
{
{1004557076870448, 1762911374844520, 1330807633622723, 384072910939787, 953849032243810},
{2178275058221458, 257933183722891, 376684351537894, 2010189102001786, 1981824297484148},
{1332915663881114, 1286540505502549, 1741691283561518, 977214932156314, 1764059494778091},
{429702949064027, 1368332611650677, 2019867176450999, 2212258376161746, 526160996742554},
},
{
{2098932988258576, 2203688382075948, 2120400160059479, 1748488020948146, 1203264167282624},
{677131386735829, 1850249298025188, 672782146532031, 2144145693078904, 2088656272813787},
{1065622343976192, 1573853211848116, 223560413590068, 333846833073379, 27832122205830},
{1781008836504573, 917619542051793, 544322748939913, 882577394308384, 1720521246471195},
},
{
{660120928379860, 2081944024858618, 1878411111349191, 424587356517195, 2111317439894005},
{1834193977811532, 1864164086863319, 797334633289424, 150410812403062, 2085177078466389},
{1438117271371866, 783915531014482, 388731514584658, 292113935417795, 1945855002546714},
{1678140823166658, 679103239148744, 614102761596238, 1052962498997885, 1863983323810390},
},
{
{1690309392496233, 1116333140326275, 1377242323631039, 717196888780674, 82724646713353},
{1722370213432106, 74265192976253, 264239578448472, 1714909985012994, 2216984958602173},
{2010482366920922, 1294036471886319, 566466395005815, 1631955803657320, 1751698647538458},
{1073230604155753, 1159087041338551, 1664057985455483, 127472702826203, 1339591128522371},
},
{
{478053307175577, 2179515791720985, 21146535423512, 1831683844029536, 462805561553981},
{1945267486565588, 1298536818409655, 2214511796262989, 1904981051429012, 252904800782086},
{268945954671210, 222740425595395, 1208025911856230, 1080418823003555, 75929831922483},
{1884784014268948, 643868448202966, 978736549726821, 46385971089796, 1296884812292320},
},
{
{1861159462859103, 7077532564710, 963010365896826, 1938780006785270, 766241051941647},
{1778966986051906, 1713995999765361, 1394565822271816, 1366699246468722, 1213407027149475},
{1978989286560907, 2135084162045594, 1951565508865477, 671788336314416, 293123929458176},
{902608944504080, 2167765718046481, 1285718473078022, 1222562171329269, 492109027844479},
},
{
{1820807832746213, 1029220580458586, 1101997555432203, 1039081975563572, 202477981158221},
{1866134980680205, 2222325502763386, 1830284629571201, 1046966214478970, 418381946936795},
{1783460633291322, 1719505443254998, 1810489639976220, 877049370713018, 2187801198742619},
{197118243000763, 305493867565736, 518814410156522, 1656246186645170, 901894734874934},
},
{
{225454942125915, 478410476654509, 600524586037746, 643450007230715, 1018615928259319},
{1733330584845708, 881092297970296, 507039890129464, 496397090721598, 2230888519577628},
{690155664737246, 1010454785646677, 753170144375012, 1651277613844874, 1622648796364156},
{1321310321891618, 1089655277873603, 235891750867089, 815878279563688, 1709264240047556},
},
{
{805027036551342, 1387174275567452, 1156538511461704, 1465897486692171, 1208567094120903},
{2228417017817483, 202885584970535, 2182114782271881, 2077405042592934, 1029684358182774},
{460447547653983, 627817697755692, 524899434670834, 1228019344939427, 740684787777653},
{849757462467675, 447476306919899, 422618957298818, 302134659227815, 675831828440895},
},
}
ge_scalarmult :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
tmp: field.Non_Montgomery_Domain_Field_Element
field.fe_from_montgomery(&tmp, sc)
_ge_scalarmult(ge, p, &tmp)
mem.zero_explicit(&tmp, size_of(tmp))
}
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
// Something like the comb method from "Fast and compact elliptic-curve
// cryptography" Section 3.3, would be more performant, but more
// complex.
//
// - https://eprint.iacr.org/2012/309
ge_scalarmult(ge, &GE_BASEPOINT, sc)
}
ge_scalarmult_vartime :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
tmp: field.Non_Montgomery_Domain_Field_Element
field.fe_from_montgomery(&tmp, sc)
_ge_scalarmult(ge, p, &tmp, true)
}
ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
ge: ^Group_Element,
a: ^Scalar,
A: ^Group_Element,
b: ^Scalar,
) {
// Strauss-Shamir, commonly referred to as the "Shamir trick",
// saves half the doublings, relative to doing this the naive way.
//
// ABGLSV-Pornin (https://eprint.iacr.org/2020/454) is faster,
// but significantly more complex, and has incompatibilities with
// mixed-order group elements.
tmp_add: Add_Scratch = ---
tmp_addend: Addend_Group_Element = ---
tmp_dbl: Double_Scratch = ---
tmp: Group_Element = ---
A_tbl: Multiply_Table = ---
mul_tbl_set(&A_tbl, A, &tmp_add)
sc_a, sc_b: field.Non_Montgomery_Domain_Field_Element
field.fe_from_montgomery(&sc_a, a)
field.fe_from_montgomery(&sc_b, b)
ge_identity(&tmp)
for i := 31; i >= 0; i = i - 1 {
limb := i / 8
shift := uint(i & 7) * 8
limb_byte_a := sc_a[limb] >> shift
limb_byte_b := sc_b[limb] >> shift
hi_a, lo_a := (limb_byte_a >> 4) & 0x0f, limb_byte_a & 0x0f
hi_b, lo_b := (limb_byte_b >> 4) & 0x0f, limb_byte_b & 0x0f
if i != 31 {
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
}
mul_tbl_add(&tmp, &A_tbl, hi_a, &tmp_add, &tmp_addend, true)
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_addend, true)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
mul_tbl_add(&tmp, &A_tbl, lo_a, &tmp_add, &tmp_addend, true)
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_addend, true)
}
ge_set(ge, &tmp)
}
@(private)
_ge_scalarmult :: proc "contextless" (
ge, p: ^Group_Element,
sc: ^field.Non_Montgomery_Domain_Field_Element,
unsafe_is_vartime := false,
) {
// Do the simplest possible thing that works and provides adequate,
// performance, which is windowed add-then-multiply.
tmp_add: Add_Scratch = ---
tmp_addend: Addend_Group_Element = ---
tmp_dbl: Double_Scratch = ---
tmp: Group_Element = ---
p_tbl: Multiply_Table = ---
mul_tbl_set(&p_tbl, p, &tmp_add)
ge_identity(&tmp)
for i := 31; i >= 0; i = i - 1 {
limb := i / 8
shift := uint(i & 7) * 8
limb_byte := sc[limb] >> shift
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
if i != 31 {
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
}
mul_tbl_add(&tmp, &p_tbl, hi, &tmp_add, &tmp_addend, unsafe_is_vartime)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
mul_tbl_add(&tmp, &p_tbl, lo, &tmp_add, &tmp_addend, unsafe_is_vartime)
}
ge_set(ge, &tmp)
if !unsafe_is_vartime {
ge_clear(&tmp)
mem.zero_explicit(&tmp_add, size_of(Add_Scratch))
mem.zero_explicit(&tmp_addend, size_of(Addend_Group_Element))
mem.zero_explicit(&tmp_dbl, size_of(Double_Scratch))
}
}
@(private)
Multiply_Table :: [15]Addend_Group_Element // 0 = inf, which is implicit.
@(private)
mul_tbl_set :: proc "contextless" (
tbl: ^Multiply_Table,
ge: ^Group_Element,
tmp_add: ^Add_Scratch,
) {
tmp: Group_Element = ---
ge_set(&tmp, ge)
ge_addend_set(&tbl[0], ge)
for i := 1; i < 15; i = i + 1 {
ge_add_addend(&tmp, &tmp, &tbl[0], tmp_add)
ge_addend_set(&tbl[i], &tmp)
}
ge_clear(&tmp)
}
@(private)
mul_tbl_add :: proc "contextless" (
ge: ^Group_Element,
tbl: ^Multiply_Table,
idx: u64,
tmp_add: ^Add_Scratch,
tmp_addend: ^Addend_Group_Element,
unsafe_is_vartime: bool,
) {
// Variable time lookup, with the addition omitted entirely if idx == 0.
if unsafe_is_vartime {
// Skip adding the point at infinity.
if idx != 0 {
ge_add_addend(ge, ge, &tbl[idx - 1], tmp_add)
}
return
}
// Constant time lookup.
tmp_addend^ = {
// Point at infinity (0, 1, 1, 0) in precomputed form
{1, 0, 0, 0, 0}, // y - x
{1, 0, 0, 0, 0}, // y + x
{0, 0, 0, 0, 0}, // t * 2d
{2, 0, 0, 0, 0}, // z * 2
}
for i := u64(1); i < 16; i = i + 1 {
_, ctrl := bits.sub_u64(0, (i ~ idx), 0)
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(~ctrl) & 1)
}
ge_add_addend(ge, ge, tmp_addend, tmp_add)
}
+2 -2
View File
@@ -9,7 +9,7 @@ package fiat
u1 :: distinct u8 u1 :: distinct u8
i1 :: distinct i8 i1 :: distinct i8
@(optimization_mode="none") @(optimization_mode = "none")
cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) { cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
x1 := (u64(arg1) * 0xffffffffffffffff) x1 := (u64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((~x1) & arg2)) x2 := ((x1 & arg3) | ((~x1) & arg2))
@@ -17,7 +17,7 @@ cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
return return
} }
@(optimization_mode="none") @(optimization_mode = "none")
cmovznz_u32 :: proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) { cmovznz_u32 :: proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
x1 := (u32(arg1) * 0xffffffff) x1 := (u32(arg1) * 0xffffffff)
x2 := ((x1 & arg3) | ((~x1) & arg2)) x2 := ((x1 & arg3) | ((~x1) & arg2))
+171 -42
View File
@@ -3,14 +3,32 @@ package field_curve25519
import "core:crypto" import "core:crypto"
import "core:mem" import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element { fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1) return transmute(^Loose_Field_Element)(arg1)
} }
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element { fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1) return transmute(^Tight_Field_Element)(arg1)
} }
fe_clear :: proc "contextless" (
arg1: $T,
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
mem.zero_explicit(arg1, size_of(arg1^))
}
fe_clear_vec :: proc "contextless" (
arg1: $T,
) where T == []^Tight_Field_Element || T == []^Loose_Field_Element {
for fe in arg1 {
fe_clear(fe)
}
}
fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) { fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
// Ignore the unused bit by copying the input and masking the bit off // Ignore the unused bit by copying the input and masking the bit off
// prior to deserialization. // prior to deserialization.
@@ -23,12 +41,25 @@ fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte
mem.zero_explicit(&tmp1, size_of(tmp1)) mem.zero_explicit(&tmp1, size_of(tmp1))
} }
fe_is_negative :: proc "contextless" (arg1: ^Tight_Field_Element) -> int {
tmp1: [32]byte = ---
fe_to_bytes(&tmp1, arg1)
ret := tmp1[0] & 1
mem.zero_explicit(&tmp1, size_of(tmp1))
return int(ret)
}
fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int { fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
tmp2: [32]byte = --- tmp1, tmp2: [32]byte = ---, ---
fe_to_bytes(&tmp1, arg1)
fe_to_bytes(&tmp2, arg2) fe_to_bytes(&tmp2, arg2)
ret := fe_equal_bytes(arg1, &tmp2) ret := crypto.compare_constant_time(tmp1[:], tmp2[:])
mem.zero_explicit(&tmp1, size_of(tmp1))
mem.zero_explicit(&tmp2, size_of(tmp2)) mem.zero_explicit(&tmp2, size_of(tmp2))
return ret return ret
@@ -46,7 +77,11 @@ fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byt
return ret return ret
} }
fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) { fe_carry_pow2k :: proc "contextless" (
out1: ^Tight_Field_Element,
arg1: ^Loose_Field_Element,
arg2: uint,
) {
// Special case: `arg1^(2 * 0) = 1`, though this should never happen. // Special case: `arg1^(2 * 0) = 1`, though this should never happen.
if arg2 == 0 { if arg2 == 0 {
fe_one(out1) fe_one(out1)
@@ -54,27 +89,46 @@ fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element,
} }
fe_carry_square(out1, arg1) fe_carry_square(out1, arg1)
for _ in 1..<arg2 { for _ in 1 ..< arg2 {
fe_carry_square(out1, fe_relax_cast(out1)) fe_carry_square(out1, fe_relax_cast(out1))
} }
} }
fe_carry_add :: #force_inline proc "contextless" (out1, arg1, arg2: ^Tight_Field_Element) {
fe_add(fe_relax_cast(out1), arg1, arg2)
fe_carry(out1, fe_relax_cast(out1))
}
fe_carry_sub :: #force_inline proc "contextless" (out1, arg1, arg2: ^Tight_Field_Element) {
fe_sub(fe_relax_cast(out1), arg1, arg2)
fe_carry(out1, fe_relax_cast(out1))
}
fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) { fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
fe_opp(fe_relax_cast(out1), arg1) fe_opp(fe_relax_cast(out1), arg1)
fe_carry(out1, fe_relax_cast(out1)) fe_carry(out1, fe_relax_cast(out1))
} }
fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int { fe_carry_abs :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
// Inverse square root taken from Monocypher. fe_cond_negate(out1, arg1, fe_is_negative(arg1))
}
fe_carry_sqrt_ratio_m1 :: proc "contextless" (
out1: ^Tight_Field_Element,
arg1: ^Loose_Field_Element, // u
arg2: ^Loose_Field_Element, // v
) -> int {
// SQRT_RATIO_M1(u, v) from RFC 9496 - 4.2, based on the inverse
// square root from Monocypher.
w: Tight_Field_Element = ---
fe_carry_mul(&w, arg1, arg2) // u * v
// r = tmp1 = u * w^((p-5)/8)
tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, --- tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, ---
fe_carry_pow2k(&tmp1, fe_relax_cast(&w), 1)
// t0 = x^((p-5)/8)
// Can be achieved with a simple double & add ladder,
// but it would be slower.
fe_carry_pow2k(&tmp1, arg1, 1)
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 2) fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 2)
fe_carry_mul(&tmp2, arg1, fe_relax_cast(&tmp2)) fe_carry_mul(&tmp2, fe_relax_cast(&w), fe_relax_cast(&tmp2))
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&tmp2)) fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&tmp2))
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 1) fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 1)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1)) fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
@@ -93,46 +147,121 @@ fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50) fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1)) fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 2) fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 2)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1) fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&w)) // w^((p-5)/8)
// quartic = x^((p-1)/4) fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1) // u * w^((p-5)/8)
quartic := &tmp2
fe_carry_square(quartic, fe_relax_cast(&tmp1))
fe_carry_mul(quartic, fe_relax_cast(quartic), arg1)
// Serialize quartic once to save on repeated serialization/sanitization. // Serialize `check` once to save on repeated serialization.
quartic_buf: [32]byte = --- r, check := &tmp1, &tmp2
fe_to_bytes(&quartic_buf, quartic) b: [32]byte = ---
check := &tmp3 fe_carry_square(check, fe_relax_cast(r))
fe_carry_mul(check, fe_relax_cast(check), arg2) // check * v
fe_to_bytes(&b, check)
fe_one(check) u, neg_u, neg_u_i := &tmp3, &w, check
p1 := fe_equal_bytes(check, &quartic_buf) fe_carry(u, arg1)
fe_carry_opp(check, check) fe_carry_opp(neg_u, u)
m1 := fe_equal_bytes(check, &quartic_buf) fe_carry_mul(neg_u_i, fe_relax_cast(neg_u), fe_relax_cast(&FE_SQRT_M1))
fe_carry_opp(check, &SQRT_M1)
ms := fe_equal_bytes(check, &quartic_buf)
// if quartic == -1 or sqrt(-1) correct_sign_sqrt := fe_equal_bytes(u, &b)
// then isr = x^((p-1)/4) * sqrt(-1) flipped_sign_sqrt := fe_equal_bytes(neg_u, &b)
// else isr = x^((p-1)/4) flipped_sign_sqrt_i := fe_equal_bytes(neg_u_i, &b)
fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1))
fe_cond_assign(out1, &tmp1, (m1|ms) ~ 1)
mem.zero_explicit(&tmp1, size_of(tmp1)) r_prime := check
mem.zero_explicit(&tmp2, size_of(tmp2)) fe_carry_mul(r_prime, fe_relax_cast(r), fe_relax_cast(&FE_SQRT_M1))
mem.zero_explicit(&tmp3, size_of(tmp3)) fe_cond_assign(r, r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i)
mem.zero_explicit(&quartic_buf, size_of(quartic_buf))
return p1 | m1 // Pick the non-negative square root.
fe_carry_abs(out1, r)
fe_clear_vec([]^Tight_Field_Element{&w, &tmp1, &tmp2, &tmp3})
mem.zero_explicit(&b, size_of(b))
return correct_sign_sqrt | flipped_sign_sqrt
} }
fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { fe_carry_inv :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
tmp1: Tight_Field_Element tmp1: Tight_Field_Element
fe_carry_square(&tmp1, arg1) fe_carry_square(&tmp1, arg1)
_ = fe_carry_invsqrt(&tmp1, fe_relax_cast(&tmp1)) _ = fe_carry_sqrt_ratio_m1(&tmp1, fe_relax_cast(&FE_ONE), fe_relax_cast(&tmp1))
fe_carry_square(&tmp1, fe_relax_cast(&tmp1)) fe_carry_square(&tmp1, fe_relax_cast(&tmp1))
fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1) fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1)
mem.zero_explicit(&tmp1, size_of(tmp1)) fe_clear(&tmp1)
}
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 1
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
x4 := arg1[3]
x5 := arg1[4]
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
out1[4] = x5
}
@(optimization_mode = "none")
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
mask := (u64(arg1) * 0xffffffffffffffff)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
x = (out1[3] ~ out2[3]) & mask
x4, y4 := out1[3] ~ x, out2[3] ~ x
x = (out1[4] ~ out2[4]) & mask
x5, y5 := out1[4] ~ x, out2[4] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
out1[3], out2[3] = x4, y4
out1[4], out2[4] = x5, y5
}
@(optimization_mode = "none")
fe_cond_select :: #force_no_inline proc "contextless" (
out1, arg1, arg2: $T,
arg3: int,
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
mask := (u64(arg3) * 0xffffffffffffffff)
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
x5 := ((mask & arg2[4]) | ((~mask) & arg1[4]))
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
out1[4] = x5
}
fe_cond_negate :: proc "contextless" (out1, arg1: ^Tight_Field_Element, ctrl: int) {
tmp1: Tight_Field_Element = ---
fe_carry_opp(&tmp1, arg1)
fe_cond_select(out1, arg1, &tmp1, ctrl)
fe_clear(&tmp1)
} }
+29 -61
View File
@@ -30,8 +30,6 @@ package field_curve25519
// //
// While the base implementation is provably correct, this implementation // While the base implementation is provably correct, this implementation
// makes no such claims as the port and optimizations were done by hand. // makes no such claims as the port and optimizations were done by hand.
// At some point, it may be worth adding support to fiat-crypto for
// generating Odin output.
// //
// TODO: // TODO:
// * When fiat-crypto supports it, using a saturated 64-bit limbs // * When fiat-crypto supports it, using a saturated 64-bit limbs
@@ -44,7 +42,10 @@ import "core:math/bits"
Loose_Field_Element :: distinct [5]u64 Loose_Field_Element :: distinct [5]u64
Tight_Field_Element :: distinct [5]u64 Tight_Field_Element :: distinct [5]u64
SQRT_M1 := Tight_Field_Element{ FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0}
FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0}
FE_SQRT_M1 := Tight_Field_Element {
1718705420411056, 1718705420411056,
234908883556509, 234908883556509,
2233514472574048, 2233514472574048,
@@ -52,7 +53,13 @@ SQRT_M1 := Tight_Field_Element{
765476049583133, 765476049583133,
} }
_addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) { _addcarryx_u51 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((u64(arg1) + arg2) + arg3) x1 := ((u64(arg1) + arg2) + arg3)
x2 := (x1 & 0x7ffffffffffff) x2 := (x1 & 0x7ffffffffffff)
x3 := fiat.u1((x1 >> 51)) x3 := fiat.u1((x1 >> 51))
@@ -61,7 +68,13 @@ _addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
return return
} }
_subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) { _subborrowx_u51 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3)) x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
x2 := fiat.i1((x1 >> 51)) x2 := fiat.i1((x1 >> 51))
x3 := (u64(x1) & 0x7ffffffffffff) x3 := (u64(x1) & 0x7ffffffffffff)
@@ -70,7 +83,7 @@ _subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
return return
} }
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) { fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13)) x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13))
x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13)) x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13))
x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13)) x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13))
@@ -169,7 +182,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme
out1[4] = x152 out1[4] = x152
} }
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
x1 := (arg1[4] * 0x13) x1 := (arg1[4] * 0x13)
x2 := (x1 * 0x2) x2 := (x1 * 0x2)
x3 := (arg1[4] * 0x2) x3 := (arg1[4] * 0x2)
@@ -305,8 +318,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
out1[4] = x5 out1[4] = x5
} }
@(optimization_mode="none") @(optimization_mode = "none")
fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) { fe_cond_assign :: #force_no_inline proc "contextless" (
out1, arg1: ^Tight_Field_Element,
arg2: int,
) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0]) x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1]) x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2]) x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
@@ -527,7 +543,10 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E
out1[4] = x5 out1[4] = x5
} }
fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { fe_carry_scmul_121666 :: proc "contextless" (
out1: ^Tight_Field_Element,
arg1: ^Loose_Field_Element,
) {
x2, x1 := bits.mul_u64(0x1db42, arg1[4]) x2, x1 := bits.mul_u64(0x1db42, arg1[4])
x4, x3 := bits.mul_u64(0x1db42, arg1[3]) x4, x3 := bits.mul_u64(0x1db42, arg1[3])
x6, x5 := bits.mul_u64(0x1db42, arg1[2]) x6, x5 := bits.mul_u64(0x1db42, arg1[2])
@@ -565,54 +584,3 @@ fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_El
out1[3] = x27 out1[3] = x27
out1[4] = x32 out1[4] = x32
} }
// The following routines were added by hand, and do not come from fiat-crypto.
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 1
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
x4 := arg1[3]
x5 := arg1[4]
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
out1[4] = x5
}
@(optimization_mode="none")
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
mask := -u64(arg1)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
x = (out1[3] ~ out2[3]) & mask
x4, y4 := out1[3] ~ x, out2[3] ~ x
x = (out1[4] ~ out2[4]) & mask
x5, y5 := out1[4] ~ x, out2[4] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
out1[3], out2[3] = x4, y4
out1[4], out2[4] = x5, y5
}
+47 -4
View File
@@ -1,17 +1,26 @@
package field_poly1305 package field_poly1305
import "base:intrinsics"
import "core:encoding/endian" import "core:encoding/endian"
import "core:mem" import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element { fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1) return transmute(^Loose_Field_Element)(arg1)
} }
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element { fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1) return transmute(^Tight_Field_Element)(arg1)
} }
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte) { fe_from_bytes :: #force_inline proc "contextless" (
out1: ^Tight_Field_Element,
arg1: []byte,
arg2: byte,
) {
// fiat-crypto's deserialization routine effectively processes a // fiat-crypto's deserialization routine effectively processes a
// single byte at a time, and wants 256-bits of input for a value // single byte at a time, and wants 256-bits of input for a value
// that will be 128-bits or 129-bits. // that will be 128-bits or 129-bits.
@@ -20,7 +29,9 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
// makes implementing the actual MAC block processing considerably // makes implementing the actual MAC block processing considerably
// neater. // neater.
assert(len(arg1) == 16) if len(arg1) != 16 {
intrinsics.trap()
}
// While it may be unwise to do deserialization here on our // While it may be unwise to do deserialization here on our
// own when fiat-crypto provides equivalent functionality, // own when fiat-crypto provides equivalent functionality,
@@ -51,3 +62,35 @@ fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
// This routine is only used to deserialize `r` which is confidential. // This routine is only used to deserialize `r` which is confidential.
mem.zero_explicit(&tmp, size_of(tmp)) mem.zero_explicit(&tmp, size_of(tmp))
} }
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
}
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
out1[0] = x1
out1[1] = x2
out1[2] = x3
}
@(optimization_mode = "none")
fe_cond_swap :: #force_no_inline proc "contextless" (
out1, out2: ^Tight_Field_Element,
arg1: bool,
) {
mask := (u64(arg1) * 0xffffffffffffffff)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
}
+35 -39
View File
@@ -39,7 +39,13 @@ import "core:math/bits"
Loose_Field_Element :: distinct [3]u64 Loose_Field_Element :: distinct [3]u64
Tight_Field_Element :: distinct [3]u64 Tight_Field_Element :: distinct [3]u64
_addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) { _addcarryx_u44 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((u64(arg1) + arg2) + arg3) x1 := ((u64(arg1) + arg2) + arg3)
x2 := (x1 & 0xfffffffffff) x2 := (x1 & 0xfffffffffff)
x3 := fiat.u1((x1 >> 44)) x3 := fiat.u1((x1 >> 44))
@@ -48,7 +54,13 @@ _addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
return return
} }
_subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) { _subborrowx_u44 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3)) x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
x2 := fiat.i1((x1 >> 44)) x2 := fiat.i1((x1 >> 44))
x3 := (u64(x1) & 0xfffffffffff) x3 := (u64(x1) & 0xfffffffffff)
@@ -57,7 +69,13 @@ _subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
return return
} }
_addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) { _addcarryx_u43 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((u64(arg1) + arg2) + arg3) x1 := ((u64(arg1) + arg2) + arg3)
x2 := (x1 & 0x7ffffffffff) x2 := (x1 & 0x7ffffffffff)
x3 := fiat.u1((x1 >> 43)) x3 := fiat.u1((x1 >> 43))
@@ -66,7 +84,13 @@ _addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
return return
} }
_subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) { _subborrowx_u43 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3)) x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
x2 := fiat.i1((x1 >> 43)) x2 := fiat.i1((x1 >> 43))
x3 := (u64(x1) & 0x7ffffffffff) x3 := (u64(x1) & 0x7ffffffffff)
@@ -75,7 +99,7 @@ _subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
return return
} }
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) { fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5)) x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5))
x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa)) x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa))
x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa)) x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa))
@@ -120,7 +144,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme
out1[2] = x62 out1[2] = x62
} }
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
x1 := (arg1[2] * 0x5) x1 := (arg1[2] * 0x5)
x2 := (x1 * 0x2) x2 := (x1 * 0x2)
x3 := (arg1[2] * 0x2) x3 := (arg1[2] * 0x2)
@@ -201,8 +225,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
out1[2] = x3 out1[2] = x3
} }
@(optimization_mode="none") @(optimization_mode = "none")
fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) { fe_cond_assign :: #force_no_inline proc "contextless" (
out1, arg1: ^Tight_Field_Element,
arg2: bool,
) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0]) x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1]) x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2]) x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
@@ -325,34 +352,3 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E
out1[1] = x2 out1[1] = x2
out1[2] = x3 out1[2] = x3
} }
// The following routines were added by hand, and do not come from fiat-crypto.
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
}
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
out1[0] = x1
out1[1] = x2
out1[2] = x3
}
@(optimization_mode="none")
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
mask := -u64(arg1)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
}
@@ -0,0 +1,153 @@
package field_scalar25519
import "base:intrinsics"
import "core:encoding/endian"
import "core:math/bits"
import "core:mem"
@(private)
_TWO_168 := Montgomery_Domain_Field_Element {
0x5b8ab432eac74798,
0x38afddd6de59d5d7,
0xa2c131b399411b7c,
0x6329a7ed9ce5a30,
}
@(private)
_TWO_336 := Montgomery_Domain_Field_Element {
0xbd3d108e2b35ecc5,
0x5c3a3718bdf9c90b,
0x63aa97a331b4f2ee,
0x3d217f5be65cb5c,
}
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
}
fe_from_bytes :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^[32]byte,
unsafe_assume_canonical := false,
) -> bool {
tmp := Non_Montgomery_Domain_Field_Element {
endian.unchecked_get_u64le(arg1[0:]),
endian.unchecked_get_u64le(arg1[8:]),
endian.unchecked_get_u64le(arg1[16:]),
endian.unchecked_get_u64le(arg1[24:]),
}
defer mem.zero_explicit(&tmp, size_of(tmp))
// Check that tmp is in the the range [0, ELL).
if !unsafe_assume_canonical {
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
if borrow != 0 {
return false
}
}
fe_to_montgomery(out1, &tmp)
return true
}
fe_from_bytes_rfc8032 :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^[32]byte,
) {
tmp: [64]byte
copy(tmp[:], arg1[:])
// Apply "clamping" as in RFC 8032.
tmp[0] &= 248
tmp[31] &= 127
tmp[31] |= 64 // Sets the 254th bit, so the encoding is non-canonical.
fe_from_bytes_wide(out1, &tmp)
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_from_bytes_wide :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^[64]byte,
) {
tmp: Montgomery_Domain_Field_Element
// Use Frank Denis' trick, as documented by Filippo Valsorda
// at https://words.filippo.io/dispatches/wide-reduction/
//
// x = c * 2^336 + b * 2^168 + a mod l
_fe_from_bytes_short(out1, arg1[:21]) // a
_fe_from_bytes_short(&tmp, arg1[21:42]) // b
fe_mul(&tmp, &tmp, &_TWO_168) // b * 2^168
fe_add(out1, out1, &tmp) // a + b * 2^168
_fe_from_bytes_short(&tmp, arg1[42:]) // c
fe_mul(&tmp, &tmp, &_TWO_336) // c * 2^336
fe_add(out1, out1, &tmp) // a + b * 2^168 + c * 2^336
fe_clear(&tmp)
}
@(private)
_fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
// INVARIANT: len(arg1) < 32.
if len(arg1) >= 32 {
intrinsics.trap()
}
tmp: [32]byte
copy(tmp[:], arg1)
_ = fe_from_bytes(out1, &tmp, true)
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
if len(out1) != 32 {
intrinsics.trap()
}
tmp: Non_Montgomery_Domain_Field_Element
fe_from_montgomery(&tmp, arg1)
endian.unchecked_put_u64le(out1[0:], tmp[0])
endian.unchecked_put_u64le(out1[8:], tmp[1])
endian.unchecked_put_u64le(out1[16:], tmp[2])
endian.unchecked_put_u64le(out1[24:], tmp[3])
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
tmp: Montgomery_Domain_Field_Element
fe_sub(&tmp, arg1, arg2)
// This will only underflow iff arg1 == arg2, and we return the borrow,
// which will be 1.
_, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0)
fe_clear(&tmp)
return int(borrow)
}
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
out1[3] = 0
}
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
x4 := arg1[3]
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
}
@@ -0,0 +1,535 @@
// The BSD 1-Clause License (BSD-1-Clause)
//
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
// 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 fiat-crypto 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 Berkeley Software Design,
// Inc. 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.
package field_scalar25519
// The file provides arithmetic on the field Z/(2^252+27742317777372353535851937790883648493)
// using a 64-bit Montgomery form internal representation. It is derived
// primarily from the machine generated Golang output from the fiat-crypto
// project.
//
// While the base implementation is provably correct, this implementation
// makes no such claims as the port and optimizations were done by hand.
import fiat "core:crypto/_fiat"
import "core:math/bits"
// ELL is the saturated representation of the field order, least-significant
// limb first.
ELL :: [4]u64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000}
Montgomery_Domain_Field_Element :: distinct [4]u64
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
x1 := arg1[1]
x2 := arg1[2]
x3 := arg1[3]
x4 := arg1[0]
x6, x5 := bits.mul_u64(x4, arg2[3])
x8, x7 := bits.mul_u64(x4, arg2[2])
x10, x9 := bits.mul_u64(x4, arg2[1])
x12, x11 := bits.mul_u64(x4, arg2[0])
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
x19 := (u64(fiat.u1(x18)) + x6)
_, x20 := bits.mul_u64(x11, 0xd2b51da312547e1b)
x23, x22 := bits.mul_u64(x20, 0x1000000000000000)
x25, x24 := bits.mul_u64(x20, 0x14def9dea2f79cd6)
x27, x26 := bits.mul_u64(x20, 0x5812631a5cf5d3ed)
x28, x29 := bits.add_u64(x27, x24, u64(0x0))
x30 := (u64(fiat.u1(x29)) + x25)
_, x32 := bits.add_u64(x11, x26, u64(0x0))
x33, x34 := bits.add_u64(x13, x28, u64(fiat.u1(x32)))
x35, x36 := bits.add_u64(x15, x30, u64(fiat.u1(x34)))
x37, x38 := bits.add_u64(x17, x22, u64(fiat.u1(x36)))
x39, x40 := bits.add_u64(x19, x23, u64(fiat.u1(x38)))
x42, x41 := bits.mul_u64(x1, arg2[3])
x44, x43 := bits.mul_u64(x1, arg2[2])
x46, x45 := bits.mul_u64(x1, arg2[1])
x48, x47 := bits.mul_u64(x1, arg2[0])
x49, x50 := bits.add_u64(x48, x45, u64(0x0))
x51, x52 := bits.add_u64(x46, x43, u64(fiat.u1(x50)))
x53, x54 := bits.add_u64(x44, x41, u64(fiat.u1(x52)))
x55 := (u64(fiat.u1(x54)) + x42)
x56, x57 := bits.add_u64(x33, x47, u64(0x0))
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
x62, x63 := bits.add_u64(x39, x53, u64(fiat.u1(x61)))
x64, x65 := bits.add_u64(u64(fiat.u1(x40)), x55, u64(fiat.u1(x63)))
_, x66 := bits.mul_u64(x56, 0xd2b51da312547e1b)
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
x76 := (u64(fiat.u1(x75)) + x71)
_, x78 := bits.add_u64(x56, x72, u64(0x0))
x79, x80 := bits.add_u64(x58, x74, u64(fiat.u1(x78)))
x81, x82 := bits.add_u64(x60, x76, u64(fiat.u1(x80)))
x83, x84 := bits.add_u64(x62, x68, u64(fiat.u1(x82)))
x85, x86 := bits.add_u64(x64, x69, u64(fiat.u1(x84)))
x87 := (u64(fiat.u1(x86)) + u64(fiat.u1(x65)))
x89, x88 := bits.mul_u64(x2, arg2[3])
x91, x90 := bits.mul_u64(x2, arg2[2])
x93, x92 := bits.mul_u64(x2, arg2[1])
x95, x94 := bits.mul_u64(x2, arg2[0])
x96, x97 := bits.add_u64(x95, x92, u64(0x0))
x98, x99 := bits.add_u64(x93, x90, u64(fiat.u1(x97)))
x100, x101 := bits.add_u64(x91, x88, u64(fiat.u1(x99)))
x102 := (u64(fiat.u1(x101)) + x89)
x103, x104 := bits.add_u64(x79, x94, u64(0x0))
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
x109, x110 := bits.add_u64(x85, x100, u64(fiat.u1(x108)))
x111, x112 := bits.add_u64(x87, x102, u64(fiat.u1(x110)))
_, x113 := bits.mul_u64(x103, 0xd2b51da312547e1b)
x116, x115 := bits.mul_u64(x113, 0x1000000000000000)
x118, x117 := bits.mul_u64(x113, 0x14def9dea2f79cd6)
x120, x119 := bits.mul_u64(x113, 0x5812631a5cf5d3ed)
x121, x122 := bits.add_u64(x120, x117, u64(0x0))
x123 := (u64(fiat.u1(x122)) + x118)
_, x125 := bits.add_u64(x103, x119, u64(0x0))
x126, x127 := bits.add_u64(x105, x121, u64(fiat.u1(x125)))
x128, x129 := bits.add_u64(x107, x123, u64(fiat.u1(x127)))
x130, x131 := bits.add_u64(x109, x115, u64(fiat.u1(x129)))
x132, x133 := bits.add_u64(x111, x116, u64(fiat.u1(x131)))
x134 := (u64(fiat.u1(x133)) + u64(fiat.u1(x112)))
x136, x135 := bits.mul_u64(x3, arg2[3])
x138, x137 := bits.mul_u64(x3, arg2[2])
x140, x139 := bits.mul_u64(x3, arg2[1])
x142, x141 := bits.mul_u64(x3, arg2[0])
x143, x144 := bits.add_u64(x142, x139, u64(0x0))
x145, x146 := bits.add_u64(x140, x137, u64(fiat.u1(x144)))
x147, x148 := bits.add_u64(x138, x135, u64(fiat.u1(x146)))
x149 := (u64(fiat.u1(x148)) + x136)
x150, x151 := bits.add_u64(x126, x141, u64(0x0))
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
x154, x155 := bits.add_u64(x130, x145, u64(fiat.u1(x153)))
x156, x157 := bits.add_u64(x132, x147, u64(fiat.u1(x155)))
x158, x159 := bits.add_u64(x134, x149, u64(fiat.u1(x157)))
_, x160 := bits.mul_u64(x150, 0xd2b51da312547e1b)
x163, x162 := bits.mul_u64(x160, 0x1000000000000000)
x165, x164 := bits.mul_u64(x160, 0x14def9dea2f79cd6)
x167, x166 := bits.mul_u64(x160, 0x5812631a5cf5d3ed)
x168, x169 := bits.add_u64(x167, x164, u64(0x0))
x170 := (u64(fiat.u1(x169)) + x165)
_, x172 := bits.add_u64(x150, x166, u64(0x0))
x173, x174 := bits.add_u64(x152, x168, u64(fiat.u1(x172)))
x175, x176 := bits.add_u64(x154, x170, u64(fiat.u1(x174)))
x177, x178 := bits.add_u64(x156, x162, u64(fiat.u1(x176)))
x179, x180 := bits.add_u64(x158, x163, u64(fiat.u1(x178)))
x181 := (u64(fiat.u1(x180)) + u64(fiat.u1(x159)))
x182, x183 := bits.sub_u64(x173, 0x5812631a5cf5d3ed, u64(0x0))
x184, x185 := bits.sub_u64(x175, 0x14def9dea2f79cd6, u64(fiat.u1(x183)))
x186, x187 := bits.sub_u64(x177, u64(0x0), u64(fiat.u1(x185)))
x188, x189 := bits.sub_u64(x179, 0x1000000000000000, u64(fiat.u1(x187)))
_, x191 := bits.sub_u64(x181, u64(0x0), u64(fiat.u1(x189)))
x192 := fiat.cmovznz_u64(fiat.u1(x191), x182, x173)
x193 := fiat.cmovznz_u64(fiat.u1(x191), x184, x175)
x194 := fiat.cmovznz_u64(fiat.u1(x191), x186, x177)
x195 := fiat.cmovznz_u64(fiat.u1(x191), x188, x179)
out1[0] = x192
out1[1] = x193
out1[2] = x194
out1[3] = x195
}
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
x1 := arg1[1]
x2 := arg1[2]
x3 := arg1[3]
x4 := arg1[0]
x6, x5 := bits.mul_u64(x4, arg1[3])
x8, x7 := bits.mul_u64(x4, arg1[2])
x10, x9 := bits.mul_u64(x4, arg1[1])
x12, x11 := bits.mul_u64(x4, arg1[0])
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
x19 := (u64(fiat.u1(x18)) + x6)
_, x20 := bits.mul_u64(x11, 0xd2b51da312547e1b)
x23, x22 := bits.mul_u64(x20, 0x1000000000000000)
x25, x24 := bits.mul_u64(x20, 0x14def9dea2f79cd6)
x27, x26 := bits.mul_u64(x20, 0x5812631a5cf5d3ed)
x28, x29 := bits.add_u64(x27, x24, u64(0x0))
x30 := (u64(fiat.u1(x29)) + x25)
_, x32 := bits.add_u64(x11, x26, u64(0x0))
x33, x34 := bits.add_u64(x13, x28, u64(fiat.u1(x32)))
x35, x36 := bits.add_u64(x15, x30, u64(fiat.u1(x34)))
x37, x38 := bits.add_u64(x17, x22, u64(fiat.u1(x36)))
x39, x40 := bits.add_u64(x19, x23, u64(fiat.u1(x38)))
x42, x41 := bits.mul_u64(x1, arg1[3])
x44, x43 := bits.mul_u64(x1, arg1[2])
x46, x45 := bits.mul_u64(x1, arg1[1])
x48, x47 := bits.mul_u64(x1, arg1[0])
x49, x50 := bits.add_u64(x48, x45, u64(0x0))
x51, x52 := bits.add_u64(x46, x43, u64(fiat.u1(x50)))
x53, x54 := bits.add_u64(x44, x41, u64(fiat.u1(x52)))
x55 := (u64(fiat.u1(x54)) + x42)
x56, x57 := bits.add_u64(x33, x47, u64(0x0))
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
x62, x63 := bits.add_u64(x39, x53, u64(fiat.u1(x61)))
x64, x65 := bits.add_u64(u64(fiat.u1(x40)), x55, u64(fiat.u1(x63)))
_, x66 := bits.mul_u64(x56, 0xd2b51da312547e1b)
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
x76 := (u64(fiat.u1(x75)) + x71)
_, x78 := bits.add_u64(x56, x72, u64(0x0))
x79, x80 := bits.add_u64(x58, x74, u64(fiat.u1(x78)))
x81, x82 := bits.add_u64(x60, x76, u64(fiat.u1(x80)))
x83, x84 := bits.add_u64(x62, x68, u64(fiat.u1(x82)))
x85, x86 := bits.add_u64(x64, x69, u64(fiat.u1(x84)))
x87 := (u64(fiat.u1(x86)) + u64(fiat.u1(x65)))
x89, x88 := bits.mul_u64(x2, arg1[3])
x91, x90 := bits.mul_u64(x2, arg1[2])
x93, x92 := bits.mul_u64(x2, arg1[1])
x95, x94 := bits.mul_u64(x2, arg1[0])
x96, x97 := bits.add_u64(x95, x92, u64(0x0))
x98, x99 := bits.add_u64(x93, x90, u64(fiat.u1(x97)))
x100, x101 := bits.add_u64(x91, x88, u64(fiat.u1(x99)))
x102 := (u64(fiat.u1(x101)) + x89)
x103, x104 := bits.add_u64(x79, x94, u64(0x0))
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
x109, x110 := bits.add_u64(x85, x100, u64(fiat.u1(x108)))
x111, x112 := bits.add_u64(x87, x102, u64(fiat.u1(x110)))
_, x113 := bits.mul_u64(x103, 0xd2b51da312547e1b)
x116, x115 := bits.mul_u64(x113, 0x1000000000000000)
x118, x117 := bits.mul_u64(x113, 0x14def9dea2f79cd6)
x120, x119 := bits.mul_u64(x113, 0x5812631a5cf5d3ed)
x121, x122 := bits.add_u64(x120, x117, u64(0x0))
x123 := (u64(fiat.u1(x122)) + x118)
_, x125 := bits.add_u64(x103, x119, u64(0x0))
x126, x127 := bits.add_u64(x105, x121, u64(fiat.u1(x125)))
x128, x129 := bits.add_u64(x107, x123, u64(fiat.u1(x127)))
x130, x131 := bits.add_u64(x109, x115, u64(fiat.u1(x129)))
x132, x133 := bits.add_u64(x111, x116, u64(fiat.u1(x131)))
x134 := (u64(fiat.u1(x133)) + u64(fiat.u1(x112)))
x136, x135 := bits.mul_u64(x3, arg1[3])
x138, x137 := bits.mul_u64(x3, arg1[2])
x140, x139 := bits.mul_u64(x3, arg1[1])
x142, x141 := bits.mul_u64(x3, arg1[0])
x143, x144 := bits.add_u64(x142, x139, u64(0x0))
x145, x146 := bits.add_u64(x140, x137, u64(fiat.u1(x144)))
x147, x148 := bits.add_u64(x138, x135, u64(fiat.u1(x146)))
x149 := (u64(fiat.u1(x148)) + x136)
x150, x151 := bits.add_u64(x126, x141, u64(0x0))
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
x154, x155 := bits.add_u64(x130, x145, u64(fiat.u1(x153)))
x156, x157 := bits.add_u64(x132, x147, u64(fiat.u1(x155)))
x158, x159 := bits.add_u64(x134, x149, u64(fiat.u1(x157)))
_, x160 := bits.mul_u64(x150, 0xd2b51da312547e1b)
x163, x162 := bits.mul_u64(x160, 0x1000000000000000)
x165, x164 := bits.mul_u64(x160, 0x14def9dea2f79cd6)
x167, x166 := bits.mul_u64(x160, 0x5812631a5cf5d3ed)
x168, x169 := bits.add_u64(x167, x164, u64(0x0))
x170 := (u64(fiat.u1(x169)) + x165)
_, x172 := bits.add_u64(x150, x166, u64(0x0))
x173, x174 := bits.add_u64(x152, x168, u64(fiat.u1(x172)))
x175, x176 := bits.add_u64(x154, x170, u64(fiat.u1(x174)))
x177, x178 := bits.add_u64(x156, x162, u64(fiat.u1(x176)))
x179, x180 := bits.add_u64(x158, x163, u64(fiat.u1(x178)))
x181 := (u64(fiat.u1(x180)) + u64(fiat.u1(x159)))
x182, x183 := bits.sub_u64(x173, 0x5812631a5cf5d3ed, u64(0x0))
x184, x185 := bits.sub_u64(x175, 0x14def9dea2f79cd6, u64(fiat.u1(x183)))
x186, x187 := bits.sub_u64(x177, u64(0x0), u64(fiat.u1(x185)))
x188, x189 := bits.sub_u64(x179, 0x1000000000000000, u64(fiat.u1(x187)))
_, x191 := bits.sub_u64(x181, u64(0x0), u64(fiat.u1(x189)))
x192 := fiat.cmovznz_u64(fiat.u1(x191), x182, x173)
x193 := fiat.cmovznz_u64(fiat.u1(x191), x184, x175)
x194 := fiat.cmovznz_u64(fiat.u1(x191), x186, x177)
x195 := fiat.cmovznz_u64(fiat.u1(x191), x188, x179)
out1[0] = x192
out1[1] = x193
out1[2] = x194
out1[3] = x195
}
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
x9, x10 := bits.sub_u64(x1, 0x5812631a5cf5d3ed, u64(0x0))
x11, x12 := bits.sub_u64(x3, 0x14def9dea2f79cd6, u64(fiat.u1(x10)))
x13, x14 := bits.sub_u64(x5, u64(0x0), u64(fiat.u1(x12)))
x15, x16 := bits.sub_u64(x7, 0x1000000000000000, u64(fiat.u1(x14)))
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
out1[0] = x19
out1[1] = x20
out1[2] = x21
out1[3] = x22
}
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
x10, x11 := bits.add_u64(x1, (x9 & 0x5812631a5cf5d3ed), u64(0x0))
x12, x13 := bits.add_u64(x3, (x9 & 0x14def9dea2f79cd6), u64(fiat.u1(x11)))
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
x16, _ := bits.add_u64(x7, (x9 & 0x1000000000000000), u64(fiat.u1(x15)))
out1[0] = x10
out1[1] = x12
out1[2] = x14
out1[3] = x16
}
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
x10, x11 := bits.add_u64(x1, (x9 & 0x5812631a5cf5d3ed), u64(0x0))
x12, x13 := bits.add_u64(x3, (x9 & 0x14def9dea2f79cd6), u64(fiat.u1(x11)))
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
x16, _ := bits.add_u64(x7, (x9 & 0x1000000000000000), u64(fiat.u1(x15)))
out1[0] = x10
out1[1] = x12
out1[2] = x14
out1[3] = x16
}
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
out1[0] = 0xd6ec31748d98951d
out1[1] = 0xc6ef5bf4737dcf70
out1[2] = 0xfffffffffffffffe
out1[3] = 0xfffffffffffffff
}
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
}
@(optimization_mode = "none")
fe_cond_assign :: #force_no_inline proc "contextless" (
out1, arg1: ^Montgomery_Domain_Field_Element,
arg2: int,
) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
}
fe_from_montgomery :: proc "contextless" (
out1: ^Non_Montgomery_Domain_Field_Element,
arg1: ^Montgomery_Domain_Field_Element,
) {
x1 := arg1[0]
_, x2 := bits.mul_u64(x1, 0xd2b51da312547e1b)
x5, x4 := bits.mul_u64(x2, 0x1000000000000000)
x7, x6 := bits.mul_u64(x2, 0x14def9dea2f79cd6)
x9, x8 := bits.mul_u64(x2, 0x5812631a5cf5d3ed)
x10, x11 := bits.add_u64(x9, x6, u64(0x0))
_, x13 := bits.add_u64(x1, x8, u64(0x0))
x14, x15 := bits.add_u64(u64(0x0), x10, u64(fiat.u1(x13)))
x16, x17 := bits.add_u64(x14, arg1[1], u64(0x0))
_, x18 := bits.mul_u64(x16, 0xd2b51da312547e1b)
x21, x20 := bits.mul_u64(x18, 0x1000000000000000)
x23, x22 := bits.mul_u64(x18, 0x14def9dea2f79cd6)
x25, x24 := bits.mul_u64(x18, 0x5812631a5cf5d3ed)
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
_, x29 := bits.add_u64(x16, x24, u64(0x0))
x30, x31 := bits.add_u64(
(u64(fiat.u1(x17)) + (u64(fiat.u1(x15)) + (u64(fiat.u1(x11)) + x7))),
x26,
u64(fiat.u1(x29)),
)
x32, x33 := bits.add_u64(x4, (u64(fiat.u1(x27)) + x23), u64(fiat.u1(x31)))
x34, x35 := bits.add_u64(x5, x20, u64(fiat.u1(x33)))
x36, x37 := bits.add_u64(x30, arg1[2], u64(0x0))
x38, x39 := bits.add_u64(x32, u64(0x0), u64(fiat.u1(x37)))
x40, x41 := bits.add_u64(x34, u64(0x0), u64(fiat.u1(x39)))
_, x42 := bits.mul_u64(x36, 0xd2b51da312547e1b)
x45, x44 := bits.mul_u64(x42, 0x1000000000000000)
x47, x46 := bits.mul_u64(x42, 0x14def9dea2f79cd6)
x49, x48 := bits.mul_u64(x42, 0x5812631a5cf5d3ed)
x50, x51 := bits.add_u64(x49, x46, u64(0x0))
_, x53 := bits.add_u64(x36, x48, u64(0x0))
x54, x55 := bits.add_u64(x38, x50, u64(fiat.u1(x53)))
x56, x57 := bits.add_u64(x40, (u64(fiat.u1(x51)) + x47), u64(fiat.u1(x55)))
x58, x59 := bits.add_u64(
(u64(fiat.u1(x41)) + (u64(fiat.u1(x35)) + x21)),
x44,
u64(fiat.u1(x57)),
)
x60, x61 := bits.add_u64(x54, arg1[3], u64(0x0))
x62, x63 := bits.add_u64(x56, u64(0x0), u64(fiat.u1(x61)))
x64, x65 := bits.add_u64(x58, u64(0x0), u64(fiat.u1(x63)))
_, x66 := bits.mul_u64(x60, 0xd2b51da312547e1b)
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
_, x77 := bits.add_u64(x60, x72, u64(0x0))
x78, x79 := bits.add_u64(x62, x74, u64(fiat.u1(x77)))
x80, x81 := bits.add_u64(x64, (u64(fiat.u1(x75)) + x71), u64(fiat.u1(x79)))
x82, x83 := bits.add_u64(
(u64(fiat.u1(x65)) + (u64(fiat.u1(x59)) + x45)),
x68,
u64(fiat.u1(x81)),
)
x84 := (u64(fiat.u1(x83)) + x69)
x85, x86 := bits.sub_u64(x78, 0x5812631a5cf5d3ed, u64(0x0))
x87, x88 := bits.sub_u64(x80, 0x14def9dea2f79cd6, u64(fiat.u1(x86)))
x89, x90 := bits.sub_u64(x82, u64(0x0), u64(fiat.u1(x88)))
x91, x92 := bits.sub_u64(x84, 0x1000000000000000, u64(fiat.u1(x90)))
_, x94 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x92)))
x95 := fiat.cmovznz_u64(fiat.u1(x94), x85, x78)
x96 := fiat.cmovznz_u64(fiat.u1(x94), x87, x80)
x97 := fiat.cmovznz_u64(fiat.u1(x94), x89, x82)
x98 := fiat.cmovznz_u64(fiat.u1(x94), x91, x84)
out1[0] = x95
out1[1] = x96
out1[2] = x97
out1[3] = x98
}
fe_to_montgomery :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^Non_Montgomery_Domain_Field_Element,
) {
x1 := arg1[1]
x2 := arg1[2]
x3 := arg1[3]
x4 := arg1[0]
x6, x5 := bits.mul_u64(x4, 0x399411b7c309a3d)
x8, x7 := bits.mul_u64(x4, 0xceec73d217f5be65)
x10, x9 := bits.mul_u64(x4, 0xd00e1ba768859347)
x12, x11 := bits.mul_u64(x4, 0xa40611e3449c0f01)
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
_, x19 := bits.mul_u64(x11, 0xd2b51da312547e1b)
x22, x21 := bits.mul_u64(x19, 0x1000000000000000)
x24, x23 := bits.mul_u64(x19, 0x14def9dea2f79cd6)
x26, x25 := bits.mul_u64(x19, 0x5812631a5cf5d3ed)
x27, x28 := bits.add_u64(x26, x23, u64(0x0))
_, x30 := bits.add_u64(x11, x25, u64(0x0))
x31, x32 := bits.add_u64(x13, x27, u64(fiat.u1(x30)))
x33, x34 := bits.add_u64(x15, (u64(fiat.u1(x28)) + x24), u64(fiat.u1(x32)))
x35, x36 := bits.add_u64(x17, x21, u64(fiat.u1(x34)))
x38, x37 := bits.mul_u64(x1, 0x399411b7c309a3d)
x40, x39 := bits.mul_u64(x1, 0xceec73d217f5be65)
x42, x41 := bits.mul_u64(x1, 0xd00e1ba768859347)
x44, x43 := bits.mul_u64(x1, 0xa40611e3449c0f01)
x45, x46 := bits.add_u64(x44, x41, u64(0x0))
x47, x48 := bits.add_u64(x42, x39, u64(fiat.u1(x46)))
x49, x50 := bits.add_u64(x40, x37, u64(fiat.u1(x48)))
x51, x52 := bits.add_u64(x31, x43, u64(0x0))
x53, x54 := bits.add_u64(x33, x45, u64(fiat.u1(x52)))
x55, x56 := bits.add_u64(x35, x47, u64(fiat.u1(x54)))
x57, x58 := bits.add_u64(
((u64(fiat.u1(x36)) + (u64(fiat.u1(x18)) + x6)) + x22),
x49,
u64(fiat.u1(x56)),
)
_, x59 := bits.mul_u64(x51, 0xd2b51da312547e1b)
x62, x61 := bits.mul_u64(x59, 0x1000000000000000)
x64, x63 := bits.mul_u64(x59, 0x14def9dea2f79cd6)
x66, x65 := bits.mul_u64(x59, 0x5812631a5cf5d3ed)
x67, x68 := bits.add_u64(x66, x63, u64(0x0))
_, x70 := bits.add_u64(x51, x65, u64(0x0))
x71, x72 := bits.add_u64(x53, x67, u64(fiat.u1(x70)))
x73, x74 := bits.add_u64(x55, (u64(fiat.u1(x68)) + x64), u64(fiat.u1(x72)))
x75, x76 := bits.add_u64(x57, x61, u64(fiat.u1(x74)))
x78, x77 := bits.mul_u64(x2, 0x399411b7c309a3d)
x80, x79 := bits.mul_u64(x2, 0xceec73d217f5be65)
x82, x81 := bits.mul_u64(x2, 0xd00e1ba768859347)
x84, x83 := bits.mul_u64(x2, 0xa40611e3449c0f01)
x85, x86 := bits.add_u64(x84, x81, u64(0x0))
x87, x88 := bits.add_u64(x82, x79, u64(fiat.u1(x86)))
x89, x90 := bits.add_u64(x80, x77, u64(fiat.u1(x88)))
x91, x92 := bits.add_u64(x71, x83, u64(0x0))
x93, x94 := bits.add_u64(x73, x85, u64(fiat.u1(x92)))
x95, x96 := bits.add_u64(x75, x87, u64(fiat.u1(x94)))
x97, x98 := bits.add_u64(
((u64(fiat.u1(x76)) + (u64(fiat.u1(x58)) + (u64(fiat.u1(x50)) + x38))) + x62),
x89,
u64(fiat.u1(x96)),
)
_, x99 := bits.mul_u64(x91, 0xd2b51da312547e1b)
x102, x101 := bits.mul_u64(x99, 0x1000000000000000)
x104, x103 := bits.mul_u64(x99, 0x14def9dea2f79cd6)
x106, x105 := bits.mul_u64(x99, 0x5812631a5cf5d3ed)
x107, x108 := bits.add_u64(x106, x103, u64(0x0))
_, x110 := bits.add_u64(x91, x105, u64(0x0))
x111, x112 := bits.add_u64(x93, x107, u64(fiat.u1(x110)))
x113, x114 := bits.add_u64(x95, (u64(fiat.u1(x108)) + x104), u64(fiat.u1(x112)))
x115, x116 := bits.add_u64(x97, x101, u64(fiat.u1(x114)))
x118, x117 := bits.mul_u64(x3, 0x399411b7c309a3d)
x120, x119 := bits.mul_u64(x3, 0xceec73d217f5be65)
x122, x121 := bits.mul_u64(x3, 0xd00e1ba768859347)
x124, x123 := bits.mul_u64(x3, 0xa40611e3449c0f01)
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
x131, x132 := bits.add_u64(x111, x123, u64(0x0))
x133, x134 := bits.add_u64(x113, x125, u64(fiat.u1(x132)))
x135, x136 := bits.add_u64(x115, x127, u64(fiat.u1(x134)))
x137, x138 := bits.add_u64(
((u64(fiat.u1(x116)) + (u64(fiat.u1(x98)) + (u64(fiat.u1(x90)) + x78))) + x102),
x129,
u64(fiat.u1(x136)),
)
_, x139 := bits.mul_u64(x131, 0xd2b51da312547e1b)
x142, x141 := bits.mul_u64(x139, 0x1000000000000000)
x144, x143 := bits.mul_u64(x139, 0x14def9dea2f79cd6)
x146, x145 := bits.mul_u64(x139, 0x5812631a5cf5d3ed)
x147, x148 := bits.add_u64(x146, x143, u64(0x0))
_, x150 := bits.add_u64(x131, x145, u64(0x0))
x151, x152 := bits.add_u64(x133, x147, u64(fiat.u1(x150)))
x153, x154 := bits.add_u64(x135, (u64(fiat.u1(x148)) + x144), u64(fiat.u1(x152)))
x155, x156 := bits.add_u64(x137, x141, u64(fiat.u1(x154)))
x157 := ((u64(fiat.u1(x156)) + (u64(fiat.u1(x138)) + (u64(fiat.u1(x130)) + x118))) + x142)
x158, x159 := bits.sub_u64(x151, 0x5812631a5cf5d3ed, u64(0x0))
x160, x161 := bits.sub_u64(x153, 0x14def9dea2f79cd6, u64(fiat.u1(x159)))
x162, x163 := bits.sub_u64(x155, u64(0x0), u64(fiat.u1(x161)))
x164, x165 := bits.sub_u64(x157, 0x1000000000000000, u64(fiat.u1(x163)))
_, x167 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x165)))
x168 := fiat.cmovznz_u64(fiat.u1(x167), x158, x151)
x169 := fiat.cmovznz_u64(fiat.u1(x167), x160, x153)
x170 := fiat.cmovznz_u64(fiat.u1(x167), x162, x155)
x171 := fiat.cmovznz_u64(fiat.u1(x167), x164, x157)
out1[0] = x168
out1[1] = x169
out1[2] = x170
out1[3] = x171
}
+10
View File
@@ -1,3 +1,7 @@
/*
package crypto implements a selection of cryptography algorithms and useful
helper routines.
*/
package crypto package crypto
import "core:mem" import "core:mem"
@@ -51,3 +55,9 @@ rand_bytes :: proc (dst: []byte) {
_rand_bytes(dst) _rand_bytes(dst)
} }
// has_rand_bytes returns true iff the target has support for accessing the
// system entropty source.
has_rand_bytes :: proc () -> bool {
return _has_rand_bytes()
}
+314
View File
@@ -0,0 +1,314 @@
/*
package ed25519 implements the Ed25519 EdDSA signature algorithm.
See:
- https://datatracker.ietf.org/doc/html/rfc8032
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
- https://eprint.iacr.org/2020/1244.pdf
*/
package ed25519
import "core:crypto"
import grp "core:crypto/_edwards25519"
import "core:crypto/sha2"
import "core:mem"
// PRIVATE_KEY_SIZE is the byte-encoded private key size.
PRIVATE_KEY_SIZE :: 32
// PUBLIC_KEY_SIZE is the byte-encoded public key size.
PUBLIC_KEY_SIZE :: 32
// SIGNATURE_SIZE is the byte-encoded signature size.
SIGNATURE_SIZE :: 64
@(private)
NONCE_SIZE :: 32
// Private_Key is an Ed25519 private key.
Private_Key :: struct {
// WARNING: All of the members are to be treated as internal (ie:
// the Private_Key structure is intended to be opaque). There are
// subtle vulnerabilities that can be introduced if the internal
// values are allowed to be altered.
//
// See: https://github.com/MystenLabs/ed25519-unsafe-libs
_b: [PRIVATE_KEY_SIZE]byte,
_s: grp.Scalar,
_nonce: [NONCE_SIZE]byte,
_pub_key: Public_Key,
_is_initialized: bool,
}
// Public_Key is an Ed25519 public key.
Public_Key :: struct {
// WARNING: All of the members are to be treated as internal (ie:
// the Public_Key structure is intended to be opaque).
_b: [PUBLIC_KEY_SIZE]byte,
_neg_A: grp.Group_Element,
_is_valid: bool,
_is_initialized: bool,
}
// private_key_set_bytes decodes a byte-encoded private key, and returns
// true iff the operation was successful.
private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
if len(b) != PRIVATE_KEY_SIZE {
return false
}
// Derive the private key.
ctx: sha2.Context_512 = ---
h_bytes: [sha2.DIGEST_SIZE_512]byte = ---
sha2.init_512(&ctx)
sha2.update(&ctx, b)
sha2.final(&ctx, h_bytes[:])
copy(priv_key._b[:], b)
copy(priv_key._nonce[:], h_bytes[32:])
grp.sc_set_bytes_rfc8032(&priv_key._s, h_bytes[:32])
// Derive the corresponding public key.
A: grp.Group_Element = ---
grp.ge_scalarmult_basepoint(&A, &priv_key._s)
grp.ge_bytes(&A, priv_key._pub_key._b[:])
grp.ge_negate(&priv_key._pub_key._neg_A, &A)
priv_key._pub_key._is_valid = !grp.ge_is_small_order(&A)
priv_key._pub_key._is_initialized = true
priv_key._is_initialized = true
return true
}
// private_key_bytes sets dst to byte-encoding of priv_key.
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
if !priv_key._is_initialized {
panic("crypto/ed25519: uninitialized private key")
}
if len(dst) != PRIVATE_KEY_SIZE {
panic("crypto/ed25519: invalid destination size")
}
copy(dst, priv_key._b[:])
}
// private_key_clear clears priv_key to the uninitialized state.
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
mem.zero_explicit(priv_key, size_of(Private_Key))
}
// sign writes the signature by priv_key over msg to sig.
sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
if !priv_key._is_initialized {
panic("crypto/ed25519: uninitialized private key")
}
if len(sig) != SIGNATURE_SIZE {
panic("crypto/ed25519: invalid destination size")
}
// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
// using SHA-512 for Ed25519. H(d) may be precomputed.
//
// 2. Using the second half of the digest hdigest2 = hb || ... || h2b-1,
// define:
//
// 2.1 For Ed25519, r = SHA-512(hdigest2 || M); Interpret r as a
// 64-octet little-endian integer.
ctx: sha2.Context_512 = ---
digest_bytes: [sha2.DIGEST_SIZE_512]byte = ---
sha2.init_512(&ctx)
sha2.update(&ctx, priv_key._nonce[:])
sha2.update(&ctx, msg)
sha2.final(&ctx, digest_bytes[:])
r: grp.Scalar = ---
grp.sc_set_bytes_wide(&r, &digest_bytes)
// 3. Compute the point [r]G. The octet string R is the encoding of
// the point [r]G.
R: grp.Group_Element = ---
R_bytes := sig[:32]
grp.ge_scalarmult_basepoint(&R, &r)
grp.ge_bytes(&R, R_bytes)
// 4. Derive s from H(d) as in the key pair generation algorithm.
// Use octet strings R, Q, and M to define:
//
// 4.1 For Ed25519, digest = SHA-512(R || Q || M).
// Interpret digest as a little-endian integer.
sha2.init_512(&ctx)
sha2.update(&ctx, R_bytes)
sha2.update(&ctx, priv_key._pub_key._b[:]) // Q in NIST terminology.
sha2.update(&ctx, msg)
sha2.final(&ctx, digest_bytes[:])
sc: grp.Scalar = --- // `digest` in NIST terminology.
grp.sc_set_bytes_wide(&sc, &digest_bytes)
// 5. Compute S = (r + digest × s) mod n. The octet string S is the
// encoding of the resultant integer.
grp.sc_mul(&sc, &sc, &priv_key._s)
grp.sc_add(&sc, &sc, &r)
// 6. Form the signature as the concatenation of the octet strings
// R and S.
grp.sc_bytes(sig[32:], &sc)
grp.sc_clear(&r)
}
// public_key_set_bytes decodes a byte-encoded public key, and returns
// true iff the operation was successful.
public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) -> bool {
if len(b) != PUBLIC_KEY_SIZE {
return false
}
A: grp.Group_Element = ---
if !grp.ge_set_bytes(&A, b) {
return false
}
copy(pub_key._b[:], b)
grp.ge_negate(&pub_key._neg_A, &A)
pub_key._is_valid = !grp.ge_is_small_order(&A)
pub_key._is_initialized = true
return 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) {
if !priv_key._is_initialized {
panic("crypto/ed25519: uninitialized public key")
}
src := &priv_key._pub_key
copy(pub_key._b[:], src._b[:])
grp.ge_set(&pub_key._neg_A, &src._neg_A)
pub_key._is_valid = src._is_valid
pub_key._is_initialized = src._is_initialized
}
// public_key_bytes sets dst to byte-encoding of pub_key.
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
if !pub_key._is_initialized {
panic("crypto/ed25519: uninitialized public key")
}
if len(dst) != PUBLIC_KEY_SIZE {
panic("crypto/ed25519: invalid destination size")
}
copy(dst, pub_key._b[:])
}
// public_key_equal returns true iff pub_key is equal to other.
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
if !pub_key._is_initialized || !other._is_initialized {
panic("crypto/ed25519: uninitialized public key")
}
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
}
// verify returns true iff sig is a valid signature by pub_key over msg.
//
// The optional `allow_small_order_A` parameter will make this
// 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.
verify :: proc(pub_key: ^Public_Key, msg, sig: []byte, allow_small_order_A := false) -> bool {
switch {
case !pub_key._is_initialized:
return false
case len(sig) != SIGNATURE_SIZE:
return false
}
// TLDR: Just use ristretto255.
//
// While there are two "standards" for EdDSA, existing implementations
// diverge (sometimes dramatically). This implementation opts for
// "Algorithm 2" from "Taming the Many EdDSAs", which provides the
// strongest notion of security (SUF-CMA + SBS).
//
// The relevant properties are:
// - Reject non-canonical S.
// - Reject non-canonical A/R.
// - Reject small-order A (Extra non-standard check).
// - Cofactored verification equation.
//
// There are 19 possible non-canonical group element encodings of
// which:
// - 2 are small order
// - 10 are mixed order
// - 7 are not on the curve
//
// While historical implementations have been lax about enforcing
// that A/R are canonically encoded, that behavior is mandated by
// both the RFC and FIPS specification. No valid key generation
// or sign implementation will ever produce non-canonically encoded
// public keys or signatures.
//
// There are 8 small-order group elements, 1 which is in the
// prime-order sub-group, and thus the probability that a properly
// generated A is small-order is cryptographically insignificant.
//
// While both the RFC and FIPS standard allow for either the
// cofactored or non-cofactored equation. It is possible to
// artificially produce signatures that are valid for the former
// but not the latter. This will NEVER occur with a valid sign
// implementation. The choice of the latter is to be compatible
// with ABGLSV-Pornin, batch verification, and FROST (among other
// things).
s_bytes, r_bytes := sig[32:], sig[:32]
// 1. Reject the signature if S is not in the range [0, L).
s: grp.Scalar = ---
if !grp.sc_set_bytes(&s, s_bytes) {
return false
}
// 2. Reject the signature if the public key A is one of 8 small
// order points.
//
// As this check is optional and not part of the standard, we allow
// the caller to bypass it if desired. Disabling the check makes
// the scheme NOT SBS-secure.
if !pub_key._is_valid && !allow_small_order_A {
return false
}
// 3. Reject the signature if A or R are non-canonical.
//
// Note: All initialized public keys are guaranteed to be canonical.
neg_R: grp.Group_Element = ---
if !grp.ge_set_bytes(&neg_R, r_bytes) {
return false
}
grp.ge_negate(&neg_R, &neg_R)
// 4. Compute the hash SHA512(R||A||M) and reduce it mod L to get a
// scalar h.
ctx: sha2.Context_512 = ---
h_bytes: [sha2.DIGEST_SIZE_512]byte = ---
sha2.init_512(&ctx)
sha2.update(&ctx, r_bytes)
sha2.update(&ctx, pub_key._b[:])
sha2.update(&ctx, msg)
sha2.final(&ctx, h_bytes[:])
h: grp.Scalar = ---
grp.sc_set_bytes_wide(&h, &h_bytes)
// 5. Accept if 8(s * G) - 8R - 8(h * A) = 0
//
// > first compute V = SB R hA and then accept if V is one of
// > 8 small order points (or alternatively compute 8V with 3
// > doublings and check against the neutral element)
V: grp.Group_Element = ---
grp.ge_double_scalarmult_basepoint_vartime(&V, &h, &pub_key._neg_A, &s)
grp.ge_add(&V, &V, &neg_R)
return grp.ge_is_small_order(&V)
}
+1 -1
View File
@@ -168,7 +168,7 @@ reset :: proc(ctx: ^Context) {
} }
@(private) @(private)
_blocks :: proc(ctx: ^Context, msg: []byte, final := false) { _blocks :: proc "contextless" (ctx: ^Context, msg: []byte, final := false) {
n: field.Tight_Field_Element = --- n: field.Tight_Field_Element = ---
final_byte := byte(!final) final_byte := byte(!final)
+4
View File
@@ -10,3 +10,7 @@ foreign libc {
_rand_bytes :: proc(dst: []byte) { _rand_bytes :: proc(dst: []byte) {
arc4random_buf(raw_data(dst), len(dst)) arc4random_buf(raw_data(dst), len(dst))
} }
_has_rand_bytes :: proc () -> bool {
return true
}
+4
View File
@@ -10,3 +10,7 @@ _rand_bytes :: proc(dst: []byte) {
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg)) panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg))
} }
} }
_has_rand_bytes :: proc () -> bool {
return true
}
+4
View File
@@ -9,3 +9,7 @@ package crypto
_rand_bytes :: proc(dst: []byte) { _rand_bytes :: proc(dst: []byte) {
unimplemented("crypto: rand_bytes not supported on this OS") unimplemented("crypto: rand_bytes not supported on this OS")
} }
_has_rand_bytes :: proc () -> bool {
return false
}
+4
View File
@@ -18,3 +18,7 @@ _rand_bytes :: proc(dst: []byte) {
dst = dst[to_read:] dst = dst[to_read:]
} }
} }
_has_rand_bytes :: proc () -> bool {
return true
}
+4
View File
@@ -34,3 +34,7 @@ _rand_bytes :: proc (dst: []byte) {
dst = dst[n_read:] dst = dst[n_read:]
} }
} }
_has_rand_bytes :: proc () -> bool {
return true
}
+4
View File
@@ -21,3 +21,7 @@ _rand_bytes :: proc(dst: []byte) {
} }
} }
} }
_has_rand_bytes :: proc () -> bool {
return true
}
+510
View File
@@ -0,0 +1,510 @@
/*
package ristretto255 implement the ristretto255 prime-order group.
See:
- https://www.rfc-editor.org/rfc/rfc9496
*/
package ristretto255
import grp "core:crypto/_edwards25519"
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
// ELEMENT_SIZE is the size of a byte-encoded ristretto255 group element.
ELEMENT_SIZE :: 32
// WIDE_ELEMENT_SIZE is the side of a wide byte-encoded ristretto255
// group element.
WIDE_ELEMENT_SIZE :: 64
@(private)
FE_NEG_ONE := field.Tight_Field_Element {
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
}
@(private)
FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
278908739862762,
821645201101625,
8113234426968,
1777959178193151,
2118520810568447,
}
@(private)
FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
1136626929484150,
1998550399581263,
496427632559748,
118527312129759,
45110755273534,
}
@(private)
FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
1507062230895904,
1572317787530805,
683053064812840,
317374165784489,
1572899562415810,
}
@(private)
FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element {
2241493124984347,
425987919032274,
2207028919301688,
1220490630685848,
974799131293748,
}
@(private)
GE_IDENTITY := Group_Element{grp.GE_IDENTITY, true}
// Group_Element is a ristretto255 group element. The zero-initialized
// value is invalid.
Group_Element :: struct {
// WARNING: While the internal representation is an Edwards25519
// group element, this is not guaranteed to always be the case,
// and your code *WILL* break if you mess with `_p`.
_p: grp.Group_Element,
_is_initialized: bool,
}
// ge_clear clears ge to the uninitialized state.
ge_clear :: proc "contextless" (ge: ^Group_Element) {
mem.zero_explicit(ge, size_of(Group_Element))
}
// ge_set sets `ge = a`.
ge_set :: proc(ge, a: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_set(&ge._p, &a._p)
ge._is_initialized = true
}
// ge_identity sets ge to the identity (neutral) element.
ge_identity :: proc "contextless" (ge: ^Group_Element) {
grp.ge_identity(&ge._p)
ge._is_initialized = true
}
// ge_generator sets ge to the group generator.
ge_generator :: proc "contextless" (ge: ^Group_Element) {
grp.ge_generator(&ge._p)
ge._is_initialized = true
}
// ge_set_bytes sets ge to the result of decoding b as a ristretto255
// group element, and returns true on success.
@(require_results)
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
// 1. Interpret the string as an unsigned integer s in little-endian
// representation. If the length of the string is not 32 bytes or
// if the resulting value is >= p, decoding fails.
//
// 2. If IS_NEGATIVE(s) returns TRUE, decoding fails.
if len(b) != ELEMENT_SIZE {
return false
}
if b[31] & 128 != 0 || b[0] & 1 != 0 {
// Fail early if b is clearly > p, or negative.
return false
}
b_ := transmute(^[32]byte)(raw_data(b))
s: field.Tight_Field_Element = ---
defer field.fe_clear(&s)
field.fe_from_bytes(&s, b_)
if field.fe_equal_bytes(&s, b_) != 1 {
// Reject non-canonical encodings of s.
return false
}
// 3. Process s as follows:
v, u1, u2: field.Loose_Field_Element = ---, ---, ---
tmp, u2_sqr: field.Tight_Field_Element = ---, ---
// ss = s^2
// u1 = 1 - ss
// u2 = 1 + ss
// u2_sqr = u2^2
field.fe_carry_square(&tmp, field.fe_relax_cast(&s))
field.fe_sub(&u1, &field.FE_ONE, &tmp)
field.fe_add(&u2, &field.FE_ONE, &tmp)
field.fe_carry_square(&u2_sqr, &u2)
// v = -(D * u1^2) - u2_sqr
field.fe_carry_square(&tmp, &u1)
field.fe_carry_mul(&tmp, field.fe_relax_cast(&grp.FE_D), field.fe_relax_cast(&tmp))
field.fe_carry_add(&tmp, &tmp, &u2_sqr)
field.fe_opp(&v, &tmp)
// (was_square, invsqrt) = SQRT_RATIO_M1(1, v * u2_sqr)
field.fe_carry_mul(&tmp, &v, field.fe_relax_cast(&u2_sqr))
was_square := field.fe_carry_sqrt_ratio_m1(
&tmp,
field.fe_relax_cast(&field.FE_ONE),
field.fe_relax_cast(&tmp),
)
// den_x = invsqrt * u2
// den_y = invsqrt * den_x * v
x, y, t: field.Tight_Field_Element = ---, ---, ---
field.fe_carry_mul(&x, field.fe_relax_cast(&tmp), &u2)
field.fe_carry_mul(&y, field.fe_relax_cast(&tmp), field.fe_relax_cast(&x))
field.fe_carry_mul(&y, field.fe_relax_cast(&y), &v)
// x = CT_ABS(2 * s * den_x)
field.fe_carry_mul(&x, field.fe_relax_cast(&s), field.fe_relax_cast(&x))
field.fe_carry_add(&x, &x, &x)
field.fe_carry_abs(&x, &x)
// y = u1 * den_y
field.fe_carry_mul(&y, &u1, field.fe_relax_cast(&y))
// t = x * y
field.fe_carry_mul(&t, field.fe_relax_cast(&x), field.fe_relax_cast(&y))
field.fe_clear_vec([]^field.Loose_Field_Element{&v, &u1, &u2})
field.fe_clear_vec([]^field.Tight_Field_Element{&tmp, &u2_sqr})
defer field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &t})
// 4. If was_square is FALSE, IS_NEGATIVE(t) returns TRUE, or y = 0,
// decoding fails. Otherwise, return the group element represented
// by the internal representation (x, y, 1, t) as the result of
// decoding.
switch {
case was_square == 0:
// Not sure why the RFC doesn't have this just fail early.
return false
case field.fe_is_negative(&t) != 0:
return false
case field.fe_equal(&y, &field.FE_ZERO) != 0:
return false
}
field.fe_set(&ge._p.x, &x)
field.fe_set(&ge._p.y, &y)
field.fe_one(&ge._p.z)
field.fe_set(&ge._p.t, &t)
ge._is_initialized = true
return true
}
// ge_set_wide_bytes sets ge to the result of deriving a ristretto255
// group element, from a wide (512-bit) byte string.
ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
if len(b) != WIDE_ELEMENT_SIZE {
panic("crypto/ristretto255: invalid wide input size")
}
// The element derivation function on an input string b proceeds as
// follows:
//
// 1. Compute P1 as MAP(b[0:32]).
// 2. Compute P2 as MAP(b[32:64]).
// 3. Return P1 + P2.
p1, p2: Group_Element = ---, ---
ge_map(&p1, b[0:32])
ge_map(&p2, b[32:64])
ge_add(ge, &p1, &p2)
ge_clear(&p1)
ge_clear(&p2)
}
// ge_bytes sets dst to the canonical encoding of ge.
ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
_ge_assert_initialized([]^Group_Element{ge})
if len(dst) != ELEMENT_SIZE {
panic("crypto/ristretto255: invalid destination size")
}
x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
// 1. Process the internal representation into a field element s as
// follows:
// u1 = (z0 + y0) * (z0 - y0)
// u2 = x0 * y0
u1, u2: field.Tight_Field_Element = ---, ---
tmp1, tmp2: field.Loose_Field_Element = ---, ---
field.fe_add(&tmp1, z0, y0)
field.fe_sub(&tmp2, z0, y0)
field.fe_carry_mul(&u1, &tmp1, &tmp2)
field.fe_carry_mul(&u2, field.fe_relax_cast(x0), field.fe_relax_cast(y0))
// Ignore was_square since this is always square.
// (_, invsqrt) = SQRT_RATIO_M1(1, u1 * u2^2)
tmp: field.Tight_Field_Element = ---
field.fe_carry_square(&tmp, field.fe_relax_cast(&u2))
field.fe_carry_mul(&tmp, field.fe_relax_cast(&u1), field.fe_relax_cast(&tmp))
_ = field.fe_carry_sqrt_ratio_m1(
&tmp,
field.fe_relax_cast(&field.FE_ONE),
field.fe_relax_cast(&tmp),
)
// den1 = invsqrt * u1
// den2 = invsqrt * u2
// z_inv = den1 * den2 * t0
den1, den2 := &u1, &u2
z_inv: field.Tight_Field_Element = ---
field.fe_carry_mul(den1, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u1))
field.fe_carry_mul(den2, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u2))
field.fe_carry_mul(&z_inv, field.fe_relax_cast(den1), field.fe_relax_cast(den2))
field.fe_carry_mul(&z_inv, field.fe_relax_cast(&z_inv), field.fe_relax_cast(t0))
// rotate = IS_NEGATIVE(t0 * z_inv)
// Note: Reordered from the RFC because invsqrt is no longer needed.
field.fe_carry_mul(&tmp, field.fe_relax_cast(t0), field.fe_relax_cast(&z_inv))
rotate := field.fe_is_negative(&tmp)
// ix0 = x0 * SQRT_M1
// iy0 = y0 * SQRT_M1
// enchanted_denominator = den1 * INVSQRT_A_MINUS_D
ix0, iy0: field.Tight_Field_Element = ---, ---
field.fe_carry_mul(&ix0, field.fe_relax_cast(x0), field.fe_relax_cast(&field.FE_SQRT_M1))
field.fe_carry_mul(&iy0, field.fe_relax_cast(y0), field.fe_relax_cast(&field.FE_SQRT_M1))
field.fe_carry_mul(&tmp, field.fe_relax_cast(den1), field.fe_relax_cast(&FE_INVSQRT_A_MINUS_D))
// Conditionally rotate x and y.
// x = CT_SELECT(iy0 IF rotate ELSE x0)
// y = CT_SELECT(ix0 IF rotate ELSE y0)
// z = z0
// den_inv = CT_SELECT(enchanted_denominator IF rotate ELSE den2)
x, y: field.Tight_Field_Element = ---, ---
field.fe_cond_select(&x, x0, &iy0, rotate)
field.fe_cond_select(&y, y0, &ix0, rotate)
field.fe_cond_select(&tmp, den2, &tmp, rotate)
// y = CT_SELECT(-y IF IS_NEGATIVE(x * z_inv) ELSE y)
field.fe_carry_mul(&x, field.fe_relax_cast(&x), field.fe_relax_cast(&z_inv))
field.fe_cond_negate(&y, &y, field.fe_is_negative(&x))
// s = CT_ABS(den_inv * (z - y))
field.fe_sub(&tmp1, z0, &y)
field.fe_carry_mul(&tmp, field.fe_relax_cast(&tmp), &tmp1)
field.fe_carry_abs(&tmp, &tmp)
// 2. Return the 32-byte little-endian encoding of s. More
// specifically, this is the encoding of the canonical
// representation of s as an integer between 0 and p-1, inclusive.
dst_ := transmute(^[32]byte)(raw_data(dst))
field.fe_to_bytes(dst_, &tmp)
field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y})
field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &tmp2})
}
// ge_add sets `ge = a + b`.
ge_add :: proc(ge, a, b: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a, b})
grp.ge_add(&ge._p, &a._p, &b._p)
ge._is_initialized = true
}
// ge_double sets `ge = a + a`.
ge_double :: proc(ge, a: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_double(&ge._p, &a._p)
ge._is_initialized = true
}
// ge_negate sets `ge = -a`.
ge_negate :: proc(ge, a: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_negate(&ge._p, &a._p)
ge._is_initialized = true
}
// ge_scalarmult sets `ge = A * sc`.
ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
_ge_assert_initialized([]^Group_Element{A})
grp.ge_scalarmult(&ge._p, &A._p, sc)
ge._is_initialized = true
}
// ge_scalarmult_generator sets `ge = G * sc`
ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
grp.ge_scalarmult_basepoint(&ge._p, sc)
ge._is_initialized = true
}
// ge_scalarmult_vartime sets `ge = A * sc` in variable time.
ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
_ge_assert_initialized([]^Group_Element{A})
grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
ge._is_initialized = true
}
// ge_double_scalarmult_generator_vartime sets `ge = A * a + G * b` in variable
// time.
ge_double_scalarmult_generator_vartime :: proc(
ge: ^Group_Element,
a: ^Scalar,
A: ^Group_Element,
b: ^Scalar,
) {
_ge_assert_initialized([]^Group_Element{A})
grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
ge._is_initialized = true
}
// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_cond_negate(&ge._p, &a._p, ctrl)
ge._is_initialized = true
}
// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
_ge_assert_initialized([]^Group_Element{ge, a})
grp.ge_cond_assign(&ge._p, &a._p, ctrl)
}
// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
_ge_assert_initialized([]^Group_Element{a, b})
grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
ge._is_initialized = true
}
// ge_equal returns 1 iff `a == b`, and 0 otherwise.
@(require_results)
ge_equal :: proc(a, b: ^Group_Element) -> int {
_ge_assert_initialized([]^Group_Element{a, b})
// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
field.fe_carry_mul(&ax_by, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.y))
field.fe_carry_mul(&ay_bx, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.x))
field.fe_carry_mul(&ay_by, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.y))
field.fe_carry_mul(&ax_bx, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.x))
ret := field.fe_equal(&ax_by, &ay_bx) | field.fe_equal(&ay_by, &ax_bx)
field.fe_clear_vec([]^field.Tight_Field_Element{&ax_by, &ay_bx, &ay_by, &ax_bx})
return ret
}
// ge_is_identity returns 1 iff `ge` is the identity element, and 0 otherwise.
@(require_results)
ge_is_identity :: proc(ge: ^Group_Element) -> int {
return ge_equal(ge, &GE_IDENTITY)
}
@(private)
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
b_ := transmute(^[32]byte)(raw_data(b))
// The MAP function is defined on 32-byte strings as:
//
// 1. Mask the most significant bit in the final byte of the string,
// and interpret the string as an unsigned integer r in little-
// endian representation. Reduce r modulo p to obtain a field
// element t.
// * Masking the most significant bit is equivalent to interpreting
// the whole string as an unsigned integer in little-endian
// representation and then reducing it modulo 2^255.
t: field.Tight_Field_Element = ---
field.fe_from_bytes(&t, b_)
// 2. Process t as follows:
//
// r = SQRT_M1 * t^2
// u = (r + 1) * ONE_MINUS_D_SQ
// v = (-1 - r*D) * (r + D)
tmp1: field.Loose_Field_Element = ---
r, u, v: field.Tight_Field_Element = ---, ---, ---
field.fe_carry_square(&r, field.fe_relax_cast(&t))
field.fe_carry_mul(&r, field.fe_relax_cast(&field.FE_SQRT_M1), field.fe_relax_cast(&r))
field.fe_add(&tmp1, &field.FE_ONE, &r)
field.fe_carry_mul(&u, &tmp1, field.fe_relax_cast(&FE_ONE_MINUS_D_SQ))
field.fe_carry_mul(&v, field.fe_relax_cast(&r), field.fe_relax_cast(&grp.FE_D))
field.fe_carry_add(&v, &field.FE_ONE, &v)
field.fe_carry_opp(&v, &v)
field.fe_add(&tmp1, &r, &grp.FE_D)
field.fe_carry_mul(&v, field.fe_relax_cast(&v), &tmp1)
// (was_square, s) = SQRT_RATIO_M1(u, v)
// s_prime = -CT_ABS(s*t)
// s = CT_SELECT(s IF was_square ELSE s_prime)
// c = CT_SELECT(-1 IF was_square ELSE r)
s, s_prime, c: field.Tight_Field_Element = ---, ---, ---
was_square := field.fe_carry_sqrt_ratio_m1(
&s,
field.fe_relax_cast(&u),
field.fe_relax_cast(&v),
)
field.fe_carry_mul(&s_prime, field.fe_relax_cast(&s), field.fe_relax_cast(&t))
field.fe_carry_abs(&s_prime, &s_prime)
field.fe_carry_opp(&s_prime, &s_prime)
field.fe_cond_select(&s, &s_prime, &s, was_square)
field.fe_cond_select(&c, &r, &FE_NEG_ONE, was_square)
// N = c * (r - 1) * D_MINUS_ONE_SQ - v
N: field.Tight_Field_Element = ---
field.fe_sub(&tmp1, &r, &field.FE_ONE)
field.fe_carry_mul(&N, field.fe_relax_cast(&c), &tmp1)
field.fe_carry_mul(&N, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_D_MINUS_ONE_SQUARED))
field.fe_carry_sub(&N, &N, &v)
// w0 = 2 * s * v
// w1 = N * SQRT_AD_MINUS_ONE
// w2 = 1 - s^2
// w3 = 1 + s^2
w0, w1: field.Tight_Field_Element = ---, ---
w2, w3: field.Loose_Field_Element = ---, ---
field.fe_carry_mul(&w0, field.fe_relax_cast(&s), field.fe_relax_cast(&v))
field.fe_carry_add(&w0, &w0, &w0)
field.fe_carry_mul(&w1, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_SQRT_AD_MINUS_ONE))
field.fe_carry_square(&s, field.fe_relax_cast(&s))
field.fe_sub(&w2, &field.FE_ONE, &s)
field.fe_add(&w3, &field.FE_ONE, &s)
// 3. Return the group element represented by the internal
// representation (w0*w3, w2*w1, w1*w3, w0*w2).
field.fe_carry_mul(&ge._p.x, field.fe_relax_cast(&w0), &w3)
field.fe_carry_mul(&ge._p.y, &w2, field.fe_relax_cast(&w1))
field.fe_carry_mul(&ge._p.z, field.fe_relax_cast(&w1), &w3)
field.fe_carry_mul(&ge._p.t, field.fe_relax_cast(&w0), &w2)
ge._is_initialized = true
field.fe_clear_vec([]^field.Tight_Field_Element{&r, &u, &v, &s, &s_prime, &c, &N, &w0, &w1})
field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &w2, &w3})
}
@(private)
_ge_assert_initialized :: proc(ges: []^Group_Element) {
for ge in ges {
if !ge._is_initialized {
panic("crypto/ristretto255: uninitialized group element")
}
}
}
@@ -0,0 +1,97 @@
package ristretto255
import grp "core:crypto/_edwards25519"
// SCALAR_SIZE is the size of a byte-encoded ristretto255 scalar.
SCALAR_SIZE :: 32
// WIDE_SCALAR_SIZE is the size of a wide byte-encoded ristretto255
// scalar.
WIDE_SCALAR_SIZE :: 64
// Scalar is a ristretto255 scalar. The zero-initialized value is valid,
// and represents `0`.
Scalar :: grp.Scalar
// sc_clear clears sc to the uninitialized state.
sc_clear :: proc "contextless" (sc: ^Scalar) {
grp.sc_clear(sc)
}
// sc_set sets `sc = a`.
sc_set :: proc "contextless" (sc, a: ^Scalar) {
grp.sc_set(sc, a)
}
// sc_set_u64 sets `sc = i`.
sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
grp.sc_set_u64(sc, i)
}
// sc_set_bytes sets sc to the result of decoding b as a ristretto255
// scalar, and returns true on success.
@(require_results)
sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool {
if len(b) != SCALAR_SIZE {
return false
}
return grp.sc_set_bytes(sc, b)
}
// sc_set_wide_bytes sets sc to the result of deriving a ristretto255
// scalar, from a wide (512-bit) byte string by interpreting b as a
// little-endian value, and reducing it mod the group order.
sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
if len(b) != WIDE_SCALAR_SIZE {
panic("crypto/ristretto255: invalid wide input size")
}
b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
grp.sc_set_bytes_wide(sc, b_)
}
// sc_bytes sets dst to the canonical encoding of sc.
sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
if len(dst) != SCALAR_SIZE {
panic("crypto/ristretto255: invalid destination size")
}
grp.sc_bytes(dst, sc)
}
// sc_add sets `sc = a + b`.
sc_add :: proc "contextless" (sc, a, b: ^Scalar) {
grp.sc_add(sc, a, b)
}
// sc_sub sets `sc = a - b`.
sc_sub :: proc "contextless" (sc, a, b: ^Scalar) {
grp.sc_sub(sc, a, b)
}
// sc_negate sets `sc = -a`.
sc_negate :: proc "contextless" (sc, a: ^Scalar) {
grp.sc_negate(sc, a)
}
// sc_mul sets `sc = a * b`.
sc_mul :: proc "contextless" (sc, a, b: ^Scalar) {
grp.sc_mul(sc, a, b)
}
// sc_square sets `sc = a^2`.
sc_square :: proc "contextless" (sc, a: ^Scalar) {
grp.sc_square(sc, a)
}
// sc_cond_assign sets `sc = sc` iff `ctrl == 0` and `sc = a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
sc_cond_assign :: proc(sc, a: ^Scalar, ctrl: int) {
grp.sc_cond_assign(sc, a, ctrl)
}
// sc_equal returns 1 iff `a == b`, and 0 otherwise.
@(require_results)
sc_equal :: proc(a, b: ^Scalar) -> int {
return grp.sc_equal(a, b)
}
+3 -9
View File
@@ -27,7 +27,7 @@ _scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
} }
@(private) @(private)
_scalarmult :: proc(out, scalar, point: ^[32]byte) { _scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) {
// Montgomery pseduo-multiplication taken from Monocypher. // Montgomery pseduo-multiplication taken from Monocypher.
// computes the scalar product // computes the scalar product
@@ -94,13 +94,8 @@ _scalarmult :: proc(out, scalar, point: ^[32]byte) {
field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2)) field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
field.fe_to_bytes(out, &x2) field.fe_to_bytes(out, &x2)
mem.zero_explicit(&x1, size_of(x1)) field.fe_clear_vec([]^field.Tight_Field_Element{&x1, &x2, &x3, &z2, &z3})
mem.zero_explicit(&x2, size_of(x2)) field.fe_clear_vec([]^field.Loose_Field_Element{&t0, &t1})
mem.zero_explicit(&x3, size_of(x3))
mem.zero_explicit(&z2, size_of(z2))
mem.zero_explicit(&z3, size_of(z3))
mem.zero_explicit(&t0, size_of(t0))
mem.zero_explicit(&t1, size_of(t1))
} }
// scalarmult "multiplies" the provided scalar and point, and writes the // scalarmult "multiplies" the provided scalar and point, and writes the
@@ -137,6 +132,5 @@ scalarmult :: proc(dst, scalar, point: []byte) {
// scalarmult_basepoint "multiplies" the provided scalar with the X25519 // scalarmult_basepoint "multiplies" the provided scalar with the X25519
// base point and writes the resulting point to dst. // base point and writes the resulting point to dst.
scalarmult_basepoint :: proc(dst, scalar: []byte) { scalarmult_basepoint :: proc(dst, scalar: []byte) {
// TODO/perf: Switch to using a precomputed table.
scalarmult(dst, scalar, _BASE_POINT[:]) scalarmult(dst, scalar, _BASE_POINT[:])
} }
+56 -7
View File
@@ -366,11 +366,63 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_end(w, opt, '}') or_return opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Struct: case runtime.Type_Info_Struct:
is_omitempty :: proc(v: any) -> bool {
v := v
if v == nil {
return true
}
ti := runtime.type_info_core(type_info_of(v.id))
#partial switch info in ti.variant {
case runtime.Type_Info_String:
switch x in v {
case string:
return x == ""
case cstring:
return x == nil || x == ""
}
case runtime.Type_Info_Any:
return v.(any) == nil
case runtime.Type_Info_Type_Id:
return v.(typeid) == nil
case runtime.Type_Info_Pointer,
runtime.Type_Info_Multi_Pointer,
runtime.Type_Info_Procedure:
return (^rawptr)(v.data)^ == nil
case runtime.Type_Info_Dynamic_Array:
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
case runtime.Type_Info_Slice:
return (^runtime.Raw_Slice)(v.data).len == 0
case runtime.Type_Info_Union,
runtime.Type_Info_Bit_Set,
runtime.Type_Info_Soa_Pointer:
return reflect.is_nil(v)
case runtime.Type_Info_Map:
return (^runtime.Raw_Map)(v.data).len == 0
}
return false
}
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) { marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
ti := runtime.type_info_base(type_info_of(v.id)) ti := runtime.type_info_base(type_info_of(v.id))
info := ti.variant.(runtime.Type_Info_Struct) info := ti.variant.(runtime.Type_Info_Struct)
for name, i in info.names { for name, i in info.names {
json_name := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json") omitempty := false
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
for flag in strings.split_iterator(&extra, ",") {
switch flag {
case "omitempty":
omitempty = true
}
}
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
the_value := any{data, id}
if is_omitempty(the_value) {
continue
}
opt_write_iteration(w, opt, i) or_return opt_write_iteration(w, opt, i) or_return
if json_name != "" { if json_name != "" {
@@ -378,18 +430,15 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
} else { } else {
// Marshal the fields of 'using _: T' fields directly into the parent struct // Marshal the fields of 'using _: T' fields directly into the parent struct
if info.usings[i] && name == "_" { if info.usings[i] && name == "_" {
id := info.types[i].id marshal_struct_fields(w, the_value, opt) or_return
data := rawptr(uintptr(v.data) + info.offsets[i])
marshal_struct_fields(w, any{data, id}, opt) or_return
continue continue
} else { } else {
opt_write_key(w, opt, name) or_return opt_write_key(w, opt, name) or_return
} }
} }
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i]) marshal_to_writer(w, the_value, opt) or_return
marshal_to_writer(w, any{data, id}, opt) or_return
} }
return return
} }
+12 -1
View File
@@ -343,6 +343,16 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca
return prev return prev
} }
@(private)
json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) {
json_name = value
if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
json_name = json_name[:comma_index]
extra = json_name[comma_index:]
}
return
}
@(private) @(private)
unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) { unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
@@ -384,7 +394,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
for field, field_idx in fields { for field, field_idx in fields {
tag_value := string(reflect.struct_tag_get(field.tag, "json")) tag_value := string(reflect.struct_tag_get(field.tag, "json"))
if key == tag_value { json_name, _ := json_name_from_tag_value(tag_value)
if key == json_name {
use_field_idx = field_idx use_field_idx = field_idx
break break
} }
+1 -1
View File
@@ -2711,7 +2711,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
} }
} else { } else {
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n) io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n) defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
for i in 0..<info.count { for i in 0..<info.count {
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
+168 -20
View File
@@ -102,37 +102,51 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width
} }
@(require_results) @(require_results)
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
Integer_Width :: 8*size_of(Backing) - Fraction_Width
x := x x := x
buf: [48]byte buf: [48]byte
i := 0 i := 0
if x.i < 0 {
if !intrinsics.type_is_unsigned(Backing) && x.i == min(Backing) {
// edge case handling for signed numbers
buf[i] = '-' buf[i] = '-'
i += 1 i += 1
x.i = -x.i i += copy(buf[i:], _power_of_two_table[Integer_Width])
} } else {
if x.i < 0 {
integer := x.i >> Fraction_Width buf[i] = '-'
fraction := x.i & (1<<Fraction_Width - 1)
s := strconv.append_uint(buf[i:], u64(integer), 10)
i += len(s)
if fraction != 0 {
buf[i] = '.'
i += 1
for fraction > 0 {
fraction *= 10
buf[i] = byte('0' + (fraction>>Fraction_Width))
i += 1 i += 1
fraction &= 1<<Fraction_Width - 1 x.i = -x.i
}
when size_of(Backing) < 16 {
T :: u64
append_uint :: strconv.append_uint
} else {
T :: u128
append_uint :: strconv.append_u128
}
integer := T(x.i) >> Fraction_Width
fraction := T(x.i) & (1<<Fraction_Width - 1)
s := append_uint(buf[i:], integer, 10)
i += len(s)
if fraction != 0 {
buf[i] = '.'
i += 1
for fraction > 0 {
fraction *= 10
buf[i] = byte('0' + (fraction>>Fraction_Width) % 10)
i += 1
fraction &= 1<<Fraction_Width - 1
}
} }
} }
n := copy(dst, buf[:i]) n := copy(dst, buf[:i])
return string(dst[:i]) return string(dst[:i])
} }
@@ -146,3 +160,137 @@ to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.a
copy(str, s) copy(str, s)
return string(str) return string(str)
} }
@(private)
_power_of_two_table := [129]string{
"0.5",
"1",
"2",
"4",
"8",
"16",
"32",
"64",
"128",
"256",
"512",
"1024",
"2048",
"4096",
"8192",
"16384",
"32768",
"65536",
"131072",
"262144",
"524288",
"1048576",
"2097152",
"4194304",
"8388608",
"16777216",
"33554432",
"67108864",
"134217728",
"268435456",
"536870912",
"1073741824",
"2147483648",
"4294967296",
"8589934592",
"17179869184",
"34359738368",
"68719476736",
"137438953472",
"274877906944",
"549755813888",
"1099511627776",
"2199023255552",
"4398046511104",
"8796093022208",
"17592186044416",
"35184372088832",
"70368744177664",
"140737488355328",
"281474976710656",
"562949953421312",
"1125899906842624",
"2251799813685248",
"4503599627370496",
"9007199254740992",
"18014398509481984",
"36028797018963968",
"72057594037927936",
"144115188075855872",
"288230376151711744",
"576460752303423488",
"1152921504606846976",
"2305843009213693952",
"4611686018427387904",
"9223372036854775808",
"18446744073709551616",
"36893488147419103232",
"73786976294838206464",
"147573952589676412928",
"295147905179352825856",
"590295810358705651712",
"1180591620717411303424",
"2361183241434822606848",
"4722366482869645213696",
"9444732965739290427392",
"18889465931478580854784",
"37778931862957161709568",
"75557863725914323419136",
"151115727451828646838272",
"302231454903657293676544",
"604462909807314587353088",
"1208925819614629174706176",
"2417851639229258349412352",
"4835703278458516698824704",
"9671406556917033397649408",
"19342813113834066795298816",
"38685626227668133590597632",
"77371252455336267181195264",
"154742504910672534362390528",
"309485009821345068724781056",
"618970019642690137449562112",
"1237940039285380274899124224",
"2475880078570760549798248448",
"4951760157141521099596496896",
"9903520314283042199192993792",
"19807040628566084398385987584",
"39614081257132168796771975168",
"79228162514264337593543950336",
"158456325028528675187087900672",
"316912650057057350374175801344",
"633825300114114700748351602688",
"1267650600228229401496703205376",
"2535301200456458802993406410752",
"5070602400912917605986812821504",
"10141204801825835211973625643008",
"20282409603651670423947251286016",
"40564819207303340847894502572032",
"81129638414606681695789005144064",
"162259276829213363391578010288128",
"324518553658426726783156020576256",
"649037107316853453566312041152512",
"1298074214633706907132624082305024",
"2596148429267413814265248164610048",
"5192296858534827628530496329220096",
"10384593717069655257060992658440192",
"20769187434139310514121985316880384",
"41538374868278621028243970633760768",
"83076749736557242056487941267521536",
"166153499473114484112975882535043072",
"332306998946228968225951765070086144",
"664613997892457936451903530140172288",
"1329227995784915872903807060280344576",
"2658455991569831745807614120560689152",
"5316911983139663491615228241121378304",
"10633823966279326983230456482242756608",
"21267647932558653966460912964485513216",
"42535295865117307932921825928971026432",
"85070591730234615865843651857942052864",
"170141183460469231731687303715884105728",
}
+21 -1
View File
@@ -617,7 +617,7 @@ field_flag_strings := [Field_Flag]string{
.Any_Int = "#any_int", .Any_Int = "#any_int",
.Subtype = "#subtype", .Subtype = "#subtype",
.By_Ptr = "#by_ptr", .By_Ptr = "#by_ptr",
.No_Broadcast ="#no_broadcast", .No_Broadcast = "#no_broadcast",
.Results = "results", .Results = "results",
.Tags = "field tag", .Tags = "field tag",
@@ -842,6 +842,23 @@ Matrix_Type :: struct {
elem: ^Expr, elem: ^Expr,
} }
Bit_Field_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
backing_type: ^Expr,
open: tokenizer.Pos,
fields: []^Bit_Field_Field,
close: tokenizer.Pos,
}
Bit_Field_Field :: struct {
using node: Node,
docs: ^Comment_Group,
name: ^Expr,
type: ^Expr,
bit_size: ^Expr,
comments: ^Comment_Group,
}
Any_Node :: union { Any_Node :: union {
^Package, ^Package,
@@ -898,6 +915,7 @@ Any_Node :: union {
^Map_Type, ^Map_Type,
^Relative_Type, ^Relative_Type,
^Matrix_Type, ^Matrix_Type,
^Bit_Field_Type,
^Bad_Stmt, ^Bad_Stmt,
^Empty_Stmt, ^Empty_Stmt,
@@ -928,6 +946,7 @@ Any_Node :: union {
^Attribute, ^Attribute,
^Field, ^Field,
^Field_List, ^Field_List,
^Bit_Field_Field,
} }
@@ -982,6 +1001,7 @@ Any_Expr :: union {
^Map_Type, ^Map_Type,
^Relative_Type, ^Relative_Type,
^Matrix_Type, ^Matrix_Type,
^Bit_Field_Type,
} }
+7
View File
@@ -336,6 +336,13 @@ clone_node :: proc(node: ^Node) -> ^Node {
case ^Relative_Type: case ^Relative_Type:
r.tag = clone(r.tag) r.tag = clone(r.tag)
r.type = clone(r.type) r.type = clone(r.type)
case ^Bit_Field_Type:
r.backing_type = clone(r.backing_type)
r.fields = auto_cast clone(r.fields)
case ^Bit_Field_Field:
r.name = clone(r.name)
r.type = clone(r.type)
r.bit_size = clone(r.bit_size)
case: case:
fmt.panicf("Unhandled node kind: %v", r) fmt.panicf("Unhandled node kind: %v", r)
} }
+9 -1
View File
@@ -414,7 +414,15 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.row_count) walk(v, n.row_count)
walk(v, n.column_count) walk(v, n.column_count)
walk(v, n.elem) walk(v, n.elem)
case ^Bit_Field_Type:
walk(v, n.backing_type)
for f in n.fields {
walk(v, f)
}
case ^Bit_Field_Field:
walk(v, n.name)
walk(v, n.type)
walk(v, n.bit_size)
case: case:
fmt.panicf("ast.walk: unexpected node type %T", n) fmt.panicf("ast.walk: unexpected node type %T", n)
} }
+58 -10
View File
@@ -416,24 +416,28 @@ end_of_line_pos :: proc(p: ^Parser, tok: tokenizer.Token) -> tokenizer.Pos {
} }
expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token { expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token {
return expect_closing_token_of_field_list(p, .Close_Brace, "field list")
}
expect_closing_token_of_field_list :: proc(p: ^Parser, closing_kind: tokenizer.Token_Kind, msg: string) -> tokenizer.Token {
token := p.curr_tok token := p.curr_tok
if allow_token(p, .Close_Brace) { if allow_token(p, closing_kind) {
return token return token
} }
if allow_token(p, .Semicolon) && !tokenizer.is_newline(token) { if allow_token(p, .Semicolon) && !tokenizer.is_newline(token) {
str := tokenizer.token_to_string(token) str := tokenizer.token_to_string(token)
error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", str) error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", str)
} }
expect_brace := expect_token(p, .Close_Brace) expect_closing := expect_token_after(p, closing_kind, msg)
if expect_brace.kind != .Close_Brace { if expect_closing.kind != closing_kind {
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) { for p.curr_tok.kind != closing_kind && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) {
advance_token(p) advance_token(p)
} }
return p.curr_tok return p.curr_tok
} }
return expect_brace return expect_closing
} }
expect_closing_parentheses_of_field_list :: proc(p: ^Parser) -> tokenizer.Token { expect_closing_parentheses_of_field_list :: proc(p: ^Parser) -> tokenizer.Token {
@@ -531,7 +535,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return is_semicolon_optional_for_node(p, n.type) return is_semicolon_optional_for_node(p, n.type)
case ^ast.Pointer_Type: case ^ast.Pointer_Type:
return is_semicolon_optional_for_node(p, n.elem) return is_semicolon_optional_for_node(p, n.elem)
case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type: case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type, ^ast.Bit_Set_Type, ^ast.Bit_Field_Type:
// Require semicolon within a procedure body // Require semicolon within a procedure body
return p.curr_proc == nil return p.curr_proc == nil
case ^ast.Proc_Lit: case ^ast.Proc_Lit:
@@ -1354,6 +1358,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
rs := ast.new(ast.Return_Stmt, tok.pos, end) rs := ast.new(ast.Return_Stmt, tok.pos, end)
rs.results = results[:] rs.results = results[:]
expect_semicolon(p, rs)
return rs return rs
case .Break, .Continue, .Fallthrough: case .Break, .Continue, .Fallthrough:
@@ -2790,6 +2795,48 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
mt.column_count = column_count mt.column_count = column_count
mt.elem = elem mt.elem = elem
return mt return mt
case .Bit_Field:
tok := expect_token(p, .Bit_Field)
backing_type := parse_type_or_ident(p)
if backing_type == nil {
token := advance_token(p)
error(p, token.pos, "Expected a backing type for a 'bit_field'")
}
skip_possible_newline_for_literal(p)
open := expect_token_after(p, .Open_Brace, "bit_field")
fields: [dynamic]^ast.Bit_Field_Field
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
name := parse_ident(p)
expect_token(p, .Colon)
type := parse_type(p)
expect_token(p, .Or)
bit_size := parse_expr(p, true)
field := ast.new(ast.Bit_Field_Field, name.pos, bit_size)
field.name = name
field.type = type
field.bit_size = bit_size
append(&fields, field)
allow_token(p, .Comma) or_break
}
close := expect_closing_brace_of_field_list(p)
bf := ast.new(ast.Bit_Field_Type, tok.pos, close.pos)
bf.tok_pos = tok.pos
bf.backing_type = backing_type
bf.open = open.pos
bf.fields = fields[:]
bf.close = close.pos
return bf
case .Asm: case .Asm:
tok := expect_token(p, .Asm) tok := expect_token(p, .Asm)
@@ -2897,7 +2944,8 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
^ast.Map_Type, ^ast.Map_Type,
^ast.Bit_Set_Type, ^ast.Bit_Set_Type,
^ast.Matrix_Type, ^ast.Matrix_Type,
^ast.Call_Expr: ^ast.Call_Expr,
^ast.Bit_Field_Type:
return true return true
} }
return false return false
@@ -2947,8 +2995,8 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
} }
p.expr_level -= 1 p.expr_level -= 1
skip_possible_newline(p) skip_possible_newline(p)
close := expect_token_after(p, .Close_Brace, "compound literal") close := expect_closing_brace_of_field_list(p)
pos := type.pos if type != nil else open.pos pos := type.pos if type != nil else open.pos
lit := ast.new(ast.Comp_Lit, pos, end_pos(close)) lit := ast.new(ast.Comp_Lit, pos, end_pos(close))
@@ -3011,7 +3059,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
allow_token(p, .Comma) or_break allow_token(p, .Comma) or_break
} }
close := expect_token_after(p, .Close_Paren, "argument list") close := expect_closing_token_of_field_list(p, .Close_Paren, "argument list")
p.expr_level -= 1 p.expr_level -= 1
ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close)) ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close))
+51 -1
View File
@@ -445,7 +445,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for value in v.values { for value in v.values {
#partial switch a in value.derived { #partial switch a in value.derived {
case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type: case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type, ^ast.Bit_Field_Type:
add_semicolon = false || called_in_stmt add_semicolon = false || called_in_stmt
case ^ast.Proc_Lit: case ^ast.Proc_Lit:
add_semicolon = false add_semicolon = false
@@ -488,6 +488,37 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}) {
} }
} }
@(private)
visit_bit_field_fields :: proc(p: ^Printer, list: []^ast.Bit_Field_Field, options := List_Options{}) {
if len(list) == 0 {
return
}
// we have to newline the expressions to respect the source
for v, i in list {
// Don't move the first expression, it looks bad
if i != 0 && .Enforce_Newline in options {
newline_position(p, 1)
} else if i != 0 {
move_line_limit(p, v.pos, 1)
}
visit_expr(p, v.name, options)
push_generic_token(p, .Colon, 0)
visit_expr(p, v.type, options)
push_generic_token(p, .Or, 1)
visit_expr(p, v.bit_size, options)
if (i != len(list) - 1 || .Trailing in options) && .Add_Comma in options {
push_generic_token(p, .Comma, 0)
}
}
if len(list) > 1 && .Enforce_Newline in options {
newline_position(p, 1)
}
}
@(private) @(private)
visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) {
if len(attributes) == 0 { if len(attributes) == 0 {
@@ -1293,6 +1324,25 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_expr(p, v.column_count) visit_expr(p, v.column_count)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
visit_expr(p, v.elem) visit_expr(p, v.elem)
case ^ast.Bit_Field_Type:
push_generic_token(p, .Bit_Field, 1)
visit_expr(p, v.backing_type)
if len(v.fields) == 0 || v.pos.line == v.close.line {
push_generic_token(p, .Open_Brace, 1)
visit_bit_field_fields(p, v.fields, {.Add_Comma})
push_generic_token(p, .Close_Brace, 0)
} else {
visit_begin_brace(p, v.pos, .Generic, len(v.fields))
newline_position(p, 1)
set_source_position(p, v.fields[0].pos)
visit_bit_field_fields(p, v.fields, {.Add_Comma, .Trailing, .Enforce_Newline})
set_source_position(p, v.close)
visit_end_brace(p, v.close)
}
set_source_position(p, v.close)
case: case:
panic(fmt.aprint(expr.derived)) panic(fmt.aprint(expr.derived))
} }
+1
View File
@@ -39,6 +39,7 @@ init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = defa
t.read_offset = 0 t.read_offset = 0
t.line_offset = 0 t.line_offset = 0
t.line_count = len(src) > 0 ? 1 : 0 t.line_count = len(src) > 0 ? 1 : 0
t.insert_semicolon = false
t.error_count = 0 t.error_count = 0
t.path = path t.path = path
+7
View File
@@ -1213,6 +1213,13 @@ Output:
append_int :: proc(buf: []byte, i: i64, base: int) -> string { append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
} }
append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
return append_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil)
}
/* /*
Converts an integer value to a string and stores it in the given buffer Converts an integer value to a string and stores it in the given buffer
+7 -1
View File
@@ -2074,7 +2074,13 @@ SRWLOCK_INIT :: SRWLOCK{}
STARTF_USESTDHANDLES: DWORD : 0x00000100 STARTF_USESTDHANDLES: DWORD : 0x00000100
VOLUME_NAME_DOS: DWORD : 0x0 VOLUME_NAME_DOS: DWORD : 0x0
MOVEFILE_REPLACE_EXISTING: DWORD : 1
MOVEFILE_COPY_ALLOWED: DWORD: 0x2
MOVEFILE_CREATE_HARDLINK: DWORD: 0x10
MOVEFILE_DELAY_UNTIL_REBOOT: DWORD: 0x4
MOVEFILE_FAIL_IF_NOT_TRACKABLE: DWORD: 0x20
MOVEFILE_REPLACE_EXISTING: DWORD : 0x1
MOVEFILE_WRITE_THROUGH: DWORD: 0x8
FILE_BEGIN: DWORD : 0 FILE_BEGIN: DWORD : 0
FILE_CURRENT: DWORD : 1 FILE_CURRENT: DWORD : 1
+4 -4
View File
@@ -56,9 +56,9 @@ validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minut
return .None return .None
} }
validate_datetime :: proc "contextless" (using datetime: DateTime) -> (err: Error) { validate_datetime :: proc "contextless" (datetime: DateTime) -> (err: Error) {
validate(date) or_return validate(datetime.date) or_return
validate(time) or_return validate(datetime.time) or_return
return .None return .None
} }
@@ -69,4 +69,4 @@ validate :: proc{
validate_hour_minute_second, validate_hour_minute_second,
validate_time, validate_time,
validate_datetime, validate_datetime,
} }
+4
View File
@@ -29,6 +29,7 @@ import blake2s "core:crypto/blake2s"
import chacha20 "core:crypto/chacha20" import chacha20 "core:crypto/chacha20"
import chacha20poly1305 "core:crypto/chacha20poly1305" import chacha20poly1305 "core:crypto/chacha20poly1305"
import crypto_hash "core:crypto/hash" import crypto_hash "core:crypto/hash"
import ed25519 "core:crypto/ed25519"
import hkdf "core:crypto/hkdf" import hkdf "core:crypto/hkdf"
import hmac "core:crypto/hmac" import hmac "core:crypto/hmac"
import kmac "core:crypto/kmac" import kmac "core:crypto/kmac"
@@ -37,6 +38,7 @@ import md5 "core:crypto/legacy/md5"
import sha1 "core:crypto/legacy/sha1" import sha1 "core:crypto/legacy/sha1"
import pbkdf2 "core:crypto/pbkdf2" import pbkdf2 "core:crypto/pbkdf2"
import poly1305 "core:crypto/poly1305" import poly1305 "core:crypto/poly1305"
import ristretto255 "core:crypto/ristretto255"
import sha2 "core:crypto/sha2" import sha2 "core:crypto/sha2"
import sha3 "core:crypto/sha3" import sha3 "core:crypto/sha3"
import shake "core:crypto/shake" import shake "core:crypto/shake"
@@ -151,6 +153,7 @@ _ :: blake2b
_ :: blake2s _ :: blake2s
_ :: chacha20 _ :: chacha20
_ :: chacha20poly1305 _ :: chacha20poly1305
_ :: ed25519
_ :: hmac _ :: hmac
_ :: hkdf _ :: hkdf
_ :: kmac _ :: kmac
@@ -158,6 +161,7 @@ _ :: keccak
_ :: md5 _ :: md5
_ :: pbkdf2 _ :: pbkdf2
_ :: poly1305 _ :: poly1305
_ :: ristretto255
_ :: sha1 _ :: sha1
_ :: sha2 _ :: sha2
_ :: sha3 _ :: sha3
+14 -7
View File
@@ -272,13 +272,16 @@ enum BuildPath : u8 {
}; };
enum VetFlags : u64 { enum VetFlags : u64 {
VetFlag_NONE = 0, VetFlag_NONE = 0,
VetFlag_Unused = 1u<<0, // 1 VetFlag_Shadowing = 1u<<0,
VetFlag_Shadowing = 1u<<1, // 2 VetFlag_UsingStmt = 1u<<1,
VetFlag_UsingStmt = 1u<<2, // 4 VetFlag_UsingParam = 1u<<2,
VetFlag_UsingParam = 1u<<3, // 8 VetFlag_Style = 1u<<3,
VetFlag_Style = 1u<<4, // 16 VetFlag_Semicolon = 1u<<4,
VetFlag_Semicolon = 1u<<5, // 32 VetFlag_UnusedVariables = 1u<<5,
VetFlag_UnusedImports = 1u<<6,
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt, VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt,
@@ -288,6 +291,10 @@ enum VetFlags : u64 {
u64 get_vet_flag_from_name(String const &name) { u64 get_vet_flag_from_name(String const &name) {
if (name == "unused") { if (name == "unused") {
return VetFlag_Unused; return VetFlag_Unused;
} else if (name == "unused-variables") {
return VetFlag_UnusedVariables;
} else if (name == "unused-imports") {
return VetFlag_UnusedImports;
} else if (name == "shadowing") { } else if (name == "shadowing") {
return VetFlag_Shadowing; return VetFlag_Shadowing;
} else if (name == "using-stmt") { } else if (name == "using-stmt") {
+2 -2
View File
@@ -4089,8 +4089,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
} }
} }
operand->mode = Addressing_OptionalOk; operand->mode = Addressing_Value;
operand->type = default_type(x.type); operand->type = make_optional_ok_type(default_type(x.type));
} }
break; break;
+2 -2
View File
@@ -2355,14 +2355,14 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
unsafe_return_error(o, "the address of a compound literal"); unsafe_return_error(o, "the address of a compound literal");
} else if (x->kind == Ast_IndexExpr) { } else if (x->kind == Ast_IndexExpr) {
Entity *f = entity_of_node(x->IndexExpr.expr); Entity *f = entity_of_node(x->IndexExpr.expr);
if (is_type_array_like(f->type) || is_type_matrix(f->type)) { if (f && (is_type_array_like(f->type) || is_type_matrix(f->type))) {
if (is_entity_local_variable(f)) { if (is_entity_local_variable(f)) {
unsafe_return_error(o, "the address of an indexed variable", f->type); unsafe_return_error(o, "the address of an indexed variable", f->type);
} }
} }
} else if (x->kind == Ast_MatrixIndexExpr) { } else if (x->kind == Ast_MatrixIndexExpr) {
Entity *f = entity_of_node(x->MatrixIndexExpr.expr); Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
if (is_type_matrix(f->type) && is_entity_local_variable(f)) { if (f && (is_type_matrix(f->type) && is_entity_local_variable(f))) {
unsafe_return_error(o, "the address of an indexed variable", f->type); unsafe_return_error(o, "the address of an indexed variable", f->type);
} }
} }
+1 -1
View File
@@ -1689,7 +1689,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
bool is_using = (p->flags&FieldFlag_using) != 0; bool is_using = (p->flags&FieldFlag_using) != 0;
if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) { if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) {
ERROR_BLOCK(); ERROR_BLOCK();
error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied"); error(param, "'using' on a procedure parameter is not allowed when '-vet' or '-vet-using-param' is applied");
error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n"); error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n");
} }
+7 -4
View File
@@ -703,11 +703,11 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
array_add(&vetted_entities, ve_unused); array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) { } else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed); array_add(&vetted_entities, ve_shadowed);
} else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0) { } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static)) == 0 && !e->Variable.is_global) {
i64 sz = type_size_of(e->type); i64 sz = type_size_of(e->type);
// TODO(bill): When is a good size warn? // TODO(bill): When is a good size warn?
// Is 128 KiB good enough? // Is >256 KiB good enough?
if (sz >= 1ll<<17) { if (sz > 1ll<<18) {
gbString type_str = type_to_string(e->type); gbString type_str = type_to_string(e->type);
warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz); warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz);
gb_string_free(type_str); gb_string_free(type_str);
@@ -728,7 +728,10 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
} else if (vet_flags) { } else if (vet_flags) {
switch (ve.kind) { switch (ve.kind) {
case VettedEntity_Unused: case VettedEntity_Unused:
if (vet_flags & VetFlag_Unused) { if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name)); error(e->token, "'%.*s' declared but not used", LIT(name));
} }
break; break;
+1 -1
View File
@@ -496,7 +496,7 @@ gb_internal bool is_entity_local_variable(Entity *e) {
if (e->scope == nullptr) { if (e->scope == nullptr) {
return true; return true;
} }
if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue)) { if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue|EntityFlag_Static)) {
return false; return false;
} }
+1 -1
View File
@@ -384,7 +384,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
LIT(obj_file), LIT(obj_file),
LIT(build_context.extra_assembler_flags) LIT(build_context.extra_assembler_flags)
); );
if (!result) { if (result) {
gb_printf_err("executing `nasm` to assemble foreing import of %.*s failed.\n\tSuggestion: `nasm` does not ship with the compiler and should be installed with your system's package manager.\n", LIT(asm_file)); gb_printf_err("executing `nasm` to assemble foreing import of %.*s failed.\n\tSuggestion: `nasm` does not ship with the compiler and should be installed with your system's package manager.\n", LIT(asm_file));
return result; return result;
} }
+4 -1
View File
@@ -4726,9 +4726,12 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
if (sub_sel.index.count > 0) { if (sub_sel.index.count > 0) {
ptr = lb_emit_deep_field_gep(p, ptr, sub_sel); ptr = lb_emit_deep_field_gep(p, ptr, sub_sel);
} }
if (is_type_pointer(type_deref(ptr.type))) {
ptr = lb_emit_load(p, ptr);
}
Type *bf_type = type_deref(ptr.type); Type *bf_type = type_deref(ptr.type);
bf_type = base_type(type_deref(bf_type)); bf_type = base_type(bf_type);
GB_ASSERT(bf_type->kind == Type_BitField); GB_ASSERT(bf_type->kind == Type_BitField);
i32 index = sel.index[sel.index.count-1]; i32 index = sel.index[sel.index.count-1];
+1 -1
View File
@@ -453,7 +453,7 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice<i
gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 index, i64 bit_offset, i64 bit_size) { gb_internal lbAddr lb_addr_bit_field(lbValue addr, Type *type, i64 index, i64 bit_offset, i64 bit_size) {
GB_ASSERT(is_type_pointer(addr.type)); GB_ASSERT(is_type_pointer(addr.type));
Type *mt = type_deref(addr.type); Type *mt = type_deref(addr.type);
GB_ASSERT(is_type_bit_field(mt)); GB_ASSERT_MSG(is_type_bit_field(mt), "%s", type_to_string(mt));
lbAddr v = {lbAddr_BitField, addr}; lbAddr v = {lbAddr_BitField, addr};
v.bitfield.type = type; v.bitfield.type = type;
+23 -8
View File
@@ -253,6 +253,8 @@ enum BuildFlagKind {
BuildFlag_Vet, BuildFlag_Vet,
BuildFlag_VetShadowing, BuildFlag_VetShadowing,
BuildFlag_VetUnused, BuildFlag_VetUnused,
BuildFlag_VetUnusedImports,
BuildFlag_VetUnusedVariables,
BuildFlag_VetUsingStmt, BuildFlag_VetUsingStmt,
BuildFlag_VetUsingParam, BuildFlag_VetUsingParam,
BuildFlag_VetStyle, BuildFlag_VetStyle,
@@ -444,6 +446,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedImports, str_lit("vet-unused-imports"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingParam, str_lit("vet-using-param"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUsingParam, str_lit("vet-using-param"), BuildFlagParam_None, Command__does_check);
@@ -1026,10 +1030,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_UseSeparateModules: case BuildFlag_UseSeparateModules:
build_context.use_separate_modules = true; build_context.use_separate_modules = true;
break; break;
case BuildFlag_NoThreadedChecker: { case BuildFlag_NoThreadedChecker:
build_context.no_threaded_checker = true; build_context.no_threaded_checker = true;
break; break;
}
case BuildFlag_ShowDebugMessages: case BuildFlag_ShowDebugMessages:
build_context.show_debug_messages = true; build_context.show_debug_messages = true;
break; break;
@@ -1037,12 +1040,14 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.vet_flags |= VetFlag_All; build_context.vet_flags |= VetFlag_All;
break; break;
case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break;
case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break;
case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break; case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break;
case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break; case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break;
case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break;
case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break;
case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break;
case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
case BuildFlag_IgnoreUnknownAttributes: case BuildFlag_IgnoreUnknownAttributes:
build_context.ignore_unknown_attributes = true; build_context.ignore_unknown_attributes = true;
@@ -1875,6 +1880,8 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Does extra checks on the code."); print_usage_line(2, "Does extra checks on the code.");
print_usage_line(2, "Extra checks include:"); print_usage_line(2, "Extra checks include:");
print_usage_line(3, "-vet-unused"); print_usage_line(3, "-vet-unused");
print_usage_line(3, "-vet-unused-variables");
print_usage_line(3, "-vet-unused-imports");
print_usage_line(3, "-vet-shadowing"); print_usage_line(3, "-vet-shadowing");
print_usage_line(3, "-vet-using-stmt"); print_usage_line(3, "-vet-using-stmt");
print_usage_line(0, ""); print_usage_line(0, "");
@@ -1883,6 +1890,14 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Checks for unused declarations."); print_usage_line(2, "Checks for unused declarations.");
print_usage_line(0, ""); print_usage_line(0, "");
print_usage_line(1, "-vet-unused-variables");
print_usage_line(2, "Checks for unused variable declarations.");
print_usage_line(0, "");
print_usage_line(1, "-vet-unused-imports");
print_usage_line(2, "Checks for unused import declarations.");
print_usage_line(0, "");
print_usage_line(1, "-vet-shadowing"); print_usage_line(1, "-vet-shadowing");
print_usage_line(2, "Checks for variable shadowing within procedures."); print_usage_line(2, "Checks for variable shadowing within procedures.");
print_usage_line(0, ""); print_usage_line(0, "");
+2 -72
View File
@@ -20,7 +20,6 @@ import "core:testing"
import "core:crypto" import "core:crypto"
import "core:crypto/chacha20" import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305" import "core:crypto/chacha20poly1305"
import "core:crypto/x25519"
import tc "tests:common" import tc "tests:common"
@@ -32,10 +31,10 @@ main :: proc() {
test_hash(&t) test_hash(&t)
test_mac(&t) test_mac(&t)
test_kdf(&t) // After hash/mac tests because those should pass first. test_kdf(&t) // After hash/mac tests because those should pass first.
test_ecc25519(&t)
test_chacha20(&t) test_chacha20(&t)
test_chacha20poly1305(&t) test_chacha20poly1305(&t)
test_x25519(&t)
test_sha3_variants(&t) test_sha3_variants(&t)
bench_crypto(&t) bench_crypto(&t)
@@ -274,80 +273,11 @@ test_chacha20poly1305 :: proc(t: ^testing.T) {
tc.expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)") tc.expect(t, !ok, "Expected false for decrypt(tag, corrupted_aad, ciphertext)")
} }
@(test)
test_x25519 :: proc(t: ^testing.T) {
tc.log(t, "Testing X25519")
// Local copy of this so that the base point doesn't need to be exported.
_BASE_POINT: [32]byte = {
9, 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, 0, 0, 0,
}
test_vectors := []struct{
scalar: string,
point: string,
product: string,
} {
// Test vectors from RFC 7748
{
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
},
{
"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
},
}
for v, _ in test_vectors {
scalar, _ := hex.decode(transmute([]byte)(v.scalar), context.temp_allocator)
point, _ := hex.decode(transmute([]byte)(v.point), context.temp_allocator)
derived_point: [x25519.POINT_SIZE]byte
x25519.scalarmult(derived_point[:], scalar[:], point[:])
derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator))
tc.expect(
t,
derived_point_str == v.product,
fmt.tprintf(
"Expected %s for %s * %s, but got %s instead",
v.product,
v.scalar,
v.point,
derived_point_str,
),
)
// Abuse the test vectors to sanity-check the scalar-basepoint multiply.
p1, p2: [x25519.POINT_SIZE]byte
x25519.scalarmult_basepoint(p1[:], scalar[:])
x25519.scalarmult(p2[:], scalar[:], _BASE_POINT[:])
p1_str := string(hex.encode(p1[:], context.temp_allocator))
p2_str := string(hex.encode(p2[:], context.temp_allocator))
tc.expect(
t,
p1_str == p2_str,
fmt.tprintf(
"Expected %s for %s * basepoint, but got %s instead",
p2_str,
v.scalar,
p1_str,
),
)
}
// TODO/tests: Run the wycheproof test vectors, once I figure out
// how to work with JSON.
}
@(test) @(test)
test_rand_bytes :: proc(t: ^testing.T) { test_rand_bytes :: proc(t: ^testing.T) {
tc.log(t, "Testing rand_bytes") tc.log(t, "Testing rand_bytes")
if ODIN_OS != .Linux { if !crypto.has_rand_bytes() {
tc.log(t, "rand_bytes not supported - skipping") tc.log(t, "rand_bytes not supported - skipping")
return return
} }
@@ -0,0 +1,766 @@
package test_core_crypto
import "base:runtime"
import "core:encoding/hex"
import "core:fmt"
import "core:testing"
import field "core:crypto/_fiat/field_curve25519"
import "core:crypto/ed25519"
import "core:crypto/ristretto255"
import "core:crypto/x25519"
import tc "tests:common"
@(test)
test_ecc25519 :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing curve25519 ECC")
test_sqrt_ratio_m1(t)
test_ristretto255(t)
test_ed25519(t)
test_x25519(t)
}
@(test)
test_sqrt_ratio_m1 :: proc(t: ^testing.T) {
tc.log(t, "Testing sqrt_ratio_m1")
test_vectors := []struct {
u: string,
v: string,
r: string,
was_square: bool,
} {
{
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
true,
},
{
"0000000000000000000000000000000000000000000000000000000000000000",
"0100000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
true,
},
{
"0100000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
false,
},
{
"0200000000000000000000000000000000000000000000000000000000000000",
"0100000000000000000000000000000000000000000000000000000000000000",
"3c5ff1b5d8e4113b871bd052f9e7bcd0582804c266ffb2d4f4203eb07fdb7c54",
false,
},
{
"0400000000000000000000000000000000000000000000000000000000000000",
"0100000000000000000000000000000000000000000000000000000000000000",
"0200000000000000000000000000000000000000000000000000000000000000",
true,
},
{
"0100000000000000000000000000000000000000000000000000000000000000",
"0400000000000000000000000000000000000000000000000000000000000000",
"f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
true,
},
}
for v, _ in test_vectors {
u_bytes, _ := hex.decode(transmute([]byte)(v.u), context.temp_allocator)
v_bytes, _ := hex.decode(transmute([]byte)(v.v), context.temp_allocator)
r_bytes, _ := hex.decode(transmute([]byte)(v.r), context.temp_allocator)
u_ := transmute(^[32]byte)(raw_data(u_bytes))
v_ := transmute(^[32]byte)(raw_data(v_bytes))
r_ := transmute(^[32]byte)(raw_data(r_bytes))
u, vee, r: field.Tight_Field_Element
field.fe_from_bytes(&u, u_)
field.fe_from_bytes(&vee, v_)
was_square := field.fe_carry_sqrt_ratio_m1(
&r,
field.fe_relax_cast(&u),
field.fe_relax_cast(&vee),
)
tc.expect(
t,
(was_square == 1) == v.was_square && field.fe_equal_bytes(&r, r_) == 1,
fmt.tprintf(
"Expected (%v, %s) for SQRT_RATIO_M1(%s, %s), got %s",
v.was_square,
v.r,
v.u,
v.v,
fe_str(&r),
),
)
}
}
@(test)
test_ristretto255 :: proc(t: ^testing.T) {
tc.log(t, "Testing ristretto255")
ge_gen: ristretto255.Group_Element
ristretto255.ge_generator(&ge_gen)
// Invalid encodings.
bad_encodings := []string {
// Non-canonical field encodings.
"00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
// Negative field elements.
"0100000000000000000000000000000000000000000000000000000000000000",
"01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20",
"c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562",
"c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78",
"47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24",
"f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72",
"87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309",
// Non-square x^2.
"26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371",
"4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f",
"de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b",
"bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042",
"2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08",
"f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22",
"8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731",
"2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b",
// Negative x * y value.
"3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e",
"a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220",
"d483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e",
"8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32",
"32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b",
"227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165",
"5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e",
"445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b",
// s = -1, which causes y = 0.
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
}
for x, _ in bad_encodings {
b, _ := hex.decode(transmute([]byte)(x), context.temp_allocator)
ge: ristretto255.Group_Element
ok := ristretto255.ge_set_bytes(&ge, b)
tc.expect(t, !ok, fmt.tprintf("Expected false for %s", x))
}
generator_multiples := []string {
"0000000000000000000000000000000000000000000000000000000000000000",
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76",
"6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919",
"94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259",
"da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57",
"e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e",
"f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403",
"44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d",
"903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c",
"02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031",
"20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f",
"bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42",
"e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460",
"aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f",
"46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e",
"e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e",
}
ges: [16]ristretto255.Group_Element
for x, i in generator_multiples {
b, _ := hex.decode(transmute([]byte)(x), context.temp_allocator)
ge := &ges[i]
ok := ristretto255.ge_set_bytes(ge, b)
tc.expect(t, ok, fmt.tprintf("Expected true for %s", x))
x_check := ge_str(ge)
tc.expect(
t,
x == x_check,
fmt.tprintf(
"Expected %s (round-trip) but got %s instead",
x,
x_check,
),
)
if i == 1 {
tc.expect(
t,
ristretto255.ge_equal(ge, &ge_gen) == 1,
"Expected element 1 to be the generator",
)
}
}
// Addition/Multiplication.
for _, i in ges {
sc: ristretto255.Scalar
ristretto255.sc_set_u64(&sc, u64(i))
ge_check: ristretto255.Group_Element
ristretto255.ge_scalarmult_generator(&ge_check, &sc)
x_check := ge_str(&ge_check)
tc.expect(
t,
x_check == generator_multiples[i],
fmt.tprintf(
"Expected %s for G * %d (specialized), got %s",
generator_multiples[i],
i,
x_check,
),
)
ristretto255.ge_scalarmult(&ge_check, &ges[1], &sc)
x_check = ge_str(&ge_check)
tc.expect(
t,
x_check == generator_multiples[i],
fmt.tprintf(
"Expected %s for G * %d (generic), got %s (slow compare)",
generator_multiples[i],
i,
x_check,
),
)
ristretto255.ge_scalarmult_vartime(&ge_check, &ges[1], &sc)
x_check = ge_str(&ge_check)
tc.expect(
t,
x_check == generator_multiples[i],
fmt.tprintf(
"Expected %s for G * %d (generic vartime), got %s (slow compare)",
generator_multiples[i],
i,
x_check,
),
)
switch i {
case 0:
case:
ge_prev := &ges[i-1]
ristretto255.ge_add(&ge_check, ge_prev, &ge_gen)
x_check = ge_str(&ge_check)
tc.expect(
t,
x_check == generator_multiples[i],
fmt.tprintf(
"Expected %s for ges[%d] + ges[%d], got %s (slow compare)",
generator_multiples[i],
i-1,
1,
x_check,
),
)
tc.expect(
t,
ristretto255.ge_equal(&ges[i], &ge_check) == 1,
fmt.tprintf(
"Expected %s for ges[%d] + ges[%d], got %s (fast compare)",
generator_multiples[i],
i-1,
1,
x_check,
),
)
}
}
wide_test_vectors := []struct {
input: string,
output: string,
} {
{
"5d1be09e3d0c82fc538112490e35701979d99e06ca3e2b5b54bffe8b4dc772c14d98b696a1bbfb5ca32c436cc61c16563790306c79eaca7705668b47dffe5bb6",
"3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46",
},
{
"f116b34b8f17ceb56e8732a60d913dd10cce47a6d53bee9204be8b44f6678b270102a56902e2488c46120e9276cfe54638286b9e4b3cdb470b542d46c2068d38",
"f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b",
},
{
"8422e1bbdaab52938b81fd602effb6f89110e1e57208ad12d9ad767e2e25510c27140775f9337088b982d83d7fcf0b2fa1edffe51952cbe7365e95c86eaf325c",
"006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826",
},
{
"ac22415129b61427bf464e17baee8db65940c233b98afce8d17c57beeb7876c2150d15af1cb1fb824bbd14955f2b57d08d388aab431a391cfc33d5bafb5dbbaf",
"f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a",
},
{
"165d697a1ef3d5cf3c38565beefcf88c0f282b8e7dbd28544c483432f1cec7675debea8ebb4e5fe7d6f6e5db15f15587ac4d4d4a1de7191e0c1ca6664abcc413",
"ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179",
},
{
"a836e6c9a9ca9f1e8d486273ad56a78c70cf18f0ce10abb1c7172ddd605d7fd2979854f47ae1ccf204a33102095b4200e5befc0465accc263175485f0e17ea5c",
"e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628",
},
{
"2cdc11eaeb95daf01189417cdddbf95952993aa9cb9c640eb5058d09702c74622c9965a697a3b345ec24ee56335b556e677b30e6f90ac77d781064f866a3c982",
"80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065",
},
// These all produce the same output.
{
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1200000000000000000000000000000000000000000000000000000000000000",
"304282791023b73128d277bdcb5c7746ef2eac08dde9f2983379cb8e5ef0517f",
},
{
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"304282791023b73128d277bdcb5c7746ef2eac08dde9f2983379cb8e5ef0517f",
},
{
"0000000000000000000000000000000000000000000000000000000000000080ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"304282791023b73128d277bdcb5c7746ef2eac08dde9f2983379cb8e5ef0517f",
},
{
"00000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000080",
"304282791023b73128d277bdcb5c7746ef2eac08dde9f2983379cb8e5ef0517f",
},
}
for v, _ in wide_test_vectors {
in_bytes, _ := hex.decode(transmute([]byte)(v.input), context.temp_allocator)
ge: ristretto255.Group_Element
ristretto255.ge_set_wide_bytes(&ge, in_bytes)
ge_check := ge_str(&ge)
tc.expect(
t,
ge_check == v.output,
fmt.tprintf(
"Expected %s for %s, got %s",
v.output,
ge_check,
),
)
}
}
@(test)
test_ed25519 :: proc(t: ^testing.T) {
tc.log(t, "Testing ed25519")
test_vectors_rfc := []struct {
priv_key: string,
pub_key: string,
msg: string,
sig: string,
} {
// Test vectors from RFC 8032
{
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"",
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
},
{
"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
"72",
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
},
{
"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
"af82",
"6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
},
// TEST 1024 omitted for brevity, because all that does is add more to SHA-512
{
"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
"dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704",
},
}
for v, _ in test_vectors_rfc {
priv_bytes, _ := hex.decode(transmute([]byte)(v.priv_key), context.temp_allocator)
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig), context.temp_allocator)
priv_key: ed25519.Private_Key
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
tc.expect(
t,
ok,
fmt.tprintf(
"Expected %s to be a valid private key",
v.priv_key,
),
)
key_bytes: [32]byte
ed25519.private_key_bytes(&priv_key, key_bytes[:])
tc.expect(
t,
ok,
fmt.tprintf(
"Expected private key %s round-trip, got %s",
v.priv_key,
string(hex.encode(key_bytes[:], context.temp_allocator)),
),
)
pub_key: ed25519.Public_Key
ok = ed25519.public_key_set_bytes(&pub_key, pub_bytes)
tc.expect(
t,
ok,
fmt.tprintf(
"Expected %s to be a valid public key (priv->pub: %s)",
v.pub_key,
string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator)),
),
)
ed25519.public_key_bytes(&pub_key, key_bytes[:])
tc.expect(
t,
ok,
fmt.tprintf(
"Expected public key %s round-trip, got %s",
v.pub_key,
string(hex.encode(key_bytes[:], context.temp_allocator)),
),
)
sig: [ed25519.SIGNATURE_SIZE]byte
ed25519.sign(&priv_key, msg_bytes, sig[:])
x := string(hex.encode(sig[:], context.temp_allocator))
tc.expect(
t,
x == v.sig,
fmt.tprintf(
"Expected %s for sign(%s, %s), got %s",
v.sig,
v.priv_key,
v.msg,
x,
),
)
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
tc.expect(
t,
ok,
fmt.tprintf(
"Expected true for verify(%s, %s, %s)",
v.pub_key,
v.msg,
v.sig,
),
)
ok = ed25519.verify(&priv_key._pub_key, msg_bytes, sig_bytes)
tc.expect(
t,
ok,
fmt.tprintf(
"Expected true for verify(pub(%s), %s %s)",
v.priv_key,
v.msg,
v.sig,
),
)
// Corrupt the message and make sure verification fails.
switch len(msg_bytes) {
case 0:
tmp_msg := []byte{69}
msg_bytes = tmp_msg[:]
case:
msg_bytes[0] = msg_bytes[0] ~ 69
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
tc.expect(
t,
ok == false,
fmt.tprintf(
"Expected false for verify(%s, %s (corrupted), %s)",
v.pub_key,
v.msg,
v.sig,
),
)
}
// Test cases from "Taming the many EdDSAs", which aim to exercise
// all of the ed25519 edge cases/implementation differences.
//
// - https://eprint.iacr.org/2020/1244
// - https://github.com/novifinancial/ed25519-speccheck
test_vectors_speccheck := []struct {
pub_key: string,
msg: string,
sig: string,
pub_key_ok: bool,
sig_ok: bool,
sig_ok_relaxed: bool, // Ok if the small-order A check is relaxed.
} {
// S = 0, small-order A, small-order R
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
true,
false,
true,
},
// 0 < S < L, small-order A, mixed-order R
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
true,
false,
true,
},
// 0 < S < L, mixed-order A, small-order R
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e",
true,
true,
true,
},
// 0 < S < L, mixed-order A, mixed-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009",
true,
true,
true,
},
// 0 < S < L, mixed-order A, mixed-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
"160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09",
true,
true, // cofactored-only
true,
},
// 0 < S < L, mixed-order A, L-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
"21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405",
true,
true, // cofactored only, (fail if 8h is pre-reduced)
true,
},
// S > L, L-order A, L-order R
{
"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
"e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514",
true,
false,
false,
},
// S >> L, L-order A, L-order R
{
"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
"8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22",
true,
false,
false,
},
// 0 < S < L, mixed-order A, small-order R (non-canonical R, reduced for hash)
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
true,
false,
false,
},
// 0 < S < L, mixed-order A, small-order R (non-canonical R, not reduced for hash)
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
true,
false,
false,
},
// 0 < S < L, small-order A, mixed-order R (non-canonical A, reduced for hash)
{
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
false,
false,
false,
},
// 0 < S < L, small-order A, mixed-order R (non-canonical A, not reduced for hash)
{
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f",
"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
false,
false,
false,
},
}
for v, i in test_vectors_speccheck {
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig), context.temp_allocator)
pub_key: ed25519.Public_Key
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes)
tc.expect(
t,
ok == v.pub_key_ok,
fmt.tprintf(
"speccheck/%d: Expected %s to be a (in)valid public key, got %v",
i,
v.pub_key,
ok,
),
)
// If A is rejected for being non-canonical, skip signature check.
if !v.pub_key_ok {
continue
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
tc.expect(
t,
ok == v.sig_ok,
fmt.tprintf(
"speccheck/%d Expected %v for verify(%s, %s, %s)",
i,
v.sig_ok,
v.pub_key,
v.msg,
v.sig,
),
)
// If the signature is accepted, skip the relaxed signature check.
if v.sig_ok {
continue
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes, true)
tc.expect(
t,
ok == v.sig_ok_relaxed,
fmt.tprintf(
"speccheck/%d Expected %v for verify(%s, %s, %s, true)",
i,
v.sig_ok_relaxed,
v.pub_key,
v.msg,
v.sig,
),
)
}
}
@(test)
test_x25519 :: proc(t: ^testing.T) {
tc.log(t, "Testing X25519")
// Local copy of this so that the base point doesn't need to be exported.
_BASE_POINT: [32]byte = {
9, 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, 0, 0, 0,
}
test_vectors := []struct {
scalar: string,
point: string,
product: string,
} {
// Test vectors from RFC 7748
{
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
},
{
"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
},
}
for v, _ in test_vectors {
scalar, _ := hex.decode(transmute([]byte)(v.scalar), context.temp_allocator)
point, _ := hex.decode(transmute([]byte)(v.point), context.temp_allocator)
derived_point: [x25519.POINT_SIZE]byte
x25519.scalarmult(derived_point[:], scalar[:], point[:])
derived_point_str := string(hex.encode(derived_point[:], context.temp_allocator))
tc.expect(
t,
derived_point_str == v.product,
fmt.tprintf(
"Expected %s for %s * %s, but got %s instead",
v.product,
v.scalar,
v.point,
derived_point_str,
),
)
// Abuse the test vectors to sanity-check the scalar-basepoint multiply.
p1, p2: [x25519.POINT_SIZE]byte
x25519.scalarmult_basepoint(p1[:], scalar[:])
x25519.scalarmult(p2[:], scalar[:], _BASE_POINT[:])
p1_str := string(hex.encode(p1[:], context.temp_allocator))
p2_str := string(hex.encode(p2[:], context.temp_allocator))
tc.expect(
t,
p1_str == p2_str,
fmt.tprintf(
"Expected %s for %s * basepoint, but got %s instead",
p2_str,
v.scalar,
p1_str,
),
)
}
}
@(private)
ge_str :: proc(ge: ^ristretto255.Group_Element) -> string {
b: [ristretto255.ELEMENT_SIZE]byte
ristretto255.ge_bytes(ge, b[:])
return string(hex.encode(b[:], context.temp_allocator))
}
@(private)
fe_str :: proc(fe: ^field.Tight_Field_Element) -> string {
b: [32]byte
field.fe_to_bytes(&b, fe)
return string(hex.encode(b[:], context.temp_allocator))
}
@@ -1,5 +1,6 @@
package test_core_crypto package test_core_crypto
import "base:runtime"
import "core:bytes" import "core:bytes"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt" import "core:fmt"
@@ -12,6 +13,8 @@ import tc "tests:common"
@(test) @(test)
test_hash :: proc(t: ^testing.T) { test_hash :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing Hashes") tc.log(t, "Testing Hashes")
// TODO: // TODO:
@@ -1,5 +1,6 @@
package test_core_crypto package test_core_crypto
import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt" import "core:fmt"
import "core:testing" import "core:testing"
@@ -12,6 +13,8 @@ import tc "tests:common"
@(test) @(test)
test_kdf :: proc(t: ^testing.T) { test_kdf :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing KDFs") tc.log(t, "Testing KDFs")
test_hkdf(t) test_hkdf(t)
@@ -1,5 +1,6 @@
package test_core_crypto package test_core_crypto
import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt" import "core:fmt"
import "core:mem" import "core:mem"
@@ -14,6 +15,8 @@ import tc "tests:common"
@(test) @(test)
test_mac :: proc(t: ^testing.T) { test_mac :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing MACs") tc.log(t, "Testing MACs")
test_hmac(t) test_hmac(t)
@@ -1,5 +1,6 @@
package test_core_crypto package test_core_crypto
import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt" import "core:fmt"
import "core:testing" import "core:testing"
@@ -12,6 +13,8 @@ import tc "tests:common"
@(test) @(test)
test_sha3_variants :: proc(t: ^testing.T) { test_sha3_variants :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
tc.log(t, "Testing SHA3 derived functions") tc.log(t, "Testing SHA3 derived functions")
test_shake(t) test_shake(t)
@@ -1,5 +1,6 @@
package test_core_crypto package test_core_crypto
import "base:runtime"
import "core:encoding/hex" import "core:encoding/hex"
import "core:fmt" import "core:fmt"
import "core:testing" import "core:testing"
@@ -7,6 +8,7 @@ import "core:time"
import "core:crypto/chacha20" import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305" import "core:crypto/chacha20poly1305"
import "core:crypto/ed25519"
import "core:crypto/poly1305" import "core:crypto/poly1305"
import "core:crypto/x25519" import "core:crypto/x25519"
@@ -16,11 +18,14 @@ import tc "tests:common"
@(test) @(test)
bench_crypto :: proc(t: ^testing.T) { bench_crypto :: proc(t: ^testing.T) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
fmt.println("Starting benchmarks:") fmt.println("Starting benchmarks:")
bench_chacha20(t) bench_chacha20(t)
bench_poly1305(t) bench_poly1305(t)
bench_chacha20poly1305(t) bench_chacha20poly1305(t)
bench_ed25519(t)
bench_x25519(t) bench_x25519(t)
} }
@@ -216,6 +221,64 @@ bench_chacha20poly1305 :: proc(t: ^testing.T) {
benchmark_print(name, options) benchmark_print(name, options)
} }
bench_ed25519 :: proc(t: ^testing.T) {
iters :: 10000
priv_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
priv_bytes, _ := hex.decode(transmute([]byte)(priv_str), context.temp_allocator)
priv_key: ed25519.Private_Key
start := time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
assert(ok, "private key should deserialize")
}
elapsed := time.since(start)
tc.log(
t,
fmt.tprintf(
"ed25519.private_key_set_bytes: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
),
)
pub_bytes := priv_key._pub_key._b[:] // "I know what I am doing"
pub_key: ed25519.Public_Key
start = time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes[:])
assert(ok, "public key should deserialize")
}
elapsed = time.since(start)
tc.log(
t,
fmt.tprintf(
"ed25519.public_key_set_bytes: ~%f us/op",
time.duration_microseconds(elapsed) / iters,
),
)
msg := "Got a job for you, 621."
sig_bytes: [ed25519.SIGNATURE_SIZE]byte
msg_bytes := transmute([]byte)(msg)
start = time.now()
for i := 0; i < iters; i = i + 1 {
ed25519.sign(&priv_key, msg_bytes, sig_bytes[:])
}
elapsed = time.since(start)
tc.log(t, fmt.tprintf("ed25519.sign: ~%f us/op", time.duration_microseconds(elapsed) / iters))
start = time.now()
for i := 0; i < iters; i = i + 1 {
ok := ed25519.verify(&pub_key, msg_bytes, sig_bytes[:])
assert(ok, "signature should validate")
}
elapsed = time.since(start)
tc.log(
t,
fmt.tprintf("ed25519.verify: ~%f us/op", time.duration_microseconds(elapsed) / iters),
)
}
bench_x25519 :: proc(t: ^testing.T) { bench_x25519 :: proc(t: ^testing.T) {
point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe" scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe"
+47 -3
View File
@@ -1,9 +1,12 @@
package test_core_odin_parser package test_core_odin_parser
import "core:testing"
import "core:fmt" import "core:fmt"
import "core:os" import "core:odin/ast"
import "core:odin/parser" import "core:odin/parser"
import "core:odin/printer"
import "core:os"
import "core:strings"
import "core:testing"
TEST_count := 0 TEST_count := 0
@@ -30,6 +33,7 @@ when ODIN_TEST {
main :: proc() { main :: proc() {
t := testing.T{} t := testing.T{}
test_parse_demo(&t) test_parse_demo(&t)
test_parse_bitfield(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 { if TEST_fail > 0 {
@@ -47,4 +51,44 @@ test_parse_demo :: proc(t: ^testing.T) {
for key, value in pkg.files { for key, value in pkg.files {
expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key)) expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key))
} }
} }
@test
test_parse_bitfield :: proc(t: ^testing.T) {
file := ast.File{
fullpath = "test.odin",
src = `
package main
Foo :: bit_field uint {}
Foo :: bit_field uint {hello: bool | 1}
Foo :: bit_field uint {
hello: bool | 1,
hello: bool | 5,
}
// Hellope 1.
Foo :: bit_field uint {
// Hellope 2.
hello: bool | 1,
hello: bool | 5, // Hellope 3.
}
`,
}
p := parser.default_parser()
ok := parser.parse_file(&p, &file)
expect(t, ok == true, "bad parse")
cfg := printer.default_style
cfg.newline_style = .LF
print := printer.make_printer(cfg)
out := printer.print(&print, &file)
tsrc := strings.trim_space(file.src)
tout := strings.trim_space(out)
expect(t, tsrc == tout, fmt.tprintf("\n%s\n!=\n%s", tsrc, tout))
}
+4 -4
View File
@@ -787,8 +787,8 @@ when !GL_DEBUG {
fmt.printf(" call: gl%s(", loc.procedure) fmt.printf(" call: gl%s(", loc.procedure)
{ {
// add input arguments // add input arguments
for arg, i in args[num_ret:] { for arg, arg_index in args[num_ret:] {
if i > 0 { fmt.printf(", ") } if arg_index > 0 { fmt.printf(", ") }
if v, ok := arg.(u32); ok { // TODO: Assumes all u32 are GLenum (they're not, GLbitfield and GLuint are also mapped to u32), fix later by better typing if v, ok := arg.(u32); ok { // TODO: Assumes all u32 are GLenum (they're not, GLbitfield and GLuint are also mapped to u32), fix later by better typing
if err == .INVALID_ENUM { if err == .INVALID_ENUM {
@@ -806,8 +806,8 @@ when !GL_DEBUG {
fmt.printf(") -> %v \n", args[0]) fmt.printf(") -> %v \n", args[0])
} else if num_ret > 1 { } else if num_ret > 1 {
fmt.printf(") -> (") fmt.printf(") -> (")
for arg, i in args[1:num_ret] { for arg, arg_index in args[1:num_ret] {
if i > 0 { fmt.printf(", ") } if arg_index > 0 { fmt.printf(", ") }
fmt.printf("%v", arg) fmt.printf("%v", arg)
} }
fmt.printf(")\n") fmt.printf(")\n")
+3
View File
@@ -0,0 +1,3 @@
package vendor_darwin_foundation
#panic(`Package moved to "core:sys/darwin/Foundation"`)