From 144504a752ec1e002684123035ed8aa02c99aa86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rikard=20Petr=C3=A9?= Date: Sat, 20 Jan 2024 15:53:19 +0100 Subject: [PATCH 01/73] 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. --- core/odin/parser/parser.odin | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index f11d0eb73..cb4d2785b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -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 { + 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 - if allow_token(p, .Close_Brace) { + if allow_token(p, closing_kind) { return token } if allow_token(p, .Semicolon) && !tokenizer.is_newline(token) { str := tokenizer.token_to_string(token) 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 { - for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) { + if expect_closing.kind != closing_kind { + for p.curr_tok.kind != closing_kind && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) { advance_token(p) } return p.curr_tok } - return expect_brace + return expect_closing } is_non_inserted_semicolon :: proc(tok: tokenizer.Token) -> bool { @@ -2922,7 +2926,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { } p.expr_level -= 1 - 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 lit := ast.new(ast.Comp_Lit, pos, end_pos(close)) @@ -2985,7 +2989,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr { 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 ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close)) From 99825a28d7357b7e884ce0387f7e5fabd3180148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rikard=20Petr=C3=A9?= Date: Sat, 20 Jan 2024 16:00:41 +0100 Subject: [PATCH 02/73] odin/parser: Allow semicolon after return statement for the case: if x do return y; else do return z; --- core/odin/parser/parser.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index cb4d2785b..f9fef15a9 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1340,6 +1340,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { rs := ast.new(ast.Return_Stmt, tok.pos, end) rs.results = results[:] + expect_semicolon(p, rs) return rs case .Break, .Continue, .Fallthrough: From 239d4e10762a12e96280bd91003acbf2170cadf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rikard=20Petr=C3=A9?= Date: Sat, 20 Jan 2024 16:09:41 +0100 Subject: [PATCH 03/73] odin/tokenizer: Reset insert_semicolon to false in tokenizer.init to fix bug when tokenizing multiple files. --- core/odin/tokenizer/tokenizer.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 41de3ac8b..62170aa10 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -39,6 +39,7 @@ init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = defa t.read_offset = 0 t.line_offset = 0 t.line_count = len(src) > 0 ? 1 : 0 + t.insert_semicolon = false t.error_count = 0 t.path = path From d83532d29e5503c3649fe9bb28a42b4ab7cb31e2 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 6 Apr 2024 20:20:16 +0200 Subject: [PATCH 04/73] fix raygui `GuiFade` being renamed to `GuiSetAlpha` --- vendor/raylib/raygui.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/raylib/raygui.odin b/vendor/raylib/raygui.odin index 726377dd6..41a4250a1 100644 --- a/vendor/raylib/raygui.odin +++ b/vendor/raylib/raygui.odin @@ -251,7 +251,7 @@ foreign lib { GuiDisable :: proc() --- // Disable gui controls (global state) GuiUnlock :: proc() --- // Unlock gui controls (global state) GuiIsLocked :: proc() -> bool --- // Check if gui is locked (global state) - GuiFade :: proc(alpha: f32) --- // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f + GuiSetAlpha :: proc(alpha: f32) --- // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f GuiSetState :: proc(state: c.int) --- // Set gui state (global state) GuiGetState :: proc() -> c.int --- // Get gui state (global state) From b150f49c464f2b3062306627ab277635383a24c7 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 6 Apr 2024 23:32:38 +0200 Subject: [PATCH 05/73] fix wasm atomics Fixes #2745 --- src/llvm_backend_proc.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 8ce116715..bb4aed3f1 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3059,9 +3059,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_wasm_memory_atomic_wait32: { char const *name = "llvm.wasm.memory.atomic.wait32"; - LLVMTypeRef types[1] = { - lb_type(p->module, t_u32), - }; Type *t_u32_ptr = alloc_type_pointer(t_u32); @@ -3072,26 +3069,24 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu lbValue res = {}; res.type = tv.type; - res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), nullptr, 0); return res; } case BuiltinProc_wasm_memory_atomic_notify32: { char const *name = "llvm.wasm.memory.atomic.notify"; - LLVMTypeRef types[1] = { - lb_type(p->module, t_u32), - }; Type *t_u32_ptr = alloc_type_pointer(t_u32); LLVMValueRef args[2] = { - lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value, - lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value }; + lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value, + lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value + }; lbValue res = {}; res.type = tv.type; - res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), nullptr, 0); return res; } From a71cd07b36533ac3e2c68f41ed8347c8ed90e23a Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Sun, 7 Apr 2024 09:02:01 +1000 Subject: [PATCH 06/73] Update "core:runtime" to "base:runtime" --- core/sync/futex_haiku.odin | 2 +- core/sys/darwin/core_foundation.odin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sync/futex_haiku.odin b/core/sync/futex_haiku.odin index 1dd719e7a..b81743cad 100644 --- a/core/sync/futex_haiku.odin +++ b/core/sync/futex_haiku.odin @@ -2,7 +2,7 @@ package sync import "core:c" -import "core:runtime" +import "base:runtime" import "core:sys/haiku" import "core:sys/unix" import "core:time" diff --git a/core/sys/darwin/core_foundation.odin b/core/sys/darwin/core_foundation.odin index eafe1a1f3..325122216 100644 --- a/core/sys/darwin/core_foundation.odin +++ b/core/sys/darwin/core_foundation.odin @@ -1,7 +1,7 @@ //+build darwin package darwin -import "core:runtime" +import "base:runtime" foreign import core_foundation "system:CoreFoundation.framework" From 0df9c8bffc3f468cab08eaad97b49952a0b6bf3e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 12:04:33 +0100 Subject: [PATCH 07/73] Improve error messages for people using keywords instead of identifiers --- src/parser.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index bf16f5c9f..01a3069ff 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1486,7 +1486,15 @@ gb_internal Token expect_token(AstFile *f, TokenKind kind) { syntax_error(f->curr_token, "Expected '%.*s', got '%.*s'", LIT(c), LIT(p)); if (kind == Token_Ident) switch (prev.kind) { case Token_context: - error_line("\tSuggestion: 'context' is a reserved keyword, would 'ctx' suffice?\n"); + error_line("\tSuggestion: '%.*s' is a keyword, would 'ctx' suffice?\n", LIT(prev.string)); + break; + case Token_package: + error_line("\tSuggestion: '%.*s' is a keyword, would 'pkg' suffice?\n", LIT(prev.string)); + break; + default: + if (token_is_keyword(prev.kind)) { + error_line("\tNote: '%.*s' is a keyword\n", LIT(prev.string)); + } break; } From 114ddc4a108eff7feb0cd83c7920998ddffd6dd1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 12:06:06 +0100 Subject: [PATCH 08/73] Fix printing error when field name could not be found --- src/check_expr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c80b08695..fe8c9599f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4595,7 +4595,8 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v String name = fv->field->Ident.token.string; Selection sub_sel = lookup_field(node->tav.type, name, false); defer (array_free(&sub_sel.index)); - if (sub_sel.index[0] == index) { + if (sub_sel.index.count > 0 && + sub_sel.index[0] == index) { value = fv->value->tav.value; found = true; break; From 667883b3d5161e86f92e25619c585292d4bd2526 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 8 Apr 2024 13:53:16 +0200 Subject: [PATCH 09/73] fix js_wasm `time.tick_now`, `performance.now()` returns a float --- core/time/time_js.odin | 4 ++-- vendor/wasm/js/runtime.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/time/time_js.odin b/core/time/time_js.odin index 932fc2b8e..c5090df90 100644 --- a/core/time/time_js.odin +++ b/core/time/time_js.odin @@ -24,9 +24,9 @@ _sleep :: proc "contextless" (d: Duration) { _tick_now :: proc "contextless" () -> Tick { foreign odin_env { - tick_now :: proc "contextless" () -> i64 --- + tick_now :: proc "contextless" () -> f32 --- } - return Tick{tick_now()*1e6} + return Tick{i64(tick_now()*1e6)} } _yield :: proc "contextless" () { diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index f5ca325f8..85be84caf 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -1335,7 +1335,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { // return a bigint to be converted to i64 time_now: () => BigInt(Date.now()), - tick_now: () => BigInt(performance.now()), + tick_now: () => performance.now(), time_sleep: (duration_ms) => { if (duration_ms > 0) { // TODO(bill): Does this even make any sense? From 9d8bb7f4e4f0c541b363a1ee4caccf9e6aa211fa Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 8 Apr 2024 13:54:23 +0200 Subject: [PATCH 10/73] fix `_end` being called before the actual end when using the step function --- vendor/wasm/js/runtime.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index 85be84caf..2e69a28c7 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -1676,6 +1676,9 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports) { exports._start(); + // Define a `@export step :: proc(dt: f32) -> (continue: bool) {` + // in your app and it will get called every frame. + // return `false` to stop the execution of the module. if (exports.step) { const odin_ctx = exports.default_context_ptr(); @@ -1687,15 +1690,20 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports) { const dt = (currTimeStamp - prevTimeStamp)*0.001; prevTimeStamp = currTimeStamp; - exports.step(dt, odin_ctx); + + if (!exports.step(dt, odin_ctx)) { + exports._end(); + return; + } + window.requestAnimationFrame(step); }; window.requestAnimationFrame(step); + } else { + exports._end(); } - exports._end(); - return; }; From ce196529dcb62a1955ca1156090c444947e92fa6 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 8 Apr 2024 13:55:23 +0200 Subject: [PATCH 11/73] enable the required target feature `atomics` when using them in wasm --- base/intrinsics/intrinsics.odin | 2 ++ src/check_builtin.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 78f4f3f41..458596adf 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -293,7 +293,9 @@ wasm_memory_size :: proc(index: uintptr) -> int --- // 0 - indicates that the thread blocked and then was woken up // 1 - the loaded value from `ptr` did not match `expected`, the thread did not block // 2 - the thread blocked, but the timeout +@(enable_target_feature="atomics") wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 --- +@(enable_target_feature="atomics") wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) --- // x86 Targets (i386, amd64) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f4aa9567d..d8fad487b 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -6014,6 +6014,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } + enable_target_feature({}, str_lit("atomics")); + Operand ptr = {}; Operand expected = {}; Operand timeout = {}; @@ -6066,6 +6068,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } + enable_target_feature({}, str_lit("atomics")); + Operand ptr = {}; Operand waiters = {}; check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false; From 16dc79fc5c98228471ed57eb0f8e853de739f6d9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 13:36:23 +0100 Subject: [PATCH 12/73] Add `"type"` field to `-json-errors` --- src/error.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/error.cpp b/src/error.cpp index c92392dce..eb167d4c3 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -667,6 +667,14 @@ gb_internal void print_all_errors(void) { gb_fprintf(f, "\t\t{\n"); + gb_fprintf(f, "\t\t\t\"type\": \""); + if (ev.kind == ErrorValue_Warning) { + gb_fprintf(f, "warning"); + } else { + gb_fprintf(f, "error"); + } + gb_fprintf(f, "\",\n"); + gb_fprintf(f, "\t\t\t\"pos\": {\n"); if (ev.pos.file_id) { From ef82f3e71e6ad08ecffa578b44a6dd1323676f0b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 13:47:46 +0100 Subject: [PATCH 13/73] Move `vendor:darwin/Foundation` to `core:sys/darwin/Foundation` --- {vendor => core/sys}/darwin/Foundation/NSApplication.odin | 0 {vendor => core/sys}/darwin/Foundation/NSArray.odin | 0 {vendor => core/sys}/darwin/Foundation/NSAutoreleasePool.odin | 0 {vendor => core/sys}/darwin/Foundation/NSBlock.odin | 0 {vendor => core/sys}/darwin/Foundation/NSBundle.odin | 0 {vendor => core/sys}/darwin/Foundation/NSColor.odin | 0 {vendor => core/sys}/darwin/Foundation/NSData.odin | 0 {vendor => core/sys}/darwin/Foundation/NSDate.odin | 0 {vendor => core/sys}/darwin/Foundation/NSDictionary.odin | 0 {vendor => core/sys}/darwin/Foundation/NSEnumerator.odin | 0 {vendor => core/sys}/darwin/Foundation/NSError.odin | 0 {vendor => core/sys}/darwin/Foundation/NSEvent.odin | 0 {vendor => core/sys}/darwin/Foundation/NSLock.odin | 0 {vendor => core/sys}/darwin/Foundation/NSMenu.odin | 0 {vendor => core/sys}/darwin/Foundation/NSNotification.odin | 0 {vendor => core/sys}/darwin/Foundation/NSNumber.odin | 0 {vendor => core/sys}/darwin/Foundation/NSObject.odin | 0 {vendor => core/sys}/darwin/Foundation/NSOpenPanel.odin | 0 {vendor => core/sys}/darwin/Foundation/NSPanel.odin | 0 {vendor => core/sys}/darwin/Foundation/NSPasteboard.odin | 0 {vendor => core/sys}/darwin/Foundation/NSRange.odin | 0 {vendor => core/sys}/darwin/Foundation/NSSavePanel.odin | 0 {vendor => core/sys}/darwin/Foundation/NSScreen.odin | 0 {vendor => core/sys}/darwin/Foundation/NSSet.odin | 0 {vendor => core/sys}/darwin/Foundation/NSString.odin | 0 {vendor => core/sys}/darwin/Foundation/NSTypes.odin | 0 {vendor => core/sys}/darwin/Foundation/NSURL.odin | 0 {vendor => core/sys}/darwin/Foundation/NSUndoManager.odin | 0 {vendor => core/sys}/darwin/Foundation/NSUserActivity.odin | 0 {vendor => core/sys}/darwin/Foundation/NSUserDefaults.odin | 0 {vendor => core/sys}/darwin/Foundation/NSWindow.odin | 0 {vendor => core/sys}/darwin/Foundation/objc.odin | 0 examples/all/all_vendor.odin | 2 +- vendor/darwin/Metal/MetalClasses.odin | 2 +- vendor/darwin/Metal/MetalEnums.odin | 2 +- vendor/darwin/Metal/MetalErrors.odin | 2 +- vendor/darwin/Metal/MetalProcedures.odin | 2 +- vendor/darwin/Metal/MetalTypes.odin | 2 +- vendor/darwin/MetalKit/MetalKit.odin | 2 +- vendor/darwin/QuartzCore/QuartzCore.odin | 2 +- vendor/glfw/native_darwin.odin | 2 +- 41 files changed, 9 insertions(+), 9 deletions(-) rename {vendor => core/sys}/darwin/Foundation/NSApplication.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSArray.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSAutoreleasePool.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSBlock.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSBundle.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSColor.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSData.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSDate.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSDictionary.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSEnumerator.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSError.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSEvent.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSLock.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSMenu.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSNotification.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSNumber.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSObject.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSOpenPanel.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSPanel.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSPasteboard.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSRange.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSSavePanel.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSScreen.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSSet.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSString.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSTypes.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSURL.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSUndoManager.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSUserActivity.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSUserDefaults.odin (100%) rename {vendor => core/sys}/darwin/Foundation/NSWindow.odin (100%) rename {vendor => core/sys}/darwin/Foundation/objc.odin (100%) diff --git a/vendor/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin similarity index 100% rename from vendor/darwin/Foundation/NSApplication.odin rename to core/sys/darwin/Foundation/NSApplication.odin diff --git a/vendor/darwin/Foundation/NSArray.odin b/core/sys/darwin/Foundation/NSArray.odin similarity index 100% rename from vendor/darwin/Foundation/NSArray.odin rename to core/sys/darwin/Foundation/NSArray.odin diff --git a/vendor/darwin/Foundation/NSAutoreleasePool.odin b/core/sys/darwin/Foundation/NSAutoreleasePool.odin similarity index 100% rename from vendor/darwin/Foundation/NSAutoreleasePool.odin rename to core/sys/darwin/Foundation/NSAutoreleasePool.odin diff --git a/vendor/darwin/Foundation/NSBlock.odin b/core/sys/darwin/Foundation/NSBlock.odin similarity index 100% rename from vendor/darwin/Foundation/NSBlock.odin rename to core/sys/darwin/Foundation/NSBlock.odin diff --git a/vendor/darwin/Foundation/NSBundle.odin b/core/sys/darwin/Foundation/NSBundle.odin similarity index 100% rename from vendor/darwin/Foundation/NSBundle.odin rename to core/sys/darwin/Foundation/NSBundle.odin diff --git a/vendor/darwin/Foundation/NSColor.odin b/core/sys/darwin/Foundation/NSColor.odin similarity index 100% rename from vendor/darwin/Foundation/NSColor.odin rename to core/sys/darwin/Foundation/NSColor.odin diff --git a/vendor/darwin/Foundation/NSData.odin b/core/sys/darwin/Foundation/NSData.odin similarity index 100% rename from vendor/darwin/Foundation/NSData.odin rename to core/sys/darwin/Foundation/NSData.odin diff --git a/vendor/darwin/Foundation/NSDate.odin b/core/sys/darwin/Foundation/NSDate.odin similarity index 100% rename from vendor/darwin/Foundation/NSDate.odin rename to core/sys/darwin/Foundation/NSDate.odin diff --git a/vendor/darwin/Foundation/NSDictionary.odin b/core/sys/darwin/Foundation/NSDictionary.odin similarity index 100% rename from vendor/darwin/Foundation/NSDictionary.odin rename to core/sys/darwin/Foundation/NSDictionary.odin diff --git a/vendor/darwin/Foundation/NSEnumerator.odin b/core/sys/darwin/Foundation/NSEnumerator.odin similarity index 100% rename from vendor/darwin/Foundation/NSEnumerator.odin rename to core/sys/darwin/Foundation/NSEnumerator.odin diff --git a/vendor/darwin/Foundation/NSError.odin b/core/sys/darwin/Foundation/NSError.odin similarity index 100% rename from vendor/darwin/Foundation/NSError.odin rename to core/sys/darwin/Foundation/NSError.odin diff --git a/vendor/darwin/Foundation/NSEvent.odin b/core/sys/darwin/Foundation/NSEvent.odin similarity index 100% rename from vendor/darwin/Foundation/NSEvent.odin rename to core/sys/darwin/Foundation/NSEvent.odin diff --git a/vendor/darwin/Foundation/NSLock.odin b/core/sys/darwin/Foundation/NSLock.odin similarity index 100% rename from vendor/darwin/Foundation/NSLock.odin rename to core/sys/darwin/Foundation/NSLock.odin diff --git a/vendor/darwin/Foundation/NSMenu.odin b/core/sys/darwin/Foundation/NSMenu.odin similarity index 100% rename from vendor/darwin/Foundation/NSMenu.odin rename to core/sys/darwin/Foundation/NSMenu.odin diff --git a/vendor/darwin/Foundation/NSNotification.odin b/core/sys/darwin/Foundation/NSNotification.odin similarity index 100% rename from vendor/darwin/Foundation/NSNotification.odin rename to core/sys/darwin/Foundation/NSNotification.odin diff --git a/vendor/darwin/Foundation/NSNumber.odin b/core/sys/darwin/Foundation/NSNumber.odin similarity index 100% rename from vendor/darwin/Foundation/NSNumber.odin rename to core/sys/darwin/Foundation/NSNumber.odin diff --git a/vendor/darwin/Foundation/NSObject.odin b/core/sys/darwin/Foundation/NSObject.odin similarity index 100% rename from vendor/darwin/Foundation/NSObject.odin rename to core/sys/darwin/Foundation/NSObject.odin diff --git a/vendor/darwin/Foundation/NSOpenPanel.odin b/core/sys/darwin/Foundation/NSOpenPanel.odin similarity index 100% rename from vendor/darwin/Foundation/NSOpenPanel.odin rename to core/sys/darwin/Foundation/NSOpenPanel.odin diff --git a/vendor/darwin/Foundation/NSPanel.odin b/core/sys/darwin/Foundation/NSPanel.odin similarity index 100% rename from vendor/darwin/Foundation/NSPanel.odin rename to core/sys/darwin/Foundation/NSPanel.odin diff --git a/vendor/darwin/Foundation/NSPasteboard.odin b/core/sys/darwin/Foundation/NSPasteboard.odin similarity index 100% rename from vendor/darwin/Foundation/NSPasteboard.odin rename to core/sys/darwin/Foundation/NSPasteboard.odin diff --git a/vendor/darwin/Foundation/NSRange.odin b/core/sys/darwin/Foundation/NSRange.odin similarity index 100% rename from vendor/darwin/Foundation/NSRange.odin rename to core/sys/darwin/Foundation/NSRange.odin diff --git a/vendor/darwin/Foundation/NSSavePanel.odin b/core/sys/darwin/Foundation/NSSavePanel.odin similarity index 100% rename from vendor/darwin/Foundation/NSSavePanel.odin rename to core/sys/darwin/Foundation/NSSavePanel.odin diff --git a/vendor/darwin/Foundation/NSScreen.odin b/core/sys/darwin/Foundation/NSScreen.odin similarity index 100% rename from vendor/darwin/Foundation/NSScreen.odin rename to core/sys/darwin/Foundation/NSScreen.odin diff --git a/vendor/darwin/Foundation/NSSet.odin b/core/sys/darwin/Foundation/NSSet.odin similarity index 100% rename from vendor/darwin/Foundation/NSSet.odin rename to core/sys/darwin/Foundation/NSSet.odin diff --git a/vendor/darwin/Foundation/NSString.odin b/core/sys/darwin/Foundation/NSString.odin similarity index 100% rename from vendor/darwin/Foundation/NSString.odin rename to core/sys/darwin/Foundation/NSString.odin diff --git a/vendor/darwin/Foundation/NSTypes.odin b/core/sys/darwin/Foundation/NSTypes.odin similarity index 100% rename from vendor/darwin/Foundation/NSTypes.odin rename to core/sys/darwin/Foundation/NSTypes.odin diff --git a/vendor/darwin/Foundation/NSURL.odin b/core/sys/darwin/Foundation/NSURL.odin similarity index 100% rename from vendor/darwin/Foundation/NSURL.odin rename to core/sys/darwin/Foundation/NSURL.odin diff --git a/vendor/darwin/Foundation/NSUndoManager.odin b/core/sys/darwin/Foundation/NSUndoManager.odin similarity index 100% rename from vendor/darwin/Foundation/NSUndoManager.odin rename to core/sys/darwin/Foundation/NSUndoManager.odin diff --git a/vendor/darwin/Foundation/NSUserActivity.odin b/core/sys/darwin/Foundation/NSUserActivity.odin similarity index 100% rename from vendor/darwin/Foundation/NSUserActivity.odin rename to core/sys/darwin/Foundation/NSUserActivity.odin diff --git a/vendor/darwin/Foundation/NSUserDefaults.odin b/core/sys/darwin/Foundation/NSUserDefaults.odin similarity index 100% rename from vendor/darwin/Foundation/NSUserDefaults.odin rename to core/sys/darwin/Foundation/NSUserDefaults.odin diff --git a/vendor/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin similarity index 100% rename from vendor/darwin/Foundation/NSWindow.odin rename to core/sys/darwin/Foundation/NSWindow.odin diff --git a/vendor/darwin/Foundation/objc.odin b/core/sys/darwin/Foundation/objc.odin similarity index 100% rename from vendor/darwin/Foundation/objc.odin rename to core/sys/darwin/Foundation/objc.odin diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 04a64ecb4..9093e0f3e 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -62,7 +62,7 @@ _ :: xlib // NOTE: needed for doc generator -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" import MTL "vendor:darwin/Metal" import MTK "vendor:darwin/MetalKit" import CA "vendor:darwin/QuartzCore" diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index 17f22e1d3..ea1711bbc 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -1,6 +1,6 @@ package objc_Metal -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" import "core:mem" _ :: mem diff --git a/vendor/darwin/Metal/MetalEnums.odin b/vendor/darwin/Metal/MetalEnums.odin index ab4782da4..5cef5f18d 100644 --- a/vendor/darwin/Metal/MetalEnums.odin +++ b/vendor/darwin/Metal/MetalEnums.odin @@ -1,6 +1,6 @@ package objc_Metal -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" AccelerationStructureUsage :: distinct bit_set[AccelerationStructureUsageFlag; NS.UInteger] AccelerationStructureUsageFlag :: enum NS.UInteger { diff --git a/vendor/darwin/Metal/MetalErrors.odin b/vendor/darwin/Metal/MetalErrors.odin index 8bc851e33..58e60e8ff 100644 --- a/vendor/darwin/Metal/MetalErrors.odin +++ b/vendor/darwin/Metal/MetalErrors.odin @@ -1,6 +1,6 @@ package objc_Metal -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" foreign import "system:Metal.framework" diff --git a/vendor/darwin/Metal/MetalProcedures.odin b/vendor/darwin/Metal/MetalProcedures.odin index dd90bd3e9..74e7903e9 100644 --- a/vendor/darwin/Metal/MetalProcedures.odin +++ b/vendor/darwin/Metal/MetalProcedures.odin @@ -1,6 +1,6 @@ package objc_Metal -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" import "core:c" @(require) diff --git a/vendor/darwin/Metal/MetalTypes.odin b/vendor/darwin/Metal/MetalTypes.odin index 6d372b925..34daf3347 100644 --- a/vendor/darwin/Metal/MetalTypes.odin +++ b/vendor/darwin/Metal/MetalTypes.odin @@ -1,6 +1,6 @@ package objc_Metal -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" import "base:intrinsics" BOOL :: NS.BOOL diff --git a/vendor/darwin/MetalKit/MetalKit.odin b/vendor/darwin/MetalKit/MetalKit.odin index 060824923..34a87cf42 100644 --- a/vendor/darwin/MetalKit/MetalKit.odin +++ b/vendor/darwin/MetalKit/MetalKit.odin @@ -1,6 +1,6 @@ package objc_MetalKit -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" import MTL "vendor:darwin/Metal" import CA "vendor:darwin/QuartzCore" import "base:intrinsics" diff --git a/vendor/darwin/QuartzCore/QuartzCore.odin b/vendor/darwin/QuartzCore/QuartzCore.odin index 8b118ba04..b19a5fec5 100644 --- a/vendor/darwin/QuartzCore/QuartzCore.odin +++ b/vendor/darwin/QuartzCore/QuartzCore.odin @@ -1,6 +1,6 @@ package objc_QuartzCore -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" import MTL "vendor:darwin/Metal" import "base:intrinsics" diff --git a/vendor/glfw/native_darwin.odin b/vendor/glfw/native_darwin.odin index 999de218b..b5191a913 100644 --- a/vendor/glfw/native_darwin.odin +++ b/vendor/glfw/native_darwin.odin @@ -2,7 +2,7 @@ package glfw -import NS "vendor:darwin/Foundation" +import NS "core:sys/darwin/Foundation" @(default_calling_convention="c", link_prefix="glfw") foreign { From 810cf22e5ddd772ee214eec306b1ba148623302c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 16:08:35 +0100 Subject: [PATCH 14/73] Obfuscate `#file` and `#procedure` when `-obfuscate-source-code-locations` is enabled --- src/check_expr.cpp | 12 ++++++++++-- src/common.cpp | 13 +++++++++++++ src/llvm_backend_const.cpp | 15 ++------------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fe8c9599f..7d8e0f829 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8157,8 +8157,12 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->mode = Addressing_Constant; String name = bd->name.string; if (name == "file") { + String file = get_file_path_string(bd->token.pos.file_id); + if (build_context.obfuscate_source_code_locations) { + file = obfuscate_string(file, "F"); + } o->type = t_untyped_string; - o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); + o->value = exact_value_string(file); } else if (name == "line") { o->type = t_untyped_integer; o->value = exact_value_i64(bd->token.pos.line); @@ -8168,8 +8172,12 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->type = t_untyped_string; o->value = exact_value_string(str_lit("")); } else { + String p = c->proc_name; + if (build_context.obfuscate_source_code_locations) { + p = obfuscate_string(p, "P"); + } o->type = t_untyped_string; - o->value = exact_value_string(c->proc_name); + o->value = exact_value_string(p); } } else if (name == "caller_location") { init_core_source_code_location(c->checker); diff --git a/src/common.cpp b/src/common.cpp index aad420325..6a53332d9 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -353,6 +353,19 @@ gb_global bool global_module_path_set = false; #include "thread_pool.cpp" +gb_internal String obfuscate_string(String const &s, char const *prefix) { + if (s.len == 0) { + return {}; + } + GB_ASSERT(prefix != nullptr); + u64 hash = gb_fnv64a(s.text, s.len); + gbString res = gb_string_make(temporary_allocator(), prefix); + res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash); + return make_string_c(res); +} + + + struct StringIntern { StringIntern *next; isize len; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 1ca5f4965..5b2af1049 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -287,17 +287,6 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type return lb_const_value(m, t, tv.value); } -gb_internal String lb_obfuscate_string(String const &s, char const *prefix) { - if (s.len == 0) { - return {}; - } - GB_ASSERT(prefix != nullptr); - u64 hash = gb_fnv64a(s.text, s.len); - gbString res = gb_string_make(temporary_allocator(), prefix); - res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash); - return make_string_c(res); -} - gb_internal i32 lb_obfuscate_i32(i32 i) { i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); if (x < 0) { @@ -314,8 +303,8 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons i32 column = pos.column; if (build_context.obfuscate_source_code_locations) { - file = lb_obfuscate_string(file, "F"); - procedure = lb_obfuscate_string(procedure, "P"); + file = obfuscate_string(file, "F"); + procedure = obfuscate_string(procedure, "P"); line = lb_obfuscate_i32(line); column = lb_obfuscate_i32(column); From ec455046316ba1bf44a6f2118512341a3f68b10c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 16:14:47 +0100 Subject: [PATCH 15/73] Obfuscate `#line` --- src/check_expr.cpp | 6 +++++- src/common.cpp | 8 ++++++++ src/llvm_backend_const.cpp | 11 ++--------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7d8e0f829..b893b3a00 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8164,8 +8164,12 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A o->type = t_untyped_string; o->value = exact_value_string(file); } else if (name == "line") { + i32 line = bd->token.pos.line; + if (build_context.obfuscate_source_code_locations) { + line = obfuscate_i32(line); + } o->type = t_untyped_integer; - o->value = exact_value_i64(bd->token.pos.line); + o->value = exact_value_i64(line); } else if (name == "procedure") { if (c->curr_proc_decl == nullptr) { error(node, "#procedure may only be used within procedures"); diff --git a/src/common.cpp b/src/common.cpp index 6a53332d9..e0a579c5d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -364,6 +364,14 @@ gb_internal String obfuscate_string(String const &s, char const *prefix) { return make_string_c(res); } +gb_internal i32 obfuscate_i32(i32 i) { + i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); + if (x < 0) { + x = 1-x; + } + return cast(i32)x; +} + struct StringIntern { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 5b2af1049..8035336d3 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -287,13 +287,6 @@ gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type return lb_const_value(m, t, tv.value); } -gb_internal i32 lb_obfuscate_i32(i32 i) { - i32 x = cast(i32)gb_fnv64a(&i, sizeof(i)); - if (x < 0) { - x = 1-x; - } - return cast(i32)x; -} gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure_, TokenPos const &pos) { String file = get_file_path_string(pos.file_id); @@ -306,8 +299,8 @@ gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String cons file = obfuscate_string(file, "F"); procedure = obfuscate_string(procedure, "P"); - line = lb_obfuscate_i32(line); - column = lb_obfuscate_i32(column); + line = obfuscate_i32(line); + column = obfuscate_i32(column); } LLVMValueRef fields[4] = {}; From ecac3aef3275e199deac2ce568647d14049f9e49 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 16:18:14 +0100 Subject: [PATCH 16/73] Change allocator to permanent --- src/common.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index e0a579c5d..69426e2a6 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -355,11 +355,11 @@ gb_global bool global_module_path_set = false; gb_internal String obfuscate_string(String const &s, char const *prefix) { if (s.len == 0) { - return {}; + return s; } GB_ASSERT(prefix != nullptr); u64 hash = gb_fnv64a(s.text, s.len); - gbString res = gb_string_make(temporary_allocator(), prefix); + gbString res = gb_string_make(permanent_allocator(), prefix); res = gb_string_append_fmt(res, "x%llx", cast(long long unsigned)hash); return make_string_c(res); } From 9c958ee66d21b3197382bf95f8db275e106930c8 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 8 Apr 2024 20:42:09 +0200 Subject: [PATCH 17/73] fix nasm check --- src/linker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linker.cpp b/src/linker.cpp index aa36b3278..498a96c5f 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -384,7 +384,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { LIT(obj_file), 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)); return result; } From 9933ca8b56cbabf32df295c266344b98801781b5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 8 Apr 2024 23:58:30 +0100 Subject: [PATCH 18/73] Make `map_total_allocation_size` public; Add `map_total_allocation_size_from_value` --- base/runtime/dynamic_map_internal.odin | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/base/runtime/dynamic_map_internal.odin b/base/runtime/dynamic_map_internal.odin index 642a6fb18..5ad155400 100644 --- a/base/runtime/dynamic_map_internal.odin +++ b/base/runtime/dynamic_map_internal.odin @@ -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 { round :: #force_inline proc "contextless" (value: uintptr) -> uintptr { CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1 @@ -350,6 +350,12 @@ map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr 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. @(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) { From a43a5b053c1d1e931eeb56d65e6a40f634a0b94f Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 24 Mar 2024 22:52:21 +0900 Subject: [PATCH 19/73] core/crypto: Add more documentation about assumptions (NFC) --- core/crypto/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/crypto/README.md b/core/crypto/README.md index 1e4e41fb8..303b1f625 100644 --- a/core/crypto/README.md +++ b/core/crypto/README.md @@ -14,6 +14,14 @@ constant-time byte comparison. - Best-effort is make to mitigate timing side-channels on reasonable architectures. Architectures that are known to be unreasonable include but are not limited to i386, i486, and WebAssembly. +- 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 will remain a "best-effort" implementation decision. As Thomas Pornin puts it "In general, such memory cleansing is a fool's quest." From b155fdf8c96d6269fe0f56a3fda76a3df1e5a7c8 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 17 Mar 2024 10:29:59 +0900 Subject: [PATCH 20/73] 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. --- core/crypto/crypto.odin | 10 ++++++++++ core/crypto/rand_bsd.odin | 4 ++++ core/crypto/rand_darwin.odin | 4 ++++ core/crypto/rand_generic.odin | 4 ++++ core/crypto/rand_js.odin | 4 ++++ core/crypto/rand_linux.odin | 4 ++++ core/crypto/rand_windows.odin | 4 ++++ tests/core/crypto/test_core_crypto.odin | 2 +- 8 files changed, 35 insertions(+), 1 deletion(-) diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index 6cdcacb9c..05f25111a 100644 --- a/core/crypto/crypto.odin +++ b/core/crypto/crypto.odin @@ -1,3 +1,7 @@ +/* +package crypto implements a selection of cryptography algorithms and useful +helper routines. +*/ package crypto import "core:mem" @@ -51,3 +55,9 @@ rand_bytes :: proc (dst: []byte) { _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() +} diff --git a/core/crypto/rand_bsd.odin b/core/crypto/rand_bsd.odin index 8e2be1d95..61eaf652f 100644 --- a/core/crypto/rand_bsd.odin +++ b/core/crypto/rand_bsd.odin @@ -10,3 +10,7 @@ foreign libc { _rand_bytes :: proc(dst: []byte) { arc4random_buf(raw_data(dst), len(dst)) } + +_has_rand_bytes :: proc () -> bool { + return true +} diff --git a/core/crypto/rand_darwin.odin b/core/crypto/rand_darwin.odin index ec44c1491..2864b46dd 100644 --- a/core/crypto/rand_darwin.odin +++ b/core/crypto/rand_darwin.odin @@ -10,3 +10,7 @@ _rand_bytes :: proc(dst: []byte) { panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg)) } } + +_has_rand_bytes :: proc () -> bool { + return true +} diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin index bf7abbbe2..006ca51fe 100644 --- a/core/crypto/rand_generic.odin +++ b/core/crypto/rand_generic.odin @@ -9,3 +9,7 @@ package crypto _rand_bytes :: proc(dst: []byte) { unimplemented("crypto: rand_bytes not supported on this OS") } + +_has_rand_bytes :: proc () -> bool { + return false +} diff --git a/core/crypto/rand_js.odin b/core/crypto/rand_js.odin index 353b1e6b9..cb2711404 100644 --- a/core/crypto/rand_js.odin +++ b/core/crypto/rand_js.odin @@ -18,3 +18,7 @@ _rand_bytes :: proc(dst: []byte) { dst = dst[to_read:] } } + +_has_rand_bytes :: proc () -> bool { + return true +} diff --git a/core/crypto/rand_linux.odin b/core/crypto/rand_linux.odin index 86fc425d6..05c05597d 100644 --- a/core/crypto/rand_linux.odin +++ b/core/crypto/rand_linux.odin @@ -34,3 +34,7 @@ _rand_bytes :: proc (dst: []byte) { dst = dst[n_read:] } } + +_has_rand_bytes :: proc () -> bool { + return true +} diff --git a/core/crypto/rand_windows.odin b/core/crypto/rand_windows.odin index 53b58c776..e1d9f6118 100644 --- a/core/crypto/rand_windows.odin +++ b/core/crypto/rand_windows.odin @@ -21,3 +21,7 @@ _rand_bytes :: proc(dst: []byte) { } } } + +_has_rand_bytes :: proc () -> bool { + return true +} diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin index a6d399097..742e3cc04 100644 --- a/tests/core/crypto/test_core_crypto.odin +++ b/tests/core/crypto/test_core_crypto.odin @@ -347,7 +347,7 @@ test_x25519 :: proc(t: ^testing.T) { test_rand_bytes :: proc(t: ^testing.T) { tc.log(t, "Testing rand_bytes") - if ODIN_OS != .Linux { + if !crypto.has_rand_bytes() { tc.log(t, "rand_bytes not supported - skipping") return } From f9b9521bf07ffece22b24ac02ae4261e3d8b3c50 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 17 Mar 2024 10:39:10 +0900 Subject: [PATCH 21/73] 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. --- core/crypto/_fiat/field_curve25519/field51.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin index 0be94eb51..1a731b31b 100644 --- a/core/crypto/_fiat/field_curve25519/field51.odin +++ b/core/crypto/_fiat/field_curve25519/field51.odin @@ -599,7 +599,7 @@ fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) { @(optimization_mode="none") fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) { - mask := -u64(arg1) + mask := (u64(arg1) * 0xffffffffffffffff) x := (out1[0] ~ out2[0]) & mask x1, y1 := out1[0] ~ x, out2[0] ~ x x = (out1[1] ~ out2[1]) & mask From 9a418fd27bcf5600ac16d74649f01e35bb8e626c Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 17 Mar 2024 19:02:11 +0900 Subject: [PATCH 22/73] core/crypto/_fiat/field_curve25519: Move routines (NFC) --- core/crypto/_fiat/field_curve25519/field.odin | 49 +++++++++++++++++ .../_fiat/field_curve25519/field51.odin | 53 ------------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/core/crypto/_fiat/field_curve25519/field.odin b/core/crypto/_fiat/field_curve25519/field.odin index faf8ae3f7..a8e0a0316 100644 --- a/core/crypto/_fiat/field_curve25519/field.odin +++ b/core/crypto/_fiat/field_curve25519/field.odin @@ -136,3 +136,52 @@ fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { mem.zero_explicit(&tmp1, size_of(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 +} diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin index 1a731b31b..3cbc296b7 100644 --- a/core/crypto/_fiat/field_curve25519/field51.odin +++ b/core/crypto/_fiat/field_curve25519/field51.odin @@ -30,8 +30,6 @@ package field_curve25519 // // While the base implementation is provably correct, this implementation // 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: // * When fiat-crypto supports it, using a saturated 64-bit limbs @@ -565,54 +563,3 @@ fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_El out1[3] = x27 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) * 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 -} From 31aba5a7280dd5c8fe70d960058002fd682baa57 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 17 Mar 2024 19:06:50 +0900 Subject: [PATCH 23/73] core/crypto/_fiat/field_poly1305: Move routines (NFC) --- core/crypto/_fiat/field_poly1305/field.odin | 29 +++++++++++++++++ .../_fiat/field_poly1305/field4344.odin | 31 ------------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin index a103f6fc7..9b00ff3ec 100644 --- a/core/crypto/_fiat/field_poly1305/field.odin +++ b/core/crypto/_fiat/field_poly1305/field.odin @@ -51,3 +51,32 @@ fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) { // This routine is only used to deserialize `r` which is confidential. 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) + 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 +} diff --git a/core/crypto/_fiat/field_poly1305/field4344.odin b/core/crypto/_fiat/field_poly1305/field4344.odin index 8e8a7cc78..ddc10de52 100644 --- a/core/crypto/_fiat/field_poly1305/field4344.odin +++ b/core/crypto/_fiat/field_poly1305/field4344.odin @@ -325,34 +325,3 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E out1[1] = x2 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 -} From c951cbdbbcb1bce484ee79ffcf3c288fde64b802 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sun, 17 Mar 2024 19:08:30 +0900 Subject: [PATCH 24/73] core/crypto/_fiat: odinfmt (NFC) --- core/crypto/_fiat/fiat.odin | 4 +- core/crypto/_fiat/field_curve25519/field.odin | 20 +++++---- .../_fiat/field_curve25519/field51.odin | 31 +++++++++---- core/crypto/_fiat/field_poly1305/field.odin | 17 +++++--- .../_fiat/field_poly1305/field4344.odin | 43 +++++++++++++++---- 5 files changed, 84 insertions(+), 31 deletions(-) diff --git a/core/crypto/_fiat/fiat.odin b/core/crypto/_fiat/fiat.odin index f0551722f..cc73c6927 100644 --- a/core/crypto/_fiat/fiat.odin +++ b/core/crypto/_fiat/fiat.odin @@ -9,7 +9,7 @@ package fiat u1 :: distinct u8 i1 :: distinct i8 -@(optimization_mode="none") +@(optimization_mode = "none") cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) { x1 := (u64(arg1) * 0xffffffffffffffff) x2 := ((x1 & arg3) | ((~x1) & arg2)) @@ -17,7 +17,7 @@ cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) { return } -@(optimization_mode="none") +@(optimization_mode = "none") cmovznz_u32 :: proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) { x1 := (u32(arg1) * 0xffffffff) x2 := ((x1 & arg3) | ((~x1) & arg2)) diff --git a/core/crypto/_fiat/field_curve25519/field.odin b/core/crypto/_fiat/field_curve25519/field.odin index a8e0a0316..cf7f694bc 100644 --- a/core/crypto/_fiat/field_curve25519/field.odin +++ b/core/crypto/_fiat/field_curve25519/field.odin @@ -3,11 +3,15 @@ package field_curve25519 import "core:crypto" 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) } -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) } @@ -46,7 +50,7 @@ fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byt return ret } -fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) { +fe_carry_pow2k :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) { // Special case: `arg1^(2 * 0) = 1`, though this should never happen. if arg2 == 0 { fe_one(out1) @@ -54,7 +58,7 @@ fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, } fe_carry_square(out1, arg1) - for _ in 1.. int { +fe_carry_invsqrt :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int { // Inverse square root taken from Monocypher. tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, --- @@ -116,7 +120,7 @@ fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element // then isr = x^((p-1)/4) * sqrt(-1) // else isr = x^((p-1)/4) fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1)) - fe_cond_assign(out1, &tmp1, (m1|ms) ~ 1) + fe_cond_assign(out1, &tmp1, (m1 | ms) ~ 1) mem.zero_explicit(&tmp1, size_of(tmp1)) mem.zero_explicit(&tmp2, size_of(tmp2)) @@ -126,7 +130,7 @@ fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element return p1 | m1 } -fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { +fe_carry_inv :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { tmp1: Tight_Field_Element fe_carry_square(&tmp1, arg1) @@ -166,7 +170,7 @@ fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) { out1[4] = x5 } -@(optimization_mode="none") +@(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 diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin index 3cbc296b7..4cda96c81 100644 --- a/core/crypto/_fiat/field_curve25519/field51.odin +++ b/core/crypto/_fiat/field_curve25519/field51.odin @@ -42,7 +42,7 @@ import "core:math/bits" Loose_Field_Element :: distinct [5]u64 Tight_Field_Element :: distinct [5]u64 -SQRT_M1 := Tight_Field_Element{ +SQRT_M1 := Tight_Field_Element { 1718705420411056, 234908883556509, 2233514472574048, @@ -50,7 +50,13 @@ SQRT_M1 := Tight_Field_Element{ 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) x2 := (x1 & 0x7ffffffffffff) x3 := fiat.u1((x1 >> 51)) @@ -59,7 +65,13 @@ _addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u 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)) x2 := fiat.i1((x1 >> 51)) x3 := (u64(x1) & 0x7ffffffffffff) @@ -68,7 +80,7 @@ _subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: return } -fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) { +fe_carry_mul :: proc(out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) { x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13)) x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13)) x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13)) @@ -167,7 +179,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme out1[4] = x152 } -fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { +fe_carry_square :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { x1 := (arg1[4] * 0x13) x2 := (x1 * 0x2) x3 := (arg1[4] * 0x2) @@ -303,8 +315,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele out1[4] = x5 } -@(optimization_mode="none") -fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) { +@(optimization_mode = "none") +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]) x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1]) x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2]) @@ -525,7 +540,7 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E out1[4] = x5 } -fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { +fe_carry_scmul_121666 :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { x2, x1 := bits.mul_u64(0x1db42, arg1[4]) x4, x3 := bits.mul_u64(0x1db42, arg1[3]) x6, x5 := bits.mul_u64(0x1db42, arg1[2]) diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin index 9b00ff3ec..f5557cf5f 100644 --- a/core/crypto/_fiat/field_poly1305/field.odin +++ b/core/crypto/_fiat/field_poly1305/field.odin @@ -3,15 +3,19 @@ package field_poly1305 import "core:encoding/endian" 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) } -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) } -fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte) { +fe_from_bytes :: #force_inline proc(out1: ^Tight_Field_Element, arg1: []byte, arg2: byte) { // fiat-crypto's deserialization routine effectively processes a // single byte at a time, and wants 256-bits of input for a value // that will be 128-bits or 129-bits. @@ -67,8 +71,11 @@ fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) { out1[2] = x3 } -@(optimization_mode="none") -fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) { +@(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 diff --git a/core/crypto/_fiat/field_poly1305/field4344.odin b/core/crypto/_fiat/field_poly1305/field4344.odin index ddc10de52..bddb0c322 100644 --- a/core/crypto/_fiat/field_poly1305/field4344.odin +++ b/core/crypto/_fiat/field_poly1305/field4344.odin @@ -39,7 +39,13 @@ import "core:math/bits" Loose_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) x2 := (x1 & 0xfffffffffff) x3 := fiat.u1((x1 >> 44)) @@ -48,7 +54,13 @@ _addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u 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)) x2 := fiat.i1((x1 >> 44)) x3 := (u64(x1) & 0xfffffffffff) @@ -57,7 +69,13 @@ _subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: 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) x2 := (x1 & 0x7ffffffffff) x3 := fiat.u1((x1 >> 43)) @@ -66,7 +84,13 @@ _addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u 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)) x2 := fiat.i1((x1 >> 43)) x3 := (u64(x1) & 0x7ffffffffff) @@ -75,7 +99,7 @@ _subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: return } -fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) { +fe_carry_mul :: proc(out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) { x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5)) x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 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 } -fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { +fe_carry_square :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { x1 := (arg1[2] * 0x5) x2 := (x1 * 0x2) x3 := (arg1[2] * 0x2) @@ -201,8 +225,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele out1[2] = x3 } -@(optimization_mode="none") -fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) { +@(optimization_mode = "none") +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]) x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1]) x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2]) From 1ce279e6a1dd59f4bffc33acc4cc281e4c45d441 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Wed, 20 Mar 2024 23:17:05 +0900 Subject: [PATCH 25/73] core/crypto/_fiat/field_curve25519: Mark more functions contextless --- core/crypto/_fiat/field_curve25519/field.odin | 13 ++++++++++--- core/crypto/_fiat/field_curve25519/field51.odin | 9 ++++++--- core/crypto/x25519/x25519.odin | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/core/crypto/_fiat/field_curve25519/field.odin b/core/crypto/_fiat/field_curve25519/field.odin index cf7f694bc..64f9f8a1f 100644 --- a/core/crypto/_fiat/field_curve25519/field.odin +++ b/core/crypto/_fiat/field_curve25519/field.odin @@ -50,7 +50,11 @@ fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byt 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. if arg2 == 0 { fe_one(out1) @@ -68,7 +72,10 @@ fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Eleme fe_carry(out1, fe_relax_cast(out1)) } -fe_carry_invsqrt :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int { +fe_carry_invsqrt :: proc "contextless" ( + out1: ^Tight_Field_Element, + arg1: ^Loose_Field_Element, +) -> int { // Inverse square root taken from Monocypher. tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, --- @@ -130,7 +137,7 @@ fe_carry_invsqrt :: proc(out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) return p1 | m1 } -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 fe_carry_square(&tmp1, arg1) diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin index 4cda96c81..81dca19e2 100644 --- a/core/crypto/_fiat/field_curve25519/field51.odin +++ b/core/crypto/_fiat/field_curve25519/field51.odin @@ -80,7 +80,7 @@ _subborrowx_u51 :: #force_inline proc "contextless" ( 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)) x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13)) x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13)) @@ -179,7 +179,7 @@ fe_carry_mul :: proc(out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Elemen 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) x2 := (x1 * 0x2) x3 := (arg1[4] * 0x2) @@ -540,7 +540,10 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E 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]) x4, x3 := bits.mul_u64(0x1db42, arg1[3]) x6, x5 := bits.mul_u64(0x1db42, arg1[2]) diff --git a/core/crypto/x25519/x25519.odin b/core/crypto/x25519/x25519.odin index 285666a32..3cd247cf8 100644 --- a/core/crypto/x25519/x25519.odin +++ b/core/crypto/x25519/x25519.odin @@ -27,7 +27,7 @@ _scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 { } @(private) -_scalarmult :: proc(out, scalar, point: ^[32]byte) { +_scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) { // Montgomery pseduo-multiplication taken from Monocypher. // computes the scalar product From 36f3001d59f0c4e1d00f3f75431830c3b463e9f6 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Wed, 20 Mar 2024 08:02:20 +0900 Subject: [PATCH 26/73] core/crypto/_fiat/field_poly1305: Use multiply to calculate the mask --- core/crypto/_fiat/field_poly1305/field.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin index f5557cf5f..f4eccc476 100644 --- a/core/crypto/_fiat/field_poly1305/field.odin +++ b/core/crypto/_fiat/field_poly1305/field.odin @@ -76,7 +76,7 @@ fe_cond_swap :: #force_no_inline proc "contextless" ( out1, out2: ^Tight_Field_Element, arg1: bool, ) { - mask := -u64(arg1) + mask := (u64(arg1) * 0xffffffffffffffff) x := (out1[0] ~ out2[0]) & mask x1, y1 := out1[0] ~ x, out2[0] ~ x x = (out1[1] ~ out2[1]) & mask From 4defe88decb740e73aaac3f5fe197a84d32b4c1e Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Wed, 20 Mar 2024 23:21:27 +0900 Subject: [PATCH 27/73] core/crypto/_fiat/field_poly1305: Mark more functions contextless --- core/crypto/_fiat/field_poly1305/field.odin | 11 +++++++++-- core/crypto/_fiat/field_poly1305/field4344.odin | 4 ++-- core/crypto/poly1305/poly1305.odin | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin index f4eccc476..c50a56b0c 100644 --- a/core/crypto/_fiat/field_poly1305/field.odin +++ b/core/crypto/_fiat/field_poly1305/field.odin @@ -1,5 +1,6 @@ package field_poly1305 +import "base:intrinsics" import "core:encoding/endian" import "core:mem" @@ -15,7 +16,11 @@ fe_tighten_cast :: #force_inline proc "contextless" ( 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 // single byte at a time, and wants 256-bits of input for a value // that will be 128-bits or 129-bits. @@ -24,7 +29,9 @@ fe_from_bytes :: #force_inline proc(out1: ^Tight_Field_Element, arg1: []byte, ar // makes implementing the actual MAC block processing considerably // neater. - assert(len(arg1) == 16) + if len(arg1) != 16 { + intrinsics.trap() + } // While it may be unwise to do deserialization here on our // own when fiat-crypto provides equivalent functionality, diff --git a/core/crypto/_fiat/field_poly1305/field4344.odin b/core/crypto/_fiat/field_poly1305/field4344.odin index bddb0c322..6a7a19d69 100644 --- a/core/crypto/_fiat/field_poly1305/field4344.odin +++ b/core/crypto/_fiat/field_poly1305/field4344.odin @@ -99,7 +99,7 @@ _subborrowx_u43 :: #force_inline proc "contextless" ( 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)) x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa)) x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa)) @@ -144,7 +144,7 @@ fe_carry_mul :: proc(out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Elemen 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) x2 := (x1 * 0x2) x3 := (arg1[2] * 0x2) diff --git a/core/crypto/poly1305/poly1305.odin b/core/crypto/poly1305/poly1305.odin index 4ca4f75e1..443917a6a 100644 --- a/core/crypto/poly1305/poly1305.odin +++ b/core/crypto/poly1305/poly1305.odin @@ -168,7 +168,7 @@ reset :: proc(ctx: ^Context) { } @(private) -_blocks :: proc(ctx: ^Context, msg: []byte, final := false) { +_blocks :: proc "contextless" (ctx: ^Context, msg: []byte, final := false) { n: field.Tight_Field_Element = --- final_byte := byte(!final) From fec42a6d741bfda489a5ab2423644f3169ff4128 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Tue, 19 Mar 2024 12:23:16 +0900 Subject: [PATCH 28/73] core/crypto/_fiat/field_scalar25519: Initial import --- .../crypto/_fiat/field_scalar25519/field.odin | 149 +++++ .../_fiat/field_scalar25519/field64.odin | 535 ++++++++++++++++++ 2 files changed, 684 insertions(+) create mode 100644 core/crypto/_fiat/field_scalar25519/field.odin create mode 100644 core/crypto/_fiat/field_scalar25519/field64.odin diff --git a/core/crypto/_fiat/field_scalar25519/field.odin b/core/crypto/_fiat/field_scalar25519/field.odin new file mode 100644 index 000000000..c741d30cf --- /dev/null +++ b/core/crypto/_fiat/field_scalar25519/field.odin @@ -0,0 +1,149 @@ +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_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 + + mem.zero_explicit(&tmp, size_of(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) + + mem.zero_explicit(&tmp, size_of(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 +} diff --git a/core/crypto/_fiat/field_scalar25519/field64.odin b/core/crypto/_fiat/field_scalar25519/field64.odin new file mode 100644 index 000000000..268752e5b --- /dev/null +++ b/core/crypto/_fiat/field_scalar25519/field64.odin @@ -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 +} From 563c52741903d3a930fd4c4f8128c275fefc1399 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Thu, 21 Mar 2024 02:17:59 +0900 Subject: [PATCH 29/73] core/crypto/_edwards25519: Initial import --- core/crypto/_edwards25519/edwards25519.odin | 428 ++++++++++++++++++ .../_edwards25519/edwards25519_scalar.odin | 61 +++ .../edwards25519_scalar_mul.odin | 288 ++++++++++++ core/crypto/_fiat/field_curve25519/field.odin | 134 ++++-- .../_fiat/field_curve25519/field51.odin | 5 +- .../crypto/_fiat/field_scalar25519/field.odin | 8 +- core/crypto/x25519/x25519.odin | 10 +- 7 files changed, 885 insertions(+), 49 deletions(-) create mode 100644 core/crypto/_edwards25519/edwards25519.odin create mode 100644 core/crypto/_edwards25519/edwards25519_scalar.odin create mode 100644 core/crypto/_edwards25519/edwards25519_scalar_mul.odin diff --git a/core/crypto/_edwards25519/edwards25519.odin b/core/crypto/_edwards25519/edwards25519.odin new file mode 100644 index 000000000..952bb9ef8 --- /dev/null +++ b/core/crypto/_edwards25519/edwards25519.odin @@ -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 +} diff --git a/core/crypto/_edwards25519/edwards25519_scalar.odin b/core/crypto/_edwards25519/edwards25519_scalar.odin new file mode 100644 index 000000000..2644fe5f7 --- /dev/null +++ b/core/crypto/_edwards25519/edwards25519_scalar.odin @@ -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 diff --git a/core/crypto/_edwards25519/edwards25519_scalar_mul.odin b/core/crypto/_edwards25519/edwards25519_scalar_mul.odin new file mode 100644 index 000000000..757a51257 --- /dev/null +++ b/core/crypto/_edwards25519/edwards25519_scalar_mul.odin @@ -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) +} diff --git a/core/crypto/_fiat/field_curve25519/field.odin b/core/crypto/_fiat/field_curve25519/field.odin index 64f9f8a1f..6b2d3b595 100644 --- a/core/crypto/_fiat/field_curve25519/field.odin +++ b/core/crypto/_fiat/field_curve25519/field.odin @@ -15,6 +15,20 @@ fe_tighten_cast :: #force_inline proc "contextless" ( 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) { // Ignore the unused bit by copying the input and masking the bit off // prior to deserialization. @@ -27,12 +41,25 @@ fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte 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 { - tmp2: [32]byte = --- + tmp1, tmp2: [32]byte = ---, --- + fe_to_bytes(&tmp1, arg1) 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)) return ret @@ -67,25 +94,37 @@ fe_carry_pow2k :: proc "contextless" ( } } +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_opp(fe_relax_cast(out1), arg1) fe_carry(out1, fe_relax_cast(out1)) } -fe_carry_invsqrt :: proc "contextless" ( +fe_carry_sqrt_ratio_m1 :: proc "contextless" ( out1: ^Tight_Field_Element, - arg1: ^Loose_Field_Element, + arg1: ^Loose_Field_Element, // u + arg2: ^Loose_Field_Element, // v ) -> int { - // Inverse square root taken from Monocypher. + // 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 = ---, ---, --- - - // 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(&tmp1, fe_relax_cast(&w), 1) 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_pow2k(&tmp1, fe_relax_cast(&tmp1), 1) fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1)) @@ -104,48 +143,49 @@ fe_carry_invsqrt :: proc "contextless" ( fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50) fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1)) 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) - quartic := &tmp2 - fe_carry_square(quartic, fe_relax_cast(&tmp1)) - fe_carry_mul(quartic, fe_relax_cast(quartic), arg1) + fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1) // u * w^((p-5)/8) - // Serialize quartic once to save on repeated serialization/sanitization. - quartic_buf: [32]byte = --- - fe_to_bytes(&quartic_buf, quartic) - check := &tmp3 + // Serialize `check` once to save on repeated serialization. + r, check := &tmp1, &tmp2 + b: [32]byte = --- + 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) - p1 := fe_equal_bytes(check, &quartic_buf) - fe_carry_opp(check, check) - m1 := fe_equal_bytes(check, &quartic_buf) - fe_carry_opp(check, &SQRT_M1) - ms := fe_equal_bytes(check, &quartic_buf) + u, neg_u, neg_u_i := &tmp3, &w, check + fe_carry(u, arg1) + fe_carry_opp(neg_u, u) + fe_carry_mul(neg_u_i, fe_relax_cast(neg_u), fe_relax_cast(&FE_SQRT_M1)) - // if quartic == -1 or sqrt(-1) - // then isr = x^((p-1)/4) * sqrt(-1) - // else isr = x^((p-1)/4) - fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1)) - fe_cond_assign(out1, &tmp1, (m1 | ms) ~ 1) + correct_sign_sqrt := fe_equal_bytes(u, &b) + flipped_sign_sqrt := fe_equal_bytes(neg_u, &b) + flipped_sign_sqrt_i := fe_equal_bytes(neg_u_i, &b) - mem.zero_explicit(&tmp1, size_of(tmp1)) - mem.zero_explicit(&tmp2, size_of(tmp2)) - mem.zero_explicit(&tmp3, size_of(tmp3)) - mem.zero_explicit(&quartic_buf, size_of(quartic_buf)) + r_prime := check + fe_carry_mul(r_prime, fe_relax_cast(r), fe_relax_cast(&FE_SQRT_M1)) + fe_cond_assign(r, r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i) - return p1 | m1 + // Pick the non-negative square root. + fe_carry_opp(r_prime, r) + fe_cond_select(out1, r, r_prime, fe_is_negative(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 "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) { tmp1: Tight_Field_Element 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_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) { @@ -196,3 +236,21 @@ fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_El 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 +} diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin index 81dca19e2..d039bd411 100644 --- a/core/crypto/_fiat/field_curve25519/field51.odin +++ b/core/crypto/_fiat/field_curve25519/field51.odin @@ -42,7 +42,10 @@ import "core:math/bits" Loose_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, 234908883556509, 2233514472574048, diff --git a/core/crypto/_fiat/field_scalar25519/field.odin b/core/crypto/_fiat/field_scalar25519/field.odin index c741d30cf..9b40661b7 100644 --- a/core/crypto/_fiat/field_scalar25519/field.odin +++ b/core/crypto/_fiat/field_scalar25519/field.odin @@ -20,6 +20,10 @@ _TWO_336 := Montgomery_Domain_Field_Element { 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, @@ -85,7 +89,7 @@ fe_from_bytes_wide :: proc "contextless" ( fe_mul(&tmp, &tmp, &_TWO_336) // c * 2^336 fe_add(out1, out1, &tmp) // a + b * 2^168 + c * 2^336 - mem.zero_explicit(&tmp, size_of(tmp)) + fe_clear(&tmp) } @(private) @@ -125,7 +129,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> // which will be 1. _, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0) - mem.zero_explicit(&tmp, size_of(tmp)) + fe_clear(&tmp) return int(borrow) } diff --git a/core/crypto/x25519/x25519.odin b/core/crypto/x25519/x25519.odin index 3cd247cf8..f8a301810 100644 --- a/core/crypto/x25519/x25519.odin +++ b/core/crypto/x25519/x25519.odin @@ -94,13 +94,8 @@ _scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) { field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2)) field.fe_to_bytes(out, &x2) - mem.zero_explicit(&x1, size_of(x1)) - mem.zero_explicit(&x2, size_of(x2)) - 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)) + field.fe_clear_vec([]^field.Tight_Field_Element{&x1, &x2, &x3, &z2, &z3}) + field.fe_clear_vec([]^field.Loose_Field_Element{&t0, &t1}) } // 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 // base point and writes the resulting point to dst. scalarmult_basepoint :: proc(dst, scalar: []byte) { - // TODO/perf: Switch to using a precomputed table. scalarmult(dst, scalar, _BASE_POINT[:]) } From d96f8bb5c1f5f7b24a23383f88c1b9a637b586b2 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Tue, 26 Mar 2024 13:05:50 +0900 Subject: [PATCH 30/73] core/crypto/ristretto255: Initial import --- core/crypto/_fiat/field_curve25519/field.odin | 15 +- core/crypto/ristretto255/ristretto255.odin | 510 ++++++++++++++++++ .../ristretto255/ristretto255_scalar.odin | 97 ++++ examples/all/all_main.odin | 2 + 4 files changed, 622 insertions(+), 2 deletions(-) create mode 100644 core/crypto/ristretto255/ristretto255.odin create mode 100644 core/crypto/ristretto255/ristretto255_scalar.odin diff --git a/core/crypto/_fiat/field_curve25519/field.odin b/core/crypto/_fiat/field_curve25519/field.odin index 6b2d3b595..8a8202ac4 100644 --- a/core/crypto/_fiat/field_curve25519/field.odin +++ b/core/crypto/_fiat/field_curve25519/field.odin @@ -109,6 +109,10 @@ fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Eleme fe_carry(out1, fe_relax_cast(out1)) } +fe_carry_abs :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) { + 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 @@ -168,8 +172,7 @@ fe_carry_sqrt_ratio_m1 :: proc "contextless" ( fe_cond_assign(r, r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i) // Pick the non-negative square root. - fe_carry_opp(r_prime, r) - fe_cond_select(out1, r, r_prime, fe_is_negative(r)) + fe_carry_abs(out1, r) fe_clear_vec([]^Tight_Field_Element{&w, &tmp1, &tmp2, &tmp3}) mem.zero_explicit(&b, size_of(b)) @@ -254,3 +257,11 @@ fe_cond_select :: #force_no_inline proc "contextless" ( 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) +} diff --git a/core/crypto/ristretto255/ristretto255.odin b/core/crypto/ristretto255/ristretto255.odin new file mode 100644 index 000000000..d1f2b6ee5 --- /dev/null +++ b/core/crypto/ristretto255/ristretto255.odin @@ -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") + } + } +} diff --git a/core/crypto/ristretto255/ristretto255_scalar.odin b/core/crypto/ristretto255/ristretto255_scalar.odin new file mode 100644 index 000000000..f581e5963 --- /dev/null +++ b/core/crypto/ristretto255/ristretto255_scalar.odin @@ -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) +} diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index bc1aff607..cc0005840 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -37,6 +37,7 @@ import md5 "core:crypto/legacy/md5" import sha1 "core:crypto/legacy/sha1" import pbkdf2 "core:crypto/pbkdf2" import poly1305 "core:crypto/poly1305" +import ristretto255 "core:crypto/ristretto255" import sha2 "core:crypto/sha2" import sha3 "core:crypto/sha3" import shake "core:crypto/shake" @@ -158,6 +159,7 @@ _ :: keccak _ :: md5 _ :: pbkdf2 _ :: poly1305 +_ :: ristretto255 _ :: sha1 _ :: sha2 _ :: sha3 From 893c3bef9a45fd58da38a11daa8ec9b0c6c323fe Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Fri, 5 Apr 2024 20:03:46 +0900 Subject: [PATCH 31/73] core/crypto/ed25519: Initial import --- core/crypto/ed25519/ed25519.odin | 314 +++++++++++++++++++++++++++++++ examples/all/all_main.odin | 2 + 2 files changed, 316 insertions(+) create mode 100644 core/crypto/ed25519/ed25519.odin diff --git a/core/crypto/ed25519/ed25519.odin b/core/crypto/ed25519/ed25519.odin new file mode 100644 index 000000000..86da35669 --- /dev/null +++ b/core/crypto/ed25519/ed25519.odin @@ -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) +} diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index cc0005840..f60088823 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -29,6 +29,7 @@ import blake2s "core:crypto/blake2s" import chacha20 "core:crypto/chacha20" import chacha20poly1305 "core:crypto/chacha20poly1305" import crypto_hash "core:crypto/hash" +import ed25519 "core:crypto/ed25519" import hkdf "core:crypto/hkdf" import hmac "core:crypto/hmac" import kmac "core:crypto/kmac" @@ -152,6 +153,7 @@ _ :: blake2b _ :: blake2s _ :: chacha20 _ :: chacha20poly1305 +_ :: ed25519 _ :: hmac _ :: hkdf _ :: kmac From fa1d681e65c3a22c8f4fa45bad42c6de8b028c66 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Fri, 29 Mar 2024 19:05:13 +0900 Subject: [PATCH 32/73] tests/core/crypto: Start adding comprehensive curve25519 tests --- tests/core/crypto/test_core_crypto.odin | 72 +- .../crypto/test_core_crypto_ecc25519.odin | 766 ++++++++++++++++++ tests/core/crypto/test_core_crypto_hash.odin | 3 + tests/core/crypto/test_core_crypto_kdf.odin | 3 + tests/core/crypto/test_core_crypto_mac.odin | 3 + .../test_core_crypto_sha3_variants.odin | 3 + tests/core/crypto/test_crypto_benchmark.odin | 63 ++ 7 files changed, 842 insertions(+), 71 deletions(-) create mode 100644 tests/core/crypto/test_core_crypto_ecc25519.odin diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin index 742e3cc04..72d8e7c78 100644 --- a/tests/core/crypto/test_core_crypto.odin +++ b/tests/core/crypto/test_core_crypto.odin @@ -20,7 +20,6 @@ import "core:testing" import "core:crypto" import "core:crypto/chacha20" import "core:crypto/chacha20poly1305" -import "core:crypto/x25519" import tc "tests:common" @@ -32,10 +31,10 @@ main :: proc() { test_hash(&t) test_mac(&t) test_kdf(&t) // After hash/mac tests because those should pass first. + test_ecc25519(&t) test_chacha20(&t) test_chacha20poly1305(&t) - test_x25519(&t) test_sha3_variants(&t) bench_crypto(&t) @@ -274,75 +273,6 @@ test_chacha20poly1305 :: proc(t: ^testing.T) { 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_rand_bytes :: proc(t: ^testing.T) { tc.log(t, "Testing rand_bytes") diff --git a/tests/core/crypto/test_core_crypto_ecc25519.odin b/tests/core/crypto/test_core_crypto_ecc25519.odin new file mode 100644 index 000000000..5ea008f90 --- /dev/null +++ b/tests/core/crypto/test_core_crypto_ecc25519.odin @@ -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)) +} diff --git a/tests/core/crypto/test_core_crypto_hash.odin b/tests/core/crypto/test_core_crypto_hash.odin index bd40a9b23..c4e8e8dd7 100644 --- a/tests/core/crypto/test_core_crypto_hash.odin +++ b/tests/core/crypto/test_core_crypto_hash.odin @@ -1,5 +1,6 @@ package test_core_crypto +import "base:runtime" import "core:bytes" import "core:encoding/hex" import "core:fmt" @@ -12,6 +13,8 @@ import tc "tests:common" @(test) test_hash :: proc(t: ^testing.T) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + tc.log(t, "Testing Hashes") // TODO: diff --git a/tests/core/crypto/test_core_crypto_kdf.odin b/tests/core/crypto/test_core_crypto_kdf.odin index d9e9a8501..73177d8be 100644 --- a/tests/core/crypto/test_core_crypto_kdf.odin +++ b/tests/core/crypto/test_core_crypto_kdf.odin @@ -1,5 +1,6 @@ package test_core_crypto +import "base:runtime" import "core:encoding/hex" import "core:fmt" import "core:testing" @@ -12,6 +13,8 @@ import tc "tests:common" @(test) test_kdf :: proc(t: ^testing.T) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + tc.log(t, "Testing KDFs") test_hkdf(t) diff --git a/tests/core/crypto/test_core_crypto_mac.odin b/tests/core/crypto/test_core_crypto_mac.odin index f0e6fa1bf..f2eeacb19 100644 --- a/tests/core/crypto/test_core_crypto_mac.odin +++ b/tests/core/crypto/test_core_crypto_mac.odin @@ -1,5 +1,6 @@ package test_core_crypto +import "base:runtime" import "core:encoding/hex" import "core:fmt" import "core:mem" @@ -14,6 +15,8 @@ import tc "tests:common" @(test) test_mac :: proc(t: ^testing.T) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + tc.log(t, "Testing MACs") test_hmac(t) diff --git a/tests/core/crypto/test_core_crypto_sha3_variants.odin b/tests/core/crypto/test_core_crypto_sha3_variants.odin index ec2d24331..8e44996bc 100644 --- a/tests/core/crypto/test_core_crypto_sha3_variants.odin +++ b/tests/core/crypto/test_core_crypto_sha3_variants.odin @@ -1,5 +1,6 @@ package test_core_crypto +import "base:runtime" import "core:encoding/hex" import "core:fmt" import "core:testing" @@ -12,6 +13,8 @@ import tc "tests:common" @(test) test_sha3_variants :: proc(t: ^testing.T) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + tc.log(t, "Testing SHA3 derived functions") test_shake(t) diff --git a/tests/core/crypto/test_crypto_benchmark.odin b/tests/core/crypto/test_crypto_benchmark.odin index 494913b6b..cc69cb16d 100644 --- a/tests/core/crypto/test_crypto_benchmark.odin +++ b/tests/core/crypto/test_crypto_benchmark.odin @@ -1,5 +1,6 @@ package test_core_crypto +import "base:runtime" import "core:encoding/hex" import "core:fmt" import "core:testing" @@ -7,6 +8,7 @@ import "core:time" import "core:crypto/chacha20" import "core:crypto/chacha20poly1305" +import "core:crypto/ed25519" import "core:crypto/poly1305" import "core:crypto/x25519" @@ -16,11 +18,14 @@ import tc "tests:common" @(test) bench_crypto :: proc(t: ^testing.T) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + fmt.println("Starting benchmarks:") bench_chacha20(t) bench_poly1305(t) bench_chacha20poly1305(t) + bench_ed25519(t) bench_x25519(t) } @@ -216,6 +221,64 @@ bench_chacha20poly1305 :: proc(t: ^testing.T) { 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) { point_str := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" scalar_str := "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe" From 9b496e82f33a626ae815281a214afcfb8537b4fe Mon Sep 17 00:00:00 2001 From: wrapperup Date: Tue, 9 Apr 2024 01:50:16 -0400 Subject: [PATCH 33/73] add movefile flags --- core/sys/windows/types.odin | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 37f953c58..4b54f0ed1 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -2074,7 +2074,13 @@ SRWLOCK_INIT :: SRWLOCK{} STARTF_USESTDHANDLES: DWORD : 0x00000100 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_CURRENT: DWORD : 1 From 60478c0e07c20937dee4401431c8258a7867b646 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 9 Apr 2024 10:57:51 +0100 Subject: [PATCH 34/73] Remove using from validate_datetime --- core/time/datetime/validation.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/time/datetime/validation.odin b/core/time/datetime/validation.odin index 110a7e78e..87d5aa1cd 100644 --- a/core/time/datetime/validation.odin +++ b/core/time/datetime/validation.odin @@ -56,9 +56,9 @@ validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minut return .None } -validate_datetime :: proc "contextless" (using datetime: DateTime) -> (err: Error) { - validate(date) or_return - validate(time) or_return +validate_datetime :: proc "contextless" (datetime: DateTime) -> (err: Error) { + validate(datetime.date) or_return + validate(datetime.time) or_return return .None } @@ -69,4 +69,4 @@ validate :: proc{ validate_hour_minute_second, validate_time, validate_datetime, -} \ No newline at end of file +} From 219eb58c08bea94dda151100c00600187f74bd6f Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 9 Apr 2024 12:03:41 +0100 Subject: [PATCH 35/73] Fix typo in using parameter error message --- src/check_type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 81e67f261..f1d991acb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1689,7 +1689,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para bool is_using = (p->flags&FieldFlag_using) != 0; if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) { 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"); } From c644f795735816d35f382b1c8dfc64d67815e533 Mon Sep 17 00:00:00 2001 From: iciuperca Date: Tue, 9 Apr 2024 18:24:57 +0100 Subject: [PATCH 36/73] 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 --- vendor/OpenGL/wrappers.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 550ba3cfa..a04df6987 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -787,8 +787,8 @@ when !GL_DEBUG { fmt.printf(" call: gl%s(", loc.procedure) { // add input arguments - for arg, i in args[num_ret:] { - if i > 0 { fmt.printf(", ") } + for arg, arg_index in args[num_ret:] { + 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 err == .INVALID_ENUM { @@ -806,8 +806,8 @@ when !GL_DEBUG { fmt.printf(") -> %v \n", args[0]) } else if num_ret > 1 { fmt.printf(") -> (") - for arg, i in args[1:num_ret] { - if i > 0 { fmt.printf(", ") } + for arg, arg_index in args[1:num_ret] { + if arg_index > 0 { fmt.printf(", ") } fmt.printf("%v", arg) } fmt.printf(")\n") From af6d2480fa7202924ea05d65cdad65aa9e764d05 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 10 Apr 2024 00:57:21 +0200 Subject: [PATCH 37/73] add bit_field parsing to `core:odin/parser` Also adds it to the core type thingy like it is in the compiler. --- base/runtime/core.odin | 5 +-- core/odin/ast/ast.odin | 22 +++++++++++++- core/odin/ast/clone.odin | 7 +++++ core/odin/ast/walk.odin | 10 +++++- core/odin/parser/parser.odin | 47 +++++++++++++++++++++++++++-- core/odin/printer/visit.odin | 52 +++++++++++++++++++++++++++++++- tests/core/odin/test_parser.odin | 50 ++++++++++++++++++++++++++++-- 7 files changed, 183 insertions(+), 10 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 7ad3ef1d6..c62301c34 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -597,8 +597,9 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { base := info loop: for { #partial switch i in base.variant { - case Type_Info_Named: base = i.base - case Type_Info_Enum: base = i.base + case Type_Info_Named: base = i.base + case Type_Info_Enum: base = i.base + case Type_Info_Bit_Field: base = i.backing_type case: break loop } } diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 3e215e0f2..be541befa 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -617,7 +617,7 @@ field_flag_strings := [Field_Flag]string{ .Any_Int = "#any_int", .Subtype = "#subtype", .By_Ptr = "#by_ptr", - .No_Broadcast ="#no_broadcast", + .No_Broadcast = "#no_broadcast", .Results = "results", .Tags = "field tag", @@ -842,6 +842,23 @@ Matrix_Type :: struct { 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 { ^Package, @@ -898,6 +915,7 @@ Any_Node :: union { ^Map_Type, ^Relative_Type, ^Matrix_Type, + ^Bit_Field_Type, ^Bad_Stmt, ^Empty_Stmt, @@ -928,6 +946,7 @@ Any_Node :: union { ^Attribute, ^Field, ^Field_List, + ^Bit_Field_Field, } @@ -982,6 +1001,7 @@ Any_Expr :: union { ^Map_Type, ^Relative_Type, ^Matrix_Type, + ^Bit_Field_Type, } diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index d105f6035..bca740dd4 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -336,6 +336,13 @@ clone_node :: proc(node: ^Node) -> ^Node { case ^Relative_Type: r.tag = clone(r.tag) 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: fmt.panicf("Unhandled node kind: %v", r) } diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index 966a8137e..63107a2e2 100644 --- a/core/odin/ast/walk.odin +++ b/core/odin/ast/walk.odin @@ -414,7 +414,15 @@ walk :: proc(v: ^Visitor, node: ^Node) { walk(v, n.row_count) walk(v, n.column_count) 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: fmt.panicf("ast.walk: unexpected node type %T", n) } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index ab723e145..9eaef4655 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -531,7 +531,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return is_semicolon_optional_for_node(p, n.type) case ^ast.Pointer_Type: 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 return p.curr_proc == nil case ^ast.Proc_Lit: @@ -2790,6 +2790,48 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { mt.column_count = column_count mt.elem = elem 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: tok := expect_token(p, .Asm) @@ -2897,7 +2939,8 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool { ^ast.Map_Type, ^ast.Bit_Set_Type, ^ast.Matrix_Type, - ^ast.Call_Expr: + ^ast.Call_Expr, + ^ast.Bit_Field_Type: return true } return false diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 7dd208a49..571e4001d 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -445,7 +445,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { for value in v.values { #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 case ^ast.Proc_Lit: 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) visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { 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) push_generic_token(p, .Close_Bracket, 0) 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: panic(fmt.aprint(expr.derived)) } diff --git a/tests/core/odin/test_parser.odin b/tests/core/odin/test_parser.odin index 3837436bc..08f73a732 100644 --- a/tests/core/odin/test_parser.odin +++ b/tests/core/odin/test_parser.odin @@ -1,9 +1,12 @@ package test_core_odin_parser -import "core:testing" import "core:fmt" -import "core:os" +import "core:odin/ast" import "core:odin/parser" +import "core:odin/printer" +import "core:os" +import "core:strings" +import "core:testing" TEST_count := 0 @@ -30,6 +33,7 @@ when ODIN_TEST { main :: proc() { t := testing.T{} test_parse_demo(&t) + test_parse_bitfield(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) if TEST_fail > 0 { @@ -47,4 +51,44 @@ test_parse_demo :: proc(t: ^testing.T) { for key, value in pkg.files { expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key)) } -} \ No newline at end of file +} + +@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)) +} From 3dfd61dd4f1aec7525a8d6820c9c977f6a3ed14e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 12:32:26 +0100 Subject: [PATCH 38/73] Make `intrinsics.overflow_*` NOT `#optional_ok` --- src/check_builtin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d8fad487b..c3c217ec7 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4089,8 +4089,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } } - operand->mode = Addressing_OptionalOk; - operand->type = default_type(x.type); + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(default_type(x.type)); } break; From d0dc7395e9d10fafc35e1b7af72c9601efd70080 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 13:29:10 +0100 Subject: [PATCH 39/73] Allow for comma-separate json names `json:"name,flag"` --- core/encoding/json/marshal.odin | 1 + core/encoding/json/unmarshal.odin | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 68d087a6e..6ec9c0d27 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -371,6 +371,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: info := ti.variant.(runtime.Type_Info_Struct) for name, i in info.names { json_name := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json") + json_name = json_name_from_tag_value(json_name) opt_write_iteration(w, opt, i) or_return if json_name != "" { diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index edc4903a1..7a5679368 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -343,6 +343,15 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca return prev } +@(private) +json_name_from_tag_value :: proc(value: string) -> (json_name: string) { + json_name = value + if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 { + json_name = json_name[:comma_index] + } + return +} + @(private) unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) { @@ -384,7 +393,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm for field, field_idx in fields { tag_value := string(reflect.struct_tag_get(field.tag, "json")) - if key == tag_value { + if key == json_name_from_tag_value(tag_value) { use_field_idx = field_idx break } From fd487f66bc921b2f0cee6aa25710712754964a22 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 13:40:25 +0100 Subject: [PATCH 40/73] Add `json:"name,omitempty"` --- core/encoding/json/marshal.odin | 64 +++++++++++++++++++++++++++---- core/encoding/json/unmarshal.odin | 6 ++- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 6ec9c0d27..3d57316b3 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -366,12 +366,63 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: opt_write_end(w, opt, '}') or_return 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) { ti := runtime.type_info_base(type_info_of(v.id)) info := ti.variant.(runtime.Type_Info_Struct) for name, i in info.names { - json_name := reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json") - json_name = json_name_from_tag_value(json_name) + 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 if json_name != "" { @@ -379,18 +430,15 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: } else { // Marshal the fields of 'using _: T' fields directly into the parent struct if info.usings[i] && name == "_" { - id := info.types[i].id - data := rawptr(uintptr(v.data) + info.offsets[i]) - marshal_struct_fields(w, any{data, id}, opt) or_return + marshal_struct_fields(w, the_value, opt) or_return continue } else { 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, any{data, id}, opt) or_return + + marshal_to_writer(w, the_value, opt) or_return } return } diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 7a5679368..b2052e43c 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -344,10 +344,11 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca } @(private) -json_name_from_tag_value :: proc(value: string) -> (json_name: string) { +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 } @@ -393,7 +394,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm for field, field_idx in fields { tag_value := string(reflect.struct_tag_get(field.tag, "json")) - if key == json_name_from_tag_value(tag_value) { + json_name, _ := json_name_from_tag_value(tag_value) + if key == json_name { use_field_idx = field_idx break } From abd5fc606ceab4709983e919ec427a2070c3b0bb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 14:12:41 +0100 Subject: [PATCH 41/73] Fix #3407 --- core/fmt/fmt.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 6f9801bc8..d3b9d7d69 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2711,7 +2711,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } } else { 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.. 0 { io.write_string(fi.writer, ", ", &fi.n) } From 3ad95d6be381ce5bae5881d18cb99e269a27ed03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 14:35:00 +0100 Subject: [PATCH 42/73] Add append_u128 --- core/strconv/strconv.odin | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index f23e619dc..c2e81ef3e 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1213,6 +1213,13 @@ Output: append_int :: proc(buf: []byte, i: i64, base: int) -> string { 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 From 3b5e515a22a4a8e284fc4891eeaf15f534916a7b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 14:35:14 +0100 Subject: [PATCH 43/73] Fix #3386 - `fixed.to_string` --- core/math/fixed/fixed.odin | 188 +++++++++++++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 20 deletions(-) diff --git a/core/math/fixed/fixed.odin b/core/math/fixed/fixed.odin index 21fab5faf..d55e24175 100644 --- a/core/math/fixed/fixed.odin +++ b/core/math/fixed/fixed.odin @@ -102,37 +102,51 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing { return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width } - - @(require_results) append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { + Integer_Width :: 8*size_of(Backing) - Fraction_Width + x := x buf: [48]byte i := 0 - if x.i < 0 { + + if !intrinsics.type_is_unsigned(Backing) && x.i == min(Backing) { + // edge case handling for signed numbers buf[i] = '-' i += 1 - x.i = -x.i - } - - integer := x.i >> Fraction_Width - fraction := x.i & (1< 0 { - fraction *= 10 - buf[i] = byte('0' + (fraction>>Fraction_Width)) + i += copy(buf[i:], _power_of_two_table[Integer_Width]) + } else { + if x.i < 0 { + buf[i] = '-' i += 1 - fraction &= 1<> Fraction_Width + fraction := T(x.i) & (1< 0 { + fraction *= 10 + buf[i] = byte('0' + (fraction>>Fraction_Width) % 10) + i += 1 + fraction &= 1< Date: Wed, 10 Apr 2024 16:18:44 +0100 Subject: [PATCH 44/73] Fix `ptr_to_bit_field.field` --- src/llvm_backend_expr.cpp | 5 ++++- src/llvm_backend_general.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 0649150ca..fcec59968 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4726,9 +4726,12 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { if (sub_sel.index.count > 0) { 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); - bf_type = base_type(type_deref(bf_type)); + bf_type = base_type(bf_type); GB_ASSERT(bf_type->kind == Type_BitField); i32 index = sel.index[sel.index.count-1]; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 889cb8822..0d8d9258a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -453,7 +453,7 @@ gb_internal lbAddr lb_addr_swizzle_large(lbValue addr, Type *array_type, Slice Date: Wed, 10 Apr 2024 19:16:38 +0200 Subject: [PATCH 45/73] fix indentation --- core/odin/parser/parser.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 415ec949d..24a44f60e 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2995,7 +2995,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { } p.expr_level -= 1 - skip_possible_newline(p) + skip_possible_newline(p) close := expect_closing_brace_of_field_list(p); pos := type.pos if type != nil else open.pos From 97db075e4569388a372237c8dfac90a9bf843b74 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Apr 2024 20:32:55 +0100 Subject: [PATCH 46/73] Add #panic to tell of moved package --- vendor/darwin/Foundation/dummy.odin | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 vendor/darwin/Foundation/dummy.odin diff --git a/vendor/darwin/Foundation/dummy.odin b/vendor/darwin/Foundation/dummy.odin new file mode 100644 index 000000000..55f3c35f8 --- /dev/null +++ b/vendor/darwin/Foundation/dummy.odin @@ -0,0 +1,3 @@ +package vendor_darwin_foundation + +#panic(`Package moved to "core:sys/darwin/Foundation"`) \ No newline at end of file From 330c1616255e2f22b8c4dfde57d6a3b17d038a15 Mon Sep 17 00:00:00 2001 From: RilleP Date: Thu, 11 Apr 2024 09:36:28 +0200 Subject: [PATCH 47/73] remove semicolon --- core/odin/parser/parser.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 24a44f60e..b2ffd3888 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2996,7 +2996,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { p.expr_level -= 1 skip_possible_newline(p) - close := expect_closing_brace_of_field_list(p); + close := expect_closing_brace_of_field_list(p) pos := type.pos if type != nil else open.pos lit := ast.new(ast.Comp_Lit, pos, end_pos(close)) From 0abbf3ba0a9ea53aa9276dfb43bbf95d949c2d91 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 13:57:37 +0100 Subject: [PATCH 48/73] Fix #3412 --- src/checker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index 244e7efbd..3556c4647 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -703,7 +703,7 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { array_add(&vetted_entities, ve_unused); } else if (is_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)) == 0 && !e->Variable.is_global) { i64 sz = type_size_of(e->type); // TODO(bill): When is a good size warn? // Is 128 KiB good enough? From 6c38ae36580156171c453579942991b431f6b27b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:24:08 +0100 Subject: [PATCH 49/73] Remove `#optional_ok` from docs --- base/intrinsics/intrinsics.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 458596adf..dca33bfd9 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -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) --- 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_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- -overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- +overflow_add :: proc(lhs, rhs: $T) -> (T, bool) --- +overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) --- +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))) --- From b6d2ac11b8a3fa34542333966f3958602b6ba2c8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:24:36 +0100 Subject: [PATCH 50/73] Add `-vet-unused-variables` and ``-vet-unused-imports` (`-vet-unused` is both) --- src/build_settings.cpp | 21 ++++++++++++++------- src/checker.cpp | 5 ++++- src/main.cpp | 31 +++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 30d6f0b3c..ad1b3e884 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -272,13 +272,16 @@ enum BuildPath : u8 { }; enum VetFlags : u64 { - VetFlag_NONE = 0, - VetFlag_Unused = 1u<<0, // 1 - VetFlag_Shadowing = 1u<<1, // 2 - VetFlag_UsingStmt = 1u<<2, // 4 - VetFlag_UsingParam = 1u<<3, // 8 - VetFlag_Style = 1u<<4, // 16 - VetFlag_Semicolon = 1u<<5, // 32 + VetFlag_NONE = 0, + VetFlag_Shadowing = 1u<<0, + VetFlag_UsingStmt = 1u<<1, + VetFlag_UsingParam = 1u<<2, + VetFlag_Style = 1u<<3, + VetFlag_Semicolon = 1u<<4, + VetFlag_UnusedVariables = 1u<<5, + VetFlag_UnusedImports = 1u<<6, + + VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt, @@ -288,6 +291,10 @@ enum VetFlags : u64 { u64 get_vet_flag_from_name(String const &name) { if (name == "unused") { return VetFlag_Unused; + } else if (name == "unused-variables") { + return VetFlag_UnusedVariables; + } else if (name == "unused-imports") { + return VetFlag_UnusedImports; } else if (name == "shadowing") { return VetFlag_Shadowing; } else if (name == "using-stmt") { diff --git a/src/checker.cpp b/src/checker.cpp index 3556c4647..35554cf44 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -728,7 +728,10 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { } else if (vet_flags) { switch (ve.kind) { 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)); } break; diff --git a/src/main.cpp b/src/main.cpp index 2dbb72ca2..36a99ec32 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,6 +253,8 @@ enum BuildFlagKind { BuildFlag_Vet, BuildFlag_VetShadowing, BuildFlag_VetUnused, + BuildFlag_VetUnusedImports, + BuildFlag_VetUnusedVariables, BuildFlag_VetUsingStmt, BuildFlag_VetUsingParam, BuildFlag_VetStyle, @@ -444,6 +446,8 @@ gb_internal bool parse_build_flags(Array args) { 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_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_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); @@ -1026,10 +1030,9 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_UseSeparateModules: build_context.use_separate_modules = true; break; - case BuildFlag_NoThreadedChecker: { + case BuildFlag_NoThreadedChecker: build_context.no_threaded_checker = true; break; - } case BuildFlag_ShowDebugMessages: build_context.show_debug_messages = true; break; @@ -1037,12 +1040,14 @@ gb_internal bool parse_build_flags(Array args) { build_context.vet_flags |= VetFlag_All; break; - case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; - case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; - case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; 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_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break; + case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break; + case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; + case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; + case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; 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: 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, "Extra checks include:"); 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-using-stmt"); 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(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(2, "Checks for variable shadowing within procedures."); print_usage_line(0, ""); From 45d7a670ce689b5be046e023102871566cac9b7b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:34:34 +0100 Subject: [PATCH 51/73] Fix `@(static)` error message bug --- src/entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity.cpp b/src/entity.cpp index 6cea0930f..a12e1d0a6 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -496,7 +496,7 @@ gb_internal bool is_entity_local_variable(Entity *e) { if (e->scope == nullptr) { return true; } - if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue)) { + if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue|EntityFlag_Static)) { return false; } From f36fb6d1ef6ad1f4c5dc56b9b761e843195546b6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:41:01 +0100 Subject: [PATCH 52/73] Add `nil` checks --- src/check_stmt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fc3b9aa43..a6def5997 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2355,14 +2355,14 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) { unsafe_return_error(o, "the address of a compound literal"); } else if (x->kind == Ast_IndexExpr) { 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)) { unsafe_return_error(o, "the address of an indexed variable", f->type); } } } else if (x->kind == Ast_MatrixIndexExpr) { 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); } } From b2e887be36a23dbfc52e50eb054ccceeeea31692 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:55:01 +0100 Subject: [PATCH 53/73] Change stack overflow check to >256 KiB --- src/checker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 35554cf44..900a4f243 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -706,8 +706,8 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0 && !e->Variable.is_global) { i64 sz = type_size_of(e->type); // TODO(bill): When is a good size warn? - // Is 128 KiB good enough? - if (sz >= 1ll<<17) { + // Is >256 KiB good enough? + if (sz > 1ll<<18) { 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); gb_string_free(type_str); From 503964c7699125e2388762780f7121a704e6e25a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:57:13 +0100 Subject: [PATCH 54/73] Add @(static) check --- src/checker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 900a4f243..0d72c1e76 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -703,13 +703,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { array_add(&vetted_entities, ve_unused); } else if (is_shadowed) { array_add(&vetted_entities, ve_shadowed); - } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0 && !e->Variable.is_global) { + } 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); // TODO(bill): When is a good size warn? // Is >256 KiB good enough? if (sz > 1ll<<18) { 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); } } From aab122ede8b04a9877e22c9013c0b020186bc9b4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 15:59:22 +0100 Subject: [PATCH 55/73] Remove `?` --- src/checker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index 0d72c1e76..2b3ca0e9f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -709,7 +709,7 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { // Is >256 KiB good enough? if (sz > 1ll<<18) { 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); } } From cb0a57eaa99499a6f427fef0f9476085df476464 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Apr 2024 17:18:51 +0100 Subject: [PATCH 56/73] Add `-target:freestanding_amd64_win64` --- src/build_settings.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ad1b3e884..106ae8a28 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -631,6 +631,15 @@ gb_global TargetMetrics target_freestanding_amd64_sysv = { TargetABI_SysV, }; +gb_global TargetMetrics target_freestanding_amd64_win64 = { + TargetOs_freestanding, + TargetArch_amd64, + 8, 8, 8, 16, + str_lit("x86_64-pc-none-msvc"), + str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), + TargetABI_Win64, +}; + gb_global TargetMetrics target_freestanding_arm64 = { TargetOs_freestanding, TargetArch_arm64, @@ -672,7 +681,9 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("js_wasm64p32"), &target_js_wasm64p32 }, { str_lit("wasi_wasm64p32"), &target_wasi_wasm64p32 }, - { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, + { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, + { str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 }, + { str_lit("freestanding_arm64"), &target_freestanding_arm64 }, }; From 4cdadeedc3b46f91dd9d863b9a8f94d9ac820067 Mon Sep 17 00:00:00 2001 From: alec hodgkinson Date: Thu, 11 Apr 2024 09:55:43 -0700 Subject: [PATCH 57/73] Added docs for trig function procedure groups --- core/math/math.odin | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/core/math/math.odin b/core/math/math.odin index 570c2d255..8d85c2381 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -60,6 +60,7 @@ sqrt :: proc{ @(require_results) sin_f32be :: proc "contextless" (θ: f32be) -> f32be { return #force_inline f32be(sin_f32(f32(θ))) } @(require_results) sin_f64le :: proc "contextless" (θ: f64le) -> f64le { return #force_inline f64le(sin_f64(f64(θ))) } @(require_results) sin_f64be :: proc "contextless" (θ: f64be) -> f64be { return #force_inline f64be(sin_f64(f64(θ))) } +// Return the sine of θ in radians. sin :: proc{ sin_f16, sin_f16le, sin_f16be, sin_f32, sin_f32le, sin_f32be, @@ -72,6 +73,7 @@ sin :: proc{ @(require_results) cos_f32be :: proc "contextless" (θ: f32be) -> f32be { return #force_inline f32be(cos_f32(f32(θ))) } @(require_results) cos_f64le :: proc "contextless" (θ: f64le) -> f64le { return #force_inline f64le(cos_f64(f64(θ))) } @(require_results) cos_f64be :: proc "contextless" (θ: f64be) -> f64be { return #force_inline f64be(cos_f64(f64(θ))) } +// Return the cosine of θ in radians. cos :: proc{ cos_f16, cos_f16le, cos_f16be, cos_f32, cos_f32le, cos_f32be, @@ -378,6 +380,7 @@ log10 :: proc{ @(require_results) tan_f64 :: proc "contextless" (θ: f64) -> f64 { return sin(θ)/cos(θ) } @(require_results) tan_f64le :: proc "contextless" (θ: f64le) -> f64le { return f64le(tan_f64(f64(θ))) } @(require_results) tan_f64be :: proc "contextless" (θ: f64be) -> f64be { return f64be(tan_f64(f64(θ))) } +// Return the tangent of θ in radians. tan :: proc{ tan_f16, tan_f16le, tan_f16be, tan_f32, tan_f32le, tan_f32be, @@ -1752,7 +1755,28 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be { // TODO(bill): Better atan2_f32 return f64be(atan2_f64(f64(y), f64(x))) } +/* + Return the arc tangent of y/x in radians. Defined on the domain [-∞, ∞] for x and y with a range of [-π, π] + Special cases: + atan2(y, NaN) = NaN + atan2(NaN, x) = NaN + atan2(+0, x>=0) = + 0 + atan2(-0, x>=0) = - 0 + atan2(+0, x<=-0) = + π + atan2(-0, x<=-0) = - π + atan2(y>0, 0) = + π/2 + atan2(y<0, 0) = - π/2 + atan2(+∞, +∞) = + π/4 + atan2(-∞, +∞) = - π/4 + atan2(+∞, -∞) = 3π/4 + atan2(-∞, -∞) = - 3π/4 + atan2(y, +∞) = 0 + atan2(y>0, -∞) = + π + atan2(y<0, -∞) = - π + atan2(+∞, x) = + π/2 + atan2(-∞, x) = - π/2 +*/ atan2 :: proc{ atan2_f64, atan2_f32, atan2_f16, atan2_f64le, atan2_f64be, @@ -1760,6 +1784,7 @@ atan2 :: proc{ atan2_f16le, atan2_f16be, } +// Return the arc tangent of x, in radians. Defined on the domain of [-∞, ∞] with a range of [-π/2, π/2] @(require_results) atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) { return atan2(x, 1) @@ -1871,6 +1896,7 @@ asin_f16le :: proc "contextless" (x: f16le) -> f16le { asin_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(asin_f64(f64(x))) } +// Return the arc sine of x, in radians. Defined on the domain of [-1, 1] with a range of [-π/2, π/2] asin :: proc{ asin_f64, asin_f32, asin_f16, asin_f64le, asin_f64be, @@ -1985,6 +2011,7 @@ acos_f16le :: proc "contextless" (x: f16le) -> f16le { acos_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(acos_f64(f64(x))) } +// Return the arc cosine of x, in radians. Defined on the domain of [-1, 1] with a range of [0, π]. acos :: proc{ acos_f64, acos_f32, acos_f16, acos_f64le, acos_f64be, From efc3f9916ec4217cd511f561166cb5f4348295c5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 12:30:16 +0100 Subject: [PATCH 58/73] Fix #3414 --- src/check_type.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/check_type.cpp b/src/check_type.cpp index f1d991acb..f4e5d7c96 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3233,6 +3233,11 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T Type *elem = t_invalid; Operand o = {}; + if (unparen_expr(pt->type) == nullptr) { + error(e, "Invalid pointer type"); + return false; + } + check_expr_or_type(&c, &o, pt->type); if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) { if (o.mode == Addressing_Variable) { From 3426af2d6cb5944d373470d10ca91f827a8f39ca Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 12:33:25 +0100 Subject: [PATCH 59/73] Fix #3415 --- src/common_memory.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common_memory.cpp b/src/common_memory.cpp index c6ee88f03..60e570eee 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -163,6 +163,10 @@ gb_internal void platform_virtual_memory_protect(void *memory, isize size); GB_ASSERT(is_protected); } #else + #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) + #define MAP_ANONYMOUS MAP_ANON + #endif + gb_internal void platform_virtual_memory_init(void) { global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel; global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel; From 46b9bd8c0e3987080f94ae42921b513a79708ef9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 13:35:14 +0100 Subject: [PATCH 60/73] Improve error messages for `switch` and `for` r-values with a suggestion --- src/check_stmt.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ src/error.cpp | 21 ++++++++++++++++----- src/tokenizer.cpp | 1 + 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a6def5997..f2b7f8661 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -474,16 +474,59 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O } Entity *e = entity_of_node(lhs->expr); + Entity *original_e = e; + + Ast *name = unparen_expr(lhs->expr); + while (name->kind == Ast_SelectorExpr) { + name = name->SelectorExpr.expr; + e = entity_of_node(name); + } + if (e == nullptr) { + e = original_e; + } gbString str = expr_to_string(lhs->expr); if (e != nullptr && e->flags & EntityFlag_Param) { + ERROR_BLOCK(); if (e->flags & EntityFlag_Using) { error(lhs->expr, "Cannot assign to '%s' which is from a 'using' procedure parameter", str); } else { error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str); } + error_line("\tSuggestion: Did you mean to pass '%.*s' by pointer?\n", LIT(e->token.string)); + show_error_on_line(e->token.pos, token_pos_end(e->token)); } else { + ERROR_BLOCK(); error(lhs->expr, "Cannot assign to '%s'", str); + + if (e) if (e->flags & EntityFlag_ForValue) { + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + if (offset < 0) { + if (is_type_map(e->type)) { + error_line("\tSuggestion: Did you mean? 'for key, &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\tSuggestion: Did you mean? 'for &%.*s in ...'\n", LIT(e->token.string)); + } + } else { + error_line("\t"); + for (isize i = 0; i < offset-1; i++) { + error_line(" "); + } + error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string)); + } + + } else if (e->flags & EntityFlag_SwitchValue) { + isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); + if (offset < 0) { + error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string)); + } else { + error_line("\t"); + for (isize i = 0; i < offset-1; i++) { + error_line(" "); + } + error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string)); + } + } } gb_string_free(str); diff --git a/src/error.cpp b/src/error.cpp index eb167d4c3..8647f60b9 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -84,6 +84,7 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { bool ok = false; GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); if (index >= global_file_path_strings.count) { array_resize(&global_file_path_strings, index+1); @@ -94,6 +95,7 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { ok = true; } + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return ok; } @@ -102,6 +104,7 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { bool ok = false; GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); if (index >= global_files.count) { array_resize(&global_files, index+1); @@ -111,7 +114,7 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { global_files[index] = file; ok = true; } - + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return ok; } @@ -119,12 +122,14 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { gb_internal String get_file_path_string(i32 index) { GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); String path = {}; if (index < global_file_path_strings.count) { path = global_file_path_strings[index]; } + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return path; } @@ -132,12 +137,14 @@ gb_internal String get_file_path_string(i32 index) { gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { GB_ASSERT(index >= 0); mutex_lock(&global_error_collector.path_mutex); + mutex_lock(&global_files_mutex); AstFile *file = nullptr; if (index < global_files.count) { file = global_files[index]; } + mutex_unlock(&global_files_mutex); mutex_unlock(&global_error_collector.path_mutex); return file; } @@ -247,10 +254,10 @@ gb_internal void terminal_reset_colours(void) { } -gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { +gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end, char const *prefix=nullptr) { get_error_value()->end = end; if (!show_error_line()) { - return false; + return -1; } i32 offset = 0; @@ -270,6 +277,10 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING, }; + if (prefix) { + error_out("\t%s\n\n", prefix); + } + error_out("\t"); terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); @@ -328,9 +339,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { terminal_reset_colours(); error_out("\n"); - return true; + return offset; } - return false; + return -1; } gb_internal void error_out_empty(void) { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3d5348074..fdff9224a 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -193,6 +193,7 @@ gb_internal void init_keyword_hash_table(void) { gb_global Array global_file_path_strings; // index is file id gb_global Array global_files; // index is file id +gb_global BlockingMutex global_files_mutex; gb_internal String get_file_path_string(i32 index); gb_internal struct AstFile *thread_safe_get_ast_file_from_id(i32 index); From caa344c88d7b2f736da58a8b9c88b510caafb68c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 14:05:36 +0100 Subject: [PATCH 61/73] Simplify scalar -> array conversions in LLVM to use a loop after a certain size --- src/llvm_backend_expr.cpp | 13 +++++++++++-- src/llvm_backend_general.cpp | 10 ---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index fcec59968..c97e63c26 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2143,9 +2143,18 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lbAddr v = lb_add_local_generated(p, t, false); isize index_count = cast(isize)get_array_type_count(dst); - for (isize i = 0; i < index_count; i++) { - lbValue elem = lb_emit_array_epi(p, v.addr, i); + if (type_size_of(dst) > build_context.max_simd_align) { + auto loop_data = lb_loop_start(p, index_count, t_int); + + lbValue elem = lb_emit_array_ep(p, v.addr, loop_data.idx); lb_emit_store(p, elem, e); + + lb_loop_end(p, loop_data); + } else { + for (isize i = 0; i < index_count; i++) { + lbValue elem = lb_emit_array_epi(p, v.addr, i); + lb_emit_store(p, elem, e); + } } return lb_addr_load(p, v); } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 0d8d9258a..73e4a00e6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -954,16 +954,6 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(value.value != nullptr); value = lb_emit_conv(p, value, lb_addr_type(addr)); - // if (lb_is_const_or_global(value)) { - // // NOTE(bill): Just bypass the actual storage and set the initializer - // if (LLVMGetValueKind(addr.addr.value) == LLVMGlobalVariableValueKind) { - // LLVMValueRef dst = addr.addr.value; - // LLVMValueRef src = value.value; - // LLVMSetInitializer(dst, src); - // return; - // } - // } - lb_emit_store(p, addr.addr, value); } From 4240e0025e1db18821148210e7c5260faef7d830 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 14:20:46 +0100 Subject: [PATCH 62/73] Improve scalar -> array assignment when the scalar is constant in LLVM --- src/llvm_backend_expr.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c97e63c26..ad28f2e5e 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2138,12 +2138,34 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (is_type_array_like(dst)) { Type *elem = base_array_type(dst); - lbValue e = lb_emit_conv(p, value, elem); - // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops - lbAddr v = lb_add_local_generated(p, t, false); isize index_count = cast(isize)get_array_type_count(dst); - if (type_size_of(dst) > build_context.max_simd_align) { + isize inlineable = type_size_of(dst) <= build_context.max_simd_align; + lbValue e = lb_emit_conv(p, value, elem); + if (inlineable && lb_is_const(e)) { + lbAddr v = {}; + if (e.value) { + TEMPORARY_ALLOCATOR_GUARD(); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, index_count); + for (isize i = 0; i < index_count; i++) { + values[i] = e.value; + } + lbValue array_const_value = {}; + array_const_value.type = t; + array_const_value.value = LLVMConstArray(lb_type(m, elem), values, cast(unsigned)index_count); + v = lb_add_global_generated(m, t, array_const_value); + } else { + v = lb_add_global_generated(m, t); + } + + lb_make_global_private_const(v); + return lb_addr_load(p, v); + } + + // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops + lbAddr v = lb_add_local_generated(p, t, false); + + if (!inlineable) { auto loop_data = lb_loop_start(p, index_count, t_int); lbValue elem = lb_emit_array_ep(p, v.addr, loop_data.idx); From 5726b7d9541d62f39cb5b04412f283a02e13c077 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 12 Apr 2024 14:51:22 +0100 Subject: [PATCH 63/73] Remove warning on clang --- src/check_stmt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f2b7f8661..971841165 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -499,7 +499,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O ERROR_BLOCK(); error(lhs->expr, "Cannot assign to '%s'", str); - if (e) if (e->flags & EntityFlag_ForValue) { + if (e && e->flags & EntityFlag_ForValue) { isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); if (offset < 0) { if (is_type_map(e->type)) { @@ -515,7 +515,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O error_line("'%.*s' is immutable, declare it as '&%.*s' to make it mutable\n", LIT(e->token.string), LIT(e->token.string)); } - } else if (e->flags & EntityFlag_SwitchValue) { + } else if (e && e->flags & EntityFlag_SwitchValue) { isize offset = show_error_on_line(e->token.pos, token_pos_end(e->token), "Suggestion:"); if (offset < 0) { error_line("\tSuggestion: Did you mean? 'switch &%.*s in ...'\n", LIT(e->token.string)); From c753711d86fda7392c2556741de0fd9b70d7c2e1 Mon Sep 17 00:00:00 2001 From: blob1807 <12388588+blob1807@users.noreply.github.com> Date: Sat, 13 Apr 2024 00:39:32 +1000 Subject: [PATCH 64/73] Added support for URL fragments Added support for a URL's fragment/anchor to `split_url` & `join_url` in `core:net` plus 4 new tests to cover it. --- core/net/url.odin | 17 +++++- tests/core/net/test_core_net.odin | 86 +++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/core/net/url.odin b/core/net/url.odin index 7ad88bd1f..5257b757c 100644 --- a/core/net/url.odin +++ b/core/net/url.odin @@ -21,7 +21,7 @@ import "core:strconv" import "core:unicode/utf8" import "core:encoding/hex" -split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string) { +split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string, fragment: string) { s := url i := strings.index(s, "://") @@ -30,6 +30,12 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, s = s[i+3:] } + i = strings.index_byte(s, '#') + if i != -1 { + fragment = s[i+1:] + s = s[:i] + } + i = strings.index(s, "?") if i != -1 { query_str := s[i+1:] @@ -62,7 +68,7 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, return } -join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string { +join_url :: proc(scheme, host, path: string, queries: map[string]string, fragment: string, allocator := context.allocator) -> string { b := strings.builder_make(allocator) strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path)) @@ -95,6 +101,13 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, allocat i += 1 } + if fragment != "" { + if fragment[0] != '#' { + strings.write_byte(&b, '#') + } + strings.write_string(&b, strings.trim_space(fragment)) + } + return strings.to_string(b) } diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 579298904..2a581c66b 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -473,6 +473,7 @@ client_sends_server_data :: proc(t: ^testing.T) { URL_Test :: struct { scheme, host, path: string, queries: map[string]string, + fragment: string, url: []string, } @@ -481,58 +482,78 @@ split_url_test :: proc(t: ^testing.T) { test_cases := []URL_Test{ { "http", "example.com", "/", - {}, + {}, "", {"http://example.com"}, }, { "https", "odin-lang.org", "/", - {}, + {}, "", {"https://odin-lang.org"}, }, { "https", "odin-lang.org", "/docs/", - {}, + {}, "", {"https://odin-lang.org/docs/"}, }, { "https", "odin-lang.org", "/docs/overview", - {}, + {}, "", {"https://odin-lang.org/docs/overview"}, }, { "http", "example.com", "/", - {"a" = "b"}, + {"a" = "b"}, "", {"http://example.com?a=b"}, }, { "http", "example.com", "/", - {"a" = ""}, + {"a" = ""}, "", {"http://example.com?a"}, }, { "http", "example.com", "/", - {"a" = "b", "c" = "d"}, + {"a" = "b", "c" = "d"}, "", {"http://example.com?a=b&c=d"}, }, { "http", "example.com", "/", - {"a" = "", "c" = "d"}, + {"a" = "", "c" = "d"}, "", {"http://example.com?a&c=d"}, }, { "http", "example.com", "/example", - {"a" = "", "b" = ""}, + {"a" = "", "b" = ""}, "", {"http://example.com/example?a&b"}, }, { "https", "example.com", "/callback", - {"redirect" = "https://other.com/login"}, + {"redirect" = "https://other.com/login"}, "", {"https://example.com/callback?redirect=https://other.com/login"}, }, + { + "http", "odin-lang.org", "/", + {}, "Hellope", + {"http://odin-lang.org#Hellope"} + }, + { + "https", "odin-lang.org", "/", + {"a" = ""}, "Hellope", + {"https://odin-lang.org?a#Hellope"} + }, + { + "http", "example.com", "/", + {"a" = "b"}, "Hellope", + {"http://example.com?a=b#Hellope"} + }, + { + "https", "example.com", "/example", + {}, "Hellope", + {"https://example.com/example#Hellope"} + }, } for test in test_cases { - scheme, host, path, queries := net.split_url(test.url[0]) + scheme, host, path, queries, fragment := net.split_url(test.url[0]) defer { delete(queries) delete(test.queries) @@ -551,6 +572,9 @@ split_url_test :: proc(t: ^testing.T) { msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", expected, v) expect(t, v == expected, msg) } + msg = fmt.tprintf("Expected `net.split_url` to return %s, got %s", test.fragment, fragment) + expect(t, fragment == test.fragment, msg) + } } @@ -560,53 +584,73 @@ join_url_test :: proc(t: ^testing.T) { test_cases := []URL_Test{ { "http", "example.com", "/", - {}, + {}, "", {"http://example.com/"}, }, { "https", "odin-lang.org", "/", - {}, + {}, "", {"https://odin-lang.org/"}, }, { "https", "odin-lang.org", "/docs/", - {}, + {}, "", {"https://odin-lang.org/docs/"}, }, { "https", "odin-lang.org", "/docs/overview", - {}, + {}, "", {"https://odin-lang.org/docs/overview"}, }, { "http", "example.com", "/", - {"a" = "b"}, + {"a" = "b"}, "", {"http://example.com/?a=b"}, }, { "http", "example.com", "/", - {"a" = ""}, + {"a" = ""}, "", {"http://example.com/?a"}, }, { "http", "example.com", "/", - {"a" = "b", "c" = "d"}, + {"a" = "b", "c" = "d"}, "", {"http://example.com/?a=b&c=d", "http://example.com/?c=d&a=b"}, }, { "http", "example.com", "/", - {"a" = "", "c" = "d"}, + {"a" = "", "c" = "d"}, "", {"http://example.com/?a&c=d", "http://example.com/?c=d&a"}, }, { "http", "example.com", "/example", - {"a" = "", "b" = ""}, + {"a" = "", "b" = ""}, "", {"http://example.com/example?a&b", "http://example.com/example?b&a"}, }, + { + "http", "odin-lang.org", "", + {}, "Hellope", + {"http://odin-lang.org#Hellope"} + }, + { + "https", "odin-lang.org", "", + {"a" = ""}, "Hellope", + {"https://odin-lang.org?a#Hellope"} + }, + { + "http", "example.com", "", + {"a" = "b"}, "Hellope", + {"http://example.com?a=b#Hellope"} + }, + { + "https", "example.com", "/example", + {}, "Hellope", + {"https://example.com/example#Hellope"} + }, } for test in test_cases { - url := net.join_url(test.scheme, test.host, test.path, test.queries) + url := net.join_url(test.scheme, test.host, test.path, test.queries, test.fragment) defer { delete(url) delete(test.queries) From 2d1260bec9874907fd6545bfbabc0829c697b361 Mon Sep 17 00:00:00 2001 From: blob1807 <12388588+blob1807@users.noreply.github.com> Date: Sat, 13 Apr 2024 00:47:49 +1000 Subject: [PATCH 65/73] uniformity change small change to check things uniform --- core/net/url.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/net/url.odin b/core/net/url.odin index 5257b757c..16aa57ec5 100644 --- a/core/net/url.odin +++ b/core/net/url.odin @@ -30,7 +30,7 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, s = s[i+3:] } - i = strings.index_byte(s, '#') + i = strings.index(s, "#") if i != -1 { fragment = s[i+1:] s = s[:i] @@ -103,7 +103,7 @@ join_url :: proc(scheme, host, path: string, queries: map[string]string, fragmen if fragment != "" { if fragment[0] != '#' { - strings.write_byte(&b, '#') + strings.write_string(&b, "#") } strings.write_string(&b, strings.trim_space(fragment)) } From 6348b56c8bfced2fc202517a322bbf9505725384 Mon Sep 17 00:00:00 2001 From: blob1807 <12388588+blob1807@users.noreply.github.com> Date: Sat, 13 Apr 2024 00:57:36 +1000 Subject: [PATCH 66/73] Move rounded tests --- tests/core/net/test_core_net.odin | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 2a581c66b..52d22a9f0 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -531,9 +531,9 @@ split_url_test :: proc(t: ^testing.T) { {"https://example.com/callback?redirect=https://other.com/login"}, }, { - "http", "odin-lang.org", "/", + "http", "example.com", "/", {}, "Hellope", - {"http://odin-lang.org#Hellope"} + {"http://example.com#Hellope"} }, { "https", "odin-lang.org", "/", @@ -542,13 +542,13 @@ split_url_test :: proc(t: ^testing.T) { }, { "http", "example.com", "/", - {"a" = "b"}, "Hellope", - {"http://example.com?a=b#Hellope"} + {"a" = "b"}, "BeesKnees", + {"http://example.com?a=b#BeesKnees"} }, { - "https", "example.com", "/example", - {}, "Hellope", - {"https://example.com/example#Hellope"} + "https", "odin-lang.org", "/docs/overview/", + {}, "hellope", + {"https://odin-lang.org/docs/overview/#hellope"} }, } @@ -628,24 +628,24 @@ join_url_test :: proc(t: ^testing.T) { {"http://example.com/example?a&b", "http://example.com/example?b&a"}, }, { - "http", "odin-lang.org", "", + "http", "example.com", "/", {}, "Hellope", - {"http://odin-lang.org#Hellope"} + {"http://example.com/#Hellope"} }, { - "https", "odin-lang.org", "", + "https", "odin-lang.org", "/", {"a" = ""}, "Hellope", - {"https://odin-lang.org?a#Hellope"} + {"https://odin-lang.org/?a#Hellope"} }, { - "http", "example.com", "", - {"a" = "b"}, "Hellope", - {"http://example.com?a=b#Hellope"} + "http", "example.com", "/", + {"a" = "b"}, "BeesKnees", + {"http://example.com/?a=b#BeesKnees"} }, { - "https", "example.com", "/example", - {}, "Hellope", - {"https://example.com/example#Hellope"} + "https", "odin-lang.org", "/docs/overview/", + {}, "hellope", + {"https://odin-lang.org/docs/overview/#hellope"} }, } From a4d16e97a1c48481ff6cda5c7541a552f1e6d9e8 Mon Sep 17 00:00:00 2001 From: blob1807 <12388588+blob1807@users.noreply.github.com> Date: Sat, 13 Apr 2024 01:14:55 +1000 Subject: [PATCH 67/73] Fix CI's parser --- tests/core/net/test_core_net.odin | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 52d22a9f0..9df03414c 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -533,22 +533,22 @@ split_url_test :: proc(t: ^testing.T) { { "http", "example.com", "/", {}, "Hellope", - {"http://example.com#Hellope"} + {"http://example.com#Hellope"}, }, { "https", "odin-lang.org", "/", {"a" = ""}, "Hellope", - {"https://odin-lang.org?a#Hellope"} + {"https://odin-lang.org?a#Hellope"}, }, { "http", "example.com", "/", {"a" = "b"}, "BeesKnees", - {"http://example.com?a=b#BeesKnees"} + {"http://example.com?a=b#BeesKnees"}, }, { "https", "odin-lang.org", "/docs/overview/", {}, "hellope", - {"https://odin-lang.org/docs/overview/#hellope"} + {"https://odin-lang.org/docs/overview/#hellope"}, }, } @@ -630,22 +630,22 @@ join_url_test :: proc(t: ^testing.T) { { "http", "example.com", "/", {}, "Hellope", - {"http://example.com/#Hellope"} + {"http://example.com/#Hellope"}, }, { "https", "odin-lang.org", "/", {"a" = ""}, "Hellope", - {"https://odin-lang.org/?a#Hellope"} + {"https://odin-lang.org/?a#Hellope"}, }, { "http", "example.com", "/", {"a" = "b"}, "BeesKnees", - {"http://example.com/?a=b#BeesKnees"} + {"http://example.com/?a=b#BeesKnees"}, }, { "https", "odin-lang.org", "/docs/overview/", {}, "hellope", - {"https://odin-lang.org/docs/overview/#hellope"} + {"https://odin-lang.org/docs/overview/#hellope"}, }, } From befb0f786891415358c33ff53de6b265335c45f5 Mon Sep 17 00:00:00 2001 From: Vitalii Kravchenko Date: Tue, 2 Apr 2024 22:23:28 +0100 Subject: [PATCH 68/73] Core Foundation and Security vendor libraries. --- core/crypto/rand_bsd.odin | 2 +- core/crypto/rand_darwin.odin | 14 +- core/crypto/rand_generic.odin | 2 +- core/crypto/rand_js.odin | 2 +- core/crypto/rand_linux.odin | 2 +- core/crypto/rand_windows.odin | 2 +- core/os/stat.odin | 4 +- core/sys/darwin/CoreFoundation/CFBase.odin | 34 ++ core/sys/darwin/CoreFoundation/CFString.odin | 203 ++++++++++ core/sys/darwin/Foundation/NSString.odin | 15 +- core/sys/darwin/Security/SecBase.odin | 386 +++++++++++++++++++ core/sys/darwin/Security/SecRandom.odin | 19 + core/sys/darwin/core_foundation.odin | 98 ----- core/sys/darwin/security.odin | 26 -- examples/demo/demo.odin | 2 +- 15 files changed, 664 insertions(+), 147 deletions(-) create mode 100644 core/sys/darwin/CoreFoundation/CFBase.odin create mode 100644 core/sys/darwin/CoreFoundation/CFString.odin create mode 100644 core/sys/darwin/Security/SecBase.odin create mode 100644 core/sys/darwin/Security/SecRandom.odin delete mode 100644 core/sys/darwin/core_foundation.odin delete mode 100644 core/sys/darwin/security.odin diff --git a/core/crypto/rand_bsd.odin b/core/crypto/rand_bsd.odin index 61eaf652f..7a0c42683 100644 --- a/core/crypto/rand_bsd.odin +++ b/core/crypto/rand_bsd.odin @@ -11,6 +11,6 @@ _rand_bytes :: proc(dst: []byte) { arc4random_buf(raw_data(dst), len(dst)) } -_has_rand_bytes :: proc () -> bool { +_has_rand_bytes :: proc() -> bool { return true } diff --git a/core/crypto/rand_darwin.odin b/core/crypto/rand_darwin.odin index 2864b46dd..c1a3d1dbc 100644 --- a/core/crypto/rand_darwin.odin +++ b/core/crypto/rand_darwin.odin @@ -1,16 +1,18 @@ package crypto import "core:fmt" -import "core:sys/darwin" + +import CF "core:sys/darwin/CoreFoundation" +import Sec "core:sys/darwin/Security" _rand_bytes :: proc(dst: []byte) { - res := darwin.SecRandomCopyBytes(count=len(dst), bytes=raw_data(dst)) - if res != .Success { - msg := darwin.CFStringCopyToOdinString(darwin.SecCopyErrorMessageString(res)) - panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg)) + err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst)) + if err != .Success { + msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err)) + panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)) } } -_has_rand_bytes :: proc () -> bool { +_has_rand_bytes :: proc() -> bool { return true } diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin index 006ca51fe..cba49f700 100644 --- a/core/crypto/rand_generic.odin +++ b/core/crypto/rand_generic.odin @@ -10,6 +10,6 @@ _rand_bytes :: proc(dst: []byte) { unimplemented("crypto: rand_bytes not supported on this OS") } -_has_rand_bytes :: proc () -> bool { +_has_rand_bytes :: proc() -> bool { return false } diff --git a/core/crypto/rand_js.odin b/core/crypto/rand_js.odin index cb2711404..90f60b99b 100644 --- a/core/crypto/rand_js.odin +++ b/core/crypto/rand_js.odin @@ -19,6 +19,6 @@ _rand_bytes :: proc(dst: []byte) { } } -_has_rand_bytes :: proc () -> bool { +_has_rand_bytes :: proc() -> bool { return true } diff --git a/core/crypto/rand_linux.odin b/core/crypto/rand_linux.odin index 05c05597d..a9dc37415 100644 --- a/core/crypto/rand_linux.odin +++ b/core/crypto/rand_linux.odin @@ -35,6 +35,6 @@ _rand_bytes :: proc (dst: []byte) { } } -_has_rand_bytes :: proc () -> bool { +_has_rand_bytes :: proc() -> bool { return true } diff --git a/core/crypto/rand_windows.odin b/core/crypto/rand_windows.odin index e1d9f6118..5cafe7fb5 100644 --- a/core/crypto/rand_windows.odin +++ b/core/crypto/rand_windows.odin @@ -22,6 +22,6 @@ _rand_bytes :: proc(dst: []byte) { } } -_has_rand_bytes :: proc () -> bool { +_has_rand_bytes :: proc() -> bool { return true } diff --git a/core/os/stat.odin b/core/os/stat.odin index 1b64ad33b..21a4961d1 100644 --- a/core/os/stat.odin +++ b/core/os/stat.odin @@ -3,8 +3,8 @@ package os import "core:time" File_Info :: struct { - fullpath: string, - name: string, + fullpath: string, // allocated + name: string, // uses `fullpath` as underlying data size: i64, mode: File_Mode, is_dir: bool, diff --git a/core/sys/darwin/CoreFoundation/CFBase.odin b/core/sys/darwin/CoreFoundation/CFBase.odin new file mode 100644 index 000000000..7335f087b --- /dev/null +++ b/core/sys/darwin/CoreFoundation/CFBase.odin @@ -0,0 +1,34 @@ +package CoreFoundation + +foreign import CoreFoundation "system:CoreFoundation.framework" + +TypeID :: distinct uint +OptionFlags :: distinct uint +HashCode :: distinct uint +Index :: distinct int +TypeRef :: distinct rawptr + +Range :: struct { + location: Index, + length: Index, +} + +foreign CoreFoundation { + // Releases a Core Foundation object. + CFRelease :: proc(cf: TypeRef) --- +} + +// Releases a Core Foundation object. +Release :: proc { + ReleaseObject, + ReleaseString, +} + +ReleaseObject :: #force_inline proc(cf: TypeRef) { + CFRelease(cf) +} + +// Releases a Core Foundation string. +ReleaseString :: #force_inline proc(theString: String) { + CFRelease(TypeRef(theString)) +} diff --git a/core/sys/darwin/CoreFoundation/CFString.odin b/core/sys/darwin/CoreFoundation/CFString.odin new file mode 100644 index 000000000..4a167c604 --- /dev/null +++ b/core/sys/darwin/CoreFoundation/CFString.odin @@ -0,0 +1,203 @@ +package CoreFoundation + +import "base:runtime" + +foreign import CoreFoundation "system:CoreFoundation.framework" + +String :: distinct TypeRef // same as CFStringRef + +StringEncoding :: distinct u32 + +StringBuiltInEncodings :: enum StringEncoding { + MacRoman = 0, + WindowsLatin1 = 0x0500, + ISOLatin1 = 0x0201, + NextStepLatin = 0x0B01, + ASCII = 0x0600, + Unicode = 0x0100, + UTF8 = 0x08000100, + NonLossyASCII = 0x0BFF, + + UTF16 = 0x0100, + UTF16BE = 0x10000100, + UTF16LE = 0x14000100, + + UTF32 = 0x0c000100, + UTF32BE = 0x18000100, + UTF32LE = 0x1c000100, +} + +StringEncodings :: enum Index { + MacJapanese = 1, + MacChineseTrad = 2, + MacKorean = 3, + MacArabic = 4, + MacHebrew = 5, + MacGreek = 6, + MacCyrillic = 7, + MacDevanagari = 9, + MacGurmukhi = 10, + MacGujarati = 11, + MacOriya = 12, + MacBengali = 13, + MacTamil = 14, + MacTelugu = 15, + MacKannada = 16, + MacMalayalam = 17, + MacSinhalese = 18, + MacBurmese = 19, + MacKhmer = 20, + MacThai = 21, + MacLaotian = 22, + MacGeorgian = 23, + MacArmenian = 24, + MacChineseSimp = 25, + MacTibetan = 26, + MacMongolian = 27, + MacEthiopic = 28, + MacCentralEurRoman = 29, + MacVietnamese = 30, + MacExtArabic = 31, + MacSymbol = 33, + MacDingbats = 34, + MacTurkish = 35, + MacCroatian = 36, + MacIcelandic = 37, + MacRomanian = 38, + MacCeltic = 39, + MacGaelic = 40, + MacFarsi = 0x8C, + MacUkrainian = 0x98, + MacInuit = 0xEC, + MacVT100 = 0xFC, + MacHFS = 0xFF, + ISOLatin2 = 0x0202, + ISOLatin3 = 0x0203, + ISOLatin4 = 0x0204, + ISOLatinCyrillic = 0x0205, + ISOLatinArabic = 0x0206, + ISOLatinGreek = 0x0207, + ISOLatinHebrew = 0x0208, + ISOLatin5 = 0x0209, + ISOLatin6 = 0x020A, + ISOLatinThai = 0x020B, + ISOLatin7 = 0x020D, + ISOLatin8 = 0x020E, + ISOLatin9 = 0x020F, + ISOLatin10 = 0x0210, + DOSLatinUS = 0x0400, + DOSGreek = 0x0405, + DOSBalticRim = 0x0406, + DOSLatin1 = 0x0410, + DOSGreek1 = 0x0411, + DOSLatin2 = 0x0412, + DOSCyrillic = 0x0413, + DOSTurkish = 0x0414, + DOSPortuguese = 0x0415, + DOSIcelandic = 0x0416, + DOSHebrew = 0x0417, + DOSCanadianFrench = 0x0418, + DOSArabic = 0x0419, + DOSNordic = 0x041A, + DOSRussian = 0x041B, + DOSGreek2 = 0x041C, + DOSThai = 0x041D, + DOSJapanese = 0x0420, + DOSChineseSimplif = 0x0421, + DOSKorean = 0x0422, + DOSChineseTrad = 0x0423, + WindowsLatin2 = 0x0501, + WindowsCyrillic = 0x0502, + WindowsGreek = 0x0503, + WindowsLatin5 = 0x0504, + WindowsHebrew = 0x0505, + WindowsArabic = 0x0506, + WindowsBalticRim = 0x0507, + WindowsVietnamese = 0x0508, + WindowsKoreanJohab = 0x0510, + ANSEL = 0x0601, + JIS_X0201_76 = 0x0620, + JIS_X0208_83 = 0x0621, + JIS_X0208_90 = 0x0622, + JIS_X0212_90 = 0x0623, + JIS_C6226_78 = 0x0624, + ShiftJIS_X0213 = 0x0628, + ShiftJIS_X0213_MenKuTen = 0x0629, + GB_2312_80 = 0x0630, + GBK_95 = 0x0631, + GB_18030_2000 = 0x0632, + KSC_5601_87 = 0x0640, + KSC_5601_92_Johab = 0x0641, + CNS_11643_92_P1 = 0x0651, + CNS_11643_92_P2 = 0x0652, + CNS_11643_92_P3 = 0x0653, + ISO_2022_JP = 0x0820, + ISO_2022_JP_2 = 0x0821, + ISO_2022_JP_1 = 0x0822, + ISO_2022_JP_3 = 0x0823, + ISO_2022_CN = 0x0830, + ISO_2022_CN_EXT = 0x0831, + ISO_2022_KR = 0x0840, + EUC_JP = 0x0920, + EUC_CN = 0x0930, + EUC_TW = 0x0931, + EUC_KR = 0x0940, + ShiftJIS = 0x0A01, + KOI8_R = 0x0A02, + Big5 = 0x0A03, + MacRomanLatin1 = 0x0A04, + HZ_GB_2312 = 0x0A05, + Big5_HKSCS_1999 = 0x0A06, + VISCII = 0x0A07, + KOI8_U = 0x0A08, + Big5_E = 0x0A09, + NextStepJapanese = 0x0B02, + EBCDIC_US = 0x0C01, + EBCDIC_CP037 = 0x0C02, + UTF7 = 0x04000100, + UTF7_IMAP = 0x0A10, + ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead. +} + +@(link_prefix = "CF", default_calling_convention = "c") +foreign CoreFoundation { + // Copies the character contents of a string to a local C string buffer after converting the characters to a given encoding. + StringGetCString :: proc(theString: String, buffer: [^]byte, bufferSize: Index, encoding: StringEncoding) -> b8 --- + + // Returns the number (in terms of UTF-16 code pairs) of Unicode characters in a string. + StringGetLength :: proc(theString: String) -> Index --- + + // Returns the maximum number of bytes a string of a specified length (in Unicode characters) will take up if encoded in a specified encoding. + StringGetMaximumSizeForEncoding :: proc(length: Index, encoding: StringEncoding) -> Index --- + + // Fetches a range of the characters from a string into a byte buffer after converting the characters to a specified encoding. + StringGetBytes :: proc(thestring: String, range: Range, encoding: StringEncoding, lossByte: u8, isExternalRepresentation: b8, buffer: [^]byte, maxBufLen: Index, usedBufLen: ^Index) -> Index --- + + StringIsEncodingAvailable :: proc(encoding: StringEncoding) -> bool --- + + @(link_name = "__CFStringMakeConstantString") + StringMakeConstantString :: proc "c" (#const c: cstring) -> String --- +} + +STR :: StringMakeConstantString + +StringCopyToOdinString :: proc( + theString: String, + allocator := context.allocator, +) -> ( + str: string, + ok: bool, +) #optional_ok { + length := StringGetLength(theString) + max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8)) + + buf, err := make([]byte, max, allocator) + if err != nil do return + + raw_str := runtime.Raw_String { + data = raw_data(buf), + } + StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), max, (^Index)(&raw_str.len)) + + return transmute(string)raw_str, true +} diff --git a/core/sys/darwin/Foundation/NSString.odin b/core/sys/darwin/Foundation/NSString.odin index d3c6c454d..b4918b3fb 100644 --- a/core/sys/darwin/Foundation/NSString.odin +++ b/core/sys/darwin/Foundation/NSString.odin @@ -23,12 +23,9 @@ StringEncoding :: enum UInteger { WindowsCP1250 = 15, ISO2022JP = 21, MacOSRoman = 30, - UTF16 = Unicode, - UTF16BigEndian = 0x90000100, UTF16LittleEndian = 0x94000100, - UTF32 = 0x8c000100, UTF32BigEndian = 0x98000100, UTF32LittleEndian = 0x9c000100, @@ -49,12 +46,9 @@ StringCompareOption :: enum UInteger { unichar :: distinct u16 -@(link_prefix="NS", default_calling_convention="c") -foreign Foundation { - StringFromClass :: proc(cls: Class) -> ^String --- -} - AT :: MakeConstantString + +// CFString is 'toll-free bridged' with its Cocoa Foundation counterpart, NSString. MakeConstantString :: proc "c" (#const c: cstring) -> ^String { foreign Foundation { __CFStringMakeConstantString :: proc "c" (c: cstring) -> ^String --- @@ -62,6 +56,10 @@ MakeConstantString :: proc "c" (#const c: cstring) -> ^String { return __CFStringMakeConstantString(c) } +@(link_prefix="NS", default_calling_convention="c") +foreign Foundation { + StringFromClass :: proc(cls: Class) -> ^String --- +} @(objc_type=String, objc_name="alloc", objc_is_class_method=true) String_alloc :: proc "c" () -> ^String { @@ -73,7 +71,6 @@ String_init :: proc "c" (self: ^String) -> ^String { return msgSend(^String, self, "init") } - @(objc_type=String, objc_name="initWithString") String_initWithString :: proc "c" (self: ^String, other: ^String) -> ^String { return msgSend(^String, self, "initWithString:", other) diff --git a/core/sys/darwin/Security/SecBase.odin b/core/sys/darwin/Security/SecBase.odin new file mode 100644 index 000000000..9cc82d6f5 --- /dev/null +++ b/core/sys/darwin/Security/SecBase.odin @@ -0,0 +1,386 @@ +package Security + +OSStatus :: distinct i32 + +errSec :: enum OSStatus { + Success = 0, // No error. + Unimplemented = -4, // Function or operation not implemented. + DiskFull = -34, // The disk is full. + IO = -36, // I/O error. + OpWr = -49, // File already open with with write permission. + Param = -50, // One or more parameters passed to a function were not valid. + WrPerm = -61, // Write permissions error. + Allocate = -108, // Failed to allocate memory. + UserCanceled = -128, // User canceled the operation. + BadReq = -909, // Bad parameter or invalid state for operation. + InternalComponent = -2070, + CoreFoundationUnknown = -4960, + MissingEntitlement, // A required entitlement isn't present. + RestrictedAPI, // Client is restricted and is not permitted to perform this operation. + NotAvailable = -25291, // No keychain is available. You may need to restart your computer. + ReadOnly = -25292, // This keychain cannot be modified. + AuthFailed = -25293, // The user name or passphrase you entered is not correct. + NoSuchKeychain = -25294, // The specified keychain could not be found. + InvalidKeychain = -25295, // The specified keychain is not a valid keychain file. + DuplicateKeychain = -25296, // A keychain with the same name already exists. + DuplicateCallback = -25297, // The specified callback function is already installed. + InvalidCallback = -25298, // The specified callback function is not valid. + DuplicateItem = -25299, // The specified item already exists in the keychain. + ItemNotFound = -25300, // The specified item could not be found in the keychain. + BufferTooSmall = -25301, // There is not enough memory available to use the specified item. + DataTooLarge = -25302, // This item contains information which is too large or in a format that cannot be displayed. + NoSuchAttr = -25303, // The specified attribute does not exist. + InvalidItemRef = -25304, // The specified item is no longer valid. It may have been deleted from the keychain. + InvalidSearchRef = -25305, // Unable to search the current keychain. + NoSuchClass = -25306, // The specified item does not appear to be a valid keychain item. + NoDefaultKeychain = -25307, // A default keychain could not be found. + InteractionNotAllowed = -25308, // User interaction is not allowed. + ReadOnlyAttr = -25309, // The specified attribute could not be modified. + WrongSecVersion = -25310, // This keychain was created by a different version of the system software and cannot be opened. + KeySizeNotAllowed = -25311, // This item specifies a key size which is too large or too small. + NoStorageModule = -25312, // A required component (data storage module) could not be loaded. You may need to restart your computer. + NoCertificateModule = -25313, // A required component (certificate module) could not be loaded. You may need to restart your computer. + NoPolicyModule = -25314, // A required component (policy module) could not be loaded. You may need to restart your computer. + InteractionRequired = -25315, // User interaction is required, but is currently not allowed. + DataNotAvailable = -25316, // The contents of this item cannot be retrieved. + DataNotModifiable = -25317, // The contents of this item cannot be modified. + CreateChainFailed = -25318, // One or more certificates required to validate this certificate cannot be found. + InvalidPrefsDomain = -25319, // The specified preferences domain is not valid. + InDarkWake = -25320, // In dark wake, no UI possible + ACLNotSimple = -25240, // The specified access control list is not in standard (simple) form. + PolicyNotFound = -25241, // The specified policy cannot be found. + InvalidTrustSetting = -25242, // The specified trust setting is invalid. + NoAccessForItem = -25243, // The specified item has no access control. + InvalidOwnerEdit = -25244, // Invalid attempt to change the owner of this item. + TrustNotAvailable = -25245, // No trust results are available. + UnsupportedFormat = -25256, // Import/Export format unsupported. + UnknownFormat = -25257, // Unknown format in import. + KeyIsSensitive = -25258, // Key material must be wrapped for export. + MultiplePrivKeys = -25259, // An attempt was made to import multiple private keys. + PassphraseRequired = -25260, // Passphrase is required for import/export. + InvalidPasswordRef = -25261, // The password reference was invalid. + InvalidTrustSettings = -25262, // The Trust Settings Record was corrupted. + NoTrustSettings = -25263, // No Trust Settings were found. + Pkcs12VerifyFailure = -25264, // MAC verification failed during PKCS12 import (wrong password?) + NotSigner = -26267, // A certificate was not signed by its proposed parent. + Decode = -26275, // Unable to decode the provided data. + ServiceNotAvailable = -67585, // The required service is not available. + InsufficientClientID = -67586, // The client ID is not correct. + DeviceReset = -67587, // A device reset has occurred. + DeviceFailed = -67588, // A device failure has occurred. + AppleAddAppACLSubject = -67589, // Adding an application ACL subject failed. + ApplePublicKeyIncomplete = -67590, // The public key is incomplete. + AppleSignatureMismatch = -67591, // A signature mismatch has occurred. + AppleInvalidKeyStartDate = -67592, // The specified key has an invalid start date. + AppleInvalidKeyEndDate = -67593, // The specified key has an invalid end date. + ConversionError = -67594, // A conversion error has occurred. + AppleSSLv2Rollback = -67595, // A SSLv2 rollback error has occurred. + QuotaExceeded = -67596, // The quota was exceeded. + FileTooBig = -67597, // The file is too big. + InvalidDatabaseBlob = -67598, // The specified database has an invalid blob. + InvalidKeyBlob = -67599, // The specified database has an invalid key blob. + IncompatibleDatabaseBlob = -67600, // The specified database has an incompatible blob. + IncompatibleKeyBlob = -67601, // The specified database has an incompatible key blob. + HostNameMismatch = -67602, // A host name mismatch has occurred. + UnknownCriticalExtensionFlag = -67603, // There is an unknown critical extension flag. + NoBasicConstraints = -67604, // No basic constraints were found. + NoBasicConstraintsCA = -67605, // No basic CA constraints were found. + InvalidAuthorityKeyID = -67606, // The authority key ID is not valid. + InvalidSubjectKeyID = -67607, // The subject key ID is not valid. + InvalidKeyUsageForPolicy = -67608, // The key usage is not valid for the specified policy. + InvalidExtendedKeyUsage = -67609, // The extended key usage is not valid. + InvalidIDLinkage = -67610, // The ID linkage is not valid. + PathLengthConstraintExceeded = -67611, // The path length constraint was exceeded. + InvalidRoot = -67612, // The root or anchor certificate is not valid. + CRLExpired = -67613, // The CRL has expired. + CRLNotValidYet = -67614, // The CRL is not yet valid. + CRLNotFound = -67615, // The CRL was not found. + CRLServerDown = -67616, // The CRL server is down. + CRLBadURI = -67617, // The CRL has a bad Uniform Resource Identifier. + UnknownCertExtension = -67618, // An unknown certificate extension was encountered. + UnknownCRLExtension = -67619, // An unknown CRL extension was encountered. + CRLNotTrusted = -67620, // The CRL is not trusted. + CRLPolicyFailed = -67621, // The CRL policy failed. + IDPFailure = -67622, // The issuing distribution point was not valid. + SMIMEEmailAddressesNotFound = -67623, // An email address mismatch was encountered. + SMIMEBadExtendedKeyUsage = -67624, // The appropriate extended key usage for SMIME was not found. + SMIMEBadKeyUsage = -67625, // The key usage is not compatible with SMIME. + SMIMEKeyUsageNotCritical = -67626, // The key usage extension is not marked as critical. + SMIMENoEmailAddress = -67627, // No email address was found in the certificate. + SMIMESubjAltNameNotCritical = -67628, // The subject alternative name extension is not marked as critical. + SSLBadExtendedKeyUsage = -67629, // The appropriate extended key usage for SSL was not found. + OCSPBadResponse = -67630, // The OCSP response was incorrect or could not be parsed. + OCSPBadRequest = -67631, // The OCSP request was incorrect or could not be parsed. + OCSPUnavailable = -67632, // OCSP service is unavailable. + OCSPStatusUnrecognized = -67633, // The OCSP server did not recognize this certificate. + EndOfData = -67634, // An end-of-data was detected. + IncompleteCertRevocationCheck = -67635, // An incomplete certificate revocation check occurred. + NetworkFailure = -67636, // A network failure occurred. + OCSPNotTrustedToAnchor = -67637, // The OCSP response was not trusted to a root or anchor certificate. + RecordModified = -67638, // The record was modified. + OCSPSignatureError = -67639, // The OCSP response had an invalid signature. + OCSPNoSigner = -67640, // The OCSP response had no signer. + OCSPResponderMalformedReq = -67641, // The OCSP responder was given a malformed request. + OCSPResponderInternalError = -67642, // The OCSP responder encountered an internal error. + OCSPResponderTryLater = -67643, // The OCSP responder is busy, try again later. + OCSPResponderSignatureRequired = -67644, // The OCSP responder requires a signature. + OCSPResponderUnauthorized = -67645, // The OCSP responder rejected this request as unauthorized. + OCSPResponseNonceMismatch = -67646, // The OCSP response nonce did not match the request. + CodeSigningBadCertChainLength = -67647, // Code signing encountered an incorrect certificate chain length. + CodeSigningNoBasicConstraints = -67648, // Code signing found no basic constraints. + CodeSigningBadPathLengthConstraint = -67649, // Code signing encountered an incorrect path length constraint. + CodeSigningNoExtendedKeyUsage = -67650, // Code signing found no extended key usage. + CodeSigningDevelopment = -67651, // Code signing indicated use of a development-only certificate. + ResourceSignBadCertChainLength = -67652, // Resource signing has encountered an incorrect certificate chain length. + ResourceSignBadExtKeyUsage = -67653, // Resource signing has encountered an error in the extended key usage. + TrustSettingDeny = -67654, // The trust setting for this policy was set to Deny. + InvalidSubjectName = -67655, // An invalid certificate subject name was encountered. + UnknownQualifiedCertStatement = -67656, // An unknown qualified certificate statement was encountered. + MobileMeRequestQueued = -67657, + MobileMeRequestRedirected = -67658, + MobileMeServerError = -67659, + MobileMeServerNotAvailable = -67660, + MobileMeServerAlreadyExists = -67661, + MobileMeServerServiceErr = -67662, + MobileMeRequestAlreadyPending = -67663, + MobileMeNoRequestPending = -67664, + MobileMeCSRVerifyFailure = -67665, + MobileMeFailedConsistencyCheck = -67666, + NotInitialized = -67667, // A function was called without initializing CSSM. + InvalidHandleUsage = -67668, // The CSSM handle does not match with the service type. + PVCReferentNotFound = -67669, // A reference to the calling module was not found in the list of authorized callers. + FunctionIntegrityFail = -67670, // A function address was not within the verified module. + InternalError = -67671, // An internal error has occurred. + MemoryError = -67672, // A memory error has occurred. + InvalidData = -67673, // Invalid data was encountered. + MDSError = -67674, // A Module Directory Service error has occurred. + InvalidPointer = -67675, // An invalid pointer was encountered. + SelfCheckFailed = -67676, // Self-check has failed. + FunctionFailed = -67677, // A function has failed. + ModuleManifestVerifyFailed = -67678, // A module manifest verification failure has occurred. + InvalidGUID = -67679, // An invalid GUID was encountered. + InvalidHandle = -67680, // An invalid handle was encountered. + InvalidDBList = -67681, // An invalid DB list was encountered. + InvalidPassthroughID = -67682, // An invalid passthrough ID was encountered. + InvalidNetworkAddress = -67683, // An invalid network address was encountered. + CRLAlreadySigned = -67684, // The certificate revocation list is already signed. + InvalidNumberOfFields = -67685, // An invalid number of fields were encountered. + VerificationFailure = -67686, // A verification failure occurred. + UnknownTag = -67687, // An unknown tag was encountered. + InvalidSignature = -67688, // An invalid signature was encountered. + InvalidName = -67689, // An invalid name was encountered. + InvalidCertificateRef = -67690, // An invalid certificate reference was encountered. + InvalidCertificateGroup = -67691, // An invalid certificate group was encountered. + TagNotFound = -67692, // The specified tag was not found. + InvalidQuery = -67693, // The specified query was not valid. + InvalidValue = -67694, // An invalid value was detected. + CallbackFailed = -67695, // A callback has failed. + ACLDeleteFailed = -67696, // An ACL delete operation has failed. + ACLReplaceFailed = -67697, // An ACL replace operation has failed. + ACLAddFailed = -67698, // An ACL add operation has failed. + ACLChangeFailed = -67699, // An ACL change operation has failed. + InvalidAccessCredentials = -67700, // Invalid access credentials were encountered. + InvalidRecord = -67701, // An invalid record was encountered. + InvalidACL = -67702, // An invalid ACL was encountered. + InvalidSampleValue = -67703, // An invalid sample value was encountered. + IncompatibleVersion = -67704, // An incompatible version was encountered. + PrivilegeNotGranted = -67705, // The privilege was not granted. + InvalidScope = -67706, // An invalid scope was encountered. + PVCAlreadyConfigured = -67707, // The PVC is already configured. + InvalidPVC = -67708, // An invalid PVC was encountered. + EMMLoadFailed = -67709, // The EMM load has failed. + EMMUnloadFailed = -67710, // The EMM unload has failed. + AddinLoadFailed = -67711, // The add-in load operation has failed. + InvalidKeyRef = -67712, // An invalid key was encountered. + InvalidKeyHierarchy = -67713, // An invalid key hierarchy was encountered. + AddinUnloadFailed = -67714, // The add-in unload operation has failed. + LibraryReferenceNotFound = -67715, // A library reference was not found. + InvalidAddinFunctionTable = -67716, // An invalid add-in function table was encountered. + InvalidServiceMask = -67717, // An invalid service mask was encountered. + ModuleNotLoaded = -67718, // A module was not loaded. + InvalidSubServiceID = -67719, // An invalid subservice ID was encountered. + AttributeNotInContext = -67720, // An attribute was not in the context. + ModuleManagerInitializeFailed = -67721, // A module failed to initialize. + ModuleManagerNotFound = -67722, // A module was not found. + EventNotificationCallbackNotFound = -67723, // An event notification callback was not found. + InputLengthError = -67724, // An input length error was encountered. + OutputLengthError = -67725, // An output length error was encountered. + PrivilegeNotSupported = -67726, // The privilege is not supported. + DeviceError = -67727, // A device error was encountered. + AttachHandleBusy = -67728, // The CSP handle was busy. + NotLoggedIn = -67729, // You are not logged in. + AlgorithmMismatch = -67730, // An algorithm mismatch was encountered. + KeyUsageIncorrect = -67731, // The key usage is incorrect. + KeyBlobTypeIncorrect = -67732, // The key blob type is incorrect. + KeyHeaderInconsistent = -67733, // The key header is inconsistent. + UnsupportedKeyFormat = -67734, // The key header format is not supported. + UnsupportedKeySize = -67735, // The key size is not supported. + InvalidKeyUsageMask = -67736, // The key usage mask is not valid. + UnsupportedKeyUsageMask = -67737, // The key usage mask is not supported. + InvalidKeyAttributeMask = -67738, // The key attribute mask is not valid. + UnsupportedKeyAttributeMask = -67739, // The key attribute mask is not supported. + InvalidKeyLabel = -67740, // The key label is not valid. + UnsupportedKeyLabel = -67741, // The key label is not supported. + InvalidKeyFormat = -67742, // The key format is not valid. + UnsupportedVectorOfBuffers = -67743, // The vector of buffers is not supported. + InvalidInputVector = -67744, // The input vector is not valid. + InvalidOutputVector = -67745, // The output vector is not valid. + InvalidContext = -67746, // An invalid context was encountered. + InvalidAlgorithm = -67747, // An invalid algorithm was encountered. + InvalidAttributeKey = -67748, // A key attribute was not valid. + MissingAttributeKey = -67749, // A key attribute was missing. + InvalidAttributeInitVector = -67750, // An init vector attribute was not valid. + MissingAttributeInitVector = -67751, // An init vector attribute was missing. + InvalidAttributeSalt = -67752, // A salt attribute was not valid. + MissingAttributeSalt = -67753, // A salt attribute was missing. + InvalidAttributePadding = -67754, // A padding attribute was not valid. + MissingAttributePadding = -67755, // A padding attribute was missing. + InvalidAttributeRandom = -67756, // A random number attribute was not valid. + MissingAttributeRandom = -67757, // A random number attribute was missing. + InvalidAttributeSeed = -67758, // A seed attribute was not valid. + MissingAttributeSeed = -67759, // A seed attribute was missing. + InvalidAttributePassphrase = -67760, // A passphrase attribute was not valid. + MissingAttributePassphrase = -67761, // A passphrase attribute was missing. + InvalidAttributeKeyLength = -67762, // A key length attribute was not valid. + MissingAttributeKeyLength = -67763, // A key length attribute was missing. + InvalidAttributeBlockSize = -67764, // A block size attribute was not valid. + MissingAttributeBlockSize = -67765, // A block size attribute was missing. + InvalidAttributeOutputSize = -67766, // An output size attribute was not valid. + MissingAttributeOutputSize = -67767, // An output size attribute was missing. + InvalidAttributeRounds = -67768, // The number of rounds attribute was not valid. + MissingAttributeRounds = -67769, // The number of rounds attribute was missing. + InvalidAlgorithmParms = -67770, // An algorithm parameters attribute was not valid. + MissingAlgorithmParms = -67771, // An algorithm parameters attribute was missing. + InvalidAttributeLabel = -67772, // A label attribute was not valid. + MissingAttributeLabel = -67773, // A label attribute was missing. + InvalidAttributeKeyType = -67774, // A key type attribute was not valid. + MissingAttributeKeyType = -67775, // A key type attribute was missing. + InvalidAttributeMode = -67776, // A mode attribute was not valid. + MissingAttributeMode = -67777, // A mode attribute was missing. + InvalidAttributeEffectiveBits = -67778, // An effective bits attribute was not valid. + MissingAttributeEffectiveBits = -67779, // An effective bits attribute was missing. + InvalidAttributeStartDate = -67780, // A start date attribute was not valid. + MissingAttributeStartDate = -67781, // A start date attribute was missing. + InvalidAttributeEndDate = -67782, // An end date attribute was not valid. + MissingAttributeEndDate = -67783, // An end date attribute was missing. + InvalidAttributeVersion = -67784, // A version attribute was not valid. + MissingAttributeVersion = -67785, // A version attribute was missing. + InvalidAttributePrime = -67786, // A prime attribute was not valid. + MissingAttributePrime = -67787, // A prime attribute was missing. + InvalidAttributeBase = -67788, // A base attribute was not valid. + MissingAttributeBase = -67789, // A base attribute was missing. + InvalidAttributeSubprime = -67790, // A subprime attribute was not valid. + MissingAttributeSubprime = -67791, // A subprime attribute was missing. + InvalidAttributeIterationCount = -67792, // An iteration count attribute was not valid. + MissingAttributeIterationCount = -67793, // An iteration count attribute was missing. + InvalidAttributeDLDBHandle = -67794, // A database handle attribute was not valid. + MissingAttributeDLDBHandle = -67795, // A database handle attribute was missing. + InvalidAttributeAccessCredentials = -67796, // An access credentials attribute was not valid. + MissingAttributeAccessCredentials = -67797, // An access credentials attribute was missing. + InvalidAttributePublicKeyFormat = -67798, // A public key format attribute was not valid. + MissingAttributePublicKeyFormat = -67799, // A public key format attribute was missing. + InvalidAttributePrivateKeyFormat = -67800, // A private key format attribute was not valid. + MissingAttributePrivateKeyFormat = -67801, // A private key format attribute was missing. + InvalidAttributeSymmetricKeyFormat = -67802, // A symmetric key format attribute was not valid. + MissingAttributeSymmetricKeyFormat = -67803, // A symmetric key format attribute was missing. + InvalidAttributeWrappedKeyFormat = -67804, // A wrapped key format attribute was not valid. + MissingAttributeWrappedKeyFormat = -67805, // A wrapped key format attribute was missing. + StagedOperationInProgress = -67806, // A staged operation is in progress. + StagedOperationNotStarted = -67807, // A staged operation was not started. + VerifyFailed = -67808, // A cryptographic verification failure has occurred. + QuerySizeUnknown = -67809, // The query size is unknown. + BlockSizeMismatch = -67810, // A block size mismatch occurred. + PublicKeyInconsistent = -67811, // The public key was inconsistent. + DeviceVerifyFailed = -67812, // A device verification failure has occurred. + InvalidLoginName = -67813, // An invalid login name was detected. + AlreadyLoggedIn = -67814, // The user is already logged in. + InvalidDigestAlgorithm = -67815, // An invalid digest algorithm was detected. + InvalidCRLGroup = -67816, // An invalid CRL group was detected. + CertificateCannotOperate = -67817, // The certificate cannot operate. + CertificateExpired = -67818, // An expired certificate was detected. + CertificateNotValidYet = -67819, // The certificate is not yet valid. + CertificateRevoked = -67820, // The certificate was revoked. + CertificateSuspended = -67821, // The certificate was suspended. + InsufficientCredentials = -67822, // Insufficient credentials were detected. + InvalidAction = -67823, // The action was not valid. + InvalidAuthority = -67824, // The authority was not valid. + VerifyActionFailed = -67825, // A verify action has failed. + InvalidCertAuthority = -67826, // The certificate authority was not valid. + InvalidCRLAuthority = -67827, // The CRL authority was not valid. + InvalidCRLEncoding = -67828, // The CRL encoding was not valid. + InvalidCRLType = -67829, // The CRL type was not valid. + InvalidCRL = -67830, // The CRL was not valid. + InvalidFormType = -67831, // The form type was not valid. + InvalidID = -67832, // The ID was not valid. + InvalidIdentifier = -67833, // The identifier was not valid. + InvalidIndex = -67834, // The index was not valid. + InvalidPolicyIdentifiers = -67835, // The policy identifiers are not valid. + InvalidTimeString = -67836, // The time specified was not valid. + InvalidReason = -67837, // The trust policy reason was not valid. + InvalidRequestInputs = -67838, // The request inputs are not valid. + InvalidResponseVector = -67839, // The response vector was not valid. + InvalidStopOnPolicy = -67840, // The stop-on policy was not valid. + InvalidTuple = -67841, // The tuple was not valid. + MultipleValuesUnsupported = -67842, // Multiple values are not supported. + NotTrusted = -67843, // The certificate was not trusted. + NoDefaultAuthority = -67844, // No default authority was detected. + RejectedForm = -67845, // The trust policy had a rejected form. + RequestLost = -67846, // The request was lost. + RequestRejected = -67847, // The request was rejected. + UnsupportedAddressType = -67848, // The address type is not supported. + UnsupportedService = -67849, // The service is not supported. + InvalidTupleGroup = -67850, // The tuple group was not valid. + InvalidBaseACLs = -67851, // The base ACLs are not valid. + InvalidTupleCredentials = -67852, // The tuple credentials are not valid. + InvalidEncoding = -67853, // The encoding was not valid. + InvalidValidityPeriod = -67854, // The validity period was not valid. + InvalidRequestor = -67855, // The requestor was not valid. + RequestDescriptor = -67856, // The request descriptor was not valid. + InvalidBundleInfo = -67857, // The bundle information was not valid. + InvalidCRLIndex = -67858, // The CRL index was not valid. + NoFieldValues = -67859, // No field values were detected. + UnsupportedFieldFormat = -67860, // The field format is not supported. + UnsupportedIndexInfo = -67861, // The index information is not supported. + UnsupportedLocality = -67862, // The locality is not supported. + UnsupportedNumAttributes = -67863, // The number of attributes is not supported. + UnsupportedNumIndexes = -67864, // The number of indexes is not supported. + UnsupportedNumRecordTypes = -67865, // The number of record types is not supported. + FieldSpecifiedMultiple = -67866, // Too many fields were specified. + IncompatibleFieldFormat = -67867, // The field format was incompatible. + InvalidParsingModule = -67868, // The parsing module was not valid. + DatabaseLocked = -67869, // The database is locked. + DatastoreIsOpen = -67870, // The data store is open. + MissingValue = -67871, // A missing value was detected. + UnsupportedQueryLimits = -67872, // The query limits are not supported. + UnsupportedNumSelectionPreds = -67873, // The number of selection predicates is not supported. + UnsupportedOperator = -67874, // The operator is not supported. + InvalidDBLocation = -67875, // The database location is not valid. + InvalidAccessRequest = -67876, // The access request is not valid. + InvalidIndexInfo = -67877, // The index information is not valid. + InvalidNewOwner = -67878, // The new owner is not valid. + InvalidModifyMode = -67879, // The modify mode is not valid. + MissingRequiredExtension = -67880, // A required certificate extension is missing. + ExtendedKeyUsageNotCritical = -67881, // The extended key usage extension was not marked critical. + TimestampMissing = -67882, // A timestamp was expected but was not found. + TimestampInvalid = -67883, // The timestamp was not valid. + TimestampNotTrusted = -67884, // The timestamp was not trusted. + TimestampServiceNotAvailable = -67885, // The timestamp service is not available. + TimestampBadAlg = -67886, // An unrecognized or unsupported Algorithm Identifier in timestamp. + TimestampBadRequest = -67887, // The timestamp transaction is not permitted or supported. + TimestampBadDataFormat = -67888, // The timestamp data submitted has the wrong format. + TimestampTimeNotAvailable = -67889, // The time source for the Timestamp Authority is not available. + TimestampUnacceptedPolicy = -67890, // The requested policy is not supported by the Timestamp Authority. + TimestampUnacceptedExtension = -67891, // The requested extension is not supported by the Timestamp Authority. + TimestampAddInfoNotAvailable = -67892, // The additional information requested is not available. + TimestampSystemFailure = -67893, // The timestamp request cannot be handled due to system failure. + SigningTimeMissing = -67894, // A signing time was expected but was not found. + TimestampRejection = -67895, // A timestamp transaction was rejected. + TimestampWaiting = -67896, // A timestamp transaction is waiting. + TimestampRevocationWarning = -67897, // A timestamp authority revocation warning was issued. + TimestampRevocationNotification = -67898, // A timestamp authority revocation notification was issued. + CertificatePolicyNotAllowed = -67899, // The requested policy is not allowed for this certificate. + CertificateNameNotAllowed = -67900, // The requested name is not allowed for this certificate. + CertificateValidityPeriodTooLong = -67901, // The validity period in the certificate exceeds the maximum allowed. + CertificateIsCA = -67902, // The verified certificate is a CA rather than an end-entity. + CertificateDuplicateExtension = -67903, // The certificate contains multiple extensions with the same extension ID. +} diff --git a/core/sys/darwin/Security/SecRandom.odin b/core/sys/darwin/Security/SecRandom.odin new file mode 100644 index 000000000..0527baca1 --- /dev/null +++ b/core/sys/darwin/Security/SecRandom.odin @@ -0,0 +1,19 @@ +package Security + +import CF "core:sys/darwin/CoreFoundation" + +foreign import Security "system:Security.framework" + +// A reference to a random number generator. +RandomRef :: distinct rawptr + +@(link_prefix="Sec", default_calling_convention="c") +foreign Security { + // Default random ref for /dev/random. Synonym for nil. + @(link_name="kSecRandomDefault") kSecRandomDefault: RandomRef + + // Generates an array of cryptographically secure random bytes. + RandomCopyBytes :: proc(rnd: RandomRef = kSecRandomDefault, count: uint, bytes: [^]byte) -> errSec --- + + CopyErrorMessageString :: proc(status: errSec, reserved: rawptr = nil) -> CF.String --- +} \ No newline at end of file diff --git a/core/sys/darwin/core_foundation.odin b/core/sys/darwin/core_foundation.odin deleted file mode 100644 index 325122216..000000000 --- a/core/sys/darwin/core_foundation.odin +++ /dev/null @@ -1,98 +0,0 @@ -//+build darwin -package darwin - -import "base:runtime" - -foreign import core_foundation "system:CoreFoundation.framework" - -CFTypeRef :: distinct rawptr - -CFStringRef :: distinct CFTypeRef - -CFIndex :: int - -CFRange :: struct { - location: CFIndex, - length: CFIndex, -} - -CFStringEncoding :: enum u32 { - ASCII = 1, - NEXTSTEP = 2, - JapaneseEUC = 3, - UTF8 = 4, - ISOLatin1 = 5, - Symbol = 6, - NonLossyASCII = 7, - ShiftJIS = 8, - ISOLatin2 = 9, - Unicode = 10, - WindowsCP1251 = 11, - WindowsCP1252 = 12, - WindowsCP1253 = 13, - WindowsCP1254 = 14, - WindowsCP1250 = 15, - ISO2022JP = 21, - MacOSRoman = 30, - - UTF16 = Unicode, - - UTF16BigEndian = 0x90000100, - UTF16LittleEndian = 0x94000100, - - UTF32 = 0x8c000100, - UTF32BigEndian = 0x98000100, - UTF32LittleEndian = 0x9c000100, -} - -foreign core_foundation { - // Copies the character contents of a string to a local C string buffer after converting the characters to a given encoding. - CFStringGetCString :: proc(theString: CFStringRef, buffer: [^]byte, bufferSize: CFIndex, encoding: CFStringEncoding) -> Bool --- - - // Returns the number (in terms of UTF-16 code pairs) of Unicode characters in a string. - CFStringGetLength :: proc(theString: CFStringRef) -> CFIndex --- - - // Returns the maximum number of bytes a string of a specified length (in Unicode characters) will take up if encoded in a specified encoding. - CFStringGetMaximumSizeForEncoding :: proc(length: CFIndex, encoding: CFStringEncoding) -> CFIndex --- - - // Fetches a range of the characters from a string into a byte buffer after converting the characters to a specified encoding. - CFStringGetBytes :: proc( - thestring: CFStringRef, - range: CFRange, - encoding: CFStringEncoding, - lossByte: u8, - isExternalRepresentation: Bool, - buffer: [^]byte, - maxBufLen: CFIndex, - usedBufLen: ^CFIndex, - ) -> CFIndex --- - - // Releases a Core Foundation object. - @(link_name="CFRelease") - _CFRelease :: proc(cf: CFTypeRef) --- -} - -// Releases a Core Foundation object. -CFRelease :: proc { - CFReleaseString, -} - -// Releases a Core Foundation string. -CFReleaseString :: #force_inline proc(theString: CFStringRef) { - _CFRelease(CFTypeRef(theString)) -} - -CFStringCopyToOdinString :: proc(theString: CFStringRef, allocator := context.allocator) -> (str: string, ok: bool) #optional_ok { - length := CFStringGetLength(theString) - max := CFStringGetMaximumSizeForEncoding(length, .UTF8) - - buf, err := make([]byte, max, allocator) - if err != nil { return } - - raw_str := runtime.Raw_String{ - data = raw_data(buf), - } - CFStringGetBytes(theString, {0, length}, .UTF8, 0, false, raw_data(buf), max, &raw_str.len) - - return transmute(string)raw_str, true -} diff --git a/core/sys/darwin/security.odin b/core/sys/darwin/security.odin deleted file mode 100644 index 0c58260e7..000000000 --- a/core/sys/darwin/security.odin +++ /dev/null @@ -1,26 +0,0 @@ -//+build darwin -package darwin - -foreign import security "system:Security.framework" - -// A reference to a random number generator. -SecRandomRef :: distinct rawptr - -OSStatus :: distinct i32 - -errSec :: enum OSStatus { - Success = 0, // No error. - Unimplemented = -4, // Function or operation not implemented. - - // Many more... -} - -foreign security { - // Synonym for nil, uses a cryptographically secure random number generator. - kSecRandomDefault: SecRandomRef - - // Generates an array of cryptographically secure random bytes. - SecRandomCopyBytes :: proc(rnd: SecRandomRef = kSecRandomDefault, count: uint, bytes: [^]byte) -> errSec --- - - SecCopyErrorMessageString :: proc(status: errSec, reserved: rawptr = nil) -> CFStringRef --- -} diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index b2736ffcd..1f6d337e8 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -7,7 +7,7 @@ import "core:os" import "core:thread" import "core:time" import "core:reflect" -import "core:runtime" +import "base:runtime" import "core:intrinsics" import "core:math/big" From d2ca91b830b8c57396e65c3ada846fee188a83c7 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 13 Apr 2024 19:32:22 +0200 Subject: [PATCH 69/73] fix wasm runtime.js storeString to support Unicode --- vendor/wasm/js/runtime.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index 2e69a28c7..320d74d68 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -104,9 +104,12 @@ class WasmMemoryInterface { storeInt(addr, value) { this.mem.setInt32 (addr, value, true); } storeUint(addr, value) { this.mem.setUint32 (addr, value, true); } + // Returned length might not be the same as `value.length` if non-ascii strings are given. storeString(addr, value) { - const bytes = this.loadBytes(addr, value.length); - new TextEncoder().encodeInto(value, bytes); + const src = new TextEncoder().encode(value); + const dst = new Uint8Array(this.memory.buffer, addr, src.length); + dst.set(src); + return src.length; } }; From 9f97056c14990c4e8645c8aaa5a7432e9fdf5102 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 13 Apr 2024 20:08:10 +0200 Subject: [PATCH 70/73] add 'odin root' command --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 36a99ec32..063b6c8b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -212,6 +212,7 @@ gb_internal void usage(String argv0) { print_usage_line(1, "doc Generates documentation on a directory of .odin files."); print_usage_line(1, "version Prints version."); print_usage_line(1, "report Prints information useful to reporting a bug."); + print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections."); print_usage_line(0, ""); print_usage_line(0, "For further details on a command, invoke command help:"); print_usage_line(1, "e.g. `odin build -help` or `odin help build`"); @@ -2572,6 +2573,9 @@ int main(int arg_count, char const **arg_ptr) { print_show_help(args[0], args[2]); return 0; } + } else if (command == "root") { + gb_printf("%.*s", LIT(odin_root_dir())); + return 0; } else { usage(args[0]); return 1; From f252084b1f04028cf94c77662fbfefa4cf0987f1 Mon Sep 17 00:00:00 2001 From: wrapperup Date: Sat, 13 Apr 2024 15:05:51 -0400 Subject: [PATCH 71/73] add dxc to all_vendor.odin --- examples/all/all_vendor.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 9093e0f3e..1ab1debea 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -73,10 +73,12 @@ _ :: MTK _ :: CA +import DXC "vendor:directx/dxc" import D3D11 "vendor:directx/d3d11" import D3D12 "vendor:directx/d3d12" import DXGI "vendor:directx/dxgi" +_ :: DXC _ :: D3D11 _ :: D3D12 _ :: DXGI From b22e43c335bd48df1f4d4016c0c8d83db08794ca Mon Sep 17 00:00:00 2001 From: wrapperup Date: Sat, 13 Apr 2024 15:27:02 -0400 Subject: [PATCH 72/73] add freebsd, openbsd to dxcdef_unix.odin --- vendor/directx/dxc/dxcdef_unix.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/directx/dxc/dxcdef_unix.odin b/vendor/directx/dxc/dxcdef_unix.odin index 649bb8a13..12a682310 100644 --- a/vendor/directx/dxc/dxcdef_unix.odin +++ b/vendor/directx/dxc/dxcdef_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin +//+build linux, darwin, freebsd, openbsd package directx_dxc import "core:c" From 9591eb2ed582205c4ed0f47ad7f45eab82f5fb75 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 14 Apr 2024 16:41:10 +0200 Subject: [PATCH 73/73] Tell Git to always treat Makefile and build_odin.sh as Nix EOL --- .gitattributes | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 0178b8a8f..e58996311 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,6 @@ *.odin linguist-language=Odin -* text=auto \ No newline at end of file +* text=auto + +# These files must always have *nix line-endings +Makefile text eol=lf +*.sh text eol=lf \ No newline at end of file