├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── build.zig └── precompiled └── libcrypto.a /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .zig-cache 3 | zig-out 4 | generated 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "boringssl"] 2 | path = boringssl 3 | url = https://github.com/google/boringssl.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The following only applies to the build script. 2 | BoringSSL's license can be found in the `boringssl/LICENSE` file. 3 | 4 | --- 5 | 6 | MIT License 7 | 8 | Copyright (c) 2023-2025 Frank Denis 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BoringSSL (boringcrypto) for WebAssembly 2 | 3 | A zig build file to compile BoringSSL's `libcrypto` to WebAssembly/WASI. 4 | 5 | ## Precompiled library 6 | 7 | For convenience, a [precompiled library](precompiled/libcrypto.a) for WebAssembly can be directly downloaded from this repository. 8 | 9 | ## Dependencies 10 | 11 | The only required dependencies to rebuild the library are: 12 | 13 | * [Go](https://www.golang.org) - Required by BoringSSL to generate the error codes map 14 | * [Zig](https://www.ziglang.org) - To compile C/C++ code to WebAssembly 15 | 16 | ## BoringSSL submodule 17 | 18 | This repository includes an unmodified version of `BoringSSL` as a submodule. If you didn't clone it with the `--recursive` flag, the following command can be used to pull the submodule: 19 | 20 | ```sh 21 | git submodule update --init --recursive --depth=1 22 | ``` 23 | 24 | ## Building the BoringSSL crypto library for WebAssembly 25 | 26 | Generic build for WebAssembly/WASI: 27 | 28 | ```sh 29 | zig build -Doptimize=ReleaseFast 30 | ``` 31 | 32 | The resulting static library is put into `zig-out/libcrypto.a`. 33 | 34 | Build modes: 35 | 36 | * `-Doptimize=ReleaseFast` 37 | * `-Doptimize=ReleaseSafe` 38 | * `-Doptimize-ReleaseSmall` (also turns `OPENSSL_SMALL` on to disable precomputed tables) 39 | * `-Doptimize=Debug` (default, not recommended in production builds) 40 | 41 | The library is compatible with the vast majority of WebAssembly runtimes, and can be used linked with C, Zig, Rust, whatever. 42 | 43 | Optimizations only compatible with some runtimes can be also enabled: 44 | 45 | ```sh 46 | zig build -Dtarget=wasm32-wasi -Drelease-fast \ 47 | -Dcpu=generic+simd128+multivalue+bulk_memory 48 | ``` 49 | 50 | Possibly relevant extensions: 51 | 52 | * `bulk_memory` 53 | * `exception_handling` 54 | * `multivalue` 55 | * `sign_ext` 56 | * `simd128` 57 | * `tail_call` 58 | 59 | ## Cross-compiling to other targets 60 | 61 | The build file can be used to cross-compile to other targets as well. However, assembly implementations will not be included. 62 | 63 | ### Examples 64 | 65 | Compile to the native architecture: 66 | 67 | ```sh 68 | zig build -Dtarget=native -Drelease-fast 69 | ``` 70 | 71 | Cross-compile to `x86_64-linux`: 72 | 73 | ```sh 74 | zig build -Dtarget=x86_64-linux -Drelease-small 75 | ``` 76 | 77 | Cross-compile to Apple Silicon: 78 | 79 | ```sh 80 | zig build -Dtarget=aarch64-macos -Drelease-safe 81 | ``` 82 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const fs = std.fs; 3 | const heap = std.heap; 4 | const mem = std.mem; 5 | const ArrayList = std.ArrayList; 6 | const Compile = std.Build.Step.Compile; 7 | 8 | const path_boringssl = "boringssl"; 9 | 10 | fn withBase(alloc: mem.Allocator, base: []const u8, name: []const u8) !ArrayList(u8) { 11 | var path = ArrayList(u8).init(alloc); 12 | try path.appendSlice(base); 13 | try path.append(fs.path.sep); 14 | try path.appendSlice(name); 15 | return path; 16 | } 17 | 18 | fn buildErrData(b: *std.Build, alloc: mem.Allocator, lib: *Compile, base: []const u8) !void { 19 | const out_name = "err_data_generate.c"; 20 | 21 | var dir = try fs.cwd().makeOpenPath(base, .{}); 22 | defer dir.close(); 23 | var fd = try dir.createFile(out_name, .{}); 24 | defer fd.close(); 25 | 26 | var arena = heap.ArenaAllocator.init(alloc); 27 | defer arena.deinit(); 28 | { 29 | var child = std.process.Child.init( 30 | &.{ "go", "build" }, 31 | arena.allocator(), 32 | ); 33 | child.cwd = path_boringssl ++ "/util/pregenerate"; 34 | child.stdout_behavior = .Pipe; 35 | try child.spawn(); 36 | _ = try child.wait(); 37 | } 38 | { 39 | var child = std.process.Child.init( 40 | &.{"util/pregenerate/pregenerate"}, 41 | arena.allocator(), 42 | ); 43 | child.cwd = path_boringssl; 44 | child.stdout_behavior = .Pipe; 45 | try child.spawn(); 46 | try fd.writeFileAll(child.stdout.?, .{}); 47 | _ = try child.wait(); 48 | } 49 | 50 | const path = try withBase(alloc, base, out_name); 51 | defer path.deinit(); 52 | lib.addCSourceFile(.{ .file = b.path(path.items), .flags = &.{} }); 53 | } 54 | 55 | fn addDir(b: *std.Build, alloc: mem.Allocator, lib: *Compile, base: []const u8) !void { 56 | var dir = try fs.cwd().openDir(base, .{ .iterate = true }); 57 | defer dir.close(); 58 | var it = dir.iterate(); 59 | while (try it.next()) |file| { 60 | if (file.kind == .directory) { 61 | if (mem.eql(u8, fs.path.extension(file.name), "test") or 62 | mem.eql(u8, fs.path.extension(file.name), "asm")) 63 | { 64 | continue; 65 | } 66 | const path = try withBase(alloc, base, file.name); 67 | defer path.deinit(); 68 | try addDir(b, alloc, lib, path.items); 69 | } 70 | if (file.kind != .file or !mem.eql(u8, fs.path.extension(file.name), ".c")) { 71 | continue; 72 | } 73 | const path = try withBase(alloc, base, file.name); 74 | defer path.deinit(); 75 | lib.addCSourceFile(.{ .file = b.path(path.items), .flags = &.{} }); 76 | } 77 | } 78 | 79 | fn addSubdirs(b: *std.Build, alloc: mem.Allocator, lib: *Compile, base: []const u8) !void { 80 | var dir = try fs.cwd().openDir(base, .{ .iterate = true }); 81 | defer dir.close(); 82 | var it = dir.iterate(); 83 | while (try it.next()) |file| { 84 | if (file.kind != .directory) { 85 | continue; 86 | } 87 | const path = try withBase(alloc, base, file.name); 88 | defer path.deinit(); 89 | try addDir(b, alloc, lib, path.items); 90 | } 91 | } 92 | 93 | pub fn build(b: *std.Build) !void { 94 | var gpa = heap.GeneralPurposeAllocator(.{}){}; 95 | defer _ = gpa.deinit(); 96 | 97 | const optimize = b.standardOptimizeOption(.{}); 98 | const target = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = .wasm32, .os_tag = .wasi } }); 99 | 100 | const lib = b.addStaticLibrary(.{ 101 | .name = "crypto", 102 | .optimize = optimize, 103 | .target = target, 104 | .strip = true, 105 | }); 106 | lib.linkLibC(); 107 | b.installArtifact(lib); 108 | if (optimize == .ReleaseSmall) { 109 | lib.root_module.addCMacro("OPENSSL_SMALL", ""); 110 | } 111 | 112 | lib.root_module.addCMacro("ARCH", "generic"); 113 | lib.root_module.addCMacro("OPENSSL_NO_ASM", ""); 114 | 115 | if (target.result.os.tag == .wasi) { 116 | lib.root_module.addCMacro("OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED", ""); 117 | lib.root_module.addCMacro("SO_KEEPALIVE", "0"); 118 | lib.root_module.addCMacro("SO_ERROR", "0"); 119 | lib.root_module.addCMacro("FREEBSD_GETRANDOM", ""); 120 | lib.root_module.addCMacro("getrandom(a,b,c)", "getentropy(a,b)|b"); 121 | lib.root_module.addCMacro("socket(a,b,c)", "-1"); 122 | lib.root_module.addCMacro("setsockopt(a,b,c,d,e)", "-1"); 123 | lib.root_module.addCMacro("connect(a,b,c)", "-1"); 124 | lib.root_module.addCMacro("GRND_NONBLOCK", "0"); 125 | } 126 | 127 | lib.addIncludePath(b.path(path_boringssl ++ fs.path.sep_str ++ "include")); 128 | const base_crypto = path_boringssl ++ fs.path.sep_str ++ "crypto"; 129 | const base_decrepit = path_boringssl ++ fs.path.sep_str ++ "decrepit"; 130 | const base_generated = "generated"; 131 | try buildErrData(b, gpa.allocator(), lib, base_generated); 132 | try addDir(b, gpa.allocator(), lib, base_crypto); 133 | try addDir(b, gpa.allocator(), lib, base_decrepit); 134 | try addSubdirs(b, gpa.allocator(), lib, base_crypto); 135 | } 136 | -------------------------------------------------------------------------------- /precompiled/libcrypto.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jedisct1/boringssl-wasm/7cddfda77f923db25ac636264f961ec680bba4f2/precompiled/libcrypto.a --------------------------------------------------------------------------------