pax_global_header00006660000000000000000000000064151467340020014514gustar00rootroot0000000000000052 comment=ba45204806daeb5b1b249d7b35ca0035cba3d641 liboprf-0.9.4/000077500000000000000000000000001514673400200131635ustar00rootroot00000000000000liboprf-0.9.4/.github/000077500000000000000000000000001514673400200145235ustar00rootroot00000000000000liboprf-0.9.4/.github/workflows/000077500000000000000000000000001514673400200165605ustar00rootroot00000000000000liboprf-0.9.4/.github/workflows/codeql-analysis.yml000066400000000000000000000025611514673400200223770ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [master] pull_request: # The branches below must be a subset of the branches above branches: [master] schedule: - cron: '0 3 * * 2' jobs: analyze: name: Analyze runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) #- name: Autobuild # uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # πŸ“š https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language - run: | sudo apt update sudo apt install -y libsodium-dev pkgconf # build-essential git # main liboprf cd src make test - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 liboprf-0.9.4/.github/workflows/python-app.yml000066400000000000000000000025111514673400200214010ustar00rootroot00000000000000# This workflow will install Python dependencies, run tests and lint with a single version of Python # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python name: Python application on: push: branches: [ "master" ] pull_request: branches: [ "master" ] permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.10 uses: actions/setup-python@v3 with: python-version: "3.10" - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 pytest sudo apt update sudo apt install -y libsodium-dev pkgconf # build-essential git pip install python/ pysodium SecureString cd src sudo PREFIX=/usr make install cd .. - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | pytest -s -v python/tests/test.py liboprf-0.9.4/.gitignore000066400000000000000000000002451514673400200151540ustar00rootroot00000000000000liboprf*.a *.o .arch bench *.pdf matrices liboprf.so dkg thmult toprf tuokms uokms attack tp-dkg python/tests/__pycache__/ src/liboprf-corrupt-dkg.so src/liboprf.pc liboprf-0.9.4/LICENSE000066400000000000000000000167441514673400200142040ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. liboprf-0.9.4/README.md000066400000000000000000000104521514673400200144440ustar00rootroot00000000000000# liboprf ## Overview liboprf is a library for Oblivious Pseudorandom Functions (OPRFs), including support for Threshold OPRFs. It is designed to make advanced cryptographic protocols easy to integrate across applications. ## What is an OPRF? An Oblivious Pseudorandom Function (OPRF) is a two-party cryptographic primitive involving a sender and receiver who jointly compute a function, `F`, in such a way that: - The sender holds a secret key `k` - The receiver provides an input `x` - The receiver learns `F(k, x)` but nothing about `k` - The sender learns nothing about `x` or `F(k, x)` OPRFs are the foundation for many privacy-preserving protocols including: - Password-based authentication without exposing passwords - Private set intersection, which allows two parties to find the intersection of their private sets without revealing the full sets - Privacy-preserving information retrieval, allowing users to get specific information from a database without revealing what information is being retrieved ## Features ### Basic OPRF liboprf implements the basic OPRF(ristretto255, SHA-512) variant from the [IRTF CFRG Draft](https://github.com/cfrg/draft-irtf-cfrg-voprf/), "Oblivious Pseudorandom Functions (OPRFs) using Prime-Order Groups". ### Threshold OPRF liboprf implements a threshold OPRF variant based on [Krawczyk et al. (2017)](https://eprint.iacr.org/2017/363) which is compatible with the [CFRG OPRF(ristretto255, SHA-512) variant](#basic-oprf). A threshold implementation distributes trust among multiple servers, requiring a minimum number (threshold) to cooperate for operation. It uses Distributed Key Generation (DKG) protocols, as described below, to distribute secret key shares among multiple servers. ### 3hashTDH This library also implements the 3hashTDH from [Gu, Jarecki, Kedzior, Nazarian, Xu (2024)](https://eprint.iacr.org/2024/1455) "Threshold PAKE with Security against Compromise of all Servers". This implementation is compatible with the aforementioned [IRTF CFRG OPRF(ristretto255, SHA-512)](#basic-oprf) variant. ### Distributed Key Generation (DKG) For the [threshold OPRF](#threshold-oprf), liboprf provides: - **Trusted Party DKG**: An implementation based on Joint Feldman DKG (JF-DKG) from the paper "[Secure Distributed Key Generation for Discrete-Log Based Cryptosystems](https://link.springer.com/article/10.1007/s00145-006-0347-3)" by R. Gennaro, S. Jarecki, Hugo Krawczyk & T. Rabin. - **Semi-trusted DKG**: Implements Fast-Track Joint Verifiable Secret Sharing (FT-Joint-DL-VSS) described in R. Gennaro, M. O. Rabin, and T. Rabin, "[Simplified VSS and fast-track multiparty computations with applications to threshold cryptography](https://dl.acm.org/doi/10.1145/277697.277716)" In B. A. Coan and Y. Afek, editors, 17th ACM PODC, pages 101–111. ACM, June/July 1998. ### Threshold OPRF Updates To update a threshold OPRF instantiation, liboprf contains multi-party multiplication described in R. Gennaro, M. O. Rabin, and T. Rabin, "[Simplified VSS and fast-track multiparty computations with applications to threshold cryptography](https://dl.acm.org/doi/10.1145/277697.277716)" In B. A. Coan and Y. Afek, editors, 17th ACM PODC, pages 101–111. ACM, June/July 1998. ## Installation ### Dependencies - **libsodium**: You must install [libsodium](https://github.com/jedisct1/libsodium) first. libsodium is a cryptographic library that provides a range of cryptographic operations including encryption, decryption, digital signatures, and secure password hashing. - **pkgconf**: Needed for building the library. ### Building from source ```bash git clone https://github.com/stef/liboprf.git cd liboprf/src make sudo make install ``` ### Python Wrapper A Python wrapper, `pyoprf`, is provided. Look at [its README](/python/README.md) for installation and usage instructions. ## Funding This project is funded through [NGI0 Entrust](https://nlnet.nl/entrust), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/ThresholdOPRF). [NLnet foundation logo](https://nlnet.nl) [NGI Zero Logo](https://nlnet.nl/entrust) liboprf-0.9.4/build.zig000066400000000000000000000052561514673400200150050ustar00rootroot00000000000000const std = @import("std"); const fmt = std.fmt; const fs = std.fs; const heap = std.heap; const mem = std.mem; const Compile = std.Build.Step.Compile; fn initLibConfig(b: *std.Build, lib: *Compile) void { lib.linkLibC(); lib.addIncludePath(b.path("src/")); lib.addIncludePath(b.path("src/noise_xk/include")); lib.addIncludePath(b.path("src/noise_xk/include/karmel")); lib.addIncludePath(b.path("src/noise_xk/include/karmel/minimal")); //lib.want_lto = false; } pub fn build(b: *std.Build) !void { const root_path = b.pathFromRoot("."); var cwd = try fs.openDirAbsolute(root_path, .{}); defer cwd.close(); const src_path = "src/"; const src_dir = try fs.Dir.openDir(cwd, src_path, .{ .iterate = true, .no_follow = true }); const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const static_lib = b.addLibrary(.{ .name = "liboprf", .linkage = .static, .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), }); const libsodium_package = b.dependency("libsodium", .{ .target = target, .optimize = optimize, .@"test" = false, // `test` is a keyword in zig .static = true, .shared = false }); static_lib.linkLibrary(libsodium_package.artifact("sodium")); static_lib.addIncludePath(libsodium_package.path("include")); b.installArtifact(static_lib); initLibConfig(b, static_lib); const flags = &.{ "-fvisibility=hidden", "-fPIC", "-fwrapv", }; static_lib.installHeadersDirectory(b.path(src_path ++ "/noise_xk/include"), "oprf/noise_xk", .{}); const allocator = heap.page_allocator; var walker = try src_dir.walk(allocator); while (try walker.next()) |entry| { if(mem.startsWith(u8, entry.path, "tests")) continue; const name = entry.basename; if(mem.eql(u8, name, "xk-ex.c")) continue; if(mem.eql(u8, name, "jni.c")) continue; if (mem.endsWith(u8, name, ".c")) { const full_path = try fmt.allocPrint(allocator, "{s}/{s}", .{ src_path, entry.path }); static_lib.addCSourceFile(.{ .file = b.path(full_path), .flags = flags, }); } else if (mem.endsWith(u8, name, ".h")) { const full_path = try fmt.allocPrint(allocator, "{s}/{s}", .{ src_path, entry.path }); if(!mem.startsWith(u8, entry.path, "noise_xk")) { const full_dest = try fmt.allocPrint(allocator, "oprf/{s}", .{ name }); static_lib.installHeader(b.path(full_path), full_dest); } } } } liboprf-0.9.4/build.zig.zon000066400000000000000000000030261514673400200156030ustar00rootroot00000000000000.{ // This is the default name used by packages depending on this one. For // example, when a user runs `zig fetch --save `, this field is used // as the key in the `dependencies` table. Although the user can choose a // different name, most users will stick with this provided value. // // It is redundant to include "zig" in this name because it is already // within the Zig package namespace. .name = .liboprf, // This is a [Semantic Version](https://semver.org/). // In a future version of Zig it will be used for package deduplication. .version = "0.9.1", .minimum_zig_version = "0.14.1", .fingerprint = 0x931fd34c8830d0d8, // This field is optional. // This is currently advisory only; Zig does not yet do anything // with this value. //.minimum_zig_version = "0.11.0", // This field is optional. // Each dependency must either provide a `url` and `hash`, or a `path`. // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. // Once all dependencies are fetched, `zig build` no longer requires // internet connectivity. .dependencies = .{ .libsodium = .{ .url = "git+https://github.com/jedisct1/libsodium.git#09e995c0c85a0026510704b8ce7f5867a09a3841", .hash = "N-V-__8AAONJYQCM2EvfTRcbTy_p9_e6hTjX34KAk0MItmwe", }, }, .paths = .{ "build.zig", "build.zig.zon", "src", // For example... //"LICENSE", //"README.md", }, } liboprf-0.9.4/docs/000077500000000000000000000000001514673400200141135ustar00rootroot00000000000000liboprf-0.9.4/docs/stp-dkg.txt000066400000000000000000001004451514673400200162310ustar00rootroot00000000000000Semi-Trusted Party (STP) Distributed Key Generation (DKG) This document specifies a proposal for a robust DKG that can work for small deployments with a small number of parties and infrequent DKG executions. Robust means that the protocol even succeeds if some parties cheat and this is detected, but no party aborts. If someone aborts then the protocol needs to run again, possibly after kicking out misbehaving parties. This protocol does support maximum 127 peers. This is probably already too much for a non-robust protocol, but it might work in very special circumstances. Broadcast is implemented by the semi-trusted party (STP) opening a channel to each peer secured by the peers long-term encryption key. Every message - both broadcast and private - are routed through the STP. Peer long-term encryption keys can be either TLS-based, or Noise_XK-based (https://noiseexplorer.com/patterns/XK/). In the latter case the long-term public keys must be known and validated in advance by the STP. Peer long-term signature keys must be known by all participating parties. The basis for this protocol is based on the FT-Joint-DL-VSS (fig 7.) protocol from [GRR98]. ------<=[ Rationale ]=>----------- Traditionally DKGs are used in setting where all parties are equal and are using the distributed key together, without having any one party having a different role in the protocol utilizing the shared key. This does not translate entirely to threshold OPRFs (tOPRF) and protocols based on these. In an OPRF there is normally two parties, one holding the key, and another one holding the input and learning the output. In a tOPRF the party holding the key is a group of peers that hold shares of the key in a threshold setting. In a special case, that of updatable threshold OPRFs the updating might be done by a semi-trusted 3rd party. In that case the semi-trusted 3rd party is merely honest-but-curious, but unable to learn anything about the input nor really the output of the OPRF, while being able to update the key of the OPRF. This can be handy for automated, regular key-updates. For updating the key, the participants must generate a new key, and this can be orchestrated by a STP acting as the broadcast and general communication medium between the parties. ------<=[ Difference to the [GRR98] paper ]=>----------- In the original paper fig. 7 describing the FT-Joint-DL-VSS protocol, does not explicitly check whether the 𝓗(Ξ±_i,ρ_i) commitments match those broadcast. However the algorithm New-VSS from fig. 1 does so. And since FT-Joint-DL-VSS is supposedly based on New-VSS we do check the commitment, broadcast and check complaints and dealers of verified complaints are disqualified. P_i after generating C_ij and share s_ij,r_ij, broadcasts a hash of all (concatenated) values C_i1,...,C_in. After broadcasting C_ij and sharing s_ij,r_ij, before checking VSPS all participants check that the hash value broadcast in Round 1 by P_i, for i=1,...,n, corresponds to the C_ij values broadcast by P_i in Round 2. If this is not the case, P_j aborts. ------<=[ Prototocol Phases ]=>----------- The protocol has the following phases: 1. Initialization and introduction: announcement of parameters n, t, long-term signing public keys of all participants, their ordering, and encryption public keys, Establishment of a jointly generated session id. 2. AKE - Setup secure P2P channels: to establish protected channels between all peers. 3. core DKG 4. Complaint analysis: In case of invalid commitments establishment of identity of cheaters. 5. Final verification of transcript and completion of protocol ------<=[ Simplified API ]=>----------- Since the protocol consists of many steps, the API is abstracted to the following schema: 0. Initialize While not done and not error: 1. Allocate input buffer(s) 2. input = receive() 3. allocate output buffer 4. run next step of protocol 5. if there is output: send(output) 6. Post-processing This simple schema simplifies the load of an implementer using this protocol, while at the same time reducing opportunities for errors and provides strict security. It also allows full abstraction of the underlying communication media. The reference implementation in stp-dkg.c follows this schema for both the STP and the peers. ------<=[ Protocol transcript ]=>----------- Transcript - all broadcast messages are accumulated into a transcript by each peer and the semi-trusted party, at the end of the protocol all parties except for the STP publish their signed transcripts and only if all signatures are correct and the transcripts match, is the protocol successful. The STP uses its own transcript to learn the of the parties agreement. The transcript is a hash, which is initialized with the string: "stp vss dkg session transcript" in pseudo-code: transcript_state = hash_init("stp vss dkg session transcript") Updating the transcript first updates the hash with the canonical 32bit size of the message to be added to the transcript, then the message itself is added to the hash. transcript_state = hash_update(transcript_state, I2OSP(len(msg)) transcript_state = hash_update(transcript_state, msg) The signature of each message is similarly added to the transcript. A function `update_ts` can be used as a high-level interface to updating the transcript with messages and their signatures: ``` update_ts(state,msg,sig) state = hash_update(state, I2OSP(len(msg)) state = hash_update(state, msg) state = hash_update(state, I2OSP(len(sig)) state = hash_update(state, sig) return state ``` ------<=[ Session id ]=>----------- Every execution of the protocol starts by the participants establishing a unique and fresh session id, this is to ensure that no messages can be replayed. The session id is a 256 bit (32B) random value of cryptographic quality entropy. Each peer P_i chooses a 256 bit nonce, signs it, broadcast it. Then everyone (including the STP) verifies the signatures, aborts if any signatures fail. And then everyone uses the hash of the concatenation of all nonces as the session identifier. The session_id is established as early as possible in the first (initialization) phase of protocol. The STP learns (and starts using) it in step 2, and the peers verify if it is correct and start using it in step 3. Every message sent after step 3 MUST contain a valid session_id. ``` nonce_i = random_bytes(32) signed_nonce_i = sign(i | nonce, ltsigkey_i) broadcast(signed_nonce_i) acc = "" for i in 1..n: signed_nonce_i = recv(i) i', nonce_i = verify(signed_nonce_i, ltsigpub_i) or abort() assert i == i' acc = acc | nonce_i sessionid = h(acc) ``` ------<=[ Message header ]=>----------- All messages have a message header: uint8 signature[32] uint0 sign_here[0] uint8 type = 0x80 uint8 version = 0 uint8 messageno uint32 len uint8 from uint8 to uint64 timestamp uint8 sessionid[32] The header begins with the signature of the sender over the rest of the header and all of the data. The field sign_here is a zero-bit field, only used for addressing the start of the data to be signed or verified. The next field is the protocol type identifier. STP-DKG has an identifier value of 128 (0x80). And a version number of 0 for the current version. The following field in the header is really a state identifier. A recipient MUST verify that the messageno is matching with the expected number related to the state of the protocol. The len field MUST be equal to the size of the packet received on the network including the packet header. The `from` field is simply the index of the peer, since peers are indexed starting from 1, the value 0 is used for the semi-trusted party. Any value greater than 128 is invalid. The state defines from whom to receive messages, and thus the from field MUST be validated against these expectations. The `to` field is similar to the `from` field, with the difference that the value 0xff is reserved for broadcast messages. The peer (or STP) MUST validate that it is indeed the recipient of a given message. The timestamp field is just a 64bit timestamp as seconds elapsed since 1970/01/01, for peers that have no accurate clock themselves but do have an RTC, the first initiating message from the STP SHOULD be used as a reference for synchronizing during the protocol. ------<=[ Message signatures ]=>----------- Every message MUST be signed using the sender peers long-term signing key. The signature is made over the complete message including the header, excluding the signature field itself, starting from the zero-width sign_here field. ------<=[ Verifying messages ]=>----------- Whenever a message is received by any participant, they first MUST check the correctness of the signature: ``` msg = recv() sign_pk = sign_keys[expected_sender_id] assert(verify(sign_pk, msg.sign_her, msg.signature)) ``` The recipient MUST also assert the correctness of all the other header fields: ``` assert(msg.type == 0x80) assert(msg.version == 0) assert(msg.sessionid == sessionid) assert(msg.messageno == expected_messageno) assert(msg.from == expected_sender_id) assert(msg.to == (own_peer_id or 0xff)) assert(ref_ts <= msg.ts < ref_ts + timeout)) ref_ts = msg.ts ``` The value `timeout` should be configurable and be set to the smallest value that doesn't cause protocol aborts due to slow responses. If at any step of the protocol any participant receives one or more messages that fail these checks, the participant MUST abort the protocol and log all violations and if possible alert the user. ------<=[ Message transmission ]=>----------- A higher level message transmission interface can be provided, for sending: ``` msg = send_msg(msgno, from, to, sign_sk, session_id, data) ts = timestamp() msg = type: 0x80, version: 0, messageno: msgno, len: len(header) + len(data) + len(sig), from: from, to: to, ts: ts, data msg.sig = sign(sign_sk, msg.sign_here) return msg ``` And for validating incoming messages: ``` data = recv_msg(msgno, from, to, ref_ts, sign_pk, session_id, msg) assert(verify(sign_pk, msg.sign_here, msg.sig) assert(msg.type == 0x80) assert(msg.version == 0) assert(msg.messageno == msgno) assert(msg.len == len(msg|sig)) assert(msg.from == from) assert(msg.to == to) assert(ref_ts < msg.ts < ref_ts + timeout)) if msg.to == 0xff: update_ts(state,msg,sig) ``` The parameters `msgno`, `from`, `to`, `session_id` should be the values expected according to the current protocol state. ------<=[ Cheater detection ]=>----------- The STP and the peers MUST report to the user all errors that can identify cheating peers in any given step. For each detected cheating peer the STP MUST record the following information: - the current protocol step, - the violating peer, - the other peer involved, and - the type of violation In order to detect other misbehaving peers in the current step, processing for the rest of the SHOULD peers continue until the end of the current step. Any further violations should be recorded as above. Before the next message to the peers is sent, the STP MUST check if there are no noted hard violations, if so the STP aborts and reports all violators with their parameters to the user. Soft violations - corruptions robustly handled by the protocol - only need to be reported, they do not cause an abort. Abort conditions include any errors detected by recv_msg(), failure to verify the hash of the commitments, or when the number of complaints is more than t for one peer, or more than t^2 in total. Soft violations are failures of dealers VSPS checks and shares not matching their commitments. Participants should log all broadcast interactions, so that any post-mortem investigation can identify cheaters. ------<=[ Second generator point ]=>----------- For the homomorphic commitment this protocol requires a second generator on the group. We generate it in the following manner: h = voprf_hash_to_group(blake2b("nothing up my sleeve number")) Where voprf_hash_to_groups is according to [RFC9497]. ------<=[ The protocol ]=>----------- ------<=[ 0. Precondition ]=>----------- Peers use TLS or STP knows long-term encryption keys for all peers. STP and peers MUST know long-term signing keys of all peers. ------<=[ 1. DKG Announcement - STP(peers, t, proto_name) ]=>---------- The protocol starts by asking the semi-trusted party (STP) to initiate a new run of the DKG protocol by providing it with: - a list of the peers, - a threshold value, and - protocol instance name used as a domain separation token. The STP then sanity checks these parameters: ``` n = len(peers) assert(10) ``` The STP then generates a hash of the DST. The STP then creates a broadcast message containing the hash (so that the message is always of fixed size) of the DST, the values n and t and its own public signing key: ``` dst_str = "STP VSS DKG for protocol %s" % proto_name dst = hash(I2OSP(len(dst_str)) | dst_str | n | t) sessionid = random_bytes(32) data = {stp_sign_pk, dst, n, t} msg_0 = send_msg(0, 0, 0xff, stp_sign_sk, sessionid, data) broadcast(msg_0) ``` Note that the STP also generates a temporary session id, which is used until the parties agree on a joint session id. The STPs copy of the transcript is initialized by the STP, and updated with the value of the 1st broadcast message: ``` state = hash_init("stp vss dkg session transcript") state = update_ts(state, msg, sig) ``` Since the order of the peers is random, and important for the protocol a custom message is created for each peer by the STP and sent individually notifying each peer of their index in this protocol run. The msg.to field conveys the index of the peer. Additionally the hashes of the long-term signing public keys of the other peers are also sent along so that each of the peers can load the corresponding long-term signing and noise public keys. ``` # sending each peer its index pkhashes = "" for i in 1..n: pkhashes = pkhashes | hash(ltsigpk[i]) for i in 1..n: msg_1 = send_msg(1, 0, i, stp_sign_sk, session_id, {pkhashes}) send(i, msg_1) ``` ------<=[ 2. each peer(msg_0) ]=>------------ In this step each peer receives the initial parameter broadcast, verifies it, initializes the transcript and adds the initial message. Then receives the message assigning its index. ``` msg_0 = recv() assert(recv_msg(0, 0, 0xff, ref_ts, msg.data.stp_sign_pk, msg.sessionid, msg_0)) sessionid = msg.sessionid ``` If the peer has no accurate internal clock but has at least an RTC, it SHOULD set the ref_ts to the message timestamp: ``` ref_ts = msg_0.ts ``` Furthermore the peer MUST also verify that the n & t parameters are sane, and if possible the peer SHOULD also check if the temporary STP-assigned session id is fresh (if it is not possible, isfresh() MAY always return true. ``` assert(1 < n <= 128) assert(2 <= msg_0.t < n) assert(isfresh(msg_0,sessionid)) ``` The transcript MUST be initialized by the peer, and updated with the value of the 1st broadcast message: ``` state = hash_init("stp vss dkg session transcript") state = update_ts(state, msg, sig) ``` After processing the broadcast message from the STP, the peers also have to process the second message from the STP in which they are assigned their index. ``` msg1 = recv() peerids = recv_msg(1, 0, msg1.to, ref_ts, stp_sign_pk, session_id, msg_1) assert(msg1.to <= n and msg1.to > 0) peerid = msg.to peers_noise_pks = [] peers_sign_pks = [] for i in 1..n peers_sign_pks[i], peers_noise_pks[i] = keyloader(peerids[i]) ``` ------<=[ 3. peers broadcast fresh session nonce ]=>------------- All peers broadcast generate a fresh session nonce for use in the session_id to all peers via the STP. ``` nonce_i = random_bytes(32) msg_2 = send_msg(2, peerid, 0xff, peer_sign_sk, session_id, nonce_i) broadcast(msg_2) ``` ------<=[ 4. STP collects and broadcasts messages ]=>------------- Then the STP acts as a broadcast medium on the messages. This is a recurring pattern where the STP acts in its broadcasting intermediary role: 1. receives the messages from each peer 2. validates the message using recv_msg() 3. extracts all nonces (or other information depending on the current step) for usage by the STP in the rest of the protocol 4. concatenates all received messages into a new message 5. signs the message of messages 6. adds this the message of messages and its signature to the transcript 7. sends it to all peers In this case the STP calculates the session id, which is the hash of the concatenation of all nonces in order of their sending peers index (with the STP always having index 0). The STP already uses the joint session_id to wrap all the peers messages into its broadcast envelope. ``` peer_sig_pks = [] msgs = [] nonces = nonce_stp for i in 1..N msg_2 = recv() nonce_i, = recv_msg(2, i, 0xff, ref_ts, msg_2.data.peer_sign_pk, nonce_i, msg_2) msgs = msgs | msg_2 nonces = nonces | nonce_i session_id = hash(nonces) msg_3 = send_msg(3, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_3) broadcast(msg_3) ``` ------<=[ 5. finish init phase, start AKE phase ]=>------- In this step all peers process the broadcast nonces received from all peers, finishing the initial phase. Every peer also verifies if the joint session_id matches the one in the STP broadcast envelope. From now on, every participant has this joint session id and uses it for all further messages. This step also marks the start of the next phase. In this AKE phase each peer initiates a noise_xk handshake with all other peers (including themselves for simplicity and thus security). ``` msg_3 = recv() msgs = recv_msg(3, 0, 0xff, ref_ts, stp_sign_pk, msg_3.session_id, msg_3) state = update_ts(state, msg_3) nonces = [] for i in 1..N msg, sig = msgs[i] nonce_i, = recv_msg(2, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg, sig) nonces = nonces | nonce_i session_id = hash(nonces) assert(msg_3.session_id == session_id) send_session = [] for i in 1..N send_session[i], handshake1 = noisexk_initiator_session(peer_noise_sk, peers_noise_pks[i]) msg, sig = send_msg(4,peerid,i,peer_sign_sk, session_id, handshake1) send(msg | sig) ``` ------<=[ 6. STP routes handshakes from each peer to each peer ]=>------- The STP receives all 1st handshake messages from all peers and routes them correctly to their destination. These messages are not broadcast, each of them is an encrypted P2P message. The benefit of the STP forming a star topology here is, that the peers can be on very different physical networks (wifi, lora, uart, nfc, bluetooth, usb, etc) and only the STP needs to be able to connect to all of them. ``` for i in 1..N handshakes = recv(i) for j in 1..N send(j, handshakes[j]) ``` ------<=[ 7. each peer responds to each handshake each peer ]=>------- Peer receives noise handshake1 from each peer and responds with handshake2 answer to each peer. ``` for i in 1..N msg, sig = recv() handshake1 = recv_msg(4, i, peerid, ref_ts, peers_sign_pks[i], session_id, msg, sig) receive_session[i], handshake2 = noisexk_responder_session(peer_noise_sk, handshake1) msg, sig = send_msg(5, peerid, i, peer_sign_sk, session_id, handshake2) send(msg | sig) ``` ------<=[ 8. STP routes handshakes from each peer to each peer ]=>------- STP just routes all P2P messages from all peers to the correct recipients of the messages. ``` for i in 1..N handshakes = recv(i) for j in 1..N send(j, handshakes[j]) ``` ------<=[ 9. end of AKE phase, start of core DKG phase ]=>------- Peers complete the noise handshake. ``` for i in 1..N msg, sig = recv() handshake3 = recv_msg(5, i, peerid, ref_ts, peers_sign_pks[i], session_id, msg, sig) send_session[i] = noisexk_initiator_session_complete(send_session[i], handshake3) ``` Each peer has a confidential connection with every peer (including self, for simplicity) The one time this channel is used, is when distributing the dealer shares. The sender uses the initiator interface of the noise session, and the receiver uses the responder interface. This step starts the core FT-Joint-DL-VSS protocol as per [GRR98] fig. 7: Player P_i chooses a random value r_i and shares it using the DL-VSS protocol, Denote by Ξ±_i,j , ρ_i,j the share player P_i gives to player P_j, and the value 𝓐_i,j = g^(Ξ±_i,j)*h^(ρ_i,j) is the dealers commitment to share Ξ±_i,j , ρ_i,j. The Noise handshake is finalized by encrypting one last empty message, which sets the final shared secret. This is needed in case later someone accuses this peer with their shares not matching their commitment, in which case the peer can reveal this final encryption key and prove everyone that the accuser was lying or not. The shares Ξ±_i,j ,ρ_i,j are encrypted using the final Noise key for the recipient, and a key-committing HMAC is also calculated over the encrypted shares, since the Noise implementation we use does use Poly1305 which is not key-committing and thus would allow the dealer to cheat. The encrypted shares and their commitments 𝓐_i,j are stored by the peer for later distribution. The HMAC for each encrypted share is broadcast together with the hash of the concatenation of all A_i,j commitments: C_i = hash(A_i0 | A_i1 | .. | A_in) ``` encrypted_shares = [] hmacs = [] for i in 1..N encrypted_shares[i] = noise_send(send_session[i], share[i]) hmacs[i] = hmac(send_session[i].key, encrypted_shares[i]) C_i = hash(commitments) msg_6 = send_msg(6, peerid, 0xff, peer_sign_sk, session_id, {C_i, hmacs}) ``` ------<=[ 10. STP collects and broadcasts all C_i commitments ]=>------- This is another broadcast pattern instance: receive-verify-collect-sign-transcript-broadcast. The STP keeps a copy of all commitment-hashes and share-HMACs being broadcast. The STP keeps a local copy of all commitment hashes and HMACs for later verification. ``` C_hashes = [] hmacs = [] msgs = [] for i in 1..N msg_6 = recv(i) C_hashes[i], hmacs[i] = recv_msg(6, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_6) msgs = msgs | msg_6 msg_7 = send_msg(7, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_7) broadcast(msg_7) ``` ------<=[ 11. Peers receive commitment hashes & HMACs ]=>------- The peers receive all C_i commitment hashes and share-HMACs and broadcast their A commitment vectors: ``` msg_7 = recv() msgs = recv_msg(7, 0, 0xff, ref_ts, stp_sign_pk, session_id, msg_7) state = update_ts(state, msg_7) C_hashes = [] share_macs = [] for i in 1..N msg_6 = msgs[i] C_hashes[i], share_macs[i] = recv_msg(6, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_6) msgs = msgs | msg_6 msg_8 = send_msg(8, peerid, 0xff, peer_sign_sk, session_id, A) ``` ------<=[ 12. STP broadcasts all commitments ]=>------- This is a classical STP broadcast step. Besides keeping a copy of all commitments, the STP does also verify the commitment hashes and does an FT-VSPS check on the commitments. The STP verifies the VSPS property of the sum of the shared secrets by running VSPS-Check on 𝓐_i,..,𝓐_n where 𝓐_j = Ξ  𝓐_i,j i If this check fails the STP runs VSPS-Checks on each individual sharing. These checks are informational, and should guide the operator in detecting and deterring cheaters. ``` commitments[][] msgs = [] for i in 1..N msg_8 = recv(i) commitments[i] = recv_msg(8, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_8) msgs = msgs | msg_6 if C_hashes != hash(commitments[i]) report(i) C = [] for i in 1..n C[i] = sum(commitments[j][i] for j in 1..n) if vsps(C) fails: for i..n if vsps(commitments[i]) fails report(i) msg_9 = send_msg(9, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_9) broadcast(msg_9) ``` ------<=[ 13. Peers receive commitments, send shares ]=>------- The peers receive the broadcast commitments of all dealers, they check the commitment hashes and abort if they don't match, otherwise they store the commitments for the next step. Peers privately send the shares to each recipient. ``` msg_9 = recv() msgs = recv_msg(9, 0, 0xff, ref_ts, stp_sign_pk, session_id, msg_9) state = update_ts(state, msg_9) commitments = [][] for i in 1..N msg_8 = msgs[i] commitments[i] = recv_msg(8, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_8) assert C_hashes[i] == hash(commitments[i]) msg_10s = [] for i in 1..N msg, sig = send_msg(9, peerid, i, peer_sign_sk, session_id, encrypted_shares[i]) send(msg,sig) ``` ------<=[ 14. STP routes shares to recipients ]=>------- STP just routes all P2P messages from all peers to the correct recipients of the messages. ``` for i in 1..N msgs = recv(i) for j in 1..N send(j, msgs[j]) ``` ------<=[ 15. each peer receives shares & check commitments ]=>------- The peers receive the private messages containing their shares. The peers verify the shares against the previously broadcast commitment vectors. For each 𝓐_i,j == g^(Ξ±_i,j) * h^(ρ_i,j) pair that fails, a complaint against the peer producing the conflicting commitment and share is logged in an array, which is broadcast to everyone. ``` s = [] for i in 1..N msg = recv() pkt = recv_msg(9, i, peerid, ref_ts, peer_sign_pks[i], session_id, msg) encrypted_share, final_noise_handshake = pkt recv_session[i] = noise_session_decrypt(recv_session[i], final_noise_handshake) Ξ±[i,peerid],ρ[i,peerid] = noise_recv(receive_session[i], pkt) complaints = [] for i in 1..N if commitment[i,peerid] != g^(Ξ±[i,peerid])*h^(ρ[i,peerid]) complaints = complaints | i msg, sig = send_msg(10, peerid, 0xff, peer_sign_sk, session_id, len(complaints) | complaints) send(msg | sig) ``` ------<=[ 16. STP collects complaints ]=>------- Another receive-verify-collect-sign-transcribe-broadcast instantiation. The STP keeps a copy of all complaints. If any peer complaints about more than t peers, that complaining peer is a cheater, and must be disqualified. Furthermore if there are in total more than t^2 complaints there are multiple cheaters and the protocol must be aborted and new peers must be chosen in case a rerun is initiated. ``` complaints = [] msgs = [] for i in 1..N msg_10 = recv(i) complaints_i = recv_msg(10, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_10) assert(len(complaints_i) < t) complaints = complaints | complaints_i msgs = msgs | msg_10 assert(len(complaints) < t^2) msg_11 = send_msg(11, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_11) broadcast(msg_11) ``` The next phase of the protocol depends on the number of complaints received, if none then the next phase is finishing, otherwise the next phase is complaint analysis. If the next STP phase is complaint analysis (there are complaints) the next input buffer size depends on the number of complaints against each peer. Each complaint is answered by the encrypted shares and the symmetric encryption key used to encrypt these shares of the accused belonging to the complainer. Each accused packs all answers into one message. ------<=[ 17. Each peer receives all complaints ]=>------- All complaint messages broadcast are received by each peer. If peer_i is being complained about by peer_j, peer_i broadcasts the encrypted shares and the symmetric encryption key that was used to encrypt them. If there are no complaints at all the peers skip over to the final phase step 20., otherwise they engage in the complaint analysis phase. ``` msg_11 = recv() msgs = recv_msg(11, 0, 0xff, ref_ts, stp_sign_pk, session_id, msg_11) state = update_ts(state, msg_11) defenses = [] for i in 1..N msg = msgs[i] complaints_len, complaints = recv_msg(10, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg) for k in 0..complaints_len if complaints[k] == peerid # complaint about current peer, publish key used to encrypt Ξ±_i,j , ρ_i,j defenses = defenses | {i, send_session[i].key, encrypted_shares[i]} if len(keys) > 0 msg_12 = send_msg(12, peer, 0xff, peer_sign_sk, session_id, keys) send(msg_12) ``` ------<=[ 18. STP collects all defenses, verifies&broadcasts them ]=>------- STP checks if all complaints lodged earlier are answered by the correct encrypted shares and their keys, by first checking if the previously recorded MAC successfully verifies the encrypted share with the disclosed key, and then decrypts the share with this key, and checks if this satisfies the previously recorded commitment for this share. If it does, the accuser is reported as a cheater, if the commitment doesn't match the share, then the accused dealer is disqualified from the protocol and its shares will not contribute to the final shared secret. ``` msgs = [] for i in 1..N if len(complaints[i]) < 1 continue msg = recv(i) defenses = recv_msg(12, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg) msgs = msgs | msg assert(len(defenses) == len(complaints[i])) for j, key, encrypted_share in defenses assert j==i if hmacs[i][peerid] == hmac(key, encrypted_share) report(i) s,r=decrypt(key, encrypted_share]) or report(i) if commitments[i][peerid] != g^s * h^r report(i) msg_13 = send_msg(13, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_13) broadcast(msg_13) ``` ------<=[ 19. Peers receive and check all defenses ]=>------- Peers receive the encrypted shares, and their encryption keys, and then run essentially the same step as the STP in the previous step, then they directly skip to the final phase in the next step. ------<=[ 20. Peers VSPS check, calculate shares and finish ]=>------- Players verify the VSPS property of the sum of the shared secrets by running VSPS-Check on 𝓐_i,..,𝓐_n where 𝓐_j = Ξ  𝓐_i,j i If this check fails the players run VSPS-Check on each individual sharing. Any player that fails this check is disqualified. The number of all qualified peers (from this step, and the complaint analysis) is checked that is greater than 1 and then number of disqualified peers is less than t. If this fails the protocol aborts. The shares dealt by the qualified peers are summed, creating the final share. The commitment for this final share is calculated. Each peer calculates the final transcripts and broadcasts this together with the final commitments to all parties. ``` C = [] for i in 1..n C[i] = sum(commitments[j][i] for j in 1..n if peer[i] is qualified) if vsps(C) fails: for i in 1..n if vsps(commitments[i]) fails disqualify(i) s = 0, r = 0 for i in 1..n if i is disqualfied: continue s += shares[i]_s r += shares[i]_r C = g^s * h^r transcript = final_ts(state) msg_20 = send_msg(20, peerid, 0, peer_sign_sk, session_id, {transcript, C}) send(msg_20) ``` ------<=[ 20. STP receives all and verifies transcripts ]=>------- STP receives all transcripts, and asserts that they all match its own transcript, it reports if any transcript mismatch is detected. It also does a final VSPS check on the commitments seen. ``` transcript = final_ts(state) msgs = [] commitments = [] for i in 1..N msg, sig = recv(i) ts, c[i] = recv_msg(20, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg, sig) if ts != transcript report transcript mismatch msgs = msgs | {msg, sig} if vsps(commitments) fails: report failure ------<=[ 21. END, peers set their share ]=>------- All peers receive the broadcasts transcripts and commitments, they run the same check as the STP in the previous step and abort if any of these fails. Otherwise the protocol completes successfully, and the peers can store the final shares and commitment together with the list of long-term public signing keys of all peers. ------<=[ References ]=>------- [GRR98] R. Gennaro, M. O. Rabin, and T. Rabin. "Simplified VSS and fact-track multiparty computations with applications to threshold cryptography" In B. A. Coan and Y. Afek, editors, 17th ACM PODC, pages 101–111. ACM, June / July 1998 [RFC9497] RFC 9497: Oblivious Pseudorandom Functions (OPRFs) Using Prime-Order Groups liboprf-0.9.4/docs/stp-update.txt000066400000000000000000001360051514673400200167470ustar00rootroot00000000000000Semi-Trusted-Party (STP) threshold-OPRF key-update Protocol [[[ note this is a draft, which matches the current implementation, changes are still expected ]]] This document specifies a proposal for a semi-robust threshold-OPRF key- update protocol that can work for small deployments with a small number of parties and infrequent executions. Semi-robust means that the protocol can fail in the DKG phase if any party aborts, but can still succeed later if some parties are detected cheating or aborting. If someone aborts in the first DKG phase then the protocol needs to run again, possibly after kicking out misbehaving parties. This protocol does support maximum 127 peers. This is probably way too much for a generic threshold protocol, but it might work in very special circumstances. Broadcast is implemented by the semi-trusted party (STP) opening a channel to each peer secured by the peers long-term encryption key. Every message is routed through the STP. Peer long-term encryption keys can be either TLS-based, or Noise_XK-based (https://noiseexplorer.com/patterns/XK/). In the latter case the long-term public keys must be known and validated in advance by the STP. The long-term signing keys of peers MUST be known by all parties before the start of the protocol. This protocol is based on the threshold updatable OPRF as described in section 6.2 of [JKR19]. The basic building blocks for this protocol are the FT-Joint-DL-VSS protocol for the DKGs as specified in Appendix H figure 7 of [GRR98], and the Multi-party Multiplication protocol which given the sharings of secret `a` and secret `b` generates a sharing of the product `aΒ·b` without learning anything about either secret. The multi-party multiplication is based on the FT-Mult protocol shown in Fig. 6 from [GRR98]. The update protocol from old key kc to new key kc' works as follows (quote from the JKR paper): The distributed update protocol assumes that n servers S1,...,Sn have a sharing (k1,..., kn) of a key k. To produce a new key kβ€² the servers jointly generate a sharing ρ1,...,ρn of a random secret ρ ∈Zq and run distributed multiplication FT-Mult(kc,ρ) to generate shares kβ€²1,...,kβ€²n of the new key defined as kβ€² = ρ Β· k. Finally, each server Si sends to clients its share ρi from which the recipient reconstructs ρ and sets βˆ†:= ρ [= kβ€²/k]. ------<=[ Roles ]=>----------- There is three roles in this protocol: - semi-trusted party (STP) orchestrating the communication between all other participants. - dealers: exactly 2t+1 participants (having shares of both kc and p) are acting as dealers - all participants (peers and STP) ------<=[ Prototocol Phases ]=>----------- Some of the following phases are optional and only executed if the previous step revealed a misbehaving party, these phases make the protocol robust to also successfully complete in the presence of misbehaving parties. Phases 3, 5, 7, and 9 are these optional corrective phases. 0. pre-condition: all parties know the long-term signing keys of each other. STP ensures that n >= 2t+1 and at least 2t+1 of them hold shares of the old key kc, otherwise abort. 1. Initialization, sharing of basic parameters, setup of p2p noise channels. 2. execute STP-DKG for all peers that hold a share of kc, calculating sharing of ρ, if a fast-track VSPS check fails disqualify the cheaters. Abort if some other hard-fail occurs during the DKG. 3. DKG dealers defend against complaints by revealing contested shares. Based on this peers disqualify any misbehaving peers from contributing to the result of the DKG. 4. execute the FT-Mult protocol, to calculate FT-Mult(kc, ρ), generating sharings of kc'. 5. Verify the commitments matching the shares of step 2, if there is failures to verify run a recovery procedure to correct the failing shares/commitments. 6. Dealers run ZK proofs on the correctness of their 𝓒_i0 commitment. If any of these fails, run a recovery procedure to correct the commitment. 7. Recover from failed ZK proofs, reconstructing the failing dealer P_i secret Ξ»_iΞ±_iΞ²_i and recalculating the correct 𝓒_i0 commitment. 8. Aggregate the final shares and commitments. Run a Fast-track VSPS check on the final commitments for the shares of the multiplications. If any of these fails, run a recovery procedure. 9. Recovery of Ξ»_iΞ±_iΞ²_i for any dealer who fails the VSPS check and deterministically re-share the recovered secret. 10. Finalization of computation: parties send their ρ_i shares to the STP which reconstructs ρ and computes βˆ†=1/p.Parties replace their kc share with their kc` share. ------<=[ Simplified API ]=>----------- Since the protocol consists of many steps, it is recommended to abstract the API to the following schema: 0. Initialize While not done and not error: 1. Allocate input buffers 2. input = receive() 3. allocate output buffer 4. run next step of protocol 5. if there is output: send(output) 6. Post-processing This simple schema simplifies the load of an implementer using this protocol, reducing opportunities for errors and provides strict security. It also allows full abstraction of the underlying communication media. The reference implementation in toprf-update.c follows this schema for both the STP and the peers. ------<=[ Protocol transcript ]=>----------- Transcript - all broadcast messages are accumulated into a transcript by each peer and the semi-trusted party, at the end of the protocol all parties publish their signed transcripts and only if all signatures are correct and the transcripts match, is the protocol successful. The transcript is a hash, that is initialized with the string: "stp update session transcript" in pseudo-code: ``` transcript_state = hash_init() transcript_state = hash_update("stp update session transcript") ``` Updating the transcript first updates the hash with the canonical 32bit size of the message to be added to the transcript, then the message itself is added to the hash. ``` transcript_state = hash_update(transcript_state, I2OSP(len(msg)) transcript_state = hash_update(transcript_state, msg) ``` A function `update_ts` can be used as a high-level interface to updating the transcript with messages: ``` update_ts(state,msg) state = hash_update(state, I2OSP(len(msg)) state = hash_update(state, msg) return state ``` ------<=[ Session id ]=>----------- Every execution of the protocol starts by the participants establishing a unique and fresh session id, this is to ensure that no messages can be replayed. The session id is a 256 bit (32B) random value of cryptographic quality. Every message sent MUST contain a valid session id. The session id is established in the very first steps of the protocol, where each peer generates a 32B nonce, and broadcasts this, and - upon reception of all peers - hashes the STP session id nonce together with the concatenation of the peers nonces (in order of the peers) to establish the final session id. ------<=[ Message header ]=>----------- All messages have a message header: uint8 signature[32] uint0 sign_here[0] uint8 type = 0x81 uint8 version = 0 uint8 messageno uint32 len uint8 from uint8 to uint64 timestamp uint8 sessionid[32] The header begins with the signature of the sender over the rest of the header and all of the data. The field sign_here is a zero-bit field, only used for addressing the start of the data to be signed or verified. The next field is the protocol type identifier. STP Update has an identifier value of 129 (0x81). And currently a version number of 0. The following field in the header is really a state identifier. A recipient MUST verify that the messageno is matching with the expected number related to the state of the protocol. The len field MUST be equal to the size of the packet received on the network including the packet header. The `from` field is simply the index of the peer, since peers are indexed starting from 1, the value 0 is used for the semi-trusted party. Any value greater than 128 is invalid. The state defines from whom to receive messages, and thus the from field MUST be validated against these expectations. The `to` field is similar to the `from` field, with the difference that the value 0xff is reserved for broadcast messages. The peer (or STP) MUST validate that it is indeed the recipient of a given message. The timestamp field is just a 64bit timestamp as seconds elapsed since 1970/01/01, for peers that have no accurate clock themselves but do have an RTC, the first initiating message from the STP SHOULD be used as a reference for synchronizing during the protocol. ------<=[ Message signatures ]=>----------- Every message MUST be signed using the sender peers long-term signing key. The signature is made over the complete message. ------<=[ Verifying messages ]=>----------- Whenever a message is received by any participant, they first MUST check the correctness of the signature: ``` msg = recv() sign_pk = sign_keys[expected_sender_id] assert(verify(sign_pk, msg)) ``` The recipient MUST also assert the correctness of all the other header fields: ``` assert(msg.type == 0x81) assert(msg.version == 0) assert(msg.sessionid == sessionid) assert(msg.messageno == expected_messageno) assert(msg.from == expected_sender_id) assert(msg.to == (own_peer_id or 0xff)) assert(ref_ts <= msg.ts < ref_ts + timeout)) ref_ts = msg.ts ``` The value `timeout` should be configurable and be set to the smallest value that doesn't cause protocol aborts due to slow responses. If at any step of the protocol any participant receives one or more messages that fail these checks, the participant MUST abort the protocol and log all violations and if possible alert the user. ------<=[ Message transmission ]=>----------- A higher level message transmission interface can be provided, for sending: ``` msg = send_msg(msgno, from, to, sign_sk, session_id, data) ts = timestamp() msg = type: 0x81, version: 0, messageno: msgno, len: len(header) + len(data) + len(sig), from: from, to: to, ts: ts, data sig = sign(sign_sk, msg) return msg ``` And for validating incoming messages: ``` data = recv_msg(msgno, from, to, ref_ts, sign_pk, session_id, msg) assert(verify(sign_pk, msg) assert(msg.type == 0x81) assert(msg.version == 0) assert(msg.messageno == msgno) assert(msg.len == len(msg)) assert(msg.from == from) assert(msg.to == to) assert(ref_ts < msg.ts < ref_ts + timeout)) if msg.to == 0xff: update_ts(state,msg) ``` The parameters `msgno`, `from`, `to`, `session_id` should be the values expected according to the current protocol state. ------<=[ Cheater detection ]=>----------- All participants MUST report to the user all errors that can identify cheating peers in any given step. For each detected cheating peer the participants MUST record and log the following information: - the current protocol step, - the violating peer, - the other peer involved, and - the type of violation In order to detect other misbehaving peers in the current step, processing for the rest of the SHOULD peers continue until the end of the current step. Any further violations should be recorded as above. Before the next message to the peers is sent, the STP must check if there are no noted violations, if so the STP aborts and reports all violators with their parameters to the user. Abort conditions include any errors detected by recv_msg(), or when the number of complaints is more than t for one peer, or more than t^2 in total. Participants should log all broadcast interactions, so that any post-mortem investigation can identify cheaters. ------<=[ Second generator point ]=>----------- FT-Mult and the FT-Joint-DL_VSS DKG require a second generator on the group. We generate it in the following manner: h = voprf_hash_to_group(blake2b(hash,"nothing up my sleeve number")) Where voprf_hash_to_groups is according to [RFC9497]. ------<=[ The FT-MULT sub-protocol ]=>----------- FT-MULT, calculates the product of two values Ξ±,Ξ² both shared in the same (n,t) threshold sharing setup. The FT-MULT sub-protocol is based on the "Fast-track multiplication" as in fig. 6 of [GRR98], page 19. It goes as follows: ------<=[ FT-MULT pre-conditions ]=>----------- 1. All Peers use TLS or the STP knows long-term encryption keys for all peers. 2. STP and peers MUST know long-term signing keys of everyone in the protocol. 3. There is at least 2t+1 peers holding shares of both operands participating in the protocol, 2t+1 of them will be acting as dealers, the rest of the participants act as passive receivers. 4. Each dealer P_i has - a share of Ξ±: Ξ±_i = 𝑓_Ξ±(i), one of the values to multiply - a share of Ξ²: Ξ²_i = 𝑓_Ξ²(i), the other value to multiply - a share of π‘Ÿ: ρ_i = π‘Ÿ(i), a blinding factor for the homomorphic commitment of Ξ± - a share of 𝑠: Οƒ_i = 𝑠(i), blinding factor for the homomorphic commitment of Ξ² public inputs, for i:=0..n 𝓐_i = 𝓗(Ξ±_i,ρ_i) = g^(Ξ±_i)*h^(ρ_i) 𝓑_i = 𝓗(Ξ²_i,Οƒ_i) = g^(Ξ²_i)*h^(Οƒ_i) These conditions MUST be guaranteed by the initialization and DKG phases of this protocol. ------<=[ FT-MULT VSPS check ]=>----------- A VSPS check on a vector of homomorphic commitments verifies that these correspond to a polynomial of degree t. The protocol relies on this check heavily. VSPS Check on 𝓒_i, i:=1..n, t+1 t+1 Ξ  𝓒_i^Ξ”_i = Ξ  𝓒_(i+t+1)^Ξ”_i i=0 i=0 t where Ξ”'_i = Ξ£ Ξ»_(j,i)*Ξ΄^j j=0 where Ξ» is an inverted Vandermonde matrix over 0..t for the lhs, and t+1..2t+1 for the rhs. ------<=[ Phase 1 - Initialization ]=>----------- In this phase the protocol establishes a joint session id, and Noise_XK protected channels between all peers. Until the joint session id is established, the session id of shared in the very first message of the STP is used. This phase starts by the STP executing the `toprf_update_start_stp()` function, which receives the parameters to the protocol, such as the values N and T, the keyid of the record do update, the long-term signatures keys of the peers. The result of this function is a initial message to be sent to the peers, notifying them of the initiation of the protocol, and all the essential parameters. This initial message contains: - the public long-term signature key of the STP, - a hash of the DST, - a keyid of the key to be updated, - and an initial session_id. The peers receive this message, and check if the public key of the STP is authorized to operate on this key, they abort if the STP is unauthorized. ------<=[ 1. Peer shares sid nonce ]=>----------- The peers all receive the initial message from the STP. This message contains a keyid, so that the client can check and load the corresponding kc key share and corresponding long-term signature keys of the peers, information stored in an internal state. Finally the peers each start their own protocol loop, in which they generate their own session id nonce and share the commitment to their share of kc. This nonce and commitment is broadcast via the STP. ------<=[ 2. STP sets sessionid and forwards keys ]=>----------- STP receives all messages from the peers, then - verifies each of them, - extracts the session id nonce and the commitment for their kc share from each, - hashes its own and the peers nonces in order - sets the final session id to the result - wraps all peer messages in a STP broadcast envelope message using the new session id. - adds the broadcast envelope message to the global transcript, and - sends this message to all peers. session_id = hash(stp_session_id | peer1_sid_nonce | peer2_sid_nonce | ... | peerN_sid_nonce) the stp_session_id is distributed to all peers in the first message of the protocol. ------<=[ 3. peers set sessionid & noise keys ]=>----------- After verifying, adding it to the transcript and unwrapping the broadcast message envelope from the STP, the peers calculate the session id like the STP did in the previous step. Furthermore they verify that the sessionid they calculated is the same as the session id used in the broadcast envelope header. Finally the peers each initiate a Noise XK handshake message to all peers. ------<=[ 4. STP routes handshakes between peers ]=>----------- The TP receives all 1st handshake messages from all peers and routes them correctly to their destination. These messages are not broadcast, each of them is a P2P message. The benefit of the STP forming a star topology here is, that the peers can be on very different physical networks (wifi, lora, uart, nfc, usb, bluetooth, etc) and only the TP needs to be able to connect to all of them. ------<=[ 5. each peer responds to each handshake ]=>----------- Peer receives noise handshake1 from each peer and responds with handshake2 answer to each peer. ------<=[ 6. STP routes handshakes between peers ]=>----------- STP just like in step 4. routes all P2P messages from all peers to the correct recipients of the messages. ------<=[ 7. Peers conclude handshakes, start p DKG ]=>----------- This step concludes the Initialization phase and starts the DKG phase. The peers receive the 3rd and final message of the Noise handshakes, completing the setup of these Noise-protected peer-to-peer channels. ------<=[ Phase 2 - STP DKG of p ]=>----------- The peers then start the DKG phase during which they collectively generate the shared delta p factor. The underlying algorithm is FT-Joint-DL-VSS from fig 7 in Appendix H of [GRR98]. 1. Player P_i chooses a random value r_i and shares it using the DL-VSS protocol, Denote by Ξ±_i,j ,ρ_i,j the share player P_i gives to player P_j. The value 𝓐_i,j = g^(Ξ±_i,j)*h^(ρ_i,j) is public. Since we might be forced to disclose the shares in case the peer gets accused with cheating, but we don't want to reveal more information than necessary we derive a dedicated key for the shares of the p value to be shared/generated. The key for each of these shares is generated in the following way: We extract the shared key from the noise session. And run it through a ``` share_key = HKDF-expand("key for encryption of p share for []", noise_key) ``` Here the [] is replaced by the index of the peer who is the recipient of this share. Encryption of the shares is a simple XSalsa20 stream cipher with the share_key, and for this step an all-zero nonce. Instead of the usual poly1305 MAC, which is not key-committing (and thus would allow a peer to cheat) we calculate an HMAC over the ciphertext with the share_key. The shares Ξ±_i,j ,ρ_i,j for the p DKG are encrypted using the above specified procedure. The encrypted shares and their commitments 𝓐_i,j are stored by the peer for later distribution. The HMAC for each encrypted share is broadcast together with the hash of the concatenation of all 𝓐_i,j commitments: C_i = hash(𝓐_i0 | 𝓐_i1 | .. | 𝓐_in) ``` encrypted_shares = [] hmacs = [] for i in 1..N encrypted_shares[i] = encrypt_share(send_session[i], share[i]) hmacs[i] = hmac(send_session[i].key, encrypted_shares[i]) C_i = hash(commitments) msg_6 = send_msg(6, peerid, 0xff, peer_sign_sk, session_id, {C_i, hmacs}) ``` ------<=[ 8. STP collects and broadcasts all C_i commitments ]=>------- STP standard broadcast procedure expanded here, referred to later: 1. receives the messages from each peer 2. validates the message using recv_msg() 3. concatenates all received messages into a new message 4. signs the message of messages 5. adds this the message of messages and its signature to the transcript 6. sends it to all peers In this case the STP keeps a copy for later of the broadcast commitment hashes and of the MACs of the encrypted shares. ``` p_C_hashes = [] p_hmacs = [] msgs = [] for i in 1..N msg_6 = recv(i) data = recv_msg(6, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_6) p_C_hashes[i], p_hmacs[i] = data msgs = msgs | msg_6 msg_7 = send_msg(7, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_7) broadcast(msg_7) ``` ------<=[ 9. Peers receive commitment hashes & HMACs ]=>------- The peers receive all C_i commitment hashes and share-HMACs and for the p DKGs and broadcast their respective 𝓐 commitment vectors: ``` msg_7 = recv() msgs = recv_msg(7, 0, 0xff, ref_ts, stp_sign_pk, session_id, msg_7) state = update_ts(state, msg_7) p_C_hashes = [] p_share_macs = [] for i in 1..N msg_6 = msgs[i] data = = recv_msg(6, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_6) p_C_hashes[i], p_share_macs[i] = data msgs = msgs | msg_6 msg_8 = send_msg(8, peerid, 0xff, peer_sign_sk, session_id, p_A) ``` ------<=[ 12. STP broadcasts all commitments ]=>------- This is a regular STP broadcast step. Besides keeping a copy of all commitments, the STP does also verify the commitment hashes and does an FT-VSPS check on the commitments. The STP verifies the VSPS property of the sum of the shared secrets by running VSPS-Check on 𝓐_i,..,𝓐_n where 𝓐_j = Ξ  𝓐_i,j i If this check fails the STP runs VSPS-Checks on each individual sharing. These checks are informational, and should guide the operator in detecting and deterring cheaters. ``` p_commitments[][] msgs = [] for i in 1..N msg_8 = recv(i) data = recv_msg(8, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_8) p_commitments[i] = data msgs = msgs | msg_6 if p_C_hashes != hash(p_commitments[i]) report(i) C = [] for i in 1..n C[i] = sum(p_commitments[j][i] for j in 1..n) if vsps(C) fails: for i..n if vsps(p_commitments[i]) fails report(i) msg_9 = send_msg(9, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_9) broadcast(msg_9) ``` ------<=[ 11. Peers receive commitments, send shares ]=>------- The peers receive the broadcast commitments of all dealers for the DKG, they check the commitment hashes and abort if they don't match, otherwise they store the commitments for the next step. Peers privately send the encrypted shares to each recipient. ``` msg_9 = recv() msgs = recv_msg(9, 0, 0xff, ref_ts, stp_sign_pk, session_id, msg_9) state = update_ts(state, msg_9) p_commitments = [][] for i in 1..N msg_8 = msgs[i] data = recv_msg(8, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_8) p_commitments[i] = data assert p_C_hashes[i] == hash(p_commitments[i]) msg_10s = [] for i in 1..N msg = send_msg(9, peerid, i, peer_sign_sk, session_id, p_encrypted_shares[i]) send(msg) ``` ------<=[ 12. STP routes shares to recipients ]=>------- STP just routes all P2P messages from all peers to the correct recipients of the messages. ``` for i in 1..N msgs = recv(i) for j in 1..N send(j, msgs[j]) ``` ------<=[ 13. each peer receives shares & check commitments ]=>------- The peers receive the private messages containing their shares. The peers verify the shares against the previously broadcast commitment vectors. For each 𝓐_i,j == g^(Ξ±_i,j) * h^(ρ_i,j) pair that fails, a complaint against the peer producing the conflicting commitment and share is logged in an array, which is broadcast to everyone. ``` s = [] for i in 1..N msg = recv() pkt = recv_msg(9, i, peerid, ref_ts, peer_sign_pks[i], session_id, msg) final_noise_handshake, p_encrypted_share = pkt recv_session[i] = noise_session_decrypt(recv_session[i], final_noise_handshake) key=derive_key(recv_session[i], i) p_Ξ±[i,peerid],p_ρ[i,peerid] = share_decrypt(p_encrypted_share) p_complaints = [] for i in 1..N if p_commitment[i,peerid] != g^(p_Ξ±[i,peerid])*h^(p_ρ[i,peerid]) p_complaints = p_complaints | i data = len(p_complaints) | p_complaints msg = send_msg(10, peerid, 0xff, peer_sign_sk, session_id, data) send(msg) ``` ------<=[ 14. STP collects complaints ]=>------- Another receive-verify-collect-sign-transcribe-broadcast instantiation. The STP keeps a copy of all complaints. If any peer complaints about more than t peers, that complaining peer is a cheater, and must be disqualified. Furthermore if there are in total more than t^2 complaints there are multiple cheaters and the protocol must be aborted and new peers must be chosen in case a rerun is initiated. ``` p_complaints = [] msgs = [] for i in 1..N msg_10 = recv(i) data = recv_msg(10, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_10) p_complaints_i = data assert(len(p_complaints_i) < t) p_complaints = p_complaints | p_complaints_i msgs = msgs | msg_10 assert(len(complaints) < t^2) msg_11 = send_msg(11, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_11) broadcast(msg_11) ``` The next phase of the protocol depends on the number of complaints received, if none then the next phase is finishing, otherwise the next phase is complaint analysis. If the next STP phase is complaint analysis (there are complaints) the next input buffer size depends on the number of complaints against each peer. Each complaint is answered by the encrypted shares and the symmetric encryption key used to encrypt these shares of the accused belonging to the complainer. Each accused packs all answers into one message. ------<=[ 15. Each peer receives all complaints ]=>------- All complaint messages broadcast are received by each peer. If peer_i is being complained about by peer_j, peer_i broadcasts the corresponding encrypted shares and the symmetric encryption key that was used to encrypt them. If there are no complaints at all the peers skip over to the final phase step 20., otherwise they engage in the complaint analysis phase. ``` msg_11 = recv() msgs = recv_msg(11, 0, 0xff, ref_ts, stp_sign_pk, session_id, msg_11) state = update_ts(state, msg_11) defenses = [] for i in 1..N msg = msgs[i] data = recv_msg(10, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg) p_complaints_len, p_complaints = data for k in 0..p_complaints_len if p_complaints[k] == peerid # complaint about current peer, publish key used to encrypt Ξ±_i,j , ρ_i,j derive_key(send_session[i].key, "p", i) defenses = defenses | {i, key, p_encrypted_shares[i]} if len(keys) > 0 msg_12 = send_msg(12, peer, 0xff, peer_sign_sk, session_id, defenses) send(msg_12) ``` ------<=[ Phase 3 - STP DKG cheater handling ]=>----------- If any complaints have been registered in the previous phase, investigate and neutralize any possible cheaters. ------<=[ 16. STP collects all defenses, verifies&broadcasts them ]=>------- STP checks if all complaints lodged earlier are answered by the correct encrypted shares and their keys, by first checking if the previously recorded MAC successfully verifies the encrypted share with the disclosed key, and then decrypts the share with this key, and checks if this satisfies the previously recorded commitment for this share. If it does, the accuser is reported as a cheater, if the commitment doesn't match the share, then the accused dealer is disqualified from the protocol and its shares will not contribute to the final shared secret. ``` msgs = [] for i in 1..N if len(complaints[i]) < 1 continue msg = recv(i) p_defenses = recv_msg(12, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg) msgs = msgs | msg assert(len(p_defenses) == len(p_complaints[i])) for j, key, encrypted_share in p_defenses assert j==i if p_hmacs[i][peerid] == hmac(key, encrypted_share) report(i) s,r=decrypt(key, encrypted_share]) or report(i) if p_commitments[i][peerid] != g^s * h^r report(i) msg_13 = send_msg(13, 0, 0xff, stp_sign_sk, session_id, msgs) state = update_ts(state, msg_13) broadcast(msg_13) ``` ------<=[ 17. Peers receive and check all defenses ]=>------- Peers receive the encrypted shares, and their encryption keys, and then run essentially the same step as the STP in the previous step, then they directly skip to the final phase in the next step. ------<=[ 20. Peers VSPS check, calculate shares and finish ]=>------- Players verify the VSPS property of the sum of the shared secrets by running VSPS-Check on 𝓐_i,..,𝓐_n for p where 𝓐_j = Ξ  𝓐_i,j i If this check fails the players run VSPS-Check on each individual sharing. Any player that fails this check is disqualified. The number of all qualified peers (from this step, and the complaint analysis) is checked that is greater than 1 and then number of disqualified peers is less than t. If this fails the protocol aborts. The shares dealt by the qualified peers are summed, creating the final share. The commitment for this final share is calculated. To finalize the 2nd phase before concluding the DKG of p, we compare the transcript hashes of all peers. Thus each peer broadcasts their own together with the final commitments to all parties. ``` p_C = [] for i in 1..n p_C[i] = sum(p_commitments[j][i] for j in 1..n if peer[i] is qualified) if vsps(p_C) fails: for i in 1..n if vsps(p_commitments[i]) fails disqualify(p,i) p_s = 0, p_r = 0 for i in 1..n if i is not disqualfied(p): p_s += p_shares[i]_s p_r += p_shares[i]_r p_C = g^p_s * h^p_r transcript = final_ts(state) msg_20 = send_msg(20, peerid, 0, peer_sign_sk, session_id, {transcript, p_C}) send(msg_20) ``` ------<=[ 21. STP receives and verifies all transcripts ]=>------- STP receives all transcripts, and asserts that they all match its own transcript, it reports if any transcript mismatch is detected. It also does a final VSPS check on the commitments seen. ``` transcript = final_ts(state) msgs = [] p_commitments = [] for i in 1..N msg = recv(i) data = recv_msg(20, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg) ts, p_commitments[i] = data if ts != transcript report transcript mismatch msgs = msgs | msg if vsps(p_commitments) fails: report failure msg_14 = send_msg(14, 0, 0xff, stp_sign_sk, session_id, msgs) broadcast(msg_14) ------<=[ 22. DKGs END, start FT_Mult ]=>------- All peers receive the broadcasts transcripts and commitments, they run the same check as the STP in the previous step and abort if any of these fails. Otherwise the protocol continues with the FT-Mult phase of calculating kc'=kc*p. ------<=[ Phase 4 - FT-Mult ]=>----------- In this phase we calculate the product (kc') of the original key kc with p. All the peers pre-compute the inverted Vandermonde matrix based on the indices of the dealers for the multiplications, and cache its first row in their internal state. Peers start their FT-MULT computation of kc*p. In the following section we describe the steps for one FT-MULT calculation, we use the following notation: - a share of Ξ±: Ξ±_i = 𝑓_Ξ±(i), one of the values to multiply - a share of Ξ²: Ξ²_i = 𝑓_Ξ²(i), the other value to multiply - a share of π‘Ÿ: ρ_i = π‘Ÿ(i), a blinding factor for the homomorphic commitment of Ξ± - a share of 𝑠: Οƒ_i = 𝑠(i), blinding factor for the homomorphic commitment of Ξ² public inputs, for i:=1..n - 𝓐_i = 𝓗(Ξ±_i,ρ_i) = g^(Ξ±_i)*h^(ρ_i) - the commitments to the shares of the value to multiply - 𝓑_i = 𝓗(Ξ²_i,Οƒ_i) = g^(Ξ²_i)*h^(Οƒ_i) - the commitments to the shares of the other value to multiply We denote as a dealer all peers, whose index is smaller or equal to 2(t-1)+1. (we use t to denote the threshold, but we need to decrease it by one to get the degree of the polynomial) Each dealer P_i shares Ξ»_iΞ±_iΞ²_i, using VSS: c_ij = 𝑓_Ξ±Ξ²,i(j), Ο„_ij = u_i(j), where 𝑓_Ξ±Ξ²,i and u_i, are random polynomials of degree t, such that 𝑓_Ξ±Ξ²,i(0) = Ξ»_iΞ±_iΞ²_i (c_ij is a t-sharing of Ξ»_iΞ±_iΞ²_i, and Ο„_ij a t-sharing of a random value.) Ξ»_i is the first row of the inverted Vandermonde matrix, as defined by GRR98 section 3.1. and pre-computed and cached in this step. secret information of P_i: share c_ji, Ο„_ji of Ξ»_jΞ±_jΞ²_j public information: 𝓒_ij = g^(c_ij) * h^(Ο„_ij) for i,j := 1..n 𝓒_i0 = g^(c_i0) * h^(Ο„_i0) for i := 1..n Dealers broadcast their a hash of their 𝓒_i0, 𝓒_ij commitments for each of their sharings for kc*p, and the HMACs for each of the shares to all peers. ------<=[ 23. STP broadcasts commitment hashes and HMACs ]=>----------- STP does the regular broadcasting procedure. The STP keeps a copy of all commitment hashes and of all share HMACs for later. ------<=[ 24. Peers receive commitments hashes and HMACs]=>----------- Peers receive and store the commitment hashes and the share HMACs in an internal state for later. Dealers broadcast their commitments. ------<=[ 25. STP broadcasts 𝓒_i0, 𝓒_ij for kc*p ]=>----------- STP does the regular broadcasting procedure. STP verifies the commitment hashes received in the previous step against commitments, aborts if any of these fails. STP may also run VSPS checks on all of the commitments, reports any failures, but otherwise doesn't react to them and just continues. ------<=[ 26. Peers receive commitments, send shares ]=>----------- Peers receive all commitments and verify them against the commitment hashes. If this fails, they abort. Dealers send the shares c_ij, Ο„_ij for both kc*p to the corresponding peers via noise protected channel. ------<=[ 27. STP standard p2p routing shares ]=>----------- In this step the STP simply distributes the incoming Noise protected shares to their proper recipients. ------<=[ 28. Peers receive shares ]=>----------- Peers receive shares, verify them against the previously broadcast commitments. Peers broadcast complaints against any dealers failing their commitments. ------<=[ 29. STP broadcasts complaints ]=>----------- STP does the regular broadcasting procedure. If there are no complaints registered the STP will continue with the regular protocol, otherwise the next step will be conflict resolution. ------<=[ 30. Peers receive complaints ]=>----------- In this step the peers decide whether to continue with the regular protocol or they engage in conflict resolution. If there is complaints, the accused dealer broadcasts the contested encrypted shares together with their encryption keys. If there is no complaints, the regular protocol continues with the verification of the ZK proofs on the correctness of 𝓒_i0. ------<=[ Phase 5 - Recover from commitment failures ]=>----------- ------<=[ 31. STP broadcasts dealer defenses ]=>----------- In case there was complaints, the STP checks for each disclosure if the previously stored share HMACs equal the hmac(key, encrypted share), and if the commitment matches the decrypted share. The STP keeps track of these results to anticipate which dealer will send how much data in the next step. ------<=[ 32. Peers receive dealer defenses ]=>----------- Each peer checks for each disclosure if the previously stored share HMACs equal the hmac(key, encrypted share), and if the commitment matches the decrypted share. If both of these checks succeed, then the accused dealer is cleared and the accuser is reported, and the complaint is cleared. If there is no valid complaints, the regular protocol continues with the next phase: verification of the ZK proofs on the correctness of 𝓒_i0. For any valid remaining complaints, the peers reconstruct the accused dealers secret by broadcasting their share to everyone. ------<=[ 33. STP broadcasts shares for reconstruction ]=>----------- STP does the regular broadcasting procedure. Based on the received shares STP also reconstructs any shares and corrects any invalid commitments it has stored. Reconstruction is done as following: 0. we set the candidate threshold to the originally configured threshold. 1. all shares are checked against their commitments and any correct ones are selected for reconstructing the shared secret. If there is not enough correct shares to satisfy the threshold the reconstruction attempt fails. 2. the reconstructed secret is verified against the 𝓒_i0 commitment, if this succeeds the reconstruction attempt is successful and we continue with the next step. If the attempt fails we increment the threshold by one and abort if we have less shares than this new threshold, otherwise we try again with step 2. 3. If step 2 succeeded in reconstructing the secret with a candidate threshold, we reconstruct any contested shares and their commitments. ------<=[ 34. Peers receive shares for reconstruction ]=>----------- Peers reconstruct any shares that have been complained about - the accuser replaces their previously possibly faulty share with the reconstructed share - and calculate the correct commitment for this share, if this is different than previously received, this is replaced by the corrected one. See the previous STP step on how the reconstruction process works. This step has no output. Peers immediately continue with the next step. ------<=[ Phase 6 - ZK proof of correctness of product ]=>----------- ------<=[ 35. Dealers prove in ZK correctness of 𝓒_i0 ]=>----------- This step has no input, we arrive here either because there was no complaints, all complaints were invalid, or the valid complaints were neutralized by reconstruction of the contested shares. We now start the ZK proofs, in which P_i proves in zk that 𝓒_i0 is a commitment of the product Ξ»_iΞ±_iΞ²_i. As per Appendix F: ZK Proof for multiplication of committed values from GRR98 Note the paper GRR98 describes a ZK proof for C = g^Ξ±Ξ² * h^Ο„ however the multiplication algorithms Mult and FT-Mult require a proof for C = g^Ξ±Ξ²Ξ» * h^Ο„ Note the extra Ξ» in the exponent of g. to make this work a few changes are necessary, these are: z = (x+eΞ±Ξ») w_1 = (s_1 + Ξ»eρ) w_2 = (s_2 + e(Ο„ - σαλ)) and in the 2nd equation of the last step instead of A^e we need A^eΞ»: g^z * h^w_1 == M_1 * A^e'_iΞ» we have applied these changes in the following steps of the ZK proof specification We apply the optimization presented at the end of this Appendix F of GRR98: Each per chooses a challenge e_j,r_j ∈ Z_q, broadcasts a commitment 𝓒_ej = g^e_j * h^r_j ------<=[ 36. STP broadcasts 𝓒_ej commitments ]=>----------- STP does the regular broadcasting procedure. ------<=[ 37. Peers receive 𝓒_ej commitments ]=>----------- The peers store the 𝓒_ej commitments for later usage. Dealer P_i chooses d, s, x, s_1, s_2 ∈ Z_q. Broadcasts the following messages: M = g^d * h^s, M_1 = g^x * h^s_1, M_2 = B^x * h^s_2 ------<=[ 38. STP broadcasts M,M_1,M_2 messages ]=>----------- STP does the regular broadcasting procedure. STP keeps a copy of all the ZK commitments received. ------<=[ 39. Peers receive M,M_1,M_2 messages ]=>----------- Peers receive and store the M, M_1 and M_2 messages. Peers now broadcast their previously chosen e_j,r_j values. ------<=[ 40. STP broadcasts e_j,r_j values ]=>----------- STP does the regular broadcasting procedure. STP computes e'_i for all dealers P_i: e'_i = Ξ£ e_j j!=i and stores these for later. ------<=[ 41. Peers receive e_j,r_j values ]=>----------- Peers receive e_j,r_j values, verify then against the previously received, 𝓒_ej commitments: 𝓒_ej == g^e_j * h^r_j abort if this fails. each peer computes e'_i for all dealers P_i: e'_i = Ξ£ e_j j!=i Each dealer P_i broadcasts the following values: y = d + e'_iΞ², w = s + e'_iΟƒ z = x + e'_iΞ±Ξ» w_1 = s_1 + e'_iρλ w_2 = s_2 + e'_i(Ο„ - σαλ) ------<=[ 42. STP broadcasts proofs ]=>----------- STP does the regular broadcasting procedure. Verifies all ZK proofs and uses this information to decide with step to continue with in the protocol. ------<=[ 43. Peers receive proofs checks them. ]=>----------- Each peer checks for each proof the following equations. g^y * h^w == M * B^e'_i g^z * h^w_1 == M_1 * A^e'_iΞ» B^z * h^w_2 == M_2 * C^e'_i If all the proofs are correct the protocol continues with completing the FT-Mult results. This step has no output, the output is generated by the next step which is immediately executed by the peers after this one. ------<=[ Phase 7 - Recover from ZK proof failures ]=>----------- In this phase peers disclose the shares from the dealer that failed to prove the correctness of C_i0 so that this C_i0 can be corrected. ------<=[ 44. Peers receive proofs checks them. ]=>----------- If any of the ZK proofs fail, the peers expose the shares of the dealer P_i who failed the proof and engage in a reconstruction phase. Peers broadcast the plaintext shares of the dealers who failed. ------<=[ 45. STP broadcasts shares for reconstruction ]=>----------- STP does the regular broadcasting procedure. STP also reconstructs the secret of the sharing and verifies that it satisfies 𝓒_i0, if that fails, the STP aborts the protocol. ------<=[ 46. Peers receive shares for reconstruction ]=>----------- Peers reconstruct the secret of any dealers failing their ZK proof, the reconstructed secret is checked if it matches the corresponding 𝓒_i0 commitment, if this fails the peers abort. This step has no output, peers immediately continue with the next phase/step. ------<=[ Phase 8 - Finish FT-Mult ]=>----------- In this phase the FT-Mult protocol concludes, one last check and if needed reconstruction phase provide robustness. ------<=[ 47. Peers calculate FT-MULT results ]=>----------- Note this step has no input it follows directly if all ZK proofs were correct or if the failing dealers secrets have been successfully reconstructed. Each peer P_i computes: 2t+1 Ξ³_i = Ξ£ c_ji j=1 which is a share of Ξ³ = Ξ±Ξ², via random polynomial of degree t and 2t+1 Ο„_i = Ξ£ Ο„_ji j=1 Each peer also computes 𝓒_i = 𝓗(Ξ³_i, Ο„_i) = g^(Ξ³_i)*h^(Ο„_i) and also: 2t+1 𝓒'_i = Ξ  𝓒_ji j=1 Then compares 𝓒_i == 𝓒'_i, and aborts if this fails. Otherwise the peers broadcast their final 𝓒_i commitment. ------<=[ 48. STP broadcasts 𝓒_i commitment ]=>----------- STP does the regular broadcasting procedure. However STP keeps a copy of each of the final commitments, for later verification of the final reconstruction of p. STP also runs a fast-track VSPS check on all the commitments, depending on the result of this deciding on the next step of the protocol regular or reconstruction. ------<=[ 49. Peers receive 𝓒_i, run VSPS on them ]=>----------- players run a VSPS Check on 𝓒_i for both kc*p, if it fails peers run a VSPS Check on every dealers sharing, identifying the one that fails the VSPS check. If the fast-track VSPS checks succeed on the final commitments the protocol continues with the regular final phase, otherwise the protocol for a last time engages in a reconstruction of secrets of dealers who failed the VSPS check of their commitments. This step has no output message, either of the two possible follow-up steps will generate an output message. ------<=[ Phase 9 - Recover from failing VSPS-checks ]=>----------- In this phase any failing VSPS-checks are countered by reconstruction of the secret of the dealer who failed the check, the secret is then re-shared using a "deterministic" sharing. ------<=[ 51. Peers disclose shares of failed dealers ]=>----------- In case the VSPS checks failed in the previous step, all peers disclose the shares of the dealer(s) who has failed the VSPS check. ------<=[ 52. STP broadcasts shares and re-shares ]=>----------- The STP broadcasts all the disclosed shares. The STP also reconstructs the secrets associated with these shares, and then creates a "deterministic" re-sharing of this secret and updates the commitments it holds to these new shares and re-calculates the final commitments based on these. The "deterministic" re-sharing of the reconstructed shares is based on a polynomial with the constant term being the secret and the other coefficients being derived by using HKDF-expand(info, prk), where the prk is the current sessionid and the info parameter is either "k0p lambda * a * b re-sharing" or "k0p blind re-sharing" for the secret and the blinding factor respectively. This deterministic re-sharing is used so that all parties in the protocol can create the same re-sharing without any party having control over this and without needing to distribute extra information to all parties. ------<=[ 53. Peers receive shares and re-share ]=>----------- Peers receive disclosed shares of all dealers who failed the VSPS check, and re-share the reconstructed secrets in the same way as the STP does using the same "deterministic" re-sharing procedure. This step has no output message, it immediately followed by the next step sending the results of both multiplications to the STP. ------<=[ Phase 10 - Finalize TOPRF Update ]=>----------- This phase concludes the update of the TOPRF secret. ------<=[ 54. Peers send their final shares to STP ]=>----------- The peers send their shares of p to the STP. ------<=[ 55. STP receives results and calculates delta ]=>----------- The STP receives all shares from peers, runs a VSPS check on their commitments, aborts if these fail. It also verifies the commitments of these shares, again, aborts if these fail. Otherwise reconstructs from them the value: βˆ† = ρ Finally the STP sends out a message of one byte of value 0 to all peers indicating the success of the protocol. The protocol finishes for the STP. ------<=[ 44. Peers complete the protocol ]=>----------- If the received message from STP is 0, they replace the share (and commitment) for the old kc with the new share (and commitment) for kc'=(ρ·kc). Otherwise they keep the old kc. The protocol finishes here for the peers. ------<=[ TODO verify transcript ]=>----------- All parties broadcast their transcript, and verify that they all match. If there is non-matching, abort. Maybe make the transcript a rolling check, by for example using it as a continuously updated sessionid. ------<=[ References ]=>----------- [GRR98] R. Gennaro, M. O. Rabin, and T. Rabin. "Simplified VSS and fact-track multiparty computations with applications to threshold cryptography" In B. A. Coan and Y. Afek, editors, 17th ACM PODC, pages 101–111. ACM, June / July 1998 [JKR19] "Updatable Oblivious Key Management for Storage Systems", by Stanislaw Jarecki, Hugo Krawczyk, and Jason Resch. [RFC9497] Oblivious Pseudorandom Functions (OPRFs) Using Prime-Order Groups https://www.rfc-editor.org/rfc/rfc9497.html liboprf-0.9.4/docs/tp-dkg.txt000066400000000000000000000702311514673400200160450ustar00rootroot00000000000000Trusted-party (TP) Distributed Key Generation (DKG) v1.0 This document specifies a proposal for a non-robust DKG that can work for small deployments with a small number of parties and infrequent DKG executions. Non-robust means that the protocol succeeds only if no party aborts. If someone aborts then the protocol needs to run again, possibly after kicking out misbehaving parties. This protocol does support maximum 127 peers. This is probably already too much for a non-robust protocol, but it might work in very special circumstances. Broadcast is implemented by the trusted party (TP) opening a channel to each peer secured by the peers long-term encryption key. Every message is routed through the TP. Peer long-term encryption keys can be either TLS-based, or Noise_XK-based (https://noiseexplorer.com/patterns/XK/). In the latter case the long-term public keys must be known and validated in advance by the TP. The basis for this protocol is JF-DKG (fig 1.) a variant on Pedersens DKG from the 2006 paper "Secure Distributed Key Generation for Discrete-Log Based Cryptosystems" by R. Gennaro, S. Jarecki, H. Krawczyk, and T. Rabin [GJKR06]. The algorithm JF-DKG is presented in the paper as a reduced example how an adversary can influence the bits in the generated secret by manipulating the complaints and thus the final composition of the QUAL set, gaining a 3/4 chance to influence a bit. Since in our TP variant is non-robust, we do not allow individual disqualifications of peers, - either all peers qualify or the protocol fails - this mitigates the case where an adversary can adaptively disqualify a peer. Thus the JF-DKG is a simple and sufficient algorithm for our purposes. ------<=[ Rationale ]=>----------- Traditionally DKGs are used in setting where all parties are equal and are using the distributed key together, without having any one party having a different role in the protocol utilizing the shared key. This does not translate entirely to threshold OPRFs (tOPRF) and protocols based on these. In an OPRF there is normally two parties, one holding the key, and another one holding the input and learning the output. In a tOPRF the party holding the key is a group of peers that hold shares of the key in a threshold setting. The whole point of OPRFs is to be able to learn the output for a certain input, without being able to do so without the contribution of the party/parties holding (parts of) the key. Hence the party with the input is in a kind of trusted role, and in many protocols based on OPRFs it is in the best interest of the input-holding party to not learn the key (or its parts) - otherwise the input-holding party could just deploy a PRF instead. And if the input holding party is in such a trusted role, there is two options to generate a threshold shared key: 1. the trusted input-holding party just generates a secret and shares it with the key-holding parties using Shamir's Secret Sharing. This is a very simple approach, with one drawback, the secret itself is however briefly know at the input-holding TP. 2. The input-holding TP can run the simple non-robust DKG specified below. This has the benefit that as long as the protocol is followed precisely the secret is never "assembled" and thus cannot leak, and is never exposed to the TP. Drawback of this is, that the protocol below consists of many rounds of communication. The protocol in this document allows for a variant, were each keyshare-holder generates a completely new set of ephemeral (encryption and signature) keys, and thus allows complete anonymity between the keyshare-holders from each other. While only the TP is aware of the identities of each of the keyshare-holders (by knowing their long-term signature and encryption keys). This increases the security of the whole scheme, as an attacker compromising one keyshare-holder will not be able to learn the identity of the other parties - and more importantly the location of the other keyshares. If this keyshare-holder anonymity is not necessary, steps 3, 4 and the first half of step 5 in the following protocol can be skipped. ------<=[ Prototocol Phases ]=>----------- The protocol has the following phases: 1. Initialization and introduction (step 1 - 5) 2. Setup secure P2P channels (step 5 - 10) 3. core DKG (step 11 - 17) 4. Finish with failure: complaint resolution (only if there are complaints) (step 17 - 19) 5. Finish with success: verification of transcript and completion of protocol (step 20 - 22) ------<=[ Simplified API ]=>----------- Since the protocol consists of many steps, it is recommended to abstract the API to the following schema: 0. Initialize While not done and not error: 1. Allocate input buffers 2. input = receive() 3. allocate output buffer 4. run next step of protocol 5. if there is output: send(output) 6. Post-processing This simple schema simplifies the load of an implementer using this protocol, reducing opportunities for errors and provides strict security. The reference implementation in tp-dkg.c follows this schema for both the TP and the peers. ------<=[ Protocol transcript ]=>----------- Transcript - all broadcast messages are accumulated into a transcript by each peer and the trusted party, at the end of the protocol all parties publish their signed transcripts and only if all signatures are correct and the transcripts match, is the protocol successful. The transcript is a hash, that is initialized with the string: "tp dkg session transcript" in pseudo-code: transcript_state = hash_init("tp dkg session transcript") Updating the transcript first updates the hash with the canonical 32bit size of the message to be added to the transcript, then the message itself is added to the hash. transcript_state = hash_update(transcript_state, I2OSP(len(msg)) transcript_state = hash_update(transcript_state, msg) The signature of each message is similarly added to the transcript. A function `update_ts` can be used as a high-level interface to updating the transcript with messages and their signatures: ``` update_ts(state,msg,sig) state = hash_update(state, I2OSP(len(msg)) state = hash_update(state, msg) state = hash_update(state, I2OSP(len(sig)) state = hash_update(state, sig) return state ``` ------<=[ Session id ]=>----------- Every execution of the protocol starts by the TP sending out a message with a unique and fresh session id, this is to ensure that no messages can be replayed. The session id is a 256 bit (32B) random value of cryptographic quality. ------<=[ Message header ]=>----------- All messages have a message header: uint8 type uint8 version = 0 uint8 messageno uint32 len uint8 from uint8 to uint64 timestamp uint8 sessionid[32] The first field is the protocol type identifier. TP-DKG has an identifier value of zero (0). The second field in the header is really a state identifier. A recipient MUST verify that the messageno is matching with the expected number related to the state of the protocol. The len field MUST be equal to the size of the packet received on the network including the packet header. The `from` field is simply the index of the peer, since peers are indexed starting from 1, the value 0 is used for the trusted party. Any value greater than 128 is invalid. The state defines from whom to receive messages, and thus the from field MUST be validated against these expectations. The `to` field is similar to the `from` field, with the difference that the value 0xff is reserved for broadcast messages. The peer (or TP) MUST validate that it is indeed the recipient of a given message. The timestamp field is just a 64bit timestamp as seconds elapsed since 1970/01/01, for peers that have no accurate clock themselves but do have an RTC, the first initiating message from the TP SHOULD be used as a reference for synchronizing during the protocol. ------<=[ Message signatures ]=>----------- Every message MUST be signed using the sender peers ephemeral signing key. The signature is made over the message and the appended session id. The session id is announced by the TP in the first message. ------<=[ Verifying messages ]=>----------- Whenever a message is received by any participant, they first MUST check the correctness of the signature: ``` msg, sig = recv() sign_pk = sign_keys[expected_sender_id] assert(verify(sign_pk, msg, sig)) ``` The recipient MUST also assert the correctness of all the other header fields: ``` assert(msg.type == 0) assert(msg.version == 0) assert(msg.messageno == expected_messageno) assert(msg.from == expected_sender_id) assert(msg.to == (own_peer_id or 0xff)) assert(ref_ts <= msg.ts < ref_ts + timeout)) ref_ts = msg.ts ``` The value `timeout` should be configurable and be set to the smallest value that doesn't cause protocol aborts due to slow responses. If at any step of the protocol the TP receives one or more messages that fail these checks, the TP MUST abort the protocol and report all violating peers to the user. ------<=[ Message transmission ]=>----------- A higher level message transmission interface can be provided, for sending: ``` msg, sig = send_msg(msgno, from, to, sign_sk, session_id, data) ts = timestamp() msg = messageno: msgno, len: len(header) + len(data) + len(sig), from: from, to: to, ts: ts, data sig = sign(sign_sk, msg) return msg, sig ``` And for validating incoming messages: ``` data = recv_msg(msgno, from, to, ref_ts, sign_pk, session_id, msg, sig) assert(verify(sign_pk, msg, sig) assert(msg.type == 0) assert(msg.version == 0) assert(msg.messageno == msgno) assert(msg.len == len(msg|sig)) assert(msg.from == from) assert(msg.to == to) assert(ref_ts < msg.ts < ref_ts + timeout)) if msg.to == 0xff: update_ts(state,msg,sig) ``` The parameters `msgno`, `from`, `to`, `session_id` should be the values expected according to the current protocol state. ------<=[ Cheater detection ]=>----------- The TP MUST report to the user all errors that can identify cheating peers in a given step. For each detected cheating peer the TP MUST record the following information: - the current protocol step, - the violating peer, - the other peer involved, and - the type of violation In order to detect other misbehaving peers in the current step, processing for the rest of the SHOULD peers continue until the end of the current step. Any further violations should be recorded as above. Before the next message to the peers is sent, the TP must check if there are no noted violations, if so the TP aborts and reports all violators with their parameters to the user. Abort conditions include any errors detected by recv_msg(), or when the number of complaints is more than t for one peer, or more than t^2 in total, as well any of the checks of the JF-DKG algorithm from GJKR06. ------<=[ The protocol ]=>----------- ------<=[ 0. Precondition ]=>----------- Peers use TLS or TP knows long-term encryption keys for all peers. Client knows long-term signing keys of all peers. ------<=[ 1. DKG Announcement - TP(peers, t, proto_name) ]=>---------- The protocol starts by asking the trusted party (TP) to initiate a new run of the DKG protocol by providing it with: - a list of the peers, - a threshold value, and - protocol instance name used as a domain separation token. The TP then sanity checks these parameters: ``` n = len(peers) assert(2<=t0) ``` The TP then generates a fresh session id, and a hash of the DST. The TP then creates a broadcast message containing the session id, a hash (so that the message is always of fixed size) of the DST, the values N and T and its own public signing key: ``` dst_str = "TP DKG for protocol %s" % proto_name dst = hash(I2OSP(len(dst_str)) | dst_str) sessionid = random_bytes(32) data = {dst, n, t, tp_sign_pk} msg_0, sig_0 = send_msg(0, 0, 0xff, tp_sign_sk, session_id, data) broadcast(msg_0 | sig_0) ``` The TPs copy of the transcript is initialized by the TP, and updated with the value of the 1st broadcast message: ``` state = hash_init("tp dkg session transcript") state = update_ts(state, msg, sig) ``` Since the order of the peers is random, and important for the protocol a custom message is created for each peer by the TP and sent individually notifying each peer of their index in this protocol run. This is essentially an empty message consisting only of a header. The msg.to field conveys the index of the peer. ``` # sending each peer its index for i in 1..n: msg_1, sig_1 = send_msg(1, 0, i, tp_sign_sk, session_id, {}) send(i, msg_1 | sig_1) ``` ------<=[ 2. each peer(msg_0, sig_0) ]=>------------ In this step each peer receives the initial parameter broadcast, verifies it, initializes the transcript and adds the initial message. Then receives the message assigning its index. ``` msg_0, sig_0 = recv() assert(recv_msg(0, 0, 0xff, ref_ts, msg.data.tp_sign_pk, session_id, msg_0, sig_0)) ``` If the peer has no accurate internal clock but has at least an RTC, it SHOULD set the ref_ts to the message timestamp: ``` ref_ts = msg_0.ts ``` Furthermore the peer MUST also verify that the N&T parameters are sane, and if possible the peer SHOULD also check if the session id is fresh (if it is not possible, isfresh() MAY always return true. ``` assert(2 <= msg_0.t < n) assert(isfresh(msg_0,sessionid)) ``` The transcript MUST be initialized by the peer, and updated with the value of the 1st broadcast message: ``` state = hash_init("tp dkg session transcript") state = update_ts(state, msg, sig) ``` After processing the broadcast message from the TP, the peers also have to process the second message from the TP in which they are assigned their index. ``` sig1, msg1 = recv() assert(recv_msg(1, 0, msg1.to, ref_ts, tp_sign_pk, session_id, msg_1, sig_1)) assert(msg1.to <= 128 and msg1.to > 0) peerid = msg.to ``` ------<=[ 3. peers broadcast their keys via TP ]=>------------- If this protocol requires anonymity from each peer all peers broadcast fresh signing and noise keys to all peers via the TP. If no peer-anonymity is required it is ok to either send long-term keys keys here, or skip to the 2nd half or step 5 below. In order to assure the TP that the peer is authentic, this message is additionally signed by the peers long-term signing key - which must be known in advance by the TP. This ensures that the fresh ephemeral keys belong to the peer and not some adversary. ``` peer_sign_sk, peer_sign_pk = sign_genkey() peer_noise_sk, peer_noise_pk = noise_genkey() msg_2, sig_2 = send_msg(2, peerid, 0xff, peer_sign_sk, session_id, {peer_sign_pk, peer_noise_pk}) ltsig = sign(peer_long_term_sig_sk, msg_2|sig_2) broadcast(ltsig | msg_2 | sig_2 ) ``` ------<=[ 4. TP collects and broadcasts all peer keys ]=>------------- The TP first checks if each of the received messages is signed by the expected long-term signing key, if this fails the TP aborts. If all long-term signatures are correct the TP MUST strip those signatures from all the messages. This is to ensure their anonymity from each other. Then the TP acts as a broadcast medium on the long-term signature-stripped messages. This is a recurring pattern where the TP acts in its broadcasting intermediary role: 1. receives the messages from each peer 2. validates the message using recv_msg() 3. extracts all signing pubkeys (or other information depending on the current step) for usage by the TP in the rest of the protocol 4. concatenates all received messages into a new message 5. signs the message of messages 6. adds this the message of messages and its signature to the transcript 7. sends it to all peers ``` peer_sig_pks = [] msgs = [] for i in 1..N ltsig, msg_2, sig_2 = recv() assert(verify(lt_sign_pk[i], msg_2 | sig_2, ltsig)) sig_pk, noise_pk = recv_msg(2, i, 0xff, ref_ts, msg_2.data.peer_sign_pk, session_id, msg_2, sig_2) peer_sig_pks[i] = sig_pk msgs = msgs | { msg_2 , sig_2 } msg_3, sig_3 = send_msg(3, 0, 0xff, tp_sign_sk, session_id, msgs) state = update_ts(state, msg_3, sig_3) broadcast(msg_3|sig_3) ``` ------<=[ 5. each peer get all keys and initiate noise channels with all peers ]=>------- In this phase all peers process the broadcast signing and noise keys received from all peers, and initiate a noise_xk handshake with each of them (including themselves for simplicity and thus security). Note: For performance it MAY be, that each peer only initiates handshakes with peers having a higher index than themselves. But this would create a packet-size and timing side-channel revealing the index of the peer. ``` msg_3, sig_3 = recv() msgs = recv_msg(3, 0, 0xff, ref_ts, tp_sign_pk, session_id, msg_3, sig_3) state = update_ts(state, msg_3, sig_3) peers_sign_pks = [] peers_noise_pks = [] send_session = [] for i in 1..N msg, sig = msgs[i] peers_sign_pks[i], peers_noise_pks[i] = recv_msg(2, i, 0xff, ref_ts, msg.peer_sign_pk, session_id, msg, sig) send_session[i], handshake1 = noisexk_initiator_session(peer_noise_sk, peers_noise_pks[i]) msg, sig = send_msg(4,peerid,i,peer_sign_sk, session_id, handshake1) send(msg | sig) ``` ------<=[ 6. TP routes handshakes from each peer to each peer ]=>------- The TP receives all 1st handshake messages from all peers and routes them correctly to their destination. These messages are not broadcast, each of them is a P2P message. The benefit of the TP forming a star topology here is, that the peers can be on very different physical networks (wifi, lora, uart, nfc, bluetooth, etc) and only the TP needs to be able to connect to all of them. ``` for i in 1..N handshakes = recv(i) for j in 1..N send(j, handshakes[j]) ``` ------<=[ 7. each peer responds to each handshake each peer ]=>------- Peer receives noise handshake1 from each peer and responds with handshake2 answer to each peer. ``` for i in 1..N msg, sig = recv() handshake1 = recv_msg(4, i, peerid, ref_ts, peers_sign_pks[i], session_id, msg, sig) receive_session[i], handshake2 = noisexk_responder_session(peer_noise_sk, handshake1) msg, sig = send_msg(5, peerid, i, peer_sign_sk, session_id, handshake2) send(msg | sig) ``` ------<=[ 8. TP routes handshakes from each peer to each peer ]=>------- TP just routes all P2P messages from all peers to the correct recipients of the messages. ``` for i in 1..N handshakes = recv(i) for j in 1..N send(j, handshakes[j]) ``` ------<=[ 9. each peer completes each handshake with each peer ]=>------- Peers complete the noise handshake. ``` for i in 1..N msg, sig = recv() handshake3 = recv_msg(5, i, peerid, ref_ts, peers_sign_pks[i], session_id, msg, sig) send_session[i] = noisexk_initiator_session_complete(send_session[i], handshake3) ``` ------<=[ 10. Setup complete ]=>------- Each peer has a confidential connection with every peer (including self, for simplicity) The one time this channel is used, when distributing the shares from step 13. The sender uses the initiator interface of the noise session, and the receiver uses the responder interface. ------<=[ 11. each peer executes DKG Round 1 ]=>------- This step is as described by GJKR06 (fig 1. JF-DKG) step 1: Each party P_i (as a dealer) chooses a random polynomial f_i(z) over Z_q of degree t: f_i(z) = a_(i0) + a_(i1)z + Β·Β·Β· + a_(it)z^t P_i broadcasts A_ik = g^(a_ik) mod p for k = 0,... ,t. Each P_i computes the shares s_ij = f_i(j) mod q for j = 1, ... ,n. ``` a = [] A = [] for i in 0..t a[i]=randombytes(32) A[i]=g*a[i] s = [] for i in 1..N for j in 0..t s[i]+=a[j]*i^j msg_6, sig_6 = send_msg(6, peerid, 0xff, peer_sign_sk, session_id, A) send(msg_6 | sig_6) ``` ------<=[ 12. TP collects and broadcasts all A vectors ]=>------- This is another broadcast pattern instance: receive-verify-collect-sign-transcript-broadcast. The TP keeps a copy of all commitments being broadcast. ``` A = [][] msgs = [] for i in 1..N msg_6, sig_6 = recv(i) A[i] = recv_msg(6, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_6, sig_6) msgs = msgs | { msg_6 , sig_6 } msg_7, sig_7 = send_msg(7, 0, 0xff, tp_sign_sk, session_id, msgs) state = update_ts(state, msg_7, sig_7) broadcast(msg_7|sig_7) ``` ------<=[ 13. each peer collects all A vectors and distributes their generated shares ]=>------- All peers receive the bundled A commitment messages which have been sent by all peers and re-broadcast by the TP. First the bundle is verified, then each message containing the j-th A commitment vector is also verified. A copy of all A commitment vectors is retained for later usage. Then the share for the j-th peer is sent using the previously established noise channel to the j-th peer. These shares have been already computed in step 11, as per the step 1 of the JF-DKG algorithm from the GJKR06 paper. ``` msg_7, sig_7 = recv() msgs = recv_msg(7, 0, 0xff, ref_ts, tp_sign_pk, session_id, msg_7, sig_7) state = update_ts(state, msg_7, sig_7) A=[][] for i in 1..N msg, sig = msgs[i] A[i] = recv_msg(6, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg, sig) pkt = noise_send(send_session[i], s[i]) msg, sig = send_msg(8,peerid,i,peer_sign_sk, session_id, pkt) send(msg | sig) ``` ------<=[ 14. TP routes noise protected messages between peers ]=>------- Since all these messages are confidential P2P messages protected by noise, all the TP is doing in this step is routing each packet to its correct destination. For the resolution of complaints and cheater identification, TP keeps a copy of all messages. ``` encrypted_shares = [][] for i in 1..N for j in 1..N msg = recv(i) send(j, msg) encrypted_shares[i][j] = msg ``` ------<=[ 15. each peer executes DKG Round 2 ]=>------- Each peer having received all their shares from all the peers, verifies the messages, and then verifies the shares against the previously broadcast A commitment vectors. For each s_ij, A_i pair that fails, a complaint against the peer producing the conflicting commitment and share is logged in an array, which is broadcast to everyone. This is essentially step 2 from the JF-DKG algorithm described in GJKR06. ``` s=[] for i in 1..N msg, sig = recv() pkt = recv_msg(8, i, peerid, ref_ts, peer_sign_pks[i], session_id, msg, sig) s[i] = noise_recv(receive_session[i], pkt) complaints = [] for i in 1..N v = 0 for k in 0..t v += A[i][k]*peerid*k if (g*s[i] != v) complaints = complaints | i msg, sig = send_msg(9, peerid, 0xff, peer_sign_sk, session_id, len(complaints) | complaints) send(msg | sig) ``` ------<=[ 16. TP collects complaints ]=>------- Another receive-verify-collect-sign-transcribe-broadcast instantiation. The TP keeps a copy of all complaints for the 18th step. If any peer complaints about more than t peers, that complaining peer is a cheater, and must be disqualified. Furthermore if there are in total more than t^2 complaints there are multiple cheaters and the protocol must be aborted and new peers must be chosen in case a rerun is initiated. ``` complaints = [] msgs = [] for i in 1..N msg_9, sig_9 = recv(i) complaints_i = recv_msg(9, i, 0xff, ref_ts, peer_sign_pks[i], session_id, msg_9, sig_9) assert(len(complaints_i) < t) complaints = complaints | complaints_i msgs = msgs | { msg_9 , sig_9 } assert(len(complaints) < t^2) msg_10, sig_10 = send_msg(10, 0, 0xff, tp_sign_sk, session_id, msgs) state = update_ts(state, msg_10, sig_10) broadcast(msg_10|sig_10) ``` The next step of the protocol depends on the number of complaints received, if none then the next step is 21. otherwise 18. If the next TP step is 18 (there are complaints) the next input buffer size depends on the number of complaints against each peer. Each complaint is answered by the symmetric encryption key used to encrypt the share of the accused belonging to the complainer. Each accused packs all answers into one message. ------<=[ 17. Each peer receives all complaints ]=>------- All complaint messages broadcast are received by each peer. If peer_i is being complained about by peer_j, peer_i sends the symmetric encryption key that was used to encrypt s_ij to the TP. This is the first part of step 3. in JF-DKG of GJKR06. There is a slight variation, instead of broadcasting the share, the accused peer reveals the symmetric encryption key that was used to encrypt the share. The TP has a copy of this encrypted message, and with the symmetric encryption key, it can decrypt the originally sent share. This is some kind of poor mans provable encryption. If any complaints have been lodged by any peer the protocol ends here for all the peers. ``` msg_10, sig_10 = recv() msgs = recv_msg(10, 0, 0xff, ref_ts, tp_sign_pk, session_id, msg_10, sig_10) state = update_ts(state, msg_10, sig_10) keys = [] for i in 1..N msg, sig = msgs[i] complaints_len, complaints = recv_msg(9, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg, sig) for k in 0..complaints_len if complaints[k] == peerid # complaint about current peer, publish key used to encrypt s_ij keys = keys | send_session[i].key if len(keys) > 0 msg_11, sig_11 = send_msg(11, peer, 0, peer_sign_sk, session_id, keys) send(msg_11, sig_11) ``` ------<=[ 18. TP collects all s_ij, broadcasts and verifies them ]=>------- In this step TP checks equation 3 from step 2 in JF-DKG of GJKR06. TP also checks if all complaints lodged earlier are answered by the correct s_ij shares. The shares to be verified are decrypted from the previously encrypted messages, using the revealed encryption keys by the accused peers. The protocol ends here, as either the complainer or the accused tried to cheat. ``` for i in 1..N if len(complaints[i]) < 1 continue msg, sig = recv(i) keys = recv_msg(11, i, 0, ref_ts, peers_sign_pks[i], session_id, msg, sig) assert(len(keys) == len(complaints[i])) sij=[][] for j, key in keys sij[i][j]=decrypt(key, encrypted_shares[i][j]) for complaint in complaints[i] v = 0 for k in 0..t v += A[i][k]*peerid*k if(g*sij[complaint.from][complaint.data] != v) suspicious = suspicious | identity(i) else suspicious = suspicious | identity(j) ``` ------<=[ 19. Compare all transcripts ]=>------- Each peer calculates the final transcripts and sends it to TP. ``` transcript = final_ts(state) msg_20, sig_20 = send_msg(20, peerid, 0, peer_sign_sk, session_id, transcript) send(msg_20, sig_20) ``` ------<=[ 20. TP receives all and verifies transcripts ]=>------- TP receives all transcripts, and asserts that they all match its own transcript, it aborts if any transcript mismatch is detected. If everything matches it broadcasts the result either as OK. ``` transcript = final_ts(state) for i in 1..N msg, sig = recv(i) ts = recv_msg(20, i, 0xff, ref_ts, peers_sign_pks[i], session_id, msg, sig) assert( ts == transcript) msg_21, sig_21 = send_msg(21, 0, 0xff, tp_sign_sk, session_id, { "OK" }) ------<=[ 21. SUCCESS, peers set their share and confirm ]=>------- All peers receive the OK acknowledgment from the TP and calculate the final share, this is equivalent with the calculation of x_j in the 4. step in JF-DKG of GJKR06. Finally all peers acknowledge this step with another "OK" message sent to the TP. This is the final step for the peers, each needs to persist the calculated x_j share for usage in later threshold protocol runs (such as tOPRF). ``` msg_21, sig_21 = recv() recv_msg(21, 0, 0xff, ref_ts, tp_sign_pk, session_id, msg_21, sig_21) share = 0 for i in 1..N share += s[i] msg_22, sig_22 = send_msg(22, peerid, 0, peers_sign_sk, session_id, "OK") persist(own_peer_id, share) ``` ------<=[ 22. TP asserts all peers respond with "OK" ]=>------- The TP collects all "OK" messages from all peers. ``` for i in 1..N msg, sig = recv(i) ok = recv_msg(22, i, 0, ref_ts, peers_sign_pks[i], session_id, msg, sig) assert( ok == "OK") ``` This successfully concludes the protocol. liboprf-0.9.4/liboprf.pc000066400000000000000000000004511514673400200151440ustar00rootroot00000000000000includedir=${prefix}/include Name: liboprf Description: implementation of OPRF (RFC9497) including (updatable) threshold variant Version: 0.7.1 Cflags: -I${includedir}/oprf -I${includedir}/oprf/noiseXK/ -I${includedir}/oprf/noiseXK/karmel -I${includedir}/oprf/noiseXK/karmel/minimal Libs: -loprf liboprf-0.9.4/misc/000077500000000000000000000000001514673400200141165ustar00rootroot00000000000000liboprf-0.9.4/misc/attack.c000066400000000000000000000074721514673400200155430ustar00rootroot00000000000000// # SPDX-FileCopyrightText: 2024, Marsiske Stefan // # SPDX-License-Identifier: GPL-3.0-or-later // build with // $ gcc -Wall -O3 attack.c -o attack -loprf -lsodium // then run: // $ ./attack test #include // memcmp #include // f?printf #include // uint8_t #include // va_list, va_start, va_end #include #include static const uint8_t k[crypto_core_ristretto255_SCALARBYTES] = {1}; void dump(const uint8_t *p, const size_t len, const char* msg, ...) { va_list args; va_start(args, msg); vfprintf(stderr,msg, args); va_end(args); fprintf(stderr,"\t"); for(size_t i=0;ibeta\n", exec); printf("usage: cat rwd | %s guess password\n", exec); return ret; } static int tamper(const uint8_t alpha[crypto_core_ristretto255_BYTES], uint8_t beta[crypto_core_ristretto255_BYTES]) { puts("tampering"); dump(k, sizeof k, "k"); if(0!=crypto_scalarmult_ristretto255(beta, k, alpha)) { fputs("failed to tamper with k\nabort.\n", stderr); return 1; } return 0; } static int guess(uint8_t rwd[OPRF_BYTES], const uint8_t *pwd, const size_t pwd_len) { //fputs("[1] hashing to group...", stdout); uint8_t h0pwd[crypto_core_ristretto255_BYTES]={0}; if(0!=voprf_hash_to_group(pwd, pwd_len, h0pwd)) { fputs("failed to hash to group\nabort\n", stderr); return 1; } // tamper(h0pwd, h0pwd) uint8_t rwd_[OPRF_BYTES]; if(0!=oprf_Finalize(pwd, pwd_len, h0pwd, rwd_)) { fputs("failed to finalize OPRF\nabort\n", stderr); return 1; } if(memcmp(rwd,rwd_, OPRF_BYTES)!=0) return -1; return 0; } static int test(void) { // regular OPRF flow on the client const uint8_t password[] = "Exploitability of this is low, OPRFs are still cool"; uint8_t alpha[crypto_core_ristretto255_BYTES]={0}; uint8_t r[crypto_core_ristretto255_SCALARBYTES]={0}; if(0!=oprf_Blind(password, sizeof password, r, alpha)) { fputs("failed to blind password\nabort\n", stderr); return 1; } //dump(r, sizeof r, "r"); // we tamper with beta uint8_t beta[crypto_core_ristretto255_BYTES]={0}; dump(alpha, sizeof alpha, "alpha"); tamper(alpha, beta); dump(beta, sizeof beta, "beta"); // regular OPRF flow on the client uint8_t N[crypto_core_ristretto255_BYTES]={0}; int x = oprf_Unblind(r, beta, N); if(0!=x) { fputs("failed to unblind beta\nabort\n", stderr); return 1; } uint8_t rwd[OPRF_BYTES]; if(0!=oprf_Finalize(password, sizeof password, N, rwd)) { fputs("failed to finalize OPRF\nabort\n", stderr); return 1; } // we "intercept" the oprf output and guess candidate inputs fprintf(stderr, "guess(\"%s\") = %d\n", password, guess(rwd, password, sizeof password-1)); fprintf(stderr, "guess(\"%s\") = %d\n", password, guess(rwd, password, sizeof password)); return 0; } int main(const int argc, const char** argv) { if(argc<2) { return usage(argv[0], 0); } if(memcmp(argv[1],"tamper",7)==0) { uint8_t alpha[crypto_core_ristretto255_BYTES]; if(fread(alpha, 1, 32, stdin) != 32) { fputs("failed to read point\nabort.\n", stderr); return 1; } uint8_t beta[crypto_core_ristretto255_BYTES]; if(0!=tamper(alpha, beta)) { return 1; }; fwrite(beta, 1, sizeof beta, stdout); return 0; } if(memcmp(argv[1],"guess",6)==0) { if(argc<3) { return usage(argv[0], 1); } uint8_t rwd[OPRF_BYTES]; if(fread(rwd, 1, OPRF_BYTES, stdin) != OPRF_BYTES) { fputs("failed to read rwd\nabort.\n", stderr); return 1; } return guess(rwd, (uint8_t*) argv[2], strlen(argv[2])); } if(memcmp(argv[1],"test",5)==0) { return test(); } return usage(argv[0], 1); } liboprf-0.9.4/python/000077500000000000000000000000001514673400200145045ustar00rootroot00000000000000liboprf-0.9.4/python/.gitignore000066400000000000000000000000561514673400200164750ustar00rootroot00000000000000pyoprf.egg-info pyoprf/__pycache__ build dist liboprf-0.9.4/python/MANIFEST.in000066400000000000000000000000371514673400200162420ustar00rootroot00000000000000include README.md include *.py liboprf-0.9.4/python/README.md000066400000000000000000000130161514673400200157640ustar00rootroot00000000000000# pyoprf pyoprf offers Python bindings for the liboprf library, allowing integration of Oblivious Pseudorandom Functions (OPRFs) into Python applications. It provides access to the [features](../README.md#features) of the [liboprf](https://github.com/stef/liboprf) library. ## Installation ### Prerequisites - [liboprf](https://github.com/stef/liboprf): The core library - [libsodium](https://github.com/jedisct1/libsodium): Required dependency for liboprf - OpenSSL: For TLS connections between the participants ### Installing from PyPI ```bash pip install pyoprf ``` ### Installing from source ```bash git clone https://github.com/stef/liboprf.git cd liboprf/python pip install . ``` ## Usage For detailed usage examples, refer to the [`test.py`](./tests/test.py) file and the [`examples`](/examples) folder. ### Basic Example Imagine a scenario where a client wants to retrieve data from a server using a password, but doesn't want to reveal the actual password to the server: ```python import pyoprf # Basic OPRF evaluation process # Step 1: Client blinds the input value input_value = b"password123" blind_factor, blinded_input = pyoprf.blind(input_value) # Step 2: Server generates a key and evaluates the blinded input server_key = pyoprf.keygen() server_evaluation = pyoprf.evaluate(server_key, blinded_input) # Step 3: Client unblinds the server's response unblinded_result = pyoprf.unblind(blind_factor, server_evaluation) # Step 4: Client finalizes the OPRF computation final_result = pyoprf.finalize(input_value, unblinded_result) print(f"OPRF result: {final_result.hex()}") # Verify that repeated evaluations with the same key and input produce the same result blind_factor2, blinded_input2 = pyoprf.blind(input_value) server_evaluation2 = pyoprf.evaluate(server_key, blinded_input2) unblinded_result2 = pyoprf.unblind(blind_factor2, server_evaluation2) final_result2 = pyoprf.finalize(input_value, unblinded_result2) print(f"Verification result: {final_result2.hex()}") assert final_result == final_result2, "OPRF evaluations should be deterministic for the same input and key" # The `final_result` can be used as a key for encryption, authentication token, and more. # Only client can derive this value without the server learning the password or the final result. ``` ### Threshold Example Suppose you want to build a password authentication system that distributes trust across multiple servers, so no single server can learn a user's password. The library also supports threshold OPRFs, where multiple servers hold shares of a key: ```python import pyoprf # Setting up a threshold OPRF with 3 servers, threshold of 2 # Server setup, which would happen on each server n = 3 # Total number of servers t = 2 # The minimum servers needed, also called the threshold # Generate a key key = pyoprf.keygen() # Create shares of the key for distributed evaluation shares = pyoprf.create_shares(key, n, t) # On client input_value = b"password123" blind_factor, blinded_input = pyoprf.blind(input_value) # Each server evaluates the input with its share evaluations = [] for i in range(n): # This evaluation happens on server i server_evaluation = pyoprf.evaluate(shares[i][1:], blinded_input) evaluations.append(shares[i][:1] + server_evaluation) # Client combines evaluations (need at least t of them) collected_evaluations = evaluations[:t] # Just use the first t evaluations combined = pyoprf.thresholdmult(collected_evaluations) # Client unblinds the combined result unblinded = pyoprf.unblind(blind_factor, combined) # Finalize to get the OPRF output final_result = pyoprf.finalize(input_value, unblinded) print(f"Threshold OPRF result: {final_result.hex()}") # Verify that it matches a direct evaluation with the key server_evaluation = pyoprf.evaluate(key, blinded_input) unblinded_direct = pyoprf.unblind(blind_factor, server_evaluation) direct_result = pyoprf.finalize(input_value, unblinded_direct) print(f"Direct OPRF result: {direct_result.hex()}") assert final_result == direct_result, "Threshold evaluation should match direct evaluation" ``` ## Troubleshooting If you encounter issues, first ensure that libsodium, liboprf and OpenSSL are properly installed. ### OpenSSL Header Issues If after installing OpenSSL, you get the error `'openssl/crypto.h' file not found`, you might need to provide OpenSSL headers to the compiler. For example, if OpenSSL was installed on Mac using Homebrew: ``` export CFLAGS="-I/opt/homebrew/opt/openssl@3/include" export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib" ``` ### Library Loading Issues When running Python code, you might encounter errors like: ``` OSError: liboprf.so.0: cannot open shared object file: No such file or directory OSError: liboprf-noiseXK.so.0: cannot open shared object file: No such file or directory ``` To fix this, you can try to install liboprf globally on your system. Either by using your distributions package manager: ```sh % sudo apt install liboprf0t64 ``` Or install liboprf from source: ```sh cd /path/to/liboprf/src sudo PREFIX=/usr make install sudo ldconfig ``` Or by using environment variables, first create symbolic links: ```bash cd /path/to/liboprf/src ln -s liboprf.so liboprf.so.0 cd noise_xk ln -s liboprf-noiseXK.so liboprf-noiseXK.so.0 ``` Then when running your Python code, use the LD_LIBRARY_PATH environment variable: ```bash LD_LIBRARY_PATH=/path/to/liboprf/src:/path/to/liboprf/src/noise_xk python your_script.py ``` ## Documentation For more information on the underlying liboprf functionality, visit the [liboprf documentation](../README.md). ## License LGPLv3.0+ liboprf-0.9.4/python/examples/000077500000000000000000000000001514673400200163225ustar00rootroot00000000000000liboprf-0.9.4/python/examples/3hashtdh.py000077500000000000000000000014761514673400200204150ustar00rootroot00000000000000#!/usr/bin/env python from pyoprf import keygen, create_shares, blind, evaluate, unblind, thresholdmult from pysodium import randombytes, crypto_core_ristretto255_from_hash, crypto_generichash, crypto_core_ristretto255_add k = keygen() shares = create_shares(k, 5, 3) zero_shares = create_shares(bytes([0]*32), 5, 3) r, alpha = blind(b"test") ssid_S = randombytes(32) betas = [] for ki, zi in zip(shares,zero_shares): h2 = evaluate( zi[1:], crypto_core_ristretto255_from_hash(crypto_generichash(ssid_S + alpha, outlen=64)), ) beta = evaluate(ki[1:], alpha) betas.append(ki[:1]+crypto_core_ristretto255_add(beta, h2)) # normal 2hashdh(k,"test") beta = evaluate(k, alpha) Nt0 = unblind(r, beta) print(Nt0) beta = thresholdmult(betas[:3]) Nt1 = unblind(r, beta) print(Nt1) assert Nt0 == Nt1 liboprf-0.9.4/python/examples/toprf-update.py000077500000000000000000000206331514673400200213150ustar00rootroot00000000000000#!/usr/bin/env python """ Test for TP DKG wrapper of pyoprf/liboprf SPDX-FileCopyrightText: 2024, Marsiske Stefan SPDX-License-Identifier: LGPL-3.0-or-later Copyright (c) 2024, Marsiske Stefan. All rights reserved. This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with liboprf. If not, see . """ import pyoprf, pysodium, ctypes as c from itertools import combinations n = 9 t = 4 ts_epsilon = 5 # enable verbose logging for tp-dkg libc = c.cdll.LoadLibrary('libc.so.6') cstderr = c.c_void_p.in_dll(libc, 'stderr') log_file = c.c_void_p.in_dll(pyoprf.liboprf,'log_file') log_file.value = cstderr.value # create some long-term keypairs sig_pks = [] sig_sks = [] for _ in range(n+1): pk, sk = pysodium.crypto_sign_keypair() sig_pks.append(pk) sig_sks.append(sk) noise_pks = [] noise_sks = [] for _ in range(n): sk = pysodium.randombytes(pysodium.crypto_scalarmult_SCALARBYTES) pk = pysodium.crypto_scalarmult_base(sk) noise_sks.append(sk) noise_pks.append(pk) # initialize the TP and get the first message stp, msg0 = pyoprf.stp_dkg_start_stp(n, t, ts_epsilon, "pyoprf stp_dkg test", sig_pks, sig_sks[0]) print(f"n: {pyoprf.stp_dkg_stpstate_n(stp)}, t: {pyoprf.stp_dkg_stpstate_t(stp)}, sid: {bytes(c for c in pyoprf.stp_dkg_stpstate_sessionid(stp)).hex()}") # initialize all peers with the 1st message from TP keystore = { pysodium.crypto_generichash(s): (s, n) for s,n in zip(sig_pks[1:], noise_pks)} #typedef int (*Keyloader_CB)(const uint8_t id[crypto_generichash_BYTES], # void *arg, # uint8_t sigpk[crypto_sign_PUBLICKEYBYTES], # uint8_t noise_pk[crypto_scalarmult_BYTES]); @c.CFUNCTYPE(c.c_int, c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte)) def load_key(keyid, arg, sig_pk, noise_pk): rec = keystore.get(bytes(keyid[:pysodium.crypto_generichash_BYTES])) if rec is None: return 1 c.memmove(sig_pk, rec[0], len(rec[0])) c.memmove(noise_pk, rec[1], len(rec[1])) return 0 peers=[] for i in range(n): peer = pyoprf.stp_dkg_peer_start(ts_epsilon, sig_sks[i+1], noise_sks[i], sig_pks[0], msg0, keyloader=load_key) peers.append(peer) for i in range(n): assert(pyoprf.stp_dkg_peerstate_sessionid(peers[i]) == pyoprf.stp_dkg_stpstate_sessionid(stp)) assert(sig_sks[i+1] == pyoprf.stp_dkg_peerstate_lt_sk(peers[i])) peer_msgs = [] while pyoprf.stp_dkg_stp_not_done(stp): ret, sizes = pyoprf.stp_dkg_stp_input_sizes(stp) # peer_msgs = (recv(size) for size in sizes) msgs = b''.join(peer_msgs) cur_step = pyoprf.stp_dkg_stpstate_step(stp) try: stp_out = pyoprf.stp_dkg_stp_next(stp, msgs) #print(f"tp: msg[{tp[0].step}]: {tp_out.raw.hex()}") except Exception as e: #cheaters, cheats = pyoprf.stp_dkg_get_cheaters(stp) #print(f"Warning during the distributed key generation the peers misbehaved: {sorted(cheaters)}") #for k, v in cheats: # print(f"\tmisbehaving peer: {k} was caught: {v}") raise ValueError(f"{e} | tp step {cur_step}") peer_msgs = [] while(len(b''.join(peer_msgs))==0 and pyoprf.stp_dkg_peer_not_done(peers[0])): for i in range(n): if(len(stp_out)>0): msg = pyoprf.stp_dkg_stp_peer_msg(stp, stp_out, i) #print(f"tp -> peer[{i+1}] {msg.hex()}") else: msg = '' out = pyoprf.stp_dkg_peer_next(peers[i], msg) if(len(out)>0): peer_msgs.append(out) #print(f"peer[{i+1}] -> tp {peer_msgs[-1].hex()}") stp_out = '' # we are done, let's check the shares k0shares = [pyoprf.stp_dkg_peerstate_share(peers[i]) for i in range(n)] k0commitments = pyoprf.stp_dkg_stpstate_commitments(stp) print("commitments", k0commitments) for i, share in enumerate(k0shares): print(f"share[{i+1}] {share.hex()} {k0commitments[i].hex()}") ci = pyoprf.stp_dkg_peerstate_commitments(peers[i]) assert ci == k0commitments kc0, blind = pyoprf.dkg_vss_reconstruct(n, t, 0, k0shares, k0commitments) print("kc0 is", kc0.hex()) for s_sub in combinations(k0shares, t): v, _ = pyoprf.dkg_vss_reconstruct(n, t, 0, s_sub) assert kc0 == v keyid = pyoprf.stp_dkg_stpstate_sessionid(stp) # clean up allocated buffers for i in range(n): pyoprf.stp_dkg_peer_free(peers[i]) # calculate some OPRF r, alpha = pyoprf.blind(b"test") betas = tuple(s[:1]+pyoprf.evaluate(s[1:33], alpha) for s in k0shares) beta = pyoprf.thresholdmult(betas) oprfed_test = pyoprf.unblind(r, beta) print('oprf("test")', oprfed_test.hex()) # tOPRF update stp, msg0 = pyoprf.tupdate_start_stp(n, t, ts_epsilon, "tOPRF update test", sig_pks, keyid, sig_sks[0], k0commitments) for s,p in zip(sig_sks, sig_pks): print("sp", s.hex(), p.hex()) for s,p in zip(noise_sks, noise_pks): print("nsp", s.hex(), p.hex()) peers=[] for i in range(n): ctx, keyid, stp_pub = pyoprf.tupdate_peer_start(ts_epsilon, sig_sks[i+1], noise_sks[i], msg0) #print(keyid.hex(), stp_pub.hex()) # based on keyid load the relevant parameters: n, t, share, commitment. ctx = pyoprf.tupdate_peer_set_bufs(ctx, n, t, i+1, sig_pks, noise_pks, k0shares[i], k0commitments) peers.append(ctx) #print(ctx) for i in range(n): assert(pyoprf.tupdate_peerstate_sessionid(peers[i]) == pyoprf.tupdate_stpstate_sessionid(stp)) peer_msgs = [] while pyoprf.tupdate_peer_not_done(peers[0]): peer_msgs = [] while(len(b''.join(peer_msgs))==0 and pyoprf.tupdate_peer_not_done(peers[0])): for i in range(n): if(len(stp_out)>0): msg = pyoprf.tupdate_stp_peer_msg(stp, stp_out, i) #print(f"tp -> peer[{i+1}] {msg.hex()}") else: msg = '' out = pyoprf.tupdate_peer_next(peers[i], msg) if(len(out)>0): peer_msgs.append(out) #print(f"peer[{i+1}] -> tp {peer_msgs[-1].hex()}") stp_out = '' if pyoprf.tupdate_stp_not_done(stp): ret, sizes = pyoprf.tupdate_stp_input_sizes(stp) # peer_msgs = (recv(size) for size in sizes) msgs = b''.join(peer_msgs) cur_step = pyoprf.tupdate_stpstate_step(stp) try: stp_out = pyoprf.tupdate_stp_next(stp, msgs) #print(f"tp: msg[{tp[0].step}]: {tp_out.raw.hex()}") except Exception as e: #cheaters, cheats = pyoprf.stp_dkg_get_cheaters(stp) #print(f"Warning during the distributed key generation the peers misbehaved: {sorted(cheaters)}") #for k, v in cheats: # print(f"\tmisbehaving peer: {k} was caught: {v}") raise ValueError(f"{e} | tp step {cur_step}") delta = pyoprf.tupdate_stpstate_delta(stp) print("delta", delta.hex()) k1shares = [pyoprf.tupdate_peerstate_share(peers[i]) for i in range(n)] k1commitments = tuple(pyoprf.tupdate_peerstate_commitment(peers[i]) for i in range(n)) assert k1commitments == pyoprf.tupdate_stpstate_commitments(stp) for i, share in enumerate(k1shares): print(f"share[{i+1}] {share.hex()} {k1commitments[i].hex()}") assert k1commitments == pyoprf.tupdate_peerstate_commitments(peers[i]) kc1, blind = pyoprf.dkg_vss_reconstruct(n, t, 0, k1shares, k1commitments) print("kc1 is", kc1.hex()) for s_sub in combinations(k1shares, t): v, _ = pyoprf.dkg_vss_reconstruct(n, t, 0, s_sub) assert kc1 == v kc0inv = pysodium.crypto_core_ristretto255_scalar_invert(kc0) deltakc = pysodium.crypto_core_ristretto255_scalar_mul(kc1, kc0inv) print("delta", deltakc.hex()) assert delta == deltakc updated_test = pysodium.crypto_scalarmult_ristretto255(deltakc, oprfed_test) r, alpha = pyoprf.blind(b"test") betas = tuple(s[:1]+pyoprf.evaluate(s[1:33], alpha) for s in k1shares) beta = pyoprf.thresholdmult(betas) updated_oprfed_test = pyoprf.unblind(r, beta) print('updated oprf\'("test")', updated_test.hex()) print('oprf\'("test") ', updated_oprfed_test.hex()) assert updated_test == updated_oprfed_test liboprf-0.9.4/python/examples/tpdkg_test.py000077500000000000000000000076341514673400200210610ustar00rootroot00000000000000#!/usr/bin/env python """ Test for TP DKG wrapper of pyoprf/liboprf SPDX-FileCopyrightText: 2024, Marsiske Stefan SPDX-License-Identifier: LGPL-3.0-or-later Copyright (c) 2024, Marsiske Stefan. All rights reserved. This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with liboprf. If not, see . """ import pyoprf, pysodium, ctypes n = 5 t = 3 ts_epsilon = 5 # enable verbose logging for tp-dkg libc = ctypes.cdll.LoadLibrary('libc.so.6') cstderr = ctypes.c_void_p.in_dll(libc, 'stderr') log_file = ctypes.c_void_p.in_dll(pyoprf.liboprf,'log_file') log_file.value = cstderr.value # create some long-term keypairs peer_lt_pks = [] peer_lt_sks = [] for _ in range(n): pk, sk = pysodium.crypto_sign_keypair() peer_lt_pks.append(pk) peer_lt_sks.append(sk) # initialize the TP and get the first message tp, msg0 = pyoprf.tpdkg_start_tp(n, t, ts_epsilon, "pyoprf tpdkg test", peer_lt_pks) print(f"n: {pyoprf.tpdkg_tpstate_n(tp)}, t: {pyoprf.tpdkg_tpstate_t(tp)}, sid: {bytes(c for c in pyoprf.tpdkg_tpstate_sessionid(tp)).hex()}") # initialize all peers with the 1st message from TP peers=[] for i in range(n): peer = pyoprf.tpdkg_peer_start(ts_epsilon, peer_lt_sks[i], msg0) peers.append(peer) for i in range(n): assert(pyoprf.tpdkg_peerstate_sessionid(peers[i]) == pyoprf.tpdkg_tpstate_sessionid(tp)) assert(peer_lt_sks[i] == pyoprf.tpdkg_peerstate_lt_sk(peers[i])) peer_msgs = [] while pyoprf.tpdkg_tp_not_done(tp): ret, sizes = pyoprf.tpdkg_tp_input_sizes(tp) # peer_msgs = (recv(size) for size in sizes) msgs = b''.join(peer_msgs) cur_step = pyoprf.tpdkg_tpstate_step(tp) try: tp_out = pyoprf.tpdkg_tp_next(tp, msgs) #print(f"tp: msg[{tp[0].step}]: {tp_out.raw.hex()}") except Exception as e: cheaters, cheats = pyoprf.tpdkg_get_cheaters(tp) print(f"Warning during the distributed key generation the peers misbehaved: {sorted(cheaters)}") for k, v in cheats: print(f"\tmisbehaving peer: {k} was caught: {v}") raise ValueError(f"{e} | tp step {cur_step}") peer_msgs = [] while(len(b''.join(peer_msgs))==0 and pyoprf.tpdkg_peer_not_done(peers[0])): for i in range(n): if(len(tp_out)>0): msg = pyoprf.tpdkg_tp_peer_msg(tp, tp_out, i) #print(f"tp -> peer[{i+1}] {msg.hex()}") else: msg = '' out = pyoprf.tpdkg_peer_next(peers[i], msg) if(len(out)>0): peer_msgs.append(out) #print(f"peer[{i+1}] -> tp {peer_msgs[-1].hex()}") tp_out = '' # we are done, let's check the shares shares = [pyoprf.tpdkg_peerstate_share(peers[i]) for i in range(n)] for i, share in enumerate(shares): print(f"share[{i+1}] {share.hex()}") v0 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in (0,1,2)]) v1 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in (2,0,3)]) assert v0 == v1 v2 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in (2,1,4)]) assert v0 == v2 secret = pyoprf.dkg_reconstruct(shares[:t]) #print("secret", secret.hex()) assert v0 == pysodium.crypto_scalarmult_ristretto255_base(secret) # clean up allocated buffers for i in range(n): pyoprf.tpdkg_peer_free(peers[i]) liboprf-0.9.4/python/pyoprf/000077500000000000000000000000001514673400200160235ustar00rootroot00000000000000liboprf-0.9.4/python/pyoprf/__init__.py000077500000000000000000002131261514673400200201440ustar00rootroot00000000000000#!/usr/bin/env python """ Wrapper for liboprf library SPDX-FileCopyrightText: 2023, Marsiske Stefan SPDX-License-Identifier: LGPL-3.0-or-later Copyright (c) 2023, Marsiske Stefan. All rights reserved. This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with liboprf. If not, see . """ import ctypes import ctypes.util import pysodium, os import platform, sys from typing import List, Tuple from itertools import zip_longest if "BYZANTINE_DKG" in os.environ: liboprf = ctypes.cdll.LoadLibrary(os.environ['BYZANTINE_DKG']) print("\x1b[1m\x1b[31mwarning: loading intentionally corrupting version of liboprf, only use this for testing!\x1b[0m", file=sys.stderr) else: liboprf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('oprf') or ctypes.util.find_library('liboprf')) if not liboprf._name: raise ValueError('Unable to find liboprf') def split_by_n(iterable, n): return list(zip_longest(*[iter(iterable)]*n, fillvalue='')) def __check(code): if code != 0: raise ValueError(f"error: {code}") # (CFRG/IRTF) OPRF section OPRF_BYTES=64 # This function generates an OPRF private key. # # This is almost the KeyGen OPRF function defined in the RFC: since # this lib does not implement V oprf, we don't need a pubkey and so # we don't bother with all that is related. # # @param [out] k - the per-user OPRF private key # void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]); def keygen() -> bytes: k = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) liboprf.oprf_KeyGen(k) return k.raw # This function converts input x into an element of the OPRF group, randomizes it # by some scalar r, producing blinded, and outputs (r, blinded). # # This is the Blind OPRF function defined in the RFC. # # @param [in] x - the input value to blind # @param [out] r - an OPRF scalar value used for randomization # @param [out] blinded - a serialized OPRF group element, a byte array of fixed length, # the blinded version of x, an input to oprf_Evaluate # @return The function raises a ValueError if there is something wrong with the inputs. # #int oprf_Blind(const uint8_t *x, const uint16_t x_len, # uint8_t r[crypto_core_ristretto255_SCALARBYTES], # uint8_t blinded[crypto_core_ristretto255_BYTES]); def blind(x: bytes) -> (bytes, bytes): r = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) blinded = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) __check(liboprf.oprf_Blind(x, ctypes.c_size_t(len(x)), r, blinded)) return r.raw, blinded.raw # This function evaluates input element blinded using private key k, yielding output # element Z. # # This is the Evaluate OPRF function defined in the RFC. # # @param [in] key - a private key - the output of keygen() # @param [in] blinded - a serialized OPRF group element, a byte array # of fixed length, an output of blind() # @param [out] Z - a serialized OPRF group element, a byte array of fixed # length, an input to oprf_Unblind # @return The function raises a ValueError if there is something wrong with the inputs. #int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], # const uint8_t blinded[crypto_core_ristretto255_BYTES], # uint8_t Z[crypto_core_ristretto255_BYTES]); def evaluate(key: bytes, blinded: bytes) -> bytes: if len(key) != pysodium.crypto_core_ristretto255_SCALARBYTES: raise ValueError("key has incorrect length") if not isinstance(key, bytes): raise ValueError("key is not of type bytes") if len(blinded) != pysodium.crypto_core_ristretto255_BYTES: raise ValueError("blinded param has incorrect length") if not isinstance(blinded, bytes): raise ValueError("blinded is not of type bytes") Z = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) __check(liboprf.oprf_Evaluate(key, blinded, Z)) return Z.raw # This function removes random scalar r from Z, yielding output N. # # This is the Unblind OPRF function defined in the RFC. # # If you do not call finalize() on the result the output is equivalent # to the OPRF protcol we refer to as HashDH - this protocol retains # the algebraic structure of the value, and has weaker security # guarantees, than the full 2HashDH which is equivalent to running # finalize on the output of blind(). The hashDH variant is not # explicitly specified by the CFRG/IRTF specification. This hashDH # variant has one property that makes it interesting: it is an # updateable OPRF - that is if the server updates their key, they can # calculate a public delta value, that can be applied by the client to # the output of blind() and the result will be as if the client and # the server run the OPRF protocol with the original input and the new # key. It is important to note that the delta value is not sensitive, # and can be public. # # @param [in] r - an OPRF scalar value used for randomization in oprf_Blind # @param [in] Z - a serialized OPRF group element, a byte array of fixed length, # an output of oprf_Evaluate # @param [out] N - a serialized OPRF group element with random scalar r removed, # a byte array of fixed length, an input to oprf_Finalize # @return The function raises a ValueError if there is something wrong with the inputs. #int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES], # const uint8_t Z[crypto_core_ristretto255_BYTES], # uint8_t N[crypto_core_ristretto255_BYTES]); def unblind(r: bytes, Z: bytes) -> bytes: if len(r) != pysodium.crypto_core_ristretto255_SCALARBYTES: raise ValueError("param r has incorrect length") if not isinstance(r, bytes): raise ValueError("param r is not of type bytes") if len(Z) != pysodium.crypto_core_ristretto255_BYTES: raise ValueError("param Z has incorrect length") if not isinstance(Z, bytes): raise ValueError("param Z is not of type bytes") N = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) __check(liboprf.oprf_Unblind(r, Z, N)) return N.raw # This function computes the OPRF output using input x, N, and domain # separation tag info. # # This is the Finalize OPRF function defined in the RFC. # # @param [in] x - a value used to compute OPRF (the same value that # was used as input to be blinded) # @param [in] N - a serialized OPRF group element, a byte array of fixed length, # an output of oprf_Unblind # @param [out] y - an OPRF output # @return The function raises a ValueError if there is something wrong with the inputs. #int oprf_Finalize(const uint8_t *x, const uint16_t x_len, # const uint8_t N[crypto_core_ristretto255_BYTES], # uint8_t rwdU[OPRF_BYTES]); def finalize(x: bytes, N: bytes) -> bytes: if len(N) != pysodium.crypto_core_ristretto255_BYTES: raise ValueError("param N has incorrect length") if not isinstance(N, bytes): raise ValueError("param N is not of type bytes") y = ctypes.create_string_buffer(OPRF_BYTES) __check(liboprf.oprf_Finalize(x, ctypes.c_size_t(len(x)), N, y)) return y.raw # This function combines unblind() and finalize() as a convenience def unblind_finalize(r: bytes, Z: bytes, x: bytes) -> bytes: return finalize(x, unblind(r,Z)) # TOPRF section TOPRF_Share_BYTES=pysodium.crypto_core_ristretto255_SCALARBYTES+1 TOPRF_Part_BYTES=pysodium.crypto_core_ristretto255_BYTES+1 # This function calculates a lagrange coefficient based on the index # and the indexes of the other contributing shareholders. # # @param [in] index - the index of the shareholder whose lagrange # coefficient we're calculating, must be greater than 0 # # @param [in] peers - list of the shares that contribute to the reconstruction # # @param [out] result - the lagrange coefficient #void coeff(const int index, const int peers_len, const uint8_t peers[peers_len], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES]); def coeff(index: int, peers: list) -> bytes: if index < 1: raise ValueError("index must be positive integer") if len(peers) < 2: raise ValueError("peers must be a list of at least 2 integers") peers_len=ctypes.c_size_t(len(peers)) c = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) liboprf.coeff(index, peers_len, peers, c) return c.raw # This function creates shares of secret in a (threshold, n) scheme # over the curve ristretto255 # # @param [in] secret - the scalar value to be secretly shared # # @param [in] n - the number of shares created # # @param [in] threshold - the threshold needed to reconstruct the secret # # @param [out] shares - n shares # # @return The function raises a ValueError if there is something wrong with the inputs. #void toprf_create_shares(const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], # const uint8_t n, # const uint8_t threshold, # uint8_t shares[n][TOPRF_Share_BYTES]); bytes_list_t = List[bytes] def create_shares(secret: bytes, n: int, t: int) -> bytes_list_t: if len(secret) != pysodium.crypto_core_ristretto255_SCALARBYTES: raise ValueError("secret has incorrect length") if not isinstance(secret, bytes): raise ValueError("secret is not of type bytes") if n < t: raise ValueError("t cannot be bigger than n") if t < 2: raise ValueError("t must be bigger than 1") shares = ctypes.create_string_buffer(n*TOPRF_Share_BYTES) __check(liboprf.toprf_create_shares(secret, n, t, shares)) return tuple([bytes(s) for s in split_by_n(shares.raw, TOPRF_Share_BYTES)]) # This function recovers the secret in the exponent using lagrange interpolation # over the curve ristretto255 # # The shareholders are not aware if they are contributing to a # threshold or non-threshold oprf evaluation, from their perspective # nothing changes in this approach. # # @param [in] responses - is an array of shares (k_i) multiplied by a # point (P) on the r255 curve # # @param [in] responses_len - the number of elements in the response array # # @param [out] result - the reconstructed value of P multipled by k # # @return The function raises a ValueError if there is something wrong with the inputs. #int toprf_thresholdmult(const size_t response_len, # const uint8_t responses[response_len][TOPRF_Part_BYTES], # uint8_t result[crypto_scalarmult_ristretto255_BYTES]); def thresholdmult(responses: bytes_list_t) -> bytes: if len(responses) < 2: raise ValueError("responses must be a list of at least 2 integers") if not all(isinstance(r,bytes) for r in responses): raise ValueError("at least one of the responses is not of type bytes") if not all(len(r)==TOPRF_Part_BYTES for r in responses): raise ValueError("at least one of the responses is not of correct size") responses_len=ctypes.c_size_t(len(responses)) responses_buf = ctypes.create_string_buffer(b''.join(responses)) result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) __check(liboprf.toprf_thresholdmult(responses_len, responses_buf, result)) return result.raw # This function is the efficient threshold version of oprf_Evaluate. # # This function needs to know in advance the indexes of all the # shares that will be combined later in the toprf_thresholdcombine() function. # by doing so this reduces the total costs and distributes them to the shareholders. # # @param [in] k - a private key (for OPAQUE, this is kU, the user's # OPRF private key) # # @param [in] blinded - a serialized OPRF group element, a byte array # of fixed length, an output of oprf_Blind (for OPAQUE, this # is the blinded pwdU, the user's password) # # @param [in] self - the index of the current shareholder # # @param [in] indexes - the indexes of the all the shareholders # contributing to this oprf evaluation, # # @param [in] index_len - the length of the indexes array, # # @param [out] Z - a serialized OPRF group element, a byte array of fixed length, # an input to oprf_Unblind # # @return The function raises a ValueError if there is something wrong with the inputs. #int toprf_Evaluate(const uint8_t k[TOPRF_Share_BYTES], # const uint8_t blinded[crypto_core_ristretto255_BYTES], # const uint8_t self, const uint8_t *indexes, const uint16_t index_len, # uint8_t Z[TOPRF_Part_BYTES]); def threshold_evaluate(k: bytes, blinded: bytes, self: int, indexes: list) -> bytes: if len(k) != TOPRF_Share_BYTES: raise ValueError("param k has incorrect length") if not isinstance(k, bytes): raise ValueError("param k is not of type bytes") if len(blinded) != pysodium.crypto_core_ristretto255_BYTES: raise ValueError("blinded param has incorrect length") if not isinstance(blinded, bytes): raise ValueError("blinded is not of type bytes") if(self>255 or self<1): raise ValueError("self outside valid range") if(not all(i>0 and i<256 for i in indexes)): raise ValueError("index(es) outside valid range") index_len=ctypes.c_uint16(len(indexes)) indexes_buf=ctypes.create_string_buffer(bytes(indexes)) Z = ctypes.create_string_buffer(TOPRF_Part_BYTES) __check(liboprf.toprf_Evaluate(k, blinded, self, indexes_buf, index_len, Z)) return Z.raw # This function is combines the results of the toprf_Evaluate() # function to recover the shared secret in the exponent. # # @param [in] responses - is an array of shares (k_i) multiplied by a point (P) on the r255 curve # # @param [in] responses_len - the number of elements in the response array # # @param [out] result - the reconstructed value of P multipled by k # # @return The function raises a ValueError if there is something wrong with the inputs. #void toprf_thresholdcombine(const size_t response_len, # const uint8_t _responses[response_len][TOPRF_Part_BYTES], # uint8_t result[crypto_scalarmult_ristretto255_BYTES]); def threshold_combine(responses: bytes_list_t) -> bytes: if len(responses) < 2: raise ValueError("responses must be a list of at least 2 integers") if not all(isinstance(r,bytes) for r in responses): raise ValueError("at least one of the responses is not of type bytes") if not all(len(r)==TOPRF_Part_BYTES for r in responses): raise ValueError("at least one of the responses is not of correct size") responses_len=ctypes.c_size_t(len(responses)) responses_buf = ctypes.create_string_buffer(b''.join(responses)) result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) __check(liboprf.toprf_thresholdcombine(responses_len, responses_buf, result)) return result.raw #int toprf_3hashtdh(const uint8_t k[TOPRF_Share_BYTES], # const uint8_t z[TOPRF_Share_BYTES], # const uint8_t alpha[crypto_core_ristretto255_BYTES], # const uint8_t *ssid_S, const uint16_t ssid_S_len, # uint8_t beta[TOPRF_Part_BYTES]); def _3hashtdh(k: bytes, z: bytes, alpha: bytes, ssid_S: bytes) -> bytes: if len(k) != TOPRF_Share_BYTES: raise ValueError("param k has incorrect length") if not isinstance(k, bytes): raise ValueError("param k is not of type bytes") if len(z) != TOPRF_Share_BYTES: raise ValueError("param z has incorrect length") if not isinstance(z, bytes): raise ValueError("param z is not of type bytes") if len(alpha) != pysodium.crypto_core_ristretto255_BYTES: raise ValueError("alpha param has incorrect length") if not isinstance(alpha, bytes): raise ValueError("alpha is not of type bytes") if not isinstance(ssid_S, bytes): raise ValueError("ssid_S is not of type bytes") if len(ssid_S) > (1<<16)-1: raise ValueError("ssid_S is too long") ssid_S_len=ctypes.c_uint16(len(ssid_S)) beta = ctypes.create_string_buffer(TOPRF_Part_BYTES) __check(liboprf.toprf_3hashtdh(k, z, alpha, ssid_S, ssid_S_len, beta)) return beta.raw # todo documentation! #int dkg_start(const uint8_t n, # const uint8_t threshold, # uint8_t commitment_hash[dkg_hash_BYTES], # uint8_t commitments[dkg_commitment_BYTES(threshold)], # TOPRF_Share shares[n]); def dkg_start(n : int, t : int) -> (bytes, bytes, bytes_list_t): if n < t: raise ValueError("t cannot be bigger than n") if t < 2: raise ValueError("t must be bigger than 1") shares = ctypes.create_string_buffer(n*TOPRF_Share_BYTES) commitments = ctypes.create_string_buffer(t*pysodium.crypto_core_ristretto255_BYTES) __check(liboprf.dkg_start(n, t, commitments, shares)) shares = tuple([bytes(s) for s in split_by_n(shares.raw, TOPRF_Share_BYTES)]) return commitments.raw, shares #int dkg_verify_commitments(const uint8_t n, # const uint8_t threshold, # const uint8_t self, # const uint8_t commitments[n][threshold*crypto_core_ristretto255_BYTES], # const TOPRF_Share shares[n], # uint8_t fails[n], # uint8_t *fails_len); def dkg_verify_commitments(n: int, t: int, self: int, commitments : bytes_list_t, shares: bytes_list_t) -> bytes: if n < t: raise ValueError("t cannot be bigger than n") if t < 2: raise ValueError("t must be bigger than 1") if self < 1 or self > n: raise ValueError("self must 1 <= self <= n") if len(commitments) != n*t*pysodium.crypto_core_ristretto255_BYTES: raise ValueError(f"signed_commitments must be {n*t*pysodium.crypto_core_ristretto255_BYTES} bytes is instead: {len(commitments)}") shares = b''.join(shares) if len(shares) != n*TOPRF_Share_BYTES: raise ValueError(f"shares must be {TOPRF_Share_BYTES*n} bytes is instead {len(shares)}") shares = ctypes.create_string_buffer(shares) fails = ctypes.create_string_buffer(n) fails_len = ctypes.c_uint8() __check(liboprf.dkg_verify_commitments(n, t, self, commitments, shares, fails, ctypes.byref(fails_len))) return fails[:fails_len.value] #void dkg_finish(const uint8_t n, # const TOPRF_Share shares[n], # const uint8_t self, # TOPRF_Share *xi); def dkg_finish(n: int, shares: List[bytes], self: int, ) -> bytes: if self < 1 or self > n: raise ValueError("self must 1 <= self <= n") shares = b''.join(shares) if len(shares) != n*TOPRF_Share_BYTES: raise ValueError(f"shares must be {TOPRF_Share_BYTES*n} bytes is instead {len(shares)}") shares = ctypes.create_string_buffer(shares) xi = ctypes.create_string_buffer(TOPRF_Share_BYTES) xi[0]=self liboprf.dkg_finish(n, shares, self, xi) return xi.raw #void dkg_reconstruct(const size_t response_len, # const TOPRF_Share responses[response_len][2], # uint8_t result[crypto_scalarmult_ristretto255_BYTES]); def dkg_reconstruct(responses) -> bytes_list_t: rlen = len(responses) responses = ctypes.create_string_buffer(b''.join(responses)) result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_BYTES) liboprf.dkg_reconstruct(rlen, responses, result) return result.raw tpdkg_sessionid_SIZE=32 tpdkg_msg0_SIZE = 179 # ( sizeof(TP_DKG_Message) \ # + crypto_generichash_BYTES/*dst*/ \ # + 2 /*n,t*/ \ # + crypto_sign_PUBLICKEYBYTES /* tp_sign_pk */) tpdkg_msg8_SIZE = 258 # (sizeof(TP_DKG_Message) /* header */ \ # + noise_xk_handshake3_SIZE /* 4th&final noise handshake */ \ # + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped share */ \ # + crypto_secretbox_xchacha20poly1305_MACBYTES /* mac of msg */ \ # + crypto_auth_hmacsha256_BYTES /* key-committing mac over msg*/ ) tpdkg_max_err_SIZE = 128 class TP_DKG_Cheater(ctypes.Structure): _fields_ = [('step', ctypes.c_int), ('error', ctypes.c_int), ('peer', ctypes.c_uint8), ('other_peer', ctypes.c_uint8), ('invalid_index', ctypes.c_int), ] #int tpdkg_start_tp(TP_DKG_TPState *ctx, const uint64_t ts_epsilon, # const uint8_t n, const uint8_t t, # const char *proto_name, const size_t proto_name_len, # const size_t msg0_len, TP_DKG_Message *msg0); # # also wraps conveniently: # # void tpdkg_tp_set_bufs(TP_DKG_TPState *ctx, # uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], # uint16_t (*complaints)[], # uint8_t (*suspicious)[], # uint8_t (*tp_peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], # uint8_t (*peer_lt_pks)[][crypto_sign_PUBLICKEYBYTES], # uint64_t (*last_ts)[]); def tpdkg_start_tp(n, t, ts_epsilon, proto_name, peer_lt_pks): b = ctypes.create_string_buffer(liboprf.tpdkg_tpstate_size()+32) b_addr = ctypes.addressof(b) s_addr = b_addr + (b_addr % 32) state = ctypes.c_void_p(s_addr) if state.value % 32 != 0: raise ValueError("cannot align at 32bytes the TP_DKG_TPState struct") msg = ctypes.create_string_buffer(tpdkg_msg0_SIZE) __check(liboprf.tpdkg_start_tp(state, ctypes.c_uint64(ts_epsilon), ctypes.c_uint8(n), ctypes.c_uint8(t), proto_name, ctypes.c_size_t(len(proto_name)), ctypes.c_size_t(len(msg.raw)), msg)) peers_sig_pks = ctypes.create_string_buffer(n*pysodium.crypto_sign_PUBLICKEYBYTES) commitments = ctypes.create_string_buffer(n*t*pysodium.crypto_core_ristretto255_BYTES) complaints = ctypes.create_string_buffer(n*n*2) noisy_shares = ctypes.create_string_buffer(n*n*tpdkg_msg8_SIZE) cheaters = (TP_DKG_Cheater * (t*t - 1))() peer_lt_pks = b''.join(peer_lt_pks) last_ts = (ctypes.c_uint64 * n)() liboprf.tpdkg_tp_set_bufs(state, ctypes.byref(commitments), ctypes.byref(complaints), ctypes.byref(noisy_shares), ctypes.byref(cheaters), len(cheaters), ctypes.byref(peers_sig_pks), peer_lt_pks, ctypes.byref(last_ts)) # we need to keep these arrays around, otherwise the gc eats them up. ctx = (state, cheaters, peers_sig_pks, commitments, complaints, noisy_shares, peer_lt_pks, last_ts, b) return ctx, msg.raw #size_t tpdkg_tp_input_size(const TP_DKG_TPState *ctx); def tpdkg_tp_input_size(ctx): return liboprf.tpdkg_tp_input_size(ctx[0]) #int tpdkg_tp_input_sizes(const TP_DKG_TPState *ctx, size_t *sizes); def tpdkg_tp_input_sizes(ctx): sizes = (ctypes.c_size_t * tpdkg_tpstate_n(ctx))() ret = liboprf.tpdkg_tp_input_sizes(ctx[0], ctypes.byref(sizes)) return ret, [x for x in sizes] #size_t tpdkg_tp_output_size(const TP_DKG_TPState *ctx); def tpdkg_tp_output_size(ctx): return liboprf.tpdkg_tp_output_size(ctx[0]) #int tpdkg_tp_next(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); def tpdkg_tp_next(ctx, msg): input_len = tpdkg_tp_input_size(ctx) if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") output_len = tpdkg_tp_output_size(ctx) output = ctypes.create_string_buffer(output_len) __check(liboprf.tpdkg_tp_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) return output #int tpdkg_tp_peer_msg(const TP_DKG_TPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); def tpdkg_tp_peer_msg(ctx, base, peer): msg = ctypes.POINTER(ctypes.c_char)() size = ctypes.c_size_t() __check(liboprf.tpdkg_tp_peer_msg(ctx[0], base, len(base.raw), peer, ctypes.byref(msg), ctypes.byref(size))) msg = b''.join([msg[i] for i in range(size.value)]) return msg #int tpdkg_tp_not_done(const TP_DKG_TPState *tp); def tpdkg_tp_not_done(ctx): return liboprf.tpdkg_tp_not_done(ctx[0]) == 1 def tpdkg_get_cheaters(ctx): cheats = [] cheaters = set() for i in range(tpdkg_tpstate_cheater_len(ctx)): err = ctypes.create_string_buffer(tpdkg_max_err_SIZE) p = liboprf.tpdkg_cheater_msg(ctypes.byref(ctx[1][i]), err, tpdkg_max_err_SIZE) if 0 >= p > tpdkg_tpstate_n(ctx): print(f"invalid cheater index: {p}, skipping this entry") continue cheaters.add(p) cheats.append((p, err.raw[:err.raw.find(b'\x00')].decode('utf8'))) return cheaters, cheats liboprf.tpdkg_peerstate_n.restype = ctypes.c_uint8 def tpdkg_peerstate_n(ctx): return liboprf.tpdkg_peerstate_n(ctx[0]) liboprf.tpdkg_peerstate_t.restype = ctypes.c_uint8 def tpdkg_peerstate_t(ctx): return liboprf.tpdkg_peerstate_t(ctx[0]) liboprf.tpdkg_peerstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) def tpdkg_peerstate_sessionid(ctx): ptr = liboprf.tpdkg_peerstate_sessionid(ctx[0]) return bytes(ptr[i] for i in range(tpdkg_sessionid_SIZE)) liboprf.tpdkg_peerstate_lt_sk.restype = ctypes.POINTER(ctypes.c_uint8) def tpdkg_peerstate_lt_sk(ctx): ptr = liboprf.tpdkg_peerstate_lt_sk(ctx[0]) return bytes(ptr[i] for i in range(pysodium.crypto_sign_SECRETKEYBYTES)) liboprf.tpdkg_peerstate_share.restype = ctypes.POINTER(ctypes.c_uint8) def tpdkg_peerstate_share(ctx): ptr = liboprf.tpdkg_peerstate_share(ctx[0]) return bytes(ptr[i] for i in range(TOPRF_Share_BYTES)) def tpdkg_peerstate_step(ctx): return liboprf.tpdkg_peerstate_step(ctx[0]) liboprf.tpdkg_tpstate_n.restype = ctypes.c_uint8 def tpdkg_tpstate_n(ctx): return liboprf.tpdkg_tpstate_n(ctx[0]) liboprf.tpdkg_tpstate_t.restype = ctypes.c_uint8 def tpdkg_tpstate_t(ctx): return liboprf.tpdkg_tpstate_t(ctx[0]) liboprf.tpdkg_tpstate_cheater_len.restype = ctypes.c_size_t def tpdkg_tpstate_cheater_len(ctx): return liboprf.tpdkg_tpstate_cheater_len(ctx[0]) liboprf.tpdkg_tpstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) def tpdkg_tpstate_sessionid(ctx): ptr = liboprf.tpdkg_tpstate_sessionid(ctx[0]) return bytes(ptr[i] for i in range(tpdkg_sessionid_SIZE)) def tpdkg_tpstate_step(ctx): return liboprf.tpdkg_tpstate_step(ctx[0]) #int tpdkg_start_peer(TP_DKG_PeerState *ctx, const uint64_t ts_epsilon, # const uint8_t peer_lt_sk[crypto_sign_SECRETKEYBYTES], # const TP_DKG_Message *msg0); # # also wraps conveniently # #void tpdkg_peer_set_bufs(TP_DKG_PeerState *ctx, # uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], # uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], # Noise_XK_session_t *(*noise_outs)[], # Noise_XK_session_t *(*noise_ins)[], # TOPRF_Share (*shares)[], # TOPRF_Share (*xshares)[], # uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], # uint16_t (*complaints)[], # uint8_t (*my_complaints)[]); def tpdkg_peer_start(ts_epsilon, peer_lt_sk, msg0): b = ctypes.create_string_buffer(liboprf.tpdkg_peerstate_size()+32) b_addr = ctypes.addressof(b) s_addr = b_addr + (b_addr % 32) state = ctypes.c_void_p(s_addr) if state.value % 32 != 0: raise ValueError("cannot align at 32bytes the TP_DKG_PeerState struct") __check(liboprf.tpdkg_start_peer(state, ctypes.c_uint64(ts_epsilon), peer_lt_sk, msg0)) n = tpdkg_peerstate_n([state]) t = tpdkg_peerstate_t([state]) peers_sig_pks = ctypes.create_string_buffer(b"peer_sig_pks", n * pysodium.crypto_sign_PUBLICKEYBYTES) peers_noise_pks = ctypes.create_string_buffer(b"peer_noise_pks", n * pysodium.crypto_scalarmult_BYTES) noise_outs = (ctypes.c_void_p * n)() noise_ins = (ctypes.c_void_p * n)() shares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES) xshares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES) commitments = ctypes.create_string_buffer(n * t * pysodium.crypto_core_ristretto255_BYTES) complaints = ctypes.create_string_buffer(n * n * 2) my_complaints = ctypes.create_string_buffer(n) last_ts = (ctypes.c_uint64 * n)() liboprf.tpdkg_peer_set_bufs(state, ctypes.byref(peers_sig_pks), ctypes.byref(peers_noise_pks), noise_outs, noise_ins, ctypes.byref(shares), ctypes.byref(xshares), ctypes.byref(commitments), ctypes.byref(complaints), ctypes.byref(my_complaints), ctypes.byref(last_ts)) # we need to keep these arrays around, otherwise the gc eats them up. ctx = (state, peers_sig_pks, peers_noise_pks, noise_outs, noise_ins, shares, xshares, commitments, complaints, my_complaints, b, last_ts) return ctx #size_t tpdkg_peer_input_size(const TP_DKG_PeerState *ctx); def tpdkg_peer_input_size(ctx): return liboprf.tpdkg_peer_input_size(ctx[0]) #size_t tpdkg_peer_output_size(const TP_DKG_PeerState *ctx); def tpdkg_peer_output_size(ctx): return liboprf.tpdkg_peer_output_size(ctx[0]) #int tpdkg_peer_next(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); def tpdkg_peer_next(ctx, msg): input_len = tpdkg_peer_input_size(ctx) if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") output_len = tpdkg_peer_output_size(ctx) output = ctypes.create_string_buffer(output_len) __check(liboprf.tpdkg_peer_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) return output.raw #int tpdkg_peer_not_done(const TP_DKG_PeerState *peer); def tpdkg_peer_not_done(ctx): return liboprf.tpdkg_peer_not_done(ctx[0]) == 1 #void tpdkg_peer_free(TP_DKG_PeerState *ctx); def tpdkg_peer_free(ctx): liboprf.tpdkg_peer_free(ctx[0]) #int dkg_vss_reconstruct(const uint8_t t, # const uint8_t x, # const size_t shares_len, # const TOPRF_Share shares[shares_len][2], # const uint8_t commitments[shares_len][crypto_scalarmult_ristretto255_BYTES] # uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES], # uint8_t blind[crypto_scalarmult_ristretto255_SCALARBYTES]) { def dkg_vss_reconstruct(n, t, x, shares, commitments = None): if len(shares) < t: raise ValueError(f"shares must be at least {TOPRF_Share_BYTES*2*n} bytes is instead {len(shares)}") for i, s in enumerate(shares): if len(s)!=TOPRF_Share_BYTES*2: raise ValueError(f"share {i+1} has incorrect length: {len(s)}, must be {TOPRF_Share_BYTES*2}") if commitments is not None: if len(commitments) < t: raise ValueError(f"commitments must be at least {pysodium.crypto_core_ristretto255_BYTES*t} bytes is instead {len(commitments)}") for i, c in enumerate(commitments): if len(c)!=pysodium.crypto_core_ristretto255_BYTES: raise ValueError(f"commitment {i+1} has incorrect length: {len(c)}, must be {pysodium.crypto_core_ristretto255_BYTES}") commitments = b''.join(commitments) shares_len = ctypes.c_size_t(len(shares)) shares = b''.join(shares) result = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) blind = ctypes.create_string_buffer(pysodium.crypto_core_ristretto255_SCALARBYTES) __check(liboprf.dkg_vss_reconstruct(ctypes.c_uint8(t), ctypes.c_uint8(x), shares_len, shares, commitments, result, blind)) return result.raw, blind.raw sessionid_SIZE=32 tupdate_msg0_SIZE = 0xd1 # ( sizeof(TP_DKG_Message) \ # + crypto_generichash_BYTES/*dst*/ \ # + 2 /*n,t*/ \ # + crypto_sign_PUBLICKEYBYTES /* tp_sign_pk */) tupdate_max_err_SIZE = 128 tupdate_keyid_SIZE = 32 tupdate_commitment_HASHBYTES = 32 noise_xk_handshake3_SIZE = 64 class Cheater(ctypes.Structure): _fields_ = [('step', ctypes.c_int), ('error', ctypes.c_int), ('peer', ctypes.c_uint8), ('other_peer', ctypes.c_uint8), ('invalid_index', ctypes.c_int), ] # int toprf_update_start_stp(TOPRF_Update_STPState *ctx, const uint64_t ts_epsilon, # const uint8_t n, const uint8_t t, # const char *proto_name, const size_t proto_name_len, # const uint8_t keyid[toprf_keyid_SIZE], # const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES], # const uint8_t ltssk[crypto_sign_SECRETKEYBYTES], # const size_t msg0_len, # TOPRF_Update_Message *msg0); # # also wraps conveniently: # # void toprf_update_stp_set_bufs(TOPRF_Update_STPState *ctx, # uint16_t p_complaints[], # uint16_t y2_complaints[], # TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, # uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], # uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], # uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], # uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], # uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], # uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], # uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], # uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES], # uint64_t *last_ts); def tupdate_start_stp(n, t, ts_epsilon, proto_name, sig_pks, keyid, ltssk): dealers = (t-1)*2 + 1 if(len(keyid)!=tupdate_keyid_SIZE): raise ValueError(f"keyid has incorrect size, must be {tupdate_keyid_SIZE}") if(len(sig_pks)!=n+1): raise ValueError(f"invalid number of long-term signature pubkeys ({len(sig_pks)}, must be equal n ({n+1})") for i, k in enumerate(sig_pks): if len(k) != pysodium.crypto_sign_PUBLICKEYBYTES: raise ValueError(f"long-term signature pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") if len(ltssk) != pysodium.crypto_sign_SECRETKEYBYTES: raise ValueError(f"long-term signature secret key of STP has invalid length ({len(ltssk)}) must be {pysodium.crypto_sign_SECRETKEYBYTES}") b = ctypes.create_string_buffer(liboprf.toprf_update_stpstate_size()+32) b_addr = ctypes.addressof(b) s_addr = b_addr + (b_addr % 32) state = ctypes.c_void_p(s_addr) if state.value % 32 != 0: raise ValueError("cannot align at 32bytes the TOPRF_Update_STPState struct") sig_pks = ctypes.create_string_buffer(b''.join(sig_pks)) msg = ctypes.create_string_buffer(tupdate_msg0_SIZE) __check(liboprf.toprf_update_start_stp(state, ctypes.c_uint64(ts_epsilon), ctypes.c_uint8(n), ctypes.c_uint8(t), proto_name, ctypes.c_size_t(len(proto_name)), keyid, ctypes.byref(sig_pks), ltssk, ctypes.c_size_t(len(msg.raw)), msg)) k0_commitments = ctypes.create_string_buffer(n*pysodium.crypto_core_ristretto255_BYTES) p_complaints = (ctypes.c_uint16 * n*n)() y2_complaints = (ctypes.c_uint16 * n*n)() cheaters = (Cheater * (t*t - 1))() p_commitments_hashes = ctypes.create_string_buffer(n*tupdate_commitment_HASHBYTES) p_share_macs = ctypes.create_string_buffer(n*n*pysodium.crypto_auth_hmacsha256_BYTES) p_commitments = ctypes.create_string_buffer(n*n*pysodium.crypto_core_ristretto255_BYTES) k0p_commitments = ctypes.create_string_buffer(dealers*(n+1)*pysodium.crypto_core_ristretto255_BYTES) zk_challenge_commitments = ctypes.create_string_buffer(dealers*2*3*pysodium.crypto_core_ristretto255_SCALARBYTES) zk_challenge_e_i = ctypes.create_string_buffer(2*dealers*pysodium.crypto_core_ristretto255_SCALARBYTES) k0p_final_commitments = ctypes.create_string_buffer(n*pysodium.crypto_core_ristretto255_BYTES) last_ts = (ctypes.c_uint64 * n)() liboprf.toprf_update_stp_set_bufs(state # uint16_t p_complaints[], ,p_complaints # uint16_t x2_complaints[], uint16_t y2_complaints[], ,y2_complaints # TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, ,ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)) # uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], ,ctypes.byref(p_commitments_hashes) # uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], ,ctypes.byref(p_share_macs) # uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], ,ctypes.byref(p_commitments) # uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], ,ctypes.byref(k0_commitments) # uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], ,ctypes.byref(k0p_commitments) # uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], ,ctypes.byref(zk_challenge_commitments) # uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], ,ctypes.byref(zk_challenge_e_i) # uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES], ,ctypes.byref(k0p_final_commitments) # uint64_t *last_ts); ,ctypes.byref(last_ts)) # we need to keep these arrays around, otherwise the gc eats them up. ctx = (state, cheaters, p_complaints, y2_complaints, p_commitments_hashes, p_share_macs, p_commitments, k0_commitments, k0p_commitments, zk_challenge_commitments, zk_challenge_e_i, k0p_final_commitments, last_ts, sig_pks, b) return ctx, msg.raw #size_t tpdkg_tp_input_size(const TP_DKG_TPState *ctx); def tupdate_stp_input_size(ctx): return liboprf.toprf_update_stp_input_size(ctx[0]) #int tpdkg_tp_input_sizes(const TP_DKG_TPState *ctx, size_t *sizes); def tupdate_stp_input_sizes(ctx): sizes = (ctypes.c_size_t * tpdkg_tpstate_n(ctx))() ret = liboprf.toprf_update_stp_input_sizes(ctx[0], ctypes.byref(sizes)) return ret, [x for x in sizes] #size_t tpdkg_tp_output_size(const TP_DKG_TPState *ctx); def tupdate_stp_output_size(ctx): return liboprf.toprf_update_stp_output_size(ctx[0]) #int tpdkg_tp_next(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); def tupdate_stp_next(ctx, msg): input_len = tupdate_stp_input_size(ctx) if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") output_len = tupdate_stp_output_size(ctx) output = ctypes.create_string_buffer(output_len) __check(liboprf.toprf_update_stp_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) return output #int tpdkg_tp_peer_msg(const TP_DKG_TPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); def tupdate_stp_peer_msg(ctx, base, peer): msg = ctypes.POINTER(ctypes.c_char)() size = ctypes.c_size_t() __check(liboprf.toprf_update_stp_peer_msg(ctx[0], base, len(base.raw), peer, ctypes.byref(msg), ctypes.byref(size))) msg = b''.join([msg[i] for i in range(size.value)]) return msg #int tpdkg_tp_not_done(const TP_DKG_TPState *tp); def tupdate_stp_not_done(ctx): return liboprf.toprf_update_stp_not_done(ctx[0]) == 1 #todo #def tupdate_get_cheaters(ctx): # cheats = [] # cheaters = set() # for i in range(tupdate_stpstate_cheater_len(ctx)): # err = ctypes.create_string_buffer(tpdkg_max_err_SIZE) # p = liboprf.toprf_update_cheater_msg(ctypes.byref(ctx[1][i]), err, tpdkg_max_err_SIZE) # if 0 >= p > tpdkg_tpstate_n(ctx): # print(f"invalid cheater index: {p}, skipping this entry") # continue # cheaters.add(p) # cheats.append((p, err.raw[:err.raw.find(b'\x00')].decode('utf8'))) # return cheaters, cheats liboprf.toprf_update_peerstate_n.restype = ctypes.c_uint8 def tupdate_peerstate_n(ctx): return liboprf.toprf_update_peerstate_n(ctx[0]) liboprf.toprf_update_peerstate_t.restype = ctypes.c_uint8 def tupdate_peerstate_t(ctx): return liboprf.toprf_update_peerstate_t(ctx[0]) liboprf.toprf_update_peerstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_peerstate_sessionid(ctx): ptr = liboprf.toprf_update_peerstate_sessionid(ctx[0]) return bytes(ptr[i] for i in range(sessionid_SIZE)) liboprf.toprf_update_peerstate_share.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_peerstate_share(ctx): ptr = liboprf.toprf_update_peerstate_share(ctx[0]) return bytes(ptr[i] for i in range(TOPRF_Share_BYTES*2)) liboprf.toprf_update_peerstate_commitment.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_peerstate_commitment(ctx): ptr = liboprf.toprf_update_peerstate_commitment(ctx[0]) return bytes(ptr[i] for i in range(pysodium.crypto_core_ristretto255_BYTES)) liboprf.toprf_update_peerstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_peerstate_commitments(ctx): ptr = liboprf.toprf_update_peerstate_commitments(ctx[0]) return tuple(bytes(ptr[p*pysodium.crypto_core_ristretto255_BYTES:(p+1)*pysodium.crypto_core_ristretto255_BYTES]) for p in range(tupdate_peerstate_n(ctx))) def tupdate_peerstate_step(ctx): return liboprf.toprf_update_peerstate_step(ctx[0]) liboprf.toprf_update_stpstate_n.restype = ctypes.c_uint8 def tupdate_stpstate_n(ctx): return liboprf.toprf_update_stpstate_n(ctx[0]) liboprf.toprf_update_stpstate_t.restype = ctypes.c_uint8 def tupdate_stpstate_t(ctx): return liboprf.toprf_update_stpstate_t(ctx[0]) liboprf.toprf_update_stpstate_cheater_len.restype = ctypes.c_size_t def tupdate_stpstate_cheater_len(ctx): return liboprf.toprf_update_stpstate_cheater_len(ctx[0]) liboprf.toprf_update_stpstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_stpstate_sessionid(ctx): ptr = liboprf.toprf_update_stpstate_sessionid(ctx[0]) return bytes(ptr[i] for i in range(sessionid_SIZE)) liboprf.toprf_update_stpstate_delta.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_stpstate_delta(ctx): ptr = liboprf.toprf_update_stpstate_delta(ctx[0]) return bytes(ptr[i] for i in range(pysodium.crypto_core_ristretto255_BYTES)) liboprf.toprf_update_stpstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) def tupdate_stpstate_commitments(ctx): ptr = liboprf.toprf_update_stpstate_commitments(ctx[0]) return tuple(bytes(ptr[p*pysodium.crypto_core_ristretto255_BYTES:(p+1)*pysodium.crypto_core_ristretto255_BYTES]) for p in range(tupdate_stpstate_n(ctx))) def tupdate_stpstate_step(ctx): return liboprf.toprf_update_stpstate_step(ctx[0]) # TOPRF_Update_Err toprf_update_start_peer(TOPRF_Update_PeerState *ctx, # const uint64_t ts_epsilon, # const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], # const TOPRF_Update_Message *msg0, # uint8_t keyid[toprf_keyid_SIZE], # uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]); def tupdate_peer_start(ts_epsilon, peer_lt_sk, noise_sk, msg0): if len(peer_lt_sk) != pysodium.crypto_sign_SECRETKEYBYTES: raise ValueError(f"peer long-term secret key has invalid size, must be {pysodium.crypto_sign_SECRETKEYBYTES}") if len(noise_sk) != pysodium.crypto_scalarmult_SCALARBYTES: raise ValueError(f"peer long-term secret noise key has invalid size, must be {pysodium.crypto_scalarmult_SCALARBYTES}") b = ctypes.create_string_buffer(liboprf.toprf_update_peerstate_size()+32) b_addr = ctypes.addressof(b) s_addr = b_addr + (b_addr % 32) state = ctypes.c_void_p(s_addr) if state.value % 32 != 0: raise ValueError("cannot align at 32bytes the TP_DKG_PeerState struct") keyid = ctypes.create_string_buffer(tupdate_keyid_SIZE) stp_ltpk = ctypes.create_string_buffer(pysodium.crypto_sign_PUBLICKEYBYTES) __check(liboprf.toprf_update_start_peer(state, ctypes.c_uint64(ts_epsilon), peer_lt_sk, noise_sk, msg0, keyid, stp_ltpk)) return (state, b), keyid.raw, stp_ltpk.raw def tupdate_peer_set_bufs(ctx, n, t, index, sig_pks, noise_pks, k0 = None, k0_commitments = None): dealers = (t-1)*2 + 1 if k0 is not None: if len(k0) != TOPRF_Share_BYTES * 2: raise ValueError(f"k0 has invalid size {len(k0)} must be {TOPRF_Share_BYTES * 2}") if(k0[0]!=index or k0[TOPRF_Share_BYTES]!=index): raise ValueError(f"k0 has a different index ({k0[0]} & {k0[TOPRF_Share_BYTES]} than provided: {index}") if k0_commitments is None: raise ValueError(f"must provide also commitments for k0") if len(k0_commitments) < dealers: raise ValueError(f"not enough dealers holding kc0 shares, need at least {dealers}") for i, c in enumerate(k0_commitments): if len(c) == pysodium.crypto_core_ristretto255_BYTES: continue raise ValueError(f"k0 commitment #{i} has invalid length ({len(c)}) must be {pysodium.crypto_core_ristretto255_BYTES}") k0_commitments = ctypes.create_string_buffer(b''.join(k0_commitments)) if(len(sig_pks)!=n+1): raise ValueError(f"invalid number of long-term signature pubkeys ({len(sig_pks)}, must be equal n ({n+1})") for i, k in enumerate(sig_pks): if len(k) != pysodium.crypto_sign_PUBLICKEYBYTES: raise ValueError(f"long-term signature pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") sig_pks = ctypes.create_string_buffer(b''.join(sig_pks)) if(len(noise_pks)!=n): raise ValueError(f"invalid number of long-term noise pubkeys ({len(noise_pks)}, must be equal n ({n})") for i, k in enumerate(noise_pks): if len(k) != pysodium.crypto_scalarmult_BYTES: raise ValueError(f"noise pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_scalarmult_BYTES}") noise_pks = ctypes.create_string_buffer(b''.join(noise_pks)) noise_outs = (ctypes.c_void_p * n)() noise_ins = (ctypes.c_void_p * n)() p_shares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES * 2) p_commitments = ctypes.create_string_buffer(n * n * pysodium.crypto_core_ristretto255_BYTES) p_commitment_hashes = ctypes.create_string_buffer(n * tupdate_commitment_HASHBYTES) p_share_macs = ctypes.create_string_buffer(n * n * pysodium.crypto_auth_hmacsha256_BYTES) encrypted_shares = ctypes.create_string_buffer(n * (noise_xk_handshake3_SIZE + TOPRF_Share_BYTES * 2)) cheaters = (Cheater * (t*t - 1))() lambdas = ctypes.create_string_buffer(dealers * pysodium.crypto_core_ristretto255_SCALARBYTES) k0p_shares = ctypes.create_string_buffer(dealers * TOPRF_Share_BYTES * 2) k0p_commitments = ctypes.create_string_buffer(dealers * (n+1) * pysodium.crypto_core_ristretto255_BYTES) zk_challenge_nonce_commitments = ctypes.create_string_buffer(n * pysodium.crypto_core_ristretto255_BYTES) zk_challenge_nonces = ctypes.create_string_buffer(n * 2 * pysodium.crypto_core_ristretto255_SCALARBYTES) zk_challenge_commitments = ctypes.create_string_buffer(dealers * 3 * pysodium.crypto_core_ristretto255_SCALARBYTES) zk_challenge_e_i = ctypes.create_string_buffer(dealers * pysodium.crypto_core_ristretto255_SCALARBYTES) p_complaints = (ctypes.c_uint16 * n*n)() p_my_complaints = ctypes.create_string_buffer(n) last_ts = (ctypes.c_uint64 * n)() # int toprf_update_peer_set_bufs(TOPRF_Update_PeerState *ctx, liboprf.toprf_update_peer_set_bufs(ctx[0] # const uint8_t self, ,ctypes.c_uint8(index) # const uint8_t n, const uint8_t t, ,ctypes.c_uint8(n), ctypes.c_uint8(t) # const TOPRF_Share k0[2], ,k0 # uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], ,ctypes.byref(k0_commitments) # const uint8_t (*sig_pks)[][], ,ctypes.byref(sig_pks) # uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], ,ctypes.byref(noise_pks) # Noise_XK_session_t *(*noise_outs)[], ,noise_outs # Noise_XK_session_t *(*noise_ins)[], ,noise_ins # TOPRF_Share (*p_shares)[][2], ,ctypes.byref(p_shares) # uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], ,ctypes.byref(p_commitments) # uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], ,ctypes.byref(p_commitment_hashes) # uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], ,ctypes.byref(p_share_macs) # uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE*2], ,ctypes.byref(encrypted_shares) # TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, ,ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)) # uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES], ,ctypes.byref(lambdas) # TOPRF_Share (*k0p_shares)[][2], ,ctypes.byref(k0p_shares) # uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], ,ctypes.byref(k0p_commitments) # uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES], ,ctypes.byref(zk_challenge_nonce_commitments) # uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES], ,ctypes.byref(zk_challenge_nonces) # uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], ,ctypes.byref(zk_challenge_commitments) # uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], ,ctypes.byref(zk_challenge_e_i) # uint16_t *p_complaints, ,p_complaints #uint8_t *my_p_complaints, ,p_my_complaints # uint64_t *last_ts); ,ctypes.byref(last_ts)) # we need to keep these arrays around, otherwise the gc eats them up. ctx = (ctx[0], noise_pks, noise_outs, noise_ins, k0_commitments, sig_pks, p_shares, p_commitments, p_commitment_hashes, p_share_macs, encrypted_shares, cheaters, lambdas, k0p_shares, k0p_commitments, zk_challenge_nonce_commitments, zk_challenge_nonces, zk_challenge_commitments, zk_challenge_e_i, p_complaints, p_my_complaints, last_ts, ctx[1]) return ctx #size_t toprf_update_peer_input_size(const TOPRF_Update_PeerState *ctx); def tupdate_peer_input_size(ctx): return liboprf.toprf_update_peer_input_size(ctx[0]) #size_t toprf_update_peer_output_size(const TOPRF_Update_PeerState *ctx); def tupdate_peer_output_size(ctx): return liboprf.toprf_update_peer_output_size(ctx[0]) #int toprf_update_peer_next(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); def tupdate_peer_next(ctx, msg): input_len = tupdate_peer_input_size(ctx) if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") output_len = tupdate_peer_output_size(ctx) output = ctypes.create_string_buffer(output_len) __check(liboprf.toprf_update_peer_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) return output.raw #int toprf_update_peer_not_done(const TOPRF_Update_PeerState *peer); def tupdate_peer_not_done(ctx): return liboprf.toprf_update_peer_not_done(ctx[0]) == 1 #void toprf_update_peer_free(TOPRF_Update_PeerState *ctx); def tupdate_peer_free(ctx): liboprf.toprf_update_peer_free(ctx[0]) stpdkg_msg0_SIZE = 0xb3 stp_dkg_commitment_HASHBYTES = 32 stp_dkg_max_err_SIZE = 128 stp_dkg_sessionid_SIZE = 32 stp_dkg_encrypted_share_SIZE = TOPRF_Share_BYTES * 2 + 16 #pysodium.crypto_secretbox_xchacha20poly1305_MACBYTES def stp_dkg_start_stp(n, t, ts_epsilon, proto_name, sig_pks, ltssk): b = ctypes.create_string_buffer(liboprf.stp_dkg_stpstate_size()+32) b_addr = ctypes.addressof(b) s_addr = b_addr + (b_addr % 32) state = ctypes.c_void_p(s_addr) if state.value % 32 != 0: raise ValueError("cannot align at 32bytes the STP_DKG_STPState struct") if(len(sig_pks)!=n+1): raise ValueError(f"invalid number of long-term signature pubkeys ({len(sig_pks)}, must be equal n ({n+1})") for i, k in enumerate(sig_pks): if len(k) != pysodium.crypto_sign_PUBLICKEYBYTES: raise ValueError(f"long-term signature pubkey #{i} has invalid length ({len(k)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") if len(ltssk) != pysodium.crypto_sign_SECRETKEYBYTES: raise ValueError(f"long-term signature secret key of STP has invalid length ({len(ltssk)}) must be {pysodium.crypto_sign_SECRETKEYBYTES}") msg = ctypes.create_string_buffer(stpdkg_msg0_SIZE) sig_pks = ctypes.create_string_buffer(b''.join(sig_pks)) __check(liboprf.stp_dkg_start_stp(state, ctypes.c_uint64(ts_epsilon), ctypes.c_uint8(n), ctypes.c_uint8(t), proto_name, ctypes.c_size_t(len(proto_name)), ctypes.byref(sig_pks), ltssk, ctypes.c_size_t(len(msg.raw)), msg)) commitment_hashes = ctypes.create_string_buffer(n*stp_dkg_commitment_HASHBYTES) share_macs = ctypes.create_string_buffer(n * n * pysodium.crypto_auth_hmacsha256_BYTES) commitments = ctypes.create_string_buffer(n*n*pysodium.crypto_core_ristretto255_BYTES) share_complaints = (ctypes.c_uint16 * n*n)() cheaters = (TP_DKG_Cheater * (t*t - 1))() last_ts = (ctypes.c_uint64 * n)() liboprf.stp_dkg_stp_set_bufs(state, ctypes.byref(commitment_hashes), ctypes.byref(share_macs), ctypes.byref(commitments), ctypes.byref(share_complaints), ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)), ctypes.byref(last_ts)) # we need to keep these arrays around, otherwise the gc eats them up. ctx = (state, cheaters, sig_pks, commitments, commitment_hashes, share_macs, share_complaints, last_ts, b) return ctx, msg.raw #size_t stp_dkg_stp_input_size(const STP_DKG_STPState *ctx); def stp_dkg_stp_input_size(ctx): return liboprf.stp_dkg_stp_input_size(ctx[0]) #int stp_dkg_stp_input_sizes(const STP_DKG_STPState *ctx, size_t *sizes); def stp_dkg_stp_input_sizes(ctx): sizes = (ctypes.c_size_t * stp_dkg_stpstate_n(ctx))() ret = liboprf.stp_dkg_stp_input_sizes(ctx[0], ctypes.byref(sizes)) return ret, [x for x in sizes] #size_t stp_dkg_stp_output_size(const STP_DKG_STPState *ctx); def stp_dkg_stp_output_size(ctx): return liboprf.stp_dkg_stp_output_size(ctx[0]) #int stp_dkg_stp_next(TP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); def stp_dkg_stp_next(ctx, msg): input_len = stp_dkg_stp_input_size(ctx) if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") output_len = stp_dkg_stp_output_size(ctx) output = ctypes.create_string_buffer(output_len) __check(liboprf.stp_dkg_stp_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) return output #int stp_dkg_stp_peer_msg(const STP_DKG_STPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); def stp_dkg_stp_peer_msg(ctx, base, peer): msg = ctypes.POINTER(ctypes.c_char)() size = ctypes.c_size_t() __check(liboprf.stp_dkg_stp_peer_msg(ctx[0], base, ctypes.c_size_t(len(base.raw)), peer, ctypes.byref(msg), ctypes.byref(size))) return msg[:size.value] #int stp_dkg_stp_not_done(const STP_DKG_STPState *tp); def stp_dkg_stp_not_done(ctx): return liboprf.stp_dkg_stp_not_done(ctx[0]) == 1 def stp_dkg_get_cheaters(ctx): cheats = [] cheaters = set() for i in range(stp_dkg_stpstate_cheater_len(ctx)): err = ctypes.create_string_buffer(stp_dkg_max_err_SIZE) p = liboprf.stp_dkg_stp_cheater_msg(ctypes.byref(ctx[1][i]), err, stp_dkg_max_err_SIZE) if 0 >= p > stp_dkg_stpstate_n(ctx): print(f"invalid cheater index: {p}, skipping this entry") continue cheaters.add(p) cheats.append((p, err.raw[:err.raw.find(b'\x00')].decode('utf8'))) return cheaters, cheats liboprf.stp_dkg_peerstate_n.restype = ctypes.c_uint8 def stp_dkg_peerstate_n(ctx): return liboprf.stp_dkg_peerstate_n(ctx[0]) liboprf.stp_dkg_peerstate_t.restype = ctypes.c_uint8 def stp_dkg_peerstate_t(ctx): return liboprf.stp_dkg_peerstate_t(ctx[0]) liboprf.stp_dkg_peerstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) def stp_dkg_peerstate_sessionid(ctx): ptr = liboprf.stp_dkg_peerstate_sessionid(ctx[0]) return bytes(ptr[i] for i in range(stp_dkg_sessionid_SIZE)) liboprf.stp_dkg_peerstate_lt_sk.restype = ctypes.POINTER(ctypes.c_uint8) def stp_dkg_peerstate_lt_sk(ctx): ptr = liboprf.stp_dkg_peerstate_lt_sk(ctx[0]) return bytes(ptr[i] for i in range(pysodium.crypto_sign_SECRETKEYBYTES)) liboprf.stp_dkg_peerstate_share.restype = ctypes.POINTER(ctypes.c_uint8) def stp_dkg_peerstate_share(ctx): ptr = liboprf.stp_dkg_peerstate_share(ctx[0]) return bytes(ptr[i] for i in range(TOPRF_Share_BYTES*2)) liboprf.stp_dkg_peerstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) def stp_dkg_peerstate_commitments(ctx): ptr = liboprf.stp_dkg_peerstate_commitments(ctx[0]) return tuple(bytes(ptr[c*pysodium.crypto_core_ristretto255_BYTES+i] for i in range(pysodium.crypto_core_ristretto255_BYTES)) for c in range(stp_dkg_peerstate_n(ctx))) def stp_dkg_peerstate_step(ctx): return liboprf.stp_dkg_peerstate_step(ctx[0]) liboprf.stp_dkg_stpstate_n.restype = ctypes.c_uint8 def stp_dkg_stpstate_n(ctx): return liboprf.stp_dkg_stpstate_n(ctx[0]) liboprf.stp_dkg_stpstate_t.restype = ctypes.c_uint8 def stp_dkg_stpstate_t(ctx): return liboprf.stp_dkg_stpstate_t(ctx[0]) liboprf.stp_dkg_stpstate_cheater_len.restype = ctypes.c_size_t def stp_dkg_stpstate_cheater_len(ctx): return liboprf.stp_dkg_stpstate_cheater_len(ctx[0]) liboprf.stp_dkg_stpstate_sessionid.restype = ctypes.POINTER(ctypes.c_uint8) def stp_dkg_stpstate_sessionid(ctx): ptr = liboprf.stp_dkg_stpstate_sessionid(ctx[0]) return bytes(ptr[i] for i in range(stp_dkg_sessionid_SIZE)) liboprf.stp_dkg_stpstate_commitments.restype = ctypes.POINTER(ctypes.c_uint8) def stp_dkg_stpstate_commitments(ctx): ptr = liboprf.stp_dkg_stpstate_commitments(ctx[0]) return tuple(bytes(ptr[c*pysodium.crypto_core_ristretto255_BYTES+i] for i in range(pysodium.crypto_core_ristretto255_BYTES)) for c in range(stp_dkg_stpstate_n(ctx))) def stp_dkg_stpstate_step(ctx): return liboprf.stp_dkg_stpstate_step(ctx[0]) #typedef int (*Keyloader_CB)(const uint8_t id[crypto_generichash_BYTES], # void *arg, # uint8_t sigpk[crypto_sign_PUBLICKEYBYTES], # uint8_t noise_pk[crypto_scalarmult_BYTES]); #@ctypes.CFUNCTYPE(c.c_int, c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte), c.POINTER(c.c_ubyte)) #def load_key(keyid, alpha, beta): # c.memmove(beta, beta_, len(beta_)) # STP_DKG_Err stp_dkg_start_peer(STP_DKG_PeerState *ctx, # const uint64_t ts_epsilon, # const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], # const STP_DKG_Message *msg0, # uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]); # also conveniently wraps # int stp_dkg_peer_set_bufs(STP_DKG_PeerState *ctx, # uint8_t (*peerids)[][crypto_generichash_BYTES], # Keyloader_CB keyloader_cb, # void *keyloader_cb_arg, # uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], # uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], # Noise_XK_session_t *(*noise_outs)[], # Noise_XK_session_t *(*noise_ins)[], # TOPRF_Share (*k_shares)[][2], # uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE], # uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], # uint8_t (*ki_commitments)[][crypto_core_ristretto255_BYTES], # uint8_t (*k_commitments)[][crypto_core_ristretto255_BYTES], # uint8_t (*commitments_hashes)[][stp_dkg_commitment_HASHBYTES], # STP_DKG_Cheater (*cheaters)[], const size_t cheater_max, # uint16_t *share_complaints, # uint8_t *my_share_complaints, # uint64_t *last_ts); def stp_dkg_peer_start(ts_epsilon, lt_sk, noise_sk, stp_ltpk, msg0, keyloader=None, keyloader_arg=None): b = ctypes.create_string_buffer(liboprf.stp_dkg_peerstate_size()+32) b_addr = ctypes.addressof(b) s_addr = b_addr + (b_addr % 32) state = ctypes.c_void_p(s_addr) if state.value % 32 != 0: raise ValueError("cannot align at 32bytes the STP_DKG_PeerState struct") if len(lt_sk) != pysodium.crypto_sign_SECRETKEYBYTES: raise ValueError(f"long-term signature secret key of peer has invalid length ({len(lt_sk)}) must be {pysodium.crypto_sign_SECRETKEYBYTES}") if len(noise_sk) != pysodium.crypto_scalarmult_SCALARBYTES: raise ValueError(f"long-term noise secret key of peer has invalid length ({len(noise_sk)}) must be {pysodium.crypto_scalarmult_SCALARBYTES}") if len(stp_ltpk) != pysodium.crypto_sign_PUBLICKEYBYTES: raise ValueError(f"long-term signature public key of STP has invalid length ({len(stp_ltpk)}) must be {pysodium.crypto_sign_PUBLICKEYBYTES}") __check(liboprf.stp_dkg_start_peer(state, ctypes.c_uint64(ts_epsilon), lt_sk, noise_sk, msg0, stp_ltpk)) n = stp_dkg_peerstate_n([state]) t = stp_dkg_peerstate_t([state]) peer_ids = ctypes.create_string_buffer(n * pysodium.crypto_generichash_BYTES) peers_sig_pks = ctypes.create_string_buffer((n+1) * pysodium.crypto_sign_PUBLICKEYBYTES) peers_noise_pks = ctypes.create_string_buffer(n * pysodium.crypto_scalarmult_BYTES) noise_outs = (ctypes.c_void_p * n)() noise_ins = (ctypes.c_void_p * n)() shares = ctypes.create_string_buffer(n * TOPRF_Share_BYTES*2) encrypted_shares = ctypes.create_string_buffer(n * (noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE)) share_macs = ctypes.create_string_buffer(n * n * pysodium.crypto_auth_hmacsha256_BYTES) commitments = ctypes.create_string_buffer(n * n * pysodium.crypto_core_ristretto255_BYTES) k_commitments = ctypes.create_string_buffer(n * pysodium.crypto_core_ristretto255_BYTES) commitment_hashes = ctypes.create_string_buffer(n * tupdate_commitment_HASHBYTES) cheaters = (TP_DKG_Cheater * (t*t - 1))() complaints = (ctypes.c_uint16 * n*n)() my_complaints = ctypes.create_string_buffer(n) last_ts = (ctypes.c_uint64 * n)() liboprf.stp_dkg_peer_set_bufs(state, ctypes.byref(peer_ids), #ctypes.byref(keyloader), keyloader, #ctypes.byref(keyloader_arg), keyloader_arg, ctypes.byref(peers_sig_pks), ctypes.byref(peers_noise_pks), noise_outs, noise_ins, ctypes.byref(shares), ctypes.byref(encrypted_shares), ctypes.byref(share_macs), ctypes.byref(commitments), ctypes.byref(k_commitments), ctypes.byref(commitment_hashes), ctypes.byref(cheaters), ctypes.c_size_t(len(cheaters)), ctypes.byref(complaints), ctypes.byref(my_complaints), ctypes.byref(last_ts)) # we need to keep these arrays around, otherwise the gc eats them up. ctx = (state, peer_ids, peers_sig_pks, peers_noise_pks, noise_outs, noise_ins, shares, encrypted_shares, share_macs, commitments, k_commitments, commitment_hashes, cheaters, complaints, my_complaints, b, last_ts) return ctx #size_t stp_dkg_peer_input_size(const STP_DKG_PeerState *ctx); def stp_dkg_peer_input_size(ctx): return liboprf.stp_dkg_peer_input_size(ctx[0]) #size_t stp_dkg_peer_output_size(const STP_DKG_PeerState *ctx); def stp_dkg_peer_output_size(ctx): return liboprf.stp_dkg_peer_output_size(ctx[0]) #int stp_dkg_peer_next(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); def stp_dkg_peer_next(ctx, msg): input_len = stp_dkg_peer_input_size(ctx) if len(msg) != input_len: raise ValueError(f"input msg is invalid size: {len(msg)}B must be: {input_len}B") output_len = stp_dkg_peer_output_size(ctx) output = ctypes.create_string_buffer(output_len) __check(liboprf.stp_dkg_peer_next(ctx[0], msg, ctypes.c_size_t(input_len), output, ctypes.c_size_t(output_len))) return output.raw #int stp_dkg_peer_not_done(const STP_DKG_PeerState *peer); def stp_dkg_peer_not_done(ctx): return liboprf.stp_dkg_peer_not_done(ctx[0]) == 1 #void stp_dkg_peer_free(STP_DKG_PeerState *ctx); def stp_dkg_peer_free(ctx): liboprf.stp_dkg_peer_free(ctx[0]) liboprf-0.9.4/python/pyoprf/multiplexer.py000077500000000000000000000403251514673400200207560ustar00rootroot00000000000000#!/usr/bin/env python import ssl, socket, select, struct, asyncio, serial, sys, time from pyoprf import noisexk from itertools import zip_longest from serial_asyncio import create_serial_connection try: from ble_serial.bluetooth.ble_client import BLE_client except ImportError: BLE_client = None try: import pyudev except ImportError: pyudev = None def split_by_n(iterable, n): return list(zip_longest(*[iter(iterable)]*n)) def get_event_loop(): try: return asyncio.get_running_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) return loop class Peer: def __init__(self, name, addr, type = "SSL", ssl_cert=None, timeout=5, alpn_proto=None): self.name = name self.type = type # currently only TCP or SSL over TCP, but # could be others like dedicated NOISE_XK, # or hybrid mceliece+x25519 over USB or # even UART self.address = addr # Currently only TCP host:port as a tuple self.ssl_cert = ssl_cert self.timeout = timeout self.alpn_proto = alpn_proto or ["oprf/1"] self.state = "new" self.fd = None def connect(self): if self.state == "connected": raise ValueError(f"{self.name} is already connected") if self.type not in {"SSL", "TCP"}: raise ValueError(f"Unsupported peer type: {self.type}") if self.type == "SSL": ctx = ssl.create_default_context() ctx.minimum_version = ssl.TLSVersion.TLSv1_2 ctx.set_alpn_protocols(self.alpn_proto) if(self.ssl_cert): ctx.load_verify_locations(self.ssl_cert) # only for dev, production system should use proper certs! ctx.check_hostname=False # only for dev, production system should use proper certs! ctx.verify_mode=ssl.CERT_NONE # only for dev, production system should use proper certs! else: ctx.load_default_certs() ctx.verify_mode = ssl.CERT_REQUIRED ctx.check_hostname = True s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(self.timeout) if self.type == "SSL": self.fd = ctx.wrap_socket(s, server_hostname=self.address[0]) try: self.fd.connect(self.address) except: return self.state="connected" def connected(self): return self.state == "connected" async def read_async(self,size): if not self.connected(): return None #raise ValueError(f"{self.name} cannot read, is not connected") res = [] read = 0 while read 0 and self.rx_len >= self.rx_pected: self.rx_available.set() async def read_raw(self,size): while(self.rx_available.is_set()): await asyncio.sleep(0.001) self.rx_pected = size if(self.rx_len < self.rx_pected): #print(f"{self.rx_len} < {self.rx_pected}", file=sys.stderr) await self.rx_available.wait() rsize = 0; ret = [] while(rsizeH",len(ct)) get_event_loop().run_until_complete(self._send(header+ct)) def close(self): if self.state == "closed": return if not self.connected(): return #raise ValueError(f"{self.name} cannot close, is not connected") get_event_loop().run_until_complete(self._disconnect()) class Serial(asyncio.Protocol): def __init__(self, *args, **kwargs): self.rx_buffer = [] self.rx_len = 0 self.rx_pexted = 0 self.rx_available = asyncio.Event() super().__init__(*args, **kwargs) def connection_made(self, transport): #print('port opened', transport, file=sys.stderr) self.transport = transport transport.serial.dtr = True #transport.serial.rts = False #transport.write(b'hello world\n') def data_received(self, data): #print('data received', len(data), data.hex(), file=sys.stderr) #print('data received', len(data), file=sys.stderr) self.rx_buffer.append(data) self.rx_len += len(data) if self.rx_pected > 0 and self.rx_len >= self.rx_pected: self.rx_available.set() def connection_lost(self, exc): print('port closed', file=sys.stderr) self.rx_available.set() #get_event_loop().stop() async def read_raw(self,size): #print(f"read_raw({size})",file=sys.stderr) #while(self.rx_available.is_set()): pass self.rx_pected = size while(self.rx_len < self.rx_pected and not self.rx_available.is_set()): await asyncio.sleep(0.001) #if(self.rx_len < self.rx_pected): #while(self.rx_available.is_set()): pass #print(f"{self.rx_len} < {self.rx_pected}", file=sys.stderr) # await self.rx_available.wait() rsize = 0; ret = [] while(rsizeH",len(ct)) self.transport.serial.write(header+ct) def close(self): if self.state == "closed": return if not self.connected(): return #raise ValueError(f"{self.name} cannot close, is not connected") if self.transport.serial is not None: self.transport.serial.dtr = False self.transport.close() self.state = "closed" class Multiplexer: def __init__(self, peers, alpn_proto=None): if asyncio.get_event_loop_policy()._local._loop is None: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) self.peers = [] for name, p in peers.items(): if 'port' in p: p = Peer(name ,(p['host'],p['port']) ,type=p.get("type", "SSL") ,ssl_cert = p.get('ssl_cert') ,timeout = p.get('timeout') ,alpn_proto=alpn_proto) elif 'bleaddr' in p: p = BLEPeer(name ,p['bleaddr'] ,p['device_pk'] ,p['client_sk'] ,timeout=p.get('timeout')) elif 'usb_serial' in p: p = USBPeer(name ,p['usb_serial'] ,p['device_pk'] ,p['client_sk'] ,timeout=p.get('timeout')) else: raise ValueError(f"cannot decide type of peer: {name}") self.peers.append(p) def __getitem__(self, idx): return self.peers[idx] def __iter__(self): for p in self.peers: yield p def __len__(self): return len(self.peers) def __enter__(self): return self def __exit__(self, exception_type, exception_value, exception_traceback): if exception_type is not None: print("exception caught", exception_type, exception_value, exception_traceback) self.close() def connect(self): for p in self.peers: p.connect() def send(self, idx, msg): self.peers[idx].send(msg) def broadcast(self, msg): for p in self.peers: p.send(msg) async def gather_async(self, expected_msg_len, n=None, proc=None): results = await asyncio.gather( *[peer.read_async(expected_msg_len) for peer in self.peers], return_exceptions=True ) for i in range(len(results)): if isinstance(results[i], Exception): print(f"client {self.peers[i].name} returned exception: {results[i]}", file=sys.stderr) results[i]=None continue if results[i] == b'\x00\x04fail': results[i]=None continue tmp = results[i] if not proc else proc(results[i]) if tmp is None: continue results[i]=tmp if n is None: n=len(self.peers) if len([1 for e in results if e is not None]) < n: raise ValueError(f"not enough responses gathered: {results}") return results def gather(self, *args, **kwargs): return get_event_loop().run_until_complete(self.gather_async(*args, **kwargs)) def close(self): for p in self.peers: p.close() liboprf-0.9.4/python/pyoprf/noisexk.py000077500000000000000000000234751514673400200200730ustar00rootroot00000000000000#!/usr/bin/env python """ Wrapper for hacl-star XK_Noise SPDX-FileCopyrightText: 2024, Marsiske Stefan SPDX-License-Identifier: LGPL-3.0-or-later Copyright (c) 2024, Marsiske Stefan. All rights reserved. This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with liboprf. If not, see . """ import ctypes import ctypes.util from ctypes import c_void_p, c_ubyte, c_uint32, c_char, c_size_t, POINTER, byref lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('oprf-noiseXK') or ctypes.util.find_library('liboprf-noiseXK')) if not lib._name: raise ValueError('Unable to find liboprf-noiseXK') libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c') or ctypes.util.find_library('libc')) if not libc._name: raise ValueError('Unable to find libc') KEYSIZE = 32 NOISE_XK_CONF_ZERO = 0 NOISE_XK_AUTH_KNOWN_SENDER_NO_KCI = 2 NOISE_XK_CONF_STRONG_FORWARD_SECRECY = 5 def __check(code): if code != 0: raise ValueError lib.Noise_XK_device_add_peer.restype = c_void_p lib.Noise_XK_device_add_peer.argtypes = [c_void_p, c_void_p, ctypes.c_char_p] def add_peer(device, name, key): return lib.Noise_XK_device_add_peer(device, name, key) def pubkey(privkey): pubkey = ctypes.create_string_buffer(KEYSIZE) lib.Noise_XK_dh_secret_to_public(pubkey, privkey) return pubkey.raw lib.Noise_XK_device_create.restype = c_void_p def create_device(prologue, name, privkey): srlz_key = b'\x00'*KEYSIZE return lib.Noise_XK_device_create(len(prologue), prologue, name, srlz_key, privkey) lib.Noise_XK_peer_get_id.restype = c_void_p lib.Noise_XK_peer_get_id.argtypes = [c_void_p] def get_peerid(peer): return lib.Noise_XK_peer_get_id(peer) lib.Noise_XK_session_create_initiator.restype = c_void_p lib.Noise_XK_session_create_initiator.argtypes = [c_void_p, c_void_p] def create_session_initiator(device, peerid): return lib.Noise_XK_session_create_initiator(device, peerid) lib.Noise_XK_session_create_initiator.restype = c_void_p lib.Noise_XK_session_create_initiator.argtypes = [c_void_p, c_void_p] def create_session_initiator(device, peerid): res = lib.Noise_XK_session_create_initiator(device, peerid) if res == 0: raise ValueError return res lib.Noise_XK_session_create_responder.restype = c_void_p lib.Noise_XK_session_create_responder.argtypes = [c_void_p] def create_session_responder(device): res = lib.Noise_XK_session_create_responder(device) if res == 0: raise ValueError return res lib.Noise_XK_pack_message_with_conf_level.restype = c_void_p lib.Noise_XK_session_write.argtypes = [c_void_p, c_void_p, POINTER(c_uint32), POINTER(POINTER(c_ubyte))] lib.Noise_XK_encap_message_p_free.argtypes = [c_void_p] def initiator_1st_msg(session): encap_msg = lib.Noise_XK_pack_message_with_conf_level(0, 0, 0); msg_len = c_uint32() msg = POINTER(c_ubyte)() if 0!=lib.Noise_XK_session_write(encap_msg, session, byref(msg_len), byref(msg)): raise ValueError lib.Noise_XK_encap_message_p_free(encap_msg) res = bytes(msg[i] for i in range(msg_len.value)) if msg_len.value > 0: libc.free(msg) return res # Noise_XK_session_read(&encap_msg, bob_session, cipher_msg_len, cipher_msg); lib.Noise_XK_session_read.argtypes = [POINTER(c_void_p), c_void_p, c_uint32, POINTER(c_ubyte)] # Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_ZERO, encap_msg), def responder_1st_msg(session, msg): encap_msg = c_void_p() msg = (c_ubyte * len(msg)).from_buffer(bytearray(msg)) msg_len = c_uint32(len(msg)) if 0 != lib.Noise_XK_session_read(byref(encap_msg), session, msg_len, msg): raise ValueError plain_msg_len = c_uint32() plain_msg = POINTER(c_ubyte)() if not lib.Noise_XK_unpack_message_with_auth_level(byref(plain_msg_len), byref(plain_msg), 0, encap_msg): raise ValueError lib.Noise_XK_encap_message_p_free(encap_msg) if plain_msg_len.value > 0: libc.free(plain_msg) return initiator_1st_msg(session) def initiator_handshake_finish(session, msg): encap_msg = c_void_p() msg = (c_ubyte * len(msg)).from_buffer(bytearray(msg)) msg_len = c_uint32(len(msg)) if 0 != lib.Noise_XK_session_read(byref(encap_msg), session, msg_len, msg): raise ValueError plain_msg_len = c_uint32() plain_msg = POINTER(c_ubyte)() if not lib.Noise_XK_unpack_message_with_auth_level(byref(plain_msg_len), byref(plain_msg), 0, encap_msg): raise ValueError lib.Noise_XK_encap_message_p_free(encap_msg) if plain_msg_len.value > 0: libc.free(plain_msg) def send_msg(session, msg): if isinstance(msg, str): msg = msg.encode('utf8') encap_msg = lib.Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_STRONG_FORWARD_SECRECY, len(msg), msg); ct_len = c_uint32() ct = POINTER(c_ubyte)() if 0!=lib.Noise_XK_session_write(encap_msg, session, byref(ct_len), byref(ct)): raise ValueError lib.Noise_XK_encap_message_p_free(encap_msg) res = bytes(ct[:ct_len.value]) if ct_len.value > 0: libc.free(ct) return res def read_msg(session, msg): encap_msg = c_void_p() u_bytes = (c_ubyte * (len(msg)))() u_bytes[:] = msg if 0 != lib.Noise_XK_session_read(byref(encap_msg), session, len(msg), u_bytes): raise ValueError plain_msg_len = c_uint32() plain_msg = POINTER(c_ubyte)() if not lib.Noise_XK_unpack_message_with_auth_level(byref(plain_msg_len), byref(plain_msg), NOISE_XK_AUTH_KNOWN_SENDER_NO_KCI, encap_msg): raise ValueError lib.Noise_XK_encap_message_p_free(encap_msg) res = bytes(plain_msg[i] for i in range(plain_msg_len.value)) if plain_msg_len.value > 0: libc.free(plain_msg) return res lib.Noise_XK_session_get_peer_id.restype = c_uint32 lib.Noise_XK_session_get_peer_id.argtypes = [c_void_p] lib.Noise_XK_device_lookup_peer_by_id.restype = c_void_p lib.Noise_XK_device_lookup_peer_by_id.argtypes = [c_void_p, c_uint32] lib.Noise_XK_peer_get_static.argtypes = [(c_char * 32), c_void_p] def get_pubkey(session, device): peerid = lib.Noise_XK_session_get_peer_id(session) peer = lib.Noise_XK_device_lookup_peer_by_id(device, peerid); pubkey = ctypes.create_string_buffer(KEYSIZE) lib.Noise_XK_peer_get_static(pubkey, peer); return pubkey.raw def initiator_session(initiator_privkey, responder_pubkey, iname=None, rname=None, dst=None): if dst is None: dst = b"liboprf-noiseXK" if iname is None: iname = b"initiator" if rname is None: rname = b"responder" #initiator_pubkey = pubkey(initiator_privkey) dev = create_device(dst, iname, initiator_privkey) peer = add_peer(dev, rname, responder_pubkey) peerid = get_peerid(peer) session = create_session_initiator(dev, peerid) msg = initiator_1st_msg(session) return session, msg libc.malloc.restype = POINTER(c_ubyte) def responder_session(responder_privkey, auth_keys, msg, dst=None, name=None): if dst is None: dst = b"liboprf-noiseXK" if name is None: name = b"responder" #responder_pubkey = pubkey(responder_privkey) dev = create_device(dst, name, responder_privkey) for key, peer in auth_keys: add_peer(dev,peer,key) session = create_session_responder(dev) msg = responder_1st_msg(session, msg) return session, msg def initiator_session_complete(session, msg): return initiator_handshake_finish(session, msg) def test(): from binascii import unhexlify, hexlify # low level alice_privkey = unhexlify("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552") alice_pubkey = pubkey(alice_privkey) bob_privkey = unhexlify("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552") bob_pubkey = pubkey(bob_privkey) adev = create_device("liboprf-noiseXK test", "Alice", alice_privkey) bpeer = add_peer(adev, "Bob", bob_pubkey) bobid = get_peerid(bpeer) bdev = create_device("liboprf-noiseXK test", "Bob", bob_privkey) add_peer(bdev, "Alice", alice_pubkey) asession = create_session_initiator(adev, bobid) bsession = create_session_responder(bdev) msg = initiator_1st_msg(asession) msg = responder_1st_msg(bsession, msg) initiator_handshake_finish(asession, msg) ct = send_msg(asession, "hello bob!") pt = read_msg(bsession, ct) peer_pk = get_pubkey(bsession, bdev) print(hexlify(peer_pk)) print(pt) ct = send_msg(bsession, "hello alice!") pt = read_msg(asession, ct) print(pt) # high-level a2session, msg = initiator_session(alice_privkey, bob_pubkey) b2session, msg = responder_session(bob_privkey, [(alice_pubkey, "Alice")], msg) initiator_session_complete(a2session, msg) ct = send_msg(a2session, "hello bob!") pt = read_msg(b2session, ct) print(pt) ct = send_msg(b2session, "hello alice!") pt = read_msg(a2session, ct) print(pt) for _ in range(1000): if ct[0] % 2 == 0: sender = a2session receiver = b2session else: sender = b2session receiver = a2session message = ct[:16+(ct[1]>>4)] * (ct[1] & 0xf) ct = send_msg(sender, message) pt = read_msg(receiver, ct) assert(pt == message) if __name__ == '__main__': test() liboprf-0.9.4/python/setup.py000077500000000000000000000024361514673400200162260ustar00rootroot00000000000000#!/usr/bin/env python # SPDX-FileCopyrightText: 2023, Marsiske Stefan # SPDX-License-Identifier: LGPL-3.0-or-later import os from setuptools import setup, find_packages # Utility function to read the README file. # Used for the long_description. It's nice, because now 1) we have a top level # README file and 2) it's easier to type in the README file than to put a raw # string in below ... def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() setup(name = 'pyoprf', version = '0.9.4', description = 'python bindings for liboprf', license = "LGPLv3", author = 'Stefan Marsiske', author_email = 'toprf@ctrlc.hu', url = 'https://github.com/stef/liboprf/python', long_description=read('README.md'), long_description_content_type="text/markdown", packages=find_packages(), install_requires = ("pysodium", "SecureString", "pyserial", "pyudev", "ble_serial", "pyserial-asyncio"), classifiers = ["Development Status :: 4 - Beta", "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", "Topic :: Security :: Cryptography", "Topic :: Security", ], #ext_modules = [liboprf], ) liboprf-0.9.4/python/tests/000077500000000000000000000000001514673400200156465ustar00rootroot00000000000000liboprf-0.9.4/python/tests/test.py000077500000000000000000000243761514673400200172160ustar00rootroot00000000000000#!/usr/bin/env python3 import unittest import pyoprf, pysodium, ctypes from binascii import unhexlify from itertools import combinations class TestEndToEnd(unittest.TestCase): def test_cfrg_irtf(self): """CFRG/IRTF spec compliant run""" # Alice blinds the input "test" r, alpha = pyoprf.blind(b"test") # Bob generates a "secret" key k = pyoprf.keygen() # Bob evaluates Alices blinded value with it's key beta = pyoprf.evaluate(k, alpha) # Alice unblinds Bobs evaluation N = pyoprf.unblind(r, beta) # Alice finalizes the calculation y = pyoprf.finalize(b"test", N) # rerun and assert that oprf(k,"test") equals all runs r, alpha = pyoprf.blind(b"test") beta = pyoprf.evaluate(k, alpha) N = pyoprf.unblind(r, beta) y2 = pyoprf.finalize(b"test", N) self.assertEqual(y, y2) def test_cfrg_irtf_testvec1(self): """IRTF/CFRG testvector 1""" x = unhexlify("00") k = unhexlify("5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063b0e") out=unhexlify("527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb7708264e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf6") r, alpha = pyoprf.blind(x) beta = pyoprf.evaluate(k, alpha) N = pyoprf.unblind(r, beta) y = pyoprf.finalize(x, N) self.assertEqual(y,out) def test_cfrg_irtf_testvec2(self): """IRTF/CFRG testvector 2""" x=unhexlify("5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a") k = unhexlify("5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063b0e") out=unhexlify("f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e750cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c73") r, alpha = pyoprf.blind(x) beta = pyoprf.evaluate(k, alpha) N = pyoprf.unblind(r, beta) y = pyoprf.finalize(x, N) self.assertEqual(y, out) def test_hashDH_update(self): """HashDH with update example""" # Alice blinds the input "test" r, alpha = pyoprf.blind(b"test") # Bob generates a "secret" key k = pyoprf.keygen() # Bob evaluates Alices blinded value with it's key beta = pyoprf.evaluate(k, alpha) # Alice unblinds Bobs evaluation N = pyoprf.unblind(r, beta) # Bob updates his key, by generating delta delta = pysodium.crypto_core_ristretto255_scalar_random() k2 = pysodium.crypto_core_ristretto255_scalar_mul(k, delta) # Alice updates her previous calculation of N with delta N2 = pysodium.crypto_scalarmult_ristretto255(delta, N) # rerun hashDH to verify if N2 is equal with a full run r, alpha = pyoprf.blind(b"test") beta = pyoprf.evaluate(k2, alpha) N2_ = pyoprf.unblind(r, beta) self.assertEqual(N2, N2_) def test_toprf_sss(self): """tOPRF (hashDH), (3,5), with centrally shared key interpolation at client""" k2 = pyoprf.keygen() shares = pyoprf.create_shares(k2, 5, 3) r, alpha = pyoprf.blind(b"test") #print(' '.join(s.hex() for s in shares)) # we reuse values from te previous test betas = tuple(s[:1]+pyoprf.evaluate(s[1:], alpha) for s in shares) #print(''.join(b.hex() for b in betas)) beta = pyoprf.thresholdmult(betas) Nt = pyoprf.unblind(r, beta) beta = pyoprf.evaluate(k2, alpha) N2 = pyoprf.unblind(r, beta) self.assertEqual(N2, Nt) def test_toprf_tcombine(self): """tOPRF (hashDH), (3,5), with centrally shared key interpolation at servers""" k2 = pyoprf.keygen() shares = pyoprf.create_shares(k2, 5, 3) r, alpha = pyoprf.blind(b"test") indexes=(4,2,1) betas = tuple(pyoprf.threshold_evaluate(shares[i-1], alpha, i, indexes) for i in indexes) beta = pyoprf.threshold_combine(betas) beta = pyoprf.evaluate(k2, alpha) Nt = pyoprf.unblind(r, beta) Nt2 = pyoprf.unblind(r, beta) self.assertEqual(Nt, Nt2) def test_raw_dkg(self): """naked Distributed KeyGen (3,5)""" n = 5 t = 3 mailboxes=[[] for _ in range(n)] commitments=[] for _ in range(n): coms, shares = pyoprf.dkg_start(n,t) commitments.append(coms) for i,s in enumerate(shares): mailboxes[i].append(s) commitments=b''.join(commitments) shares = [] for i in range(n): fails = pyoprf.dkg_verify_commitments(n,t,i+1, commitments, mailboxes[i]) if len(fails) > 0: for fail in fails: print(f"fail: peer {fail}") raise ValueError("failed to verify contributions, aborting") xi = pyoprf.dkg_finish(n, mailboxes[i], i+1) #print(i, xi.hex(), x_i.hex()) shares.append(xi) # test if the final shares all reproduce the same shared `secret` v0 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in (0,1,2)]) for peers in combinations(range(1,5), 3): v1 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in peers]) self.assertEqual(v0, v1) secret = pyoprf.dkg_reconstruct(shares[:t]) #print("secret", secret.hex()) self.assertEqual(v0, pysodium.crypto_scalarmult_ristretto255_base(secret)) def test_explicit_3hashtdh(self): """toprf based on 2024/1455 [JSPPJ24] https://eprint.iacr.org/2024/1455 using explicit implementation of 3hashtdh""" print("tOPRF (3hashTDH), (3,5), with centrally shared key interpolation at client") k2 = pyoprf.keygen() shares = pyoprf.create_shares(k2, 5, 3) zero_shares = pyoprf.create_shares(bytes([0]*32), 5, 3) r, alpha = pyoprf.blind(b"test") ssid_S = pysodium.randombytes(32) betas = [] for k, z in zip(shares,zero_shares): h2 = pyoprf.evaluate( z[1:], pysodium.crypto_core_ristretto255_from_hash(pysodium.crypto_generichash(ssid_S + alpha, outlen=64)), ) beta = pyoprf.evaluate(k[1:], alpha) betas.append(k[:1]+pysodium.crypto_core_ristretto255_add(beta, h2)) # normal 2hashdh(k2,"test") beta = pyoprf.evaluate(k2, alpha) Nt0 = pyoprf.unblind(r, beta) for peers in combinations(betas, 3): beta = pyoprf.thresholdmult(betas[:3]) Nt1 = pyoprf.unblind(r, beta) self.assertEqual(Nt0, Nt1) def test_native_3hashtdh(self): """toprf based on 2024/1455 [JSPPJ24] https://eprint.iacr.org/2024/1455 using libopr native implementation of 3hashtdh tOPRF (3hashTDH), (3,5), with centrally shared key interpolation at client""" k2 = pyoprf.keygen() shares = pyoprf.create_shares(k2, 5, 3) zero_shares = pyoprf.create_shares(bytes([0]*32), 5, 3) r, alpha = pyoprf.blind(b"test") ssid_S = pysodium.randombytes(32) betas = [] for k, z in zip(shares,zero_shares): betas.append(pyoprf._3hashtdh(k, z, alpha, ssid_S)) beta = pyoprf.evaluate(k2, alpha) Nt0 = pyoprf.unblind(r, beta) for peers in combinations(betas, 3): beta = pyoprf.thresholdmult(betas[:3]) Nt1 = pyoprf.unblind(r, beta) self.assertEqual(Nt0, Nt1) def test_tp_dkg(self): """Trusted Party Distributed KeyGeneration""" n = 5 t = 3 ts_epsilon = 5 # enable verbose logging for tp-dkg #libc = ctypes.cdll.LoadLibrary('libc.so.6') #cstderr = ctypes.c_void_p.in_dll(libc, 'stderr') #log_file = ctypes.c_void_p.in_dll(pyoprf.liboprf,'log_file') #log_file.value = cstderr.value # create some long-term keypairs peer_lt_pks = [] peer_lt_sks = [] for _ in range(n): pk, sk = pysodium.crypto_sign_keypair() peer_lt_pks.append(pk) peer_lt_sks.append(sk) # initialize the TP and get the first message tp, msg0 = pyoprf.tpdkg_start_tp(n, t, ts_epsilon, "pyoprf tpdkg test", peer_lt_pks) print(f"\nn: {pyoprf.tpdkg_tpstate_n(tp)}, t: {pyoprf.tpdkg_tpstate_t(tp)}, sid: {bytes(c for c in pyoprf.tpdkg_tpstate_sessionid(tp)).hex()}") # initialize all peers with the 1st message from TP peers=[] for i in range(n): peer = pyoprf.tpdkg_peer_start(ts_epsilon, peer_lt_sks[i], msg0) peers.append(peer) for i in range(n): self.assertEqual(pyoprf.tpdkg_peerstate_sessionid(peers[i]), pyoprf.tpdkg_tpstate_sessionid(tp)) self.assertEqual(peer_lt_sks[i], pyoprf.tpdkg_peerstate_lt_sk(peers[i])) peer_msgs = [] while pyoprf.tpdkg_tp_not_done(tp): ret, sizes = pyoprf.tpdkg_tp_input_sizes(tp) # peer_msgs = (recv(size) for size in sizes) msgs = b''.join(peer_msgs) cur_step = pyoprf.tpdkg_tpstate_step(tp) try: tp_out = pyoprf.tpdkg_tp_next(tp, msgs) #print(f"tp: msg[{tp[0].step}]: {tp_out.raw.hex()}") except Exception as e: cheaters, cheats = pyoprf.tpdkg_get_cheaters(tp) print(f"Warning during the distributed key generation the peers misbehaved: {sorted(cheaters)}") for k, v in cheats: print(f"\tmisbehaving peer: {k} was caught: {v}") raise ValueError(f"{e} | tp step {cur_step}") peer_msgs = [] while(len(b''.join(peer_msgs))==0 and pyoprf.tpdkg_peer_not_done(peers[0])): for i in range(n): if(len(tp_out)>0): msg = pyoprf.tpdkg_tp_peer_msg(tp, tp_out, i) #print(f"tp -> peer[{i+1}] {msg.hex()}") else: msg = '' out = pyoprf.tpdkg_peer_next(peers[i], msg) if(len(out)>0): peer_msgs.append(out) #print(f"peer[{i+1}] -> tp {peer_msgs[-1].hex()}") tp_out = '' # we are done, let's check the shares shares = [pyoprf.tpdkg_peerstate_share(peers[i]) for i in range(n)] for i, share in enumerate(shares): print(f"share[{i+1}] {share.hex()}") v0 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in (0,1,2)]) for peers_idxs in combinations(range(1,5), 3): v1 = pyoprf.thresholdmult([bytes([i+1])+pysodium.crypto_scalarmult_ristretto255_base(shares[i][1:]) for i in peers_idxs]) self.assertEqual(v0, v1) secret = pyoprf.dkg_reconstruct(shares[:t]) #print("secret", secret.hex()) self.assertEqual(v0, pysodium.crypto_scalarmult_ristretto255_base(secret)) # clean up allocated buffers for i in range(n): pyoprf.tpdkg_peer_free(peers[i]) liboprf-0.9.4/src/000077500000000000000000000000001514673400200137525ustar00rootroot00000000000000liboprf-0.9.4/src/aux_/000077500000000000000000000000001514673400200147065ustar00rootroot00000000000000liboprf-0.9.4/src/aux_/crypto_kdf_hkdf_sha256.h000066400000000000000000000045011514673400200213070ustar00rootroot00000000000000#ifndef crypto_kdf_hkdf_sha256_H #define crypto_kdf_hkdf_sha256_H #include #include #include #include //#include "crypto_kdf.h" //#include "crypto_auth_hmacsha256.h" //#include "export.h" #ifdef __cplusplus # ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wlong-long" # endif extern "C" { #endif #define crypto_kdf_hkdf_sha256_KEYBYTES crypto_auth_hmacsha256_BYTES SODIUM_EXPORT size_t crypto_kdf_hkdf_sha256_keybytes(void); #define crypto_kdf_hkdf_sha256_BYTES_MIN 0U SODIUM_EXPORT size_t crypto_kdf_hkdf_sha256_bytes_min(void); #define crypto_kdf_hkdf_sha256_BYTES_MAX (0xff * crypto_auth_hmacsha256_BYTES) SODIUM_EXPORT size_t crypto_kdf_hkdf_sha256_bytes_max(void); SODIUM_EXPORT int crypto_kdf_hkdf_sha256_extract(unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES], const unsigned char *salt, size_t salt_len, const unsigned char *ikm, size_t ikm_len) __attribute__ ((nonnull(4))); SODIUM_EXPORT void crypto_kdf_hkdf_sha256_keygen(unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]); SODIUM_EXPORT int crypto_kdf_hkdf_sha256_expand(unsigned char *out, size_t out_len, const char *ctx, size_t ctx_len, const unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]) __attribute__ ((nonnull(1))); /* ------------------------------------------------------------------------- */ typedef struct crypto_kdf_hkdf_sha256_state { crypto_auth_hmacsha256_state st; } crypto_kdf_hkdf_sha256_state; SODIUM_EXPORT size_t crypto_kdf_hkdf_sha256_statebytes(void); SODIUM_EXPORT int crypto_kdf_hkdf_sha256_extract_init(crypto_kdf_hkdf_sha256_state *state, const unsigned char *salt, size_t salt_len) __attribute__ ((nonnull(1))); SODIUM_EXPORT int crypto_kdf_hkdf_sha256_extract_update(crypto_kdf_hkdf_sha256_state *state, const unsigned char *ikm, size_t ikm_len) __attribute__ ((nonnull)); SODIUM_EXPORT int crypto_kdf_hkdf_sha256_extract_final(crypto_kdf_hkdf_sha256_state *state, unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]) __attribute__ ((nonnull)); #ifdef __cplusplus } #endif #endif liboprf-0.9.4/src/aux_/kdf_hkdf_sha256.c000066400000000000000000000075551514673400200177160ustar00rootroot00000000000000#include #include #include "sodium/crypto_auth_hmacsha256.h" #include "sodium/crypto_kdf.h" #include "crypto_kdf_hkdf_sha256.h" #include "sodium/randombytes.h" #include "sodium/utils.h" int crypto_kdf_hkdf_sha256_extract_init(crypto_kdf_hkdf_sha256_state *state, const unsigned char *salt, size_t salt_len) { return crypto_auth_hmacsha256_init(&state->st, salt, salt_len); } int crypto_kdf_hkdf_sha256_extract_update(crypto_kdf_hkdf_sha256_state *state, const unsigned char *ikm, size_t ikm_len) { return crypto_auth_hmacsha256_update(&state->st, ikm, ikm_len); } int crypto_kdf_hkdf_sha256_extract_final(crypto_kdf_hkdf_sha256_state *state, unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]) { crypto_auth_hmacsha256_final(&state->st, prk); sodium_memzero(state, sizeof *state); return 0; } int crypto_kdf_hkdf_sha256_extract( unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES], const unsigned char *salt, size_t salt_len, const unsigned char *ikm, size_t ikm_len) { crypto_kdf_hkdf_sha256_state state; crypto_kdf_hkdf_sha256_extract_init(&state, salt, salt_len); crypto_kdf_hkdf_sha256_extract_update(&state, ikm, ikm_len); return crypto_kdf_hkdf_sha256_extract_final(&state, prk); } void crypto_kdf_hkdf_sha256_keygen(unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]) { randombytes_buf(prk, crypto_kdf_hkdf_sha256_KEYBYTES); } int crypto_kdf_hkdf_sha256_expand(unsigned char *out, size_t out_len, const char *ctx, size_t ctx_len, const unsigned char prk[crypto_kdf_hkdf_sha256_KEYBYTES]) { crypto_auth_hmacsha256_state st; unsigned char tmp[crypto_auth_hmacsha256_BYTES]; size_t i; size_t left; unsigned char counter = 1U; if (out_len > crypto_kdf_hkdf_sha256_BYTES_MAX) { errno = EINVAL; return -1; } for (i = (size_t) 0U; i + crypto_auth_hmacsha256_BYTES <= out_len; i += crypto_auth_hmacsha256_BYTES) { crypto_auth_hmacsha256_init(&st, prk, crypto_kdf_hkdf_sha256_KEYBYTES); if (i != (size_t) 0U) { crypto_auth_hmacsha256_update(&st, &out[i - crypto_auth_hmacsha256_BYTES], crypto_auth_hmacsha256_BYTES); } crypto_auth_hmacsha256_update(&st, (const unsigned char *) ctx, ctx_len); crypto_auth_hmacsha256_update(&st, &counter, (size_t) 1U); crypto_auth_hmacsha256_final(&st, &out[i]); counter++; } if ((left = out_len & (crypto_auth_hmacsha256_BYTES - 1U)) != (size_t) 0U) { crypto_auth_hmacsha256_init(&st, prk, crypto_kdf_hkdf_sha256_KEYBYTES); if (i != (size_t) 0U) { crypto_auth_hmacsha256_update(&st, &out[i - crypto_auth_hmacsha256_BYTES], crypto_auth_hmacsha256_BYTES); } crypto_auth_hmacsha256_update(&st, (const unsigned char *) ctx, ctx_len); crypto_auth_hmacsha256_update(&st, &counter, (size_t) 1U); crypto_auth_hmacsha256_final(&st, tmp); memcpy(&out[i], tmp, left); sodium_memzero(tmp, sizeof tmp); } sodium_memzero(&st, sizeof st); return 0; } size_t crypto_kdf_hkdf_sha256_keybytes(void) { return crypto_kdf_hkdf_sha256_KEYBYTES; } size_t crypto_kdf_hkdf_sha256_bytes_min(void) { return crypto_kdf_hkdf_sha256_BYTES_MIN; } size_t crypto_kdf_hkdf_sha256_bytes_max(void) { return crypto_kdf_hkdf_sha256_BYTES_MAX; } size_t crypto_kdf_hkdf_sha256_statebytes(void) { return sizeof(crypto_kdf_hkdf_sha256_state); } liboprf-0.9.4/src/dkg-vss.c000066400000000000000000000132641514673400200155020ustar00rootroot00000000000000#include #include #include #include "toprf.h" #include "utils.h" #include "dkg.h" // nothing up my sleeve generator H, generated with: // hash_to_group((uint8_t*)"DKG Generator H on ristretto255", 32, H) const __attribute__((visibility("hidden"))) uint8_t H[crypto_core_ristretto255_BYTES]= { 0x66, 0x4e, 0x4c, 0xb5, 0x89, 0x0e, 0xb3, 0xe4, 0xc0, 0xd5, 0x48, 0x02, 0x74, 0x8a, 0xb2, 0x25, 0xf9, 0x73, 0xda, 0xe5, 0xc0, 0xef, 0xc1, 0x68, 0xf4, 0x4d, 0x1b, 0x60, 0x28, 0x97, 0x8f, 0x07}; int dkg_vss_commit(const uint8_t a[crypto_core_ristretto255_SCALARBYTES], const uint8_t r[crypto_core_ristretto255_SCALARBYTES], uint8_t C[crypto_core_ristretto255_BYTES]) { // compute commitments uint8_t X[crypto_core_ristretto255_BYTES]; uint8_t R[crypto_core_ristretto255_BYTES]; // x = g^a_ik crypto_scalarmult_ristretto255_base(X, a); // r = h^b_ik if(crypto_scalarmult_ristretto255(R, r, H)) return 1; // C_ik = x+r crypto_core_ristretto255_add(C,X,R); return 0; } int dkg_vss_share(const uint8_t n, const uint8_t threshold, const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], uint8_t commitments[n][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n][2], uint8_t blind[crypto_core_ristretto255_SCALARBYTES]) { if(threshold==0) return 1; uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES]; uint8_t b[threshold][crypto_core_ristretto255_SCALARBYTES]; if(secret!=NULL) memcpy(a[0],secret, crypto_core_ristretto255_SCALARBYTES); for(int k=0;kvalue, crypto_core_ristretto255_SCALARBYTES, "x[%d] ", self); //dump(x_i->value, crypto_core_ristretto255_SCALARBYTES, "x'[%d] ", self); if(0!=dkg_vss_commit(share[0].value, share[1].value, commitment)) return 1; return 0; } static void sort_shares(const int n, uint8_t arr[n], uint8_t indexes[n]) { for (uint8_t c = 1 ; c <= n - 1; c++) { uint8_t d = c, t, t1; while(d > 0 && arr[d] < arr[d-1]) { t = arr[d]; t1 = indexes[d]; arr[d] = arr[d-1]; indexes[d] = indexes[d-1]; arr[d-1] = t; indexes[d-1] = t1; d--; } } } int dkg_vss_reconstruct(const uint8_t t, const uint8_t x, const size_t shares_len, const TOPRF_Share shares[shares_len][2], const uint8_t commitments[shares_len][crypto_scalarmult_ristretto255_BYTES], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t blind[crypto_scalarmult_ristretto255_SCALARBYTES]) { if(shares_len>128) return 1; uint8_t qual[t]; uint8_t indexes[t]; unsigned j=0; for(uint8_t i=0;i #include #include "dkg.h" /** * @brief Generator for the VSS scheme * * This generator was derived by hashing the fixed string * "DKG Generator H on ristretto255" into the Ristretto255 group. */ extern const uint8_t H[crypto_core_ristretto255_BYTES]; /** * @brief Creates shares of a secret with commitments * * @param[in] n The number of participants * @param[in] threshold The minimum number of shares needed to reconstruct the secret * @param[in] secret The secret to share * @param[out] commitments Array of commitments to shares, one for each participant * @param[out] shares Array of generated shares (each has secret and blinding shares) * @param[out] blind The blinding factor used for the commitment to the secret * * @return 0 on success, non-zero on error */ int dkg_vss_share(const uint8_t n, const uint8_t threshold, const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], uint8_t commitments[n][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n][2], uint8_t blind[crypto_core_ristretto255_SCALARBYTES]); /** * @brief Verifies that a share matches its commitment * * @param[in] commitment The commitment to verify against * @param[in] share The received secret share and its blinding share * * @return 0 if the commitment is valid, non-zero otherwise */ int dkg_vss_verify_commitment(const uint8_t commitment[crypto_core_ristretto255_BYTES], const TOPRF_Share share[2]); /** * @brief Finalizes the DKG VSS protocol for a participant * * Combines valid shares from qualified participants to compute the final * secret share and its corresponding commitment. * * @param[in] n The number of participants * @param[in] qual Array of indices of qualified participants * @param[in] shares Array of shares received from all participants * @param[in] self The index of the current participant * @param[out] share The final secret share and its blinding share * @param[out] commitment The commitment to the final share * * @return 0 on success, non-zero on error */ int dkg_vss_finish(const uint8_t n, const uint8_t qual[n], const TOPRF_Share shares[n][2], const uint8_t self, TOPRF_Share share[2], uint8_t commitment[crypto_core_ristretto255_BYTES]); /** * @brief Reconstructs a secret from a set of shares * * Given at least `t` valid shares, reconstructs the original secret * and optionally its blinding share. * * @param[in] t The threshold (minimum number of shares needed to reconstruct the secret) * @param[in] x The point at which to evaluate the polynomial (0 for the secret) * @param[in] shares_len The number of shares provided * @param[in] shares Array of shares provided for reconstruction * @param[in] commitments Array of commitments to verify shares * @param[out] result Buffer to store the reconstructed secret * @param[out] blind optional Buffer to store the reconstructed blinding factor skipped if NULL * * @return 0 on success, non-zero on error */ int dkg_vss_reconstruct(const uint8_t t, const uint8_t x, const size_t shares_len, const TOPRF_Share shares[shares_len][2], const uint8_t commitments[shares_len][crypto_scalarmult_ristretto255_BYTES], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t blind[crypto_scalarmult_ristretto255_SCALARBYTES]); /** * @brief Creates a Pedersen commitment to a value * * Computes C = g^a Β· h^r where `g` is the base point of the curve, * `h` is the fixed generator `H`, `a` is the value being committed to, * and `r` is the blinding factor. * * @param[in] a The value to commit to * @param[in] r The blinding factor * @param[out] C The resulting commitment * * @return 0 on success, non-zero on error */ int dkg_vss_commit(const uint8_t a[crypto_core_ristretto255_SCALARBYTES], const uint8_t r[crypto_core_ristretto255_SCALARBYTES], uint8_t C[crypto_core_ristretto255_BYTES]); #endif // DKG_VSS_H liboprf-0.9.4/src/dkg.c000066400000000000000000000432171514673400200146720ustar00rootroot00000000000000#include #include #include #include // time #include //htons #include "toprf.h" #include "utils.h" #include "dkg.h" #include "noise_private.h" #ifdef __ZEPHYR__ uint64_t ztime(void); #endif /* @copyright 2023-24, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ /* warning this is a low-level interface. Do not use directly unless you use it to implement DKG protocols which have proper sessionids and other protections against replay and confused deputy attacks. for an example of a high-level DKG protocol see tp-dkg.[ch] */ // calculates polynomial f(j) given the polynomials threshold coefficients in // array a void __attribute__((visibility("hidden"))) polynom(const uint8_t j, const uint8_t threshold, const uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES], TOPRF_Share *result) { //f(z) = a_0 + a_1*z + a_2*z^2 + a_3*z^3 + β‹― + (a_t)*(z^t) result->index=j; // f(z) = result = a[0] +..... memcpy(result->value, a[0], crypto_core_ristretto255_SCALARBYTES); // z = j uint8_t z[crypto_core_ristretto255_SCALARBYTES]={j}; // z^t -> for(int t=1;tvalue, result->value, tmp); } } int dkg_start(const uint8_t n, const uint8_t threshold, uint8_t commitments[threshold][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n]) { uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES]; if(0!=sodium_mlock(a,sizeof a)) { return -1; } for(int k=0;kvalue, 0, crypto_core_ristretto255_SCALARBYTES); for(int i=0;ivalue, xi->value, shares[i].value); //dump((uint8_t*)&shares[i][0], sizeof(TOPRF_Share), "s[%d,%d] ", qual[i], self); } //dump(xi->value, crypto_core_ristretto255_SCALARBYTES, "x[%d] ", self); return 0; } void dkg_reconstruct(const size_t threshold, const TOPRF_Share shares[threshold], uint8_t secret[crypto_scalarmult_ristretto255_BYTES]) { uint8_t lpoly[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t tmp[crypto_scalarmult_ristretto255_SCALARBYTES]; memset(secret,0,crypto_scalarmult_ristretto255_SCALARBYTES); uint8_t indexes[threshold]; for(size_t i=0;i now + ts_epsilon) return 4; } else { if(*last_ts > ts) return 1; if(ts > *last_ts + ts_epsilon) return 2; } *last_ts = ts; return 0; } int __attribute__((visibility("hidden"))) send_msg(uint8_t* msg_buf, const size_t msg_buf_len, const uint8_t type, const uint8_t version, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_sk, const uint8_t sessionid[dkg_sessionid_SIZE]) { if(msg_buf==NULL) return 1; DKG_Message* msg = (DKG_Message*) msg_buf; msg->type = type; msg->version = version; msg->len = htonl((uint32_t)msg_buf_len); msg->msgno = msgno; msg->from = from; msg->to = to; #ifdef __ZEPHYR__ msg->ts = htonll((uint64_t) ztime()); #else msg->ts = htonll((uint64_t)time(NULL)); #endif memcpy(msg->sessionid, sessionid, dkg_sessionid_SIZE); crypto_sign_detached(msg->sig, NULL, &msg->type, sizeof(DKG_Message) - crypto_sign_BYTES, sig_sk); return 0; } int __attribute__((visibility("hidden"))) recv_msg(const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t type, const uint8_t version, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_pk, const uint8_t sessionid[dkg_sessionid_SIZE], const uint64_t ts_epsilon, uint64_t *last_ts ) { if(msg_buf==NULL) return 8; const DKG_Message* msg = (const DKG_Message*) msg_buf; if(msg->type != type) return 9; if(msg->version != version) return 10; if(ntohl(msg->len) != msg_buf_len) return 1; if(msg->msgno != msgno) return 2; if(msg->from != from) return 3; if(msg->to != to) return 4; if(sodium_memcmp(msg->sessionid, sessionid, dkg_sessionid_SIZE)!=0) return 7; #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(NO_TIME) int ret = check_ts(ts_epsilon, last_ts, ntohll(msg->ts)); if(0!=ret) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, "checkts fail: %d, last_ts: %ld, ts: %ld, lt+e: %ld\n", ret, *last_ts, ntohll(msg->ts),*last_ts + ts_epsilon); } return 5; } #endif const size_t unsigned_buf_len = msg_buf_len - crypto_sign_BYTES; uint8_t with_sessionid[unsigned_buf_len + dkg_sessionid_SIZE]; memcpy(with_sessionid, msg_buf + crypto_sign_BYTES, unsigned_buf_len); memcpy(with_sessionid + unsigned_buf_len, sessionid, dkg_sessionid_SIZE); #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if(0!=crypto_sign_verify_detached(msg->sig, &msg->type, sizeof(DKG_Message) - crypto_sign_BYTES, sig_pk)) return 6; #endif return 0; } int dkg_init_noise_handshake(const uint8_t index, Noise_XK_device_t *dev, uint8_t rpk[crypto_scalarmult_BYTES], uint8_t *rname, Noise_XK_session_t** session, uint8_t msg[noise_xk_handshake1_SIZE]) { //if(liboprf_log_file != NULL) fprintf(liboprf_log_file, "[%d] creating noise session -> %s\n", index, rname); // fixme: damnit this allocates stuff on the heap... Noise_XK_peer_t *peer = Noise_XK_device_add_peer(dev, rname, rpk); if(!peer) return 1; uint32_t peer_id = Noise_XK_peer_get_id(peer); *session = Noise_XK_session_create_initiator(dev, peer_id); if(!*session) return 2; Noise_XK_encap_message_t *encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_ZERO, 0, NULL); uint32_t cipher_msg_len; uint8_t *cipher_msg; Noise_XK_rcode ret = Noise_XK_session_write(encap_msg, *session, &cipher_msg_len, &cipher_msg); Noise_XK_encap_message_p_free(encap_msg); if(!Noise_XK_rcode_is_success(ret)) { Noise_XK_session_free(*session); return 3; } if(cipher_msg_len!=noise_xk_handshake1_SIZE) { Noise_XK_session_free(*session); free(cipher_msg); return 4; } if(msg==NULL) return 5; memcpy(msg,cipher_msg,cipher_msg_len); free(cipher_msg); return 0; } int dkg_respond_noise_handshake(const uint8_t index, Noise_XK_device_t *dev, uint8_t *rname, Noise_XK_session_t** session, uint8_t inmsg[noise_xk_handshake1_SIZE], uint8_t outmsg[noise_xk_handshake2_SIZE]) { //if(liboprf_log_file != NULL) fprintf(liboprf_log_file, "[%d] responding noise session -> %s\n", index, rname); // fixme: damnit this allocates stuff on the heap... *session = Noise_XK_session_create_responder(dev); if(!*session) return 1; Noise_XK_encap_message_t *encap_msg; Noise_XK_rcode ret = Noise_XK_session_read(&encap_msg, *session, noise_xk_handshake1_SIZE, inmsg); if(!Noise_XK_rcode_is_success(ret)) { Noise_XK_session_free(*session); return 2; } uint32_t plain_msg_len; uint8_t *plain_msg; if(!Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_ZERO, encap_msg)) { Noise_XK_session_free(*session); return 3; } Noise_XK_encap_message_p_free(encap_msg); encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_ZERO, 0, NULL); uint32_t cipher_msg_len; uint8_t *cipher_msg; ret = Noise_XK_session_write(encap_msg, *session, &cipher_msg_len, &cipher_msg); Noise_XK_encap_message_p_free(encap_msg); if(!Noise_XK_rcode_is_success(ret)) { Noise_XK_session_free(*session); return 4; } if(cipher_msg_len!=noise_xk_handshake2_SIZE) { Noise_XK_session_free(*session); free(cipher_msg); return 4; } if(outmsg==NULL) return 5; memcpy(outmsg,cipher_msg,cipher_msg_len); free(cipher_msg); return 0; } int dkg_finish_noise_handshake(const uint8_t index, Noise_XK_device_t *dev, Noise_XK_session_t** session, uint8_t msg[noise_xk_handshake2_SIZE]) { if(!*session) { return 1; } if(liboprf_log_file!=NULL) { // get peer name uint32_t peer_id = Noise_XK_session_get_peer_id(*session); Noise_XK_peer_t *peer = Noise_XK_device_lookup_peer_by_id(dev, peer_id); if(peer==NULL) { Noise_XK_session_free(*session); return 2; } uint8_t *pinfo; Noise_XK_peer_get_info(&pinfo, peer); if(pinfo==NULL) { Noise_XK_session_free(*session); return 3; } //fprintf(liboprf_log_file, "[%d] finishing noise session -> %s\n", index, pinfo); free(pinfo); } Noise_XK_encap_message_t *encap_msg; Noise_XK_rcode ret = Noise_XK_session_read(&encap_msg, *session, noise_xk_handshake2_SIZE, msg); if(!Noise_XK_rcode_is_success(ret)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "session read fail: %d\n", ret.val.case_Error); Noise_XK_session_free(*session); return 4; } uint32_t plain_msg_len; uint8_t *plain_msg; if(!Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_ZERO, encap_msg)) { Noise_XK_session_free(*session); return 5; } Noise_XK_encap_message_p_free(encap_msg); return 0; } int dkg_noise_encrypt(uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, Noise_XK_session_t** session) { if(!*session) { return 1; } if(input_len > 1024) { return 2; } Noise_XK_encap_message_t *encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_STRONG_FORWARD_SECRECY, (uint32_t) input_len, input); uint32_t cipher_msg_len; uint8_t *cipher_msg; Noise_XK_rcode ret = Noise_XK_session_write(encap_msg, *session, &cipher_msg_len, &cipher_msg); Noise_XK_encap_message_p_free(encap_msg); if(!Noise_XK_rcode_is_success(ret)) { return 3; } if(cipher_msg_len!=output_len) { if(cipher_msg!=NULL) free(cipher_msg); return 4; } if(output == NULL) return 5; if(cipher_msg==NULL) return 6; memcpy(output,cipher_msg,cipher_msg_len); free(cipher_msg); return 0; } int dkg_noise_decrypt(const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, Noise_XK_session_t** session) { if(*session==NULL) { return 1; } if(input_len > 1024) { return 2; } Noise_XK_encap_message_t *encap_msg; Noise_XK_rcode ret = Noise_XK_session_read(&encap_msg, *session, (uint32_t) input_len, (uint8_t*) input); if(!Noise_XK_rcode_is_success(ret)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "session read fail: %d\n", ret.val.case_Error); return 3; } uint32_t plain_msg_len; uint8_t *plain_msg=NULL; if(!Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_KNOWN_SENDER_NO_KCI, encap_msg)) { return 4; } Noise_XK_encap_message_p_free(encap_msg); if(plain_msg_len!=output_len) { if(plain_msg!=NULL) free(plain_msg); return 5; } if(plain_msg!=NULL) { if(output == NULL) return 6; memcpy(output,plain_msg,plain_msg_len); free(plain_msg); } return 0; } /** Return the session unique send key, needed for tp-dkg reveal share. */ uint8_t __attribute__((visibility("hidden"))) *Noise_XK_session_get_key(const Noise_XK_session_t *sn) { Noise_XK_session_t st = sn[0U]; if (st.tag == Noise_XK_DS_Initiator && st.val.case_DS_Initiator.state.tag == Noise_XK_IMS_Transport) return st.val.case_DS_Initiator.state.val.case_IMS_Transport.send_key; if (st.tag == Noise_XK_DS_Responder && st.val.case_DS_Responder.state.tag == Noise_XK_IMS_Transport) return st.val.case_DS_Responder.state.val.case_IMS_Transport.receive_key; return NULL; } void __attribute__((visibility("hidden"))) update_transcript(crypto_generichash_state *transcript, const uint8_t *msg, const size_t msg_len) { uint32_t msg_size_32b = htonl((uint32_t)msg_len); crypto_generichash_update(transcript, (uint8_t*) &msg_size_32b, sizeof(msg_size_32b)); crypto_generichash_update(transcript, msg, msg_len); } char* dkg_recv_err(const int code) { switch(code) { case 0: return "no error"; case 1: return "invalid message len"; case 2: return "invalid message number"; case 3: return "invalid sender"; case 4: return "invalid recipient"; case 5: return "expired message"; case 6: return "invalid signature"; case 7: return "invalid sessionid"; } return "invalid recv_msg error code"; } void dkg_dump_msg(const uint8_t* ptr, const size_t msglen, const uint8_t type) { if(liboprf_log_file!=NULL) { const DKG_Message *msg = (const DKG_Message*) ptr; if(type==0) { fprintf(liboprf_log_file,"[!] msgno: %d, len: %d, from: %d to: %x ", msg->msgno, htonl(msg->len), msg->from, msg->to); } else { fprintf(liboprf_log_file,"[%d] msgno: %d, len: %d, from: %d to: %x ", type, msg->msgno, htonl(msg->len), msg->from, msg->to); } dump(ptr, msglen, "msg"); if(liboprf_debug==0) fprintf(liboprf_log_file, "\n"); } } liboprf-0.9.4/src/dkg.h000066400000000000000000000375251514673400200147040ustar00rootroot00000000000000#ifndef DKG_H #define DKG_H /** * @file dkg.h * @brief API for the Distributed Key Generation (DKG) protocol * * SPDX-FileCopyrightText: 2025, Marsiske Stefan * SPDX-License-Identifier: LGPL-3.0-or-later * * @warning This is a low-level interface. Do not use directly unless * you use it to implement DKG protocols which have proper sessionids * and other protections against replay and confused deputy attacks. * * For an example of a high-level DKG protocol, see tp-dkg.[ch] * * This library provides the core cryptographic operations for implementing * a secure Distributed Key Generation protocol. In DKG, a group of participants * jointly generate a cryptographic key without any single party knowing the * complete key. Each participant receives a share of the key, and a threshold * of these shares is required to perform operations with the key. * * The library also uses the Noise_XK handshake pattern * (https://noiseexplorer.com/patterns/XK/) to establish a secure * communication channel between participants. **/ #include #include #include "XK.h" #include "toprf.h" #define dkg_hash_BYTES crypto_generichash_BYTES #define dkg_commitment_BYTES(threshold) (threshold * crypto_core_ristretto255_BYTES) /** @struct DKG_Cheater This struct communicates one detected violation of the protocol. @var DKG_Cheater::step The step in which the violation occurred @var DKG_Cheater::error The error code specifying the violation @var DKG_Cheater::peer The peer that caused the violation @var DKG_Cheater::other_peer Optionally the peer that reported the violation (set to 0xfe if unused) @var DKG_Cheater::invalid_index */ typedef struct { int step; int error; uint8_t peer; uint8_t other_peer; int invalid_index; } DKG_Cheater; /** * @brief Evaluates a polynomial at a given point * * Evaluates a polynomial defined by coefficients in array `a` at point `j`. * It is used in the DKG protocol for share generation. The result is stored * as a share. * * @param[in] j The index at which to evaluate the polynomial * @param[in] threshold The number of coefficients * @param[in] a Array of coefficients defining the polynomial * @param[out] result The resulting share containing the polynomial * evaluation at `j` */ void polynom(const uint8_t j, const uint8_t threshold, const uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES], TOPRF_Share *result); /** * @brief Initiates the first step in the DKG protocol * * Generates polynomial coefficients, calculates commitments, and creates * shares for all participants. Each peer should execute this function * at the start of the DKG protocol. * * @param[in] n The number of peers participating in the DKG * @param[in] threshold The minimum number of shares needed to reconstruct * the secret (must be greater 1 and less than `n`) * @param[out] commitments Array of commitments to be broadcast to all * peers. NOTE: in this scheme the commitments are to the * coefficients of the polynomial. * @param[out] shares Array of `n` shares, one for each peer, to be sent * privately to each peer after receiving all of the commitment * broadcasts * * @return 0 on success, non-zero on error */ int dkg_start(const uint8_t n, const uint8_t threshold, uint8_t commitments[threshold][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n]); /** * @brief Verifies a commitment from a specific peer * * Validates that a received share matches the corresponding commitment * from a specific peer. * * @param[in] n The number of peers participating in the DKG * @param[in] threshold The minimum number of shares needed to reconstruct * the secret * @param[in] self The index of the current peer (1-based) * @param[in] i The index of the peer whose commitment is being verified * (1-based) * @param[in] commitments The commitments from peer `i` * @param[in] share The share received from peer `i` * * @return 0 if the commitment is valid, non-zero otherwise */ int dkg_verify_commitment(const uint8_t n, const uint8_t threshold, const uint8_t self, const uint8_t i, const uint8_t commitments[threshold][crypto_core_ristretto255_BYTES], const TOPRF_Share share); /** * @brief Verifies commitments from all peers * * Validates that all received shares match their corresponding commitments * from all peers. It collects IDs of participants whose shares are invalid. * * @param[in] n The number of peers participating in the DKG * @param[in] threshold The minimum number of shares needed to reconstruct * the secret * @param[in] self The index of the current peer (1-based) * @param[in] commitments Array of commitments from all peers * @param[in] shares Array of shares received from all peers * @param[out] fails Array to hold IDs of peers whose shares failed * verification * @param[out] fails_len Number of peers whose shares failed * * @return 0 if all shares are valid, non-zero otherwise */ int dkg_verify_commitments(const uint8_t n, const uint8_t threshold, const uint8_t self, const uint8_t commitments[n][threshold][crypto_core_ristretto255_BYTES], const TOPRF_Share shares[n], uint8_t fails[n], uint8_t *fails_len); /** * @brief Finalizes the DKG protocol for a peer * * Combines the shares received from all peers to compute the final * secret share for this peer. * * @param[in] n The number of peers participating in the DKG * @param[in] shares Array of shares addressed to this peer received * from all peers * @param[in] self The index of the current peer (1-based) * @param[out] xi Final computed secret share for this peer * * @return 0 on success, non-zero on error */ int dkg_finish(const uint8_t n, const TOPRF_Share shares[n], const uint8_t self, TOPRF_Share *xi); /** * @brief Reconstructs the shared secret from a set of shares * * Combines shares into the group secret. This is used in the final * phase of DKG, where a threshold of participants collaborate to * recover the secret. * * @param[in] threshold the threshold of the sharing * @param[in] shares Array of participant shares used in reconstruction * @param[out] secret Output buffer to store the reconstructed secret */ void dkg_reconstruct(const size_t threshold, const TOPRF_Share shares[threshold], uint8_t secret[crypto_scalarmult_ristretto255_SCALARBYTES]); #define dkg_freshness_TIMEOUT 120000 #define noise_xk_handshake1_SIZE 48UL #define noise_xk_handshake2_SIZE 48UL #define noise_xk_handshake3_SIZE 64UL #define dkg_noise_key_SIZE 32UL #define dkg_sessionid_SIZE 32U #define dkg_max_err_SIZE 128 /** @struct DKG_Message This is the header for messages sent in higher level instantiations of DKG/MPC protocols in liboprf @var DKG_Message::sig Signature over the message header and the message body @var DKG_Message::type Message type identifier @var DKG_Message::version Protocol version @var DKG_Message::msgno The "type" of this message, which is strictly tied to the current step of the protocol @var DKG_Message::len Length of the complete message, including the header @var DKG_Message::from Sender ID (0 for STP, otherwise the peer index) @var DKG_Message::to Recipient of the message (0 for STP, 0xff for broadcast, all other values <= `n` are the indices of the peers) @var DKG_Message::ts Timestamp proving the freshness of the message. The timestamp is a 64 bit value counting seconds since 1970-01-01 @var DKG_Message::sessionid Unique session identifier @var STP_DKG_Message::data The payload of the message */ typedef struct { uint8_t sig[crypto_sign_BYTES]; uint8_t type; uint8_t version; uint8_t msgno; uint32_t len; uint8_t from; uint8_t to; uint64_t ts; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t data[]; } __attribute((packed)) DKG_Message; #define MSG_TYPE_DKG 0 #define MSG_TYPE_UPDATE 1 #define MSG_TYPE_SEMI_TRUSTED (1 << 7) #define MSG_TYPE_TRUSTED (0 << 7) /** * @brief Validates message timestamp for freshness * * If `last_ts` is 0, compares `ts` to the current system time with an * allowed epsilon. Otherwise, checks monotonically increasing timestamps * within a window of `ts_epsilon`. * * @param[in] ts_epsilon Maximum allowed time difference in seconds * @param[in,out] last_ts Pointer to the last seen timestamp * @param[in] ts The timestamp to check * * @return 0 if timestamp is valid, non-zero otherwise */ int check_ts(const uint64_t ts_epsilon, uint64_t *last_ts, const uint64_t ts); /** * @brief Creates and signs a protocol message * * Populates a `DKG_Message` structure and signs it using the provided * signing key. Used to exchange protocol messages between participants * securely. * * @param[out] msg_buf Pointer to the buffer to fill with the message header * @param[in] msg_buf_len Size of the message buffer * @param[in] type Message type * @param[in] version Protocol version * @param[in] msgno Message sequence number * @param[in] from Sender ID * @param[in] to Recipient ID * @param[in] sig_sk Signing key * @param[in] sessionid Session identifier * * @return 0 on success, non-zero on error */ int send_msg(uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t type, const uint8_t version, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_sk, const uint8_t sessionid[dkg_sessionid_SIZE]); /** * @brief Receives, validates, and verifies a DKG protocol message * * Performs multiple checks: type, version, size, sequence numbers, * session ID, timestamp validation, and signature verification. * * @param[in] msg_buf Buffer containing the received message * @param[in] msg_buf_len Size of the message buffer * @param[in] type Expected message type * @param[in] version Expected protocol version * @param[in] msgno Expected message number * @param[in] from Expected sender ID * @param[in] to Expected recipient ID * @param[in] sig_pk Sender's public signing key * @param[in] sessionid Expected session identifier * @param[in] ts_epsilon Maximum allowed time difference in seconds * @param[in,out] last_ts Pointer to the last accepted timestamp * * @return 0 if message is valid, non-zero otherwise */ int recv_msg(const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t type, const uint8_t version, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_pk, const uint8_t sessionid[dkg_sessionid_SIZE], const uint64_t ts_epsilon, uint64_t *last_ts); /** * @brief Initializes a Noise_XK handshake session with a given remote peer * * Adds the peer to the local Noise_XK context, creates a session as * initiator, and generates the first handshake message * * @param[in] index Index of the current peer * @param[in] dev Pointer to the local Noise_XK device * @param[in] rpk Remote peer's static public key * @param[in] rname Remote peer's name * @param[out] session Active Noise_XK session pointer * @param[out] msg First handshake message * * @return 0 on success, non-zero on error */ int dkg_init_noise_handshake(const uint8_t index, Noise_XK_device_t *dev, uint8_t rpk[crypto_scalarmult_BYTES], uint8_t *rname, Noise_XK_session_t **session, uint8_t msg[noise_xk_handshake1_SIZE]); /** * @brief Responds to a Noise_XK handshake initiation from a remote peer * * Processes the first handshake message and produces the second handshake * message to send back to the initiator. * * @param[in] index Peer index * @param[in] dev Pointer to the local Noise_XK device * @param[in] rname Remote peer's name * @param[out] session Active Noise_XK session pointer * @param[in] inmsg First handshake message * @param[out] outmsg Second handshake message * * @return 0 on success, non-zero on error */ int dkg_respond_noise_handshake(const uint8_t index, Noise_XK_device_t *dev, uint8_t *rname, Noise_XK_session_t **session, uint8_t inmsg[noise_xk_handshake1_SIZE], uint8_t outmsg[noise_xk_handshake2_SIZE]); /** * @brief Completes a Noise_XK handshake * * Processes the second handshake message and finalizes the handshake. * * @param[in] index Peer index * @param[in] dev Pointer to the local Noise_XK device * @param[in,out] session Active Noise_XK session pointer * @param[in] msg Second handshake message * * @return 0 on success, non-zero on error */ int dkg_finish_noise_handshake(const uint8_t index, Noise_XK_device_t *dev, Noise_XK_session_t **session, uint8_t msg[noise_xk_handshake2_SIZE]); /** * @brief Encrypts data using a Noise_XK session * * @param[in] input Data to encrypt * @param[in] input_len Length of input data (must be <= 1024) * @param[out] output Buffer for encrypted data * @param[in] output_len Size of output buffer * @param[in,out] session Active Noise_XK session pointer * * @return 0 on success, non-zero on error */ int dkg_noise_encrypt(uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, Noise_XK_session_t **session); /** * @brief Decrypts data using a Noise_XK session * * @param[in] input Encrypted data * @param[in] input_len Length of encrypted data * @param[out] output Buffer for decrypted data * @param[in] output_len Size of output buffer * @param[in,out] session Active Noise_XK session pointer * * @return 0 on success, non-zero on error */ int dkg_noise_decrypt(const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, Noise_XK_session_t **session); /** * @brief Gets the current Noise_XK transport key used for * sending or receiving * * Depending on whether the session is an initiator or responder, * this function returns the appropriate session key. * * @param[in] sn Pointer to the Noise_XK session object * * @return Pointer to the session key if available, or NULL on error */ uint8_t *Noise_XK_session_get_key(const Noise_XK_session_t *sn); /** * @brief Updates the protocol transcript with a new message * * Adds a message to the transcript hash. * * @param[in,out] transcript Pointer to the transcript state * @param[in] msg Message to add to the transcript * @param[in] msg_len Size of the message */ void update_transcript(crypto_generichash_state *transcript, const uint8_t *msg, const size_t msg_len); /** * @brief Returns a human-readable error message for a DKG error code returned by the recv_msg() fn * * Maps integer error codes to descriptive strings * * @param[in] code The error code * * @return String containing the error message */ char *dkg_recv_err(const int code); /** * @brief Logs metadata and hex dump of a DKG message * * Prints the message number, size, sender, and recipient to * the log file, followed by a hex dump of the full message. * * @param[in] ptr Pointer to the message buffer * @param[in] msglen Size of the message buffer in bytes * @param[in] type Message type */ void dkg_dump_msg(const uint8_t *ptr, const size_t msglen, const uint8_t type); #endif // DKG_H liboprf-0.9.4/src/jni.c000066400000000000000000000767731514673400200147220ustar00rootroot00000000000000#include #include #include #include #include #include "oprf.h" #include "toprf.h" #include "tp-dkg.h" JNIEXPORT jobject JNICALL Java_org_hsbp_androsphinx_Oprf_blind(JNIEnv *env, jobject ignore, jbyteArray inputValue) { jobject result = NULL; jbyte* bufferPtrInputValue = (*env)->GetByteArrayElements(env, inputValue, NULL); jsize inputValueLen = (*env)->GetArrayLength(env, inputValue); jbyteArray r = (*env)->NewByteArray(env, crypto_core_ristretto255_SCALARBYTES); jbyteArray blinded = (*env)->NewByteArray(env, crypto_core_ristretto255_BYTES); jbyte *bufferPtrR = (*env)->GetByteArrayElements(env, r, NULL); jbyte *bufferPtrBlinded = (*env)->GetByteArrayElements(env, blinded, NULL); if (oprf_Blind((const uint8_t*)bufferPtrInputValue, inputValueLen, (uint8_t*)bufferPtrR, (uint8_t*)bufferPtrBlinded) == 0) { (*env)->ReleaseByteArrayElements(env, r, bufferPtrR, 0); (*env)->ReleaseByteArrayElements(env, blinded, bufferPtrBlinded, 0); jclass clazz = (*env)->FindClass(env, "kotlin/Pair"); jmethodID constructor = (*env)->GetMethodID(env, clazz, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); result = (*env)->NewObject(env, clazz, constructor, r, blinded); } else { (*env)->ReleaseByteArrayElements(env, r, bufferPtrR, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, blinded, bufferPtrBlinded, JNI_ABORT); } (*env)->ReleaseByteArrayElements(env, inputValue, bufferPtrInputValue, JNI_ABORT); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Oprf_evaluate(JNIEnv *env, jobject ignore, jbyteArray key, jbyteArray blinded) { if (key == NULL || blinded == NULL || (*env)->GetArrayLength(env, key) != crypto_core_ristretto255_SCALARBYTES || (*env)->GetArrayLength(env, blinded) != crypto_core_ristretto255_BYTES) { return NULL; } jbyteArray result = NULL; jbyteArray z = (*env)->NewByteArray(env, crypto_core_ristretto255_BYTES); jbyte *bufferPtrZ = (*env)->GetByteArrayElements(env, z, NULL); jbyte *bufferPtrBlinded = (*env)->GetByteArrayElements(env, blinded, NULL); jbyte *bufferPtrKey = (*env)->GetByteArrayElements(env, key, NULL); if (oprf_Evaluate((const uint8_t*)bufferPtrKey, (const uint8_t*)bufferPtrBlinded, (uint8_t*)bufferPtrZ) == 0) { (*env)->ReleaseByteArrayElements(env, z, bufferPtrZ, 0); result = z; } else { (*env)->ReleaseByteArrayElements(env, z, bufferPtrZ, JNI_ABORT); } (*env)->ReleaseByteArrayElements(env, key, bufferPtrKey, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, blinded, bufferPtrBlinded, JNI_ABORT); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Oprf_unblindFinalize(JNIEnv *env, jobject ignore, jbyteArray r, jbyteArray z, jbyteArray x) { if (r == NULL || z == NULL || x == NULL || (*env)->GetArrayLength(env, r) != crypto_core_ristretto255_SCALARBYTES || (*env)->GetArrayLength(env, z) != crypto_core_ristretto255_BYTES) { return NULL; } jbyte n[crypto_core_ristretto255_BYTES]; jbyteArray result = NULL; jbyte *bufferPtrR = (*env)->GetByteArrayElements(env, r, NULL); jbyte *bufferPtrZ = (*env)->GetByteArrayElements(env, z, NULL); if (oprf_Unblind((const uint8_t*)bufferPtrR, (const uint8_t*)bufferPtrZ, (uint8_t*)n) == 0) { jbyte *bufferPtrX = (*env)->GetByteArrayElements(env, x, NULL); jsize xLen = (*env)->GetArrayLength(env, x); jbyteArray y = (*env)->NewByteArray(env, OPRF_BYTES); jbyte *bufferPtrY = (*env)->GetByteArrayElements(env, y, NULL); if (oprf_Finalize((const uint8_t*)bufferPtrX, xLen, (const uint8_t*)n, (uint8_t*)bufferPtrY) == 0) { result = y; (*env)->ReleaseByteArrayElements(env, y, bufferPtrY, 0); } else { (*env)->ReleaseByteArrayElements(env, y, bufferPtrY, JNI_ABORT); } (*env)->ReleaseByteArrayElements(env, x, bufferPtrX, JNI_ABORT); } (*env)->ReleaseByteArrayElements(env, r, bufferPtrR, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, z, bufferPtrZ, JNI_ABORT); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Oprf_scalarMultRistretto255base(JNIEnv *env, jobject ignore, jbyteArray r) { if (r == NULL || (*env)->GetArrayLength(env, r) != crypto_core_ristretto255_SCALARBYTES) return NULL; jbyteArray gr = (*env)->NewByteArray(env, crypto_core_ristretto255_BYTES); jbyte *bufferPtrR = (*env)->GetByteArrayElements(env, r, NULL); jbyte *bufferPtrGr = (*env)->GetByteArrayElements(env, gr, NULL); crypto_scalarmult_ristretto255_base((uint8_t*)bufferPtrGr, (const uint8_t*)bufferPtrR); (*env)->ReleaseByteArrayElements(env, r, bufferPtrR, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, gr, bufferPtrGr, 0); return gr; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Oprf_thresholdMult(JNIEnv *env, jobject ignore, jobject responses) { if (responses == NULL) return NULL; jclass list = (*env)->FindClass(env, "java/util/List"); jmethodID listSize = (*env)->GetMethodID(env, list, "size", "()I"); jint responsesSize = (*env)->CallIntMethod(env, responses, listSize); if (responsesSize < 2) return NULL; jclass iterable = (*env)->FindClass(env, "java/lang/Iterable"); jmethodID iterableIterator = (*env)->GetMethodID(env, iterable, "iterator", "()Ljava/util/Iterator;"); jobject responsesIterator = (*env)->CallObjectMethod(env, responses, iterableIterator); if (responsesIterator == NULL) return NULL; jbyte responsesBuf[responsesSize * TOPRF_Part_BYTES]; jclass iterator = (*env)->FindClass(env, "java/util/Iterator"); jmethodID iteratorHasNext = (*env)->GetMethodID(env, iterator, "hasNext", "()Z"); jmethodID iteratorNext = (*env)->GetMethodID(env, iterator, "next", "()Ljava/lang/Object;"); jclass byteArray = (*env)->FindClass(env, "[B"); jsize offset = 0; while ((*env)->CallBooleanMethod(env, responsesIterator, iteratorHasNext) == JNI_TRUE) { if (offset >= responsesSize) return NULL; /* should not happen, yet impl's can cheat */ jobject item = (*env)->CallObjectMethod(env, responsesIterator, iteratorNext); if (item == NULL) return NULL; if ((*env)->IsInstanceOf(env, item, byteArray) == JNI_FALSE) return NULL; jbyteArray arrayItem = (jbyteArray)item; if ((*env)->GetArrayLength(env, arrayItem) != TOPRF_Part_BYTES) return NULL; (*env)->GetByteArrayRegion(env, arrayItem, 0, TOPRF_Part_BYTES, responsesBuf + offset++ * TOPRF_Part_BYTES); } jbyteArray result = (*env)->NewByteArray(env, crypto_core_ristretto255_BYTES); jbyte *bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); if (toprf_thresholdmult(responsesSize, (const uint8_t(*)[33])responsesBuf, (uint8_t*)bufferPtrResult) == 0) { (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); return result; } else { (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, JNI_ABORT); return NULL; } } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Oprf_tpDkgStartTp(JNIEnv *env, jobject ignore, jbyte n, jbyte t, jlong tsEpsilon, jstring protoName, jobject peerLongTermPublicKeysList) { if (protoName == NULL || peerLongTermPublicKeysList == NULL) return NULL; jbyte *buf = malloc(tpdkg_tpstate_size() + 32); fprintf(stderr, "buf = %p\n", buf); jbyte *ctx = buf + (32 - ((unsigned long long)buf % 32)); fprintf(stderr, "ctx = %p\n", ctx); jbyteArray msg = (*env)->NewByteArray(env, tpdkg_msg0_SIZE); fprintf(stderr, "msg = %p\n", msg); jbyte *bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); fprintf(stderr, "bufferPtrMsg = %p\n", bufferPtrMsg); const char *protoNameChars = (*env)->GetStringUTFChars(env, protoName, NULL); fprintf(stderr, "protoNameChars = %p\n", protoNameChars); jsize protoNameLen = (*env)->GetStringUTFLength(env, protoName); fprintf(stderr, "protoNameLen = %d\n", protoNameLen); int st_result = tpdkg_start_tp((TP_DKG_TPState*)ctx, tsEpsilon, n, t, protoNameChars, protoNameLen, tpdkg_msg0_SIZE, (DKG_Message*)bufferPtrMsg); fprintf(stderr, "tpdkg_start_tp() result = %08x\n", st_result); (*env)->ReleaseStringUTFChars(env, protoName, protoNameChars); uint8_t (*peersSignaturePublicKeys)[][crypto_sign_PUBLICKEYBYTES] = malloc(n * crypto_sign_PUBLICKEYBYTES); fprintf(stderr, "peersSignaturePublicKeys = %p\n", peersSignaturePublicKeys); uint8_t (*commitments)[][crypto_core_ristretto255_BYTES] = malloc(n * t * crypto_core_ristretto255_BYTES); fprintf(stderr, "commitments = %p\n", commitments); uint16_t (*complaints)[] = malloc(n * n * 2); fprintf(stderr, "complaints = %p\n", complaints); uint8_t (*noisyShares)[][tpdkg_msg8_SIZE] = malloc(n * n * tpdkg_msg8_SIZE); fprintf(stderr, "noisyShares = %p\n", noisyShares); size_t cheatersLen = sizeof(tpdkg_msg8_SIZE) * (t * t - 1); fprintf(stderr, "cheatersLen = %zu\n", cheatersLen); TP_DKG_Cheater (*cheaters)[] = malloc(cheatersLen); fprintf(stderr, "cheaters = %p\n", cheaters); uint64_t *lastTimestamps = malloc(n * 8); fprintf(stderr, "lastTimestamps = %p\n", lastTimestamps); jclass list = (*env)->FindClass(env, "java/util/List"); fprintf(stderr, "list = %p\n", list); jmethodID listSize = (*env)->GetMethodID(env, list, "size", "()I"); fprintf(stderr, "listSize = %p\n", listSize); jint pkListSize = (*env)->CallIntMethod(env, peerLongTermPublicKeysList, listSize); fprintf(stderr, "pkListSize = %d\n", pkListSize); uint8_t (*peerLongTermPublicKeys)[][crypto_sign_PUBLICKEYBYTES] = malloc(pkListSize * crypto_sign_PUBLICKEYBYTES); jclass iterable = (*env)->FindClass(env, "java/lang/Iterable"); fprintf(stderr, "iterable = %p\n", iterable); jmethodID iterableIterator = (*env)->GetMethodID(env, iterable, "iterator", "()Ljava/util/Iterator;"); fprintf(stderr, "iterableIterator = %p\n", iterableIterator); jobject pkListIterator = (*env)->CallObjectMethod(env, peerLongTermPublicKeysList, iterableIterator); fprintf(stderr, "pkListIterator = %p\n", pkListIterator); if (pkListIterator == NULL) return NULL; jclass iterator = (*env)->FindClass(env, "java/util/Iterator"); jmethodID iteratorHasNext = (*env)->GetMethodID(env, iterator, "hasNext", "()Z"); jmethodID iteratorNext = (*env)->GetMethodID(env, iterator, "next", "()Ljava/lang/Object;"); jclass byteArray = (*env)->FindClass(env, "[B"); jsize offset = 0; while ((*env)->CallBooleanMethod(env, pkListIterator, iteratorHasNext) == JNI_TRUE) { if (offset >= pkListSize) return NULL; /* should not happen, yet impl's can cheat */ jobject item = (*env)->CallObjectMethod(env, pkListIterator, iteratorNext); if (item == NULL) return NULL; if ((*env)->IsInstanceOf(env, item, byteArray) == JNI_FALSE) return NULL; jbyteArray arrayItem = (jbyteArray)item; if ((*env)->GetArrayLength(env, arrayItem) != crypto_sign_PUBLICKEYBYTES) return NULL; (*env)->GetByteArrayRegion(env, arrayItem, 0, crypto_sign_PUBLICKEYBYTES, (jbyte*)peerLongTermPublicKeys + offset++ * crypto_sign_PUBLICKEYBYTES); } for (int i = 0; i < pkListSize * crypto_sign_PUBLICKEYBYTES; i++) { fprintf(stderr, "%02hhx", ((jbyte*)peerLongTermPublicKeys)[i]); } fprintf(stderr, "\n"); tpdkg_tp_set_bufs((TP_DKG_TPState*)ctx, commitments, complaints, noisyShares, cheaters, cheatersLen, peersSignaturePublicKeys, peerLongTermPublicKeys, lastTimestamps); jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); jmethodID ctxConstructor = (*env)->GetMethodID(env, ctxClass, "", "(JJ)V"); jobject ctxObj = (*env)->NewObject(env, ctxClass, ctxConstructor, ctx, buf); jclass pair = (*env)->FindClass(env, "kotlin/Pair"); jmethodID pairConstructor = (*env)->GetMethodID(env, pair, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); jobject result = (*env)->NewObject(env, pair, pairConstructor, ctxObj, msg); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, st_result ? JNI_ABORT : 0); return st_result ? NULL : result; } JNIEXPORT jbyte JNICALL Java_org_hsbp_androsphinx_TpdkgContext_getN(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; return tpdkg_tpstate_n(state); } JNIEXPORT jbyte JNICALL Java_org_hsbp_androsphinx_TpdkgContext_getT(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; return tpdkg_tpstate_t(state); } JNIEXPORT jint JNICALL Java_org_hsbp_androsphinx_TpdkgContext_getStep(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; return tpdkg_tpstate_step(state); } JNIEXPORT jboolean JNICALL Java_org_hsbp_androsphinx_TpdkgContext_isNotDone(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; return tpdkg_tp_not_done(state) == 1 ? JNI_TRUE : JNI_FALSE; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgContext_next(JNIEnv *env, jobject ctxObj, jbyteArray msg) { if (msg == NULL) return NULL; const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; jsize msgLen = (*env)->GetArrayLength(env, msg); if (tpdkg_tp_input_size(state) != msgLen) { fprintf(stderr, "input size is %d byte(s) long, should be %zu byte(s) long\n", msgLen, tpdkg_tp_input_size(state)); return NULL; } jsize outLen = tpdkg_tp_output_size(state); jbyteArray result = (*env)->NewByteArray(env, outLen); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); jbyte* bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); int ret = tpdkg_tp_next(state, (uint8_t*)bufferPtrMsg, msgLen, (uint8_t*)bufferPtrResult, outLen); if (ret != 0) { fprintf(stderr, "tpdkg_tp_next() returned %d\n", ret); } (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, JNI_ABORT); return ret == 0 ? result : NULL; } JNIEXPORT jobject JNICALL Java_org_hsbp_androsphinx_TpdkgContext_getInputSizes(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; jbyte n = tpdkg_tpstate_n(state); size_t sizes[n]; int ret = tpdkg_tp_input_sizes(state, sizes); jlongArray result = (*env)->NewLongArray(env, n); jlong* bufferResult = (*env)->GetLongArrayElements(env, result, NULL); for (int i = 0; i < n; i++) { bufferResult[i] = sizes[i]; } (*env)->ReleaseLongArrayElements(env, result, bufferResult, 0); jclass clazz = (*env)->FindClass(env, "kotlin/Pair"); jmethodID constructor = (*env)->GetMethodID(env, clazz, "", "(Ljava/lang/Object;Ljava/lang/Object;)V"); jclass boolCls = (*env)->FindClass(env, "java/lang/Boolean"); jfieldID field = (*env)->GetStaticFieldID(env, boolCls, ret == 0 ? "FALSE" : "TRUE", "Ljava/lang/Boolean;"); jobject boolObj = (*env)->GetStaticObjectField(env, boolCls, field); jobject pair = (*env)->NewObject(env, clazz, constructor, boolObj, result); return pair; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgContext_getSessionId(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; jbyteArray result = (*env)->NewByteArray(env, dkg_sessionid_SIZE); (*env)->SetByteArrayRegion(env, result, 0, dkg_sessionid_SIZE, (const jbyte*)tpdkg_tpstate_sessionid(state)); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgContext_peerMessage(JNIEnv *env, jobject ctxObj, jbyteArray base, jbyte peer) { if (base == NULL) return NULL; const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_TPState *state = (TP_DKG_TPState*)ctxValue; jbyte *bufferPtrBase = (*env)->GetByteArrayElements(env, base, NULL); fprintf(stderr, "bufferPtrBase = %p\n", bufferPtrBase); jsize baseLen = (*env)->GetArrayLength(env, base); fprintf(stderr, "baseLen = %d\n", baseLen); fprintf(stderr, "ctx->prev = %d\n", state->prev); jbyte *msg; size_t size; jbyteArray result = NULL; if (tpdkg_tp_peer_msg(state, (const uint8_t*)bufferPtrBase, baseLen, peer, (const uint8_t**)&msg, &size) == 0) { fprintf(stderr, "msg = %p\n", msg); fprintf(stderr, "size = %zu\n", size); fprintf(stderr, "ctx->prev = %d\n", state->prev); result = (*env)->NewByteArray(env, size); (*env)->SetByteArrayRegion(env, result, 0, size, msg); } (*env)->ReleaseByteArrayElements(env, base, bufferPtrBase, JNI_ABORT); return result; } JNIEXPORT void JNICALL Java_org_hsbp_androsphinx_TpdkgContext_dispose(JNIEnv *env, jobject ignore, jlong ctx, jlong buf) { fprintf(stderr, "dispose() called\n"); TP_DKG_TPState *state = (TP_DKG_TPState*)ctx; free(state->commitments); fprintf(stderr, "commitments freed\n"); free(state->complaints); free(state->encrypted_shares); free(state->cheaters); fprintf(stderr, "cheaters freed\n"); free(state->peer_sig_pks); fprintf(stderr, "peer_sig_pks freed\n"); free(state->peer_lt_pks); fprintf(stderr, "peer_lt_pks freed\n"); free(state->last_ts); fprintf(stderr, "last_ts freed\n"); free((void*)buf); fprintf(stderr, "dispose() finished\n"); } JNIEXPORT jobject JNICALL Java_org_hsbp_androsphinx_Oprf_tpDkgPeerStart(JNIEnv *env, jobject ignore, jlong tsEpsilon, jbyteArray peerLongTermSecretKey, jbyteArray msg0) { if (msg0 == NULL || peerLongTermSecretKey == NULL) return NULL; const jbyte *buf = malloc(tpdkg_peerstate_size() + 32); fprintf(stderr, "buf = %p\n", buf); const jbyte *ctx = buf + (32 - ((unsigned long long)buf % 32)); fprintf(stderr, "ctx = %p\n", ctx); TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctx; jbyte* bufferPeerLongTermSecretKey = (*env)->GetByteArrayElements(env, peerLongTermSecretKey, NULL); jbyte* bufferMsg0 = (*env)->GetByteArrayElements(env, msg0, NULL); const int st_result = tpdkg_start_peer(state, tsEpsilon, (const uint8_t*)bufferPeerLongTermSecretKey, (const DKG_Message*)bufferMsg0); (*env)->ReleaseByteArrayElements(env, msg0, bufferMsg0, 0); (*env)->ReleaseByteArrayElements(env, peerLongTermSecretKey, bufferPeerLongTermSecretKey, 0); const jbyte n = tpdkg_peerstate_n(state); const jbyte t = tpdkg_peerstate_t(state); fprintf(stderr, "n = %d, t = %d\n", n, t); uint8_t (*peersSigPublicKeys)[][crypto_sign_PUBLICKEYBYTES] = malloc(n * crypto_sign_PUBLICKEYBYTES); uint8_t (*peersNoisePublicKeys)[][crypto_scalarmult_BYTES] = malloc(n * crypto_scalarmult_BYTES); Noise_XK_session_t *(*noiseOuts)[] = malloc(sizeof(void*) * n); Noise_XK_session_t *(* noiseIns)[] = malloc(sizeof(void*) * n); TOPRF_Share (* shares)[] = malloc(n * TOPRF_Share_BYTES); TOPRF_Share (*xshares)[] = malloc(n * TOPRF_Share_BYTES); uint8_t (*commitments)[][crypto_core_ristretto255_BYTES] = malloc(n * t * crypto_core_ristretto255_BYTES); uint16_t *complaints = malloc(n * n * 2); uint8_t *myComplaints = malloc(n); uint64_t *lastTimestamps = malloc(n * 8); tpdkg_peer_set_bufs(state, peersSigPublicKeys, peersNoisePublicKeys, noiseOuts, noiseIns, shares, xshares, commitments, complaints, myComplaints, lastTimestamps); jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgPeerContext"); jmethodID ctxConstructor = (*env)->GetMethodID(env, ctxClass, "", "(JJ)V"); jobject ctxObj = (*env)->NewObject(env, ctxClass, ctxConstructor, ctx, buf); return ctxObj; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgPeerContext_getSessionId(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgPeerContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctxValue; jbyteArray result = (*env)->NewByteArray(env, dkg_sessionid_SIZE); (*env)->SetByteArrayRegion(env, result, 0, dkg_sessionid_SIZE, (const jbyte*)tpdkg_peerstate_sessionid(state)); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgPeerContext_getLongTermSecretKey(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgPeerContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctxValue; jbyteArray result = (*env)->NewByteArray(env, crypto_sign_SECRETKEYBYTES); (*env)->SetByteArrayRegion(env, result, 0, crypto_sign_SECRETKEYBYTES, (const jbyte*)tpdkg_peerstate_lt_sk(state)); return result; } JNIEXPORT jboolean JNICALL Java_org_hsbp_androsphinx_TpdkgPeerContext_isNotDone(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgPeerContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctxValue; return tpdkg_peer_not_done(state) == 1 ? JNI_TRUE : JNI_FALSE; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgPeerContext_getShare(JNIEnv *env, jobject ctxObj) { const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgPeerContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); const TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctxValue; jbyteArray result = (*env)->NewByteArray(env, TOPRF_Share_BYTES); (*env)->SetByteArrayRegion(env, result, 0, TOPRF_Share_BYTES, (const jbyte*)tpdkg_peerstate_share(state)); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_TpdkgPeerContext_next(JNIEnv *env, jobject ctxObj, jbyteArray msg) { if (msg == NULL) return NULL; const jclass ctxClass = (*env)->FindClass(env, "org/hsbp/androsphinx/TpdkgPeerContext"); const jfieldID ctxField = (*env)->GetFieldID(env, ctxClass, "ctx", "J"); const jlong ctxValue = (*env)->GetLongField(env, ctxObj, ctxField); TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctxValue; const size_t inputSize = tpdkg_peer_input_size(state); jsize msgLen = (*env)->GetArrayLength(env, msg); if (msgLen != (jsize)inputSize) return NULL; jbyte *bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); const size_t outputSize = tpdkg_peer_output_size(state); jbyteArray result = (*env)->NewByteArray(env, (jsize)outputSize); jbyte *bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); int ret = tpdkg_peer_next(state, (const uint8_t*)bufferPtrMsg, inputSize, (uint8_t*)bufferPtrResult, outputSize); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, JNI_ABORT); if (ret == 0) { (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); return result; } else { (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, JNI_ABORT); return NULL; } } JNIEXPORT void JNICALL Java_org_hsbp_androsphinx_TpdkgPeerContext_dispose(JNIEnv *env, jobject ignore, jlong ctx, jlong buf) { fprintf(stderr, "PC dispose() called\n"); TP_DKG_PeerState *state = (TP_DKG_PeerState*)ctx; free(state->peer_sig_pks); free(state->peer_noise_pks); free(state->noise_outs); free(state->noise_ins); free(state->shares); free(state->xshares); free(state->commitments); free(state->complaints); free(state->my_complaints); free(state->last_ts); free((void*)buf); fprintf(stderr, "PC dispose() finished\n"); } /* ======== generic libsodium bindings ========= */ JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_genericHash(JNIEnv *env, jobject ignore, jbyteArray msg, jbyteArray salt, jint outlen) { if (outlen <= 0) return NULL; jbyte* bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); jbyte* bufferPtrSalt = salt == NULL ? NULL : (*env)->GetByteArrayElements(env, salt, NULL); jsize msgLen = (*env)->GetArrayLength(env, msg); jsize saltLen = salt == NULL ? 0 : (*env)->GetArrayLength(env, salt); jbyteArray hash = (*env)->NewByteArray(env, outlen); jbyte* bufferPtrHash = (*env)->GetByteArrayElements(env, hash, NULL); crypto_generichash(bufferPtrHash, outlen, bufferPtrMsg, msgLen, bufferPtrSalt, saltLen); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, JNI_ABORT); if (salt != NULL) { (*env)->ReleaseByteArrayElements(env, salt, bufferPtrSalt, JNI_ABORT); } (*env)->ReleaseByteArrayElements(env, hash, bufferPtrHash, 0); return hash; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_randomBytes(JNIEnv *env, jobject ignore, jint length) { jbyteArray result = (*env)->NewByteArray(env, length); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); randombytes_buf(bufferPtrResult, length); (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_cryptoSignSeedKeypair(JNIEnv *env, jobject ignore, jbyteArray seed) { unsigned char ignored_pk[crypto_sign_PUBLICKEYBYTES]; jbyteArray result = (*env)->NewByteArray(env, crypto_sign_SECRETKEYBYTES); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); jbyte* bufferPtrSeed = (*env)->GetByteArrayElements(env, seed, NULL); crypto_sign_seed_keypair(ignored_pk, bufferPtrResult, bufferPtrSeed); (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); (*env)->ReleaseByteArrayElements(env, seed, bufferPtrSeed, JNI_ABORT); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_cryptoSignEd25519SkToPk(JNIEnv *env, jobject ignore, jbyteArray sk) { jbyteArray result = (*env)->NewByteArray(env, crypto_sign_PUBLICKEYBYTES); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); jbyte* bufferPtrSk = (*env)->GetByteArrayElements(env, sk, NULL); crypto_sign_ed25519_sk_to_pk(bufferPtrResult, bufferPtrSk); (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); (*env)->ReleaseByteArrayElements(env, sk, bufferPtrSk, JNI_ABORT); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_cryptoSignDetached(JNIEnv *env, jobject ignore, jbyteArray sk, jbyteArray msg) { jbyteArray result = (*env)->NewByteArray(env, crypto_sign_BYTES); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); jbyte* bufferPtrSk = (*env)->GetByteArrayElements(env, sk, NULL); jbyte* bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); jsize msgLen = (*env)->GetArrayLength(env, msg); unsigned long long ignored_siglen = crypto_sign_BYTES; crypto_sign_detached(bufferPtrResult, &ignored_siglen, bufferPtrMsg, msgLen, bufferPtrSk); (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, 0); (*env)->ReleaseByteArrayElements(env, sk, bufferPtrSk, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, JNI_ABORT); return result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_cryptoAeadXchachaPoly1305IetfEasy(JNIEnv *env, jobject ignore, jbyteArray msg, jbyteArray ad, jbyteArray key) { jbyte* bufferPtrKey = (*env)->GetByteArrayElements(env, key, NULL); jbyte* bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); jbyte* bufferPtrAd = (*env)->GetByteArrayElements(env, ad, NULL); jsize msgLen = (*env)->GetArrayLength(env, msg); jsize adLen = (*env)->GetArrayLength(env, ad); jbyteArray result = (*env)->NewByteArray(env, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + msgLen + crypto_aead_xchacha20poly1305_ietf_ABYTES); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); randombytes_buf(bufferPtrResult, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); int sodium_result = crypto_aead_xchacha20poly1305_ietf_encrypt(bufferPtrResult + crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, NULL, bufferPtrMsg, msgLen, bufferPtrAd, adLen, NULL, bufferPtrResult, bufferPtrKey); (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, sodium_result ? JNI_ABORT : 0); (*env)->ReleaseByteArrayElements(env, ad, bufferPtrAd , JNI_ABORT); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, key, bufferPtrKey, JNI_ABORT); return sodium_result ? NULL : result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_cryptoAeadXchachaPoly1305IetfOpenEasy(JNIEnv *env, jobject ignore, jbyteArray msg, jbyteArray ad, jbyteArray key) { jbyte* bufferPtrKey = (*env)->GetByteArrayElements(env, key, NULL); jbyte* bufferPtrMsg = (*env)->GetByteArrayElements(env, msg, NULL); jbyte* bufferPtrAd = (*env)->GetByteArrayElements(env, ad, NULL); jsize msgLen = (*env)->GetArrayLength(env, msg); jsize adLen = (*env)->GetArrayLength(env, ad); jbyteArray result = (*env)->NewByteArray(env, msgLen - (crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + crypto_aead_xchacha20poly1305_ietf_ABYTES)); jbyte* bufferPtrResult = (*env)->GetByteArrayElements(env, result, NULL); int sodium_result = crypto_aead_xchacha20poly1305_ietf_decrypt(bufferPtrResult, NULL, NULL, bufferPtrMsg + crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, msgLen - crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, bufferPtrAd, adLen, bufferPtrMsg, bufferPtrKey); (*env)->ReleaseByteArrayElements(env, result, bufferPtrResult, sodium_result ? JNI_ABORT : 0); (*env)->ReleaseByteArrayElements(env, ad, bufferPtrAd , JNI_ABORT); (*env)->ReleaseByteArrayElements(env, msg, bufferPtrMsg, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, key, bufferPtrKey, JNI_ABORT); return sodium_result ? NULL : result; } JNIEXPORT jbyteArray JNICALL Java_org_hsbp_androsphinx_Sodium_passwordHash(JNIEnv *env, jobject ignore, jint outlen, jbyteArray passwd, jbyteArray salt) { if (outlen <= 0 || passwd == NULL || salt == NULL || (*env)->GetArrayLength(env, salt) != crypto_pwhash_SALTBYTES) return NULL; const jbyte* bufferPtrPasswd = (*env)->GetByteArrayElements(env, passwd, NULL); const jbyte* bufferPtrSalt = (*env)->GetByteArrayElements(env, salt, NULL); const jsize passwdLen = (*env)->GetArrayLength(env, passwd); jbyteArray hash = (*env)->NewByteArray(env, outlen); jbyte* bufferPtrHash = (*env)->GetByteArrayElements(env, hash, NULL); int sodium_result = crypto_pwhash((unsigned char * const)bufferPtrHash, (unsigned long long)outlen, (const char* const)bufferPtrPasswd, (unsigned long long)passwdLen, (const unsigned char* const)bufferPtrSalt, crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT); (*env)->ReleaseByteArrayElements(env, passwd, bufferPtrPasswd, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, salt, bufferPtrSalt, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, hash, bufferPtrHash, sodium_result ? JNI_ABORT : 0); return sodium_result ? NULL : hash; } liboprf-0.9.4/src/makefile000066400000000000000000000174461514673400200154660ustar00rootroot00000000000000PREFIX?=/usr/local INCLUDES=-Inoise_xk/include -Inoise_xk/include/karmel -Inoise_xk/include/karmel/minimal CFLAGS?=-Wall -O2 -g -ffunction-sections -fdata-sections \ -Werror=attributes -Werror=format-security -Werror=format-truncation -Werror=implicit-function-declaration \ -Wformat=2 -Wconversion -Wimplicit-fallthrough \ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \ -fstack-protector-strong -fasynchronous-unwind-tables -fpic \ -ftrapv -D_GLIBCXX_ASSERTIONS $(DEFINES) $(EXTRA_CFLAGS) LIBS=-lsodium -loprf-noiseXK -Lnoise_xk CC?=gcc LD?=ld OBJCOPY?=objcopy STRIP?=strip SOEXT?=so STATICEXT?=a SOVER=0 DEBUGDIR?=lib/debug/usr/lib # To statically link the noiseXK library, use these LIBS (and comment out the other LIBS) #LIBS=-lsodium -l:liboprf-noiseXK.a -Wl,--exclude-libs,ALL -Lnoise_xk UNAME := $(if $(UNAME),$(UNAME),$(shell uname -s)) ARCH := $(if $(ARCH),$(ARCH),$(shell uname -m)) ifeq ($(UNAME),Darwin) SOEXT=dylib SOFLAGS=-Wl,-install_name,$(DESTDIR)$(PREFIX)/lib/liboprf.$(SOEXT) else CFLAGS+=-Wl,-z,defs -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now -Wtrampolines \ -fsanitize=signed-integer-overflow -fsanitize-undefined-trap-on-error #-fstrict-flex-arrays=3 -mbranch-protection=standard SOEXT=so SOFLAGS=-Wl,-soname,liboprf.$(SOEXT).$(SOVER) ifeq ($(ARCH),x86_64) CFLAGS+=-fcf-protection=full endif ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),armv7-a) else CFLAGS+=-fstack-clash-protection endif endif PKGCONF_MISSING := $(shell pkgconf --version >/dev/null; echo $$?) ifneq ($(PKGCONF_MISSING),0) $(error liboprf: Cannot find pkgconf) endif SODIUM_MISSING := $(shell pkgconf --exists libsodium; echo $$?) ifneq ($(SODIUM_MISSING),0) $(error liboprf: Cannot find libsodium via pkgconf. Check that libsodium has been installed) endif SODIUM_NEWER_THAN_1_0_18 := $(shell pkgconf --atleast-version=1.0.19 libsodium; echo $$?) ifneq ($(SODIUM_NEWER_THAN_1_0_18),0) INCLUDES+= -Iaux_ EXTRA_SOURCES+= aux_/kdf_hkdf_sha256.c $(info liboprf: Using auxiliary sources because libsodium is too old) else CFLAGS+= -DHAVE_SODIUM_HKDF=1 endif all: liboprf.$(SOEXT) liboprf_release.$(STATICEXT) noise_xk/liboprf-noiseXK.$(SOEXT) liboprf.pc android: $(MAKE) EXTRA_SOURCES+=jni.c liboprf.$(SOEXT) LIBOPRF_SOURCES=oprf.c toprf.c dkg.c dkg-vss.c utils.c tp-dkg.c mpmult.c stp-dkg.c toprf-update.c SOURCES=$(LIBOPRF_SOURCES) $(EXTRA_SOURCES) OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) # Uncomment to use $ORIGIN as the runtime search path #SOFLAGS += -Wl,-rpath,'$$'ORIGIN # Terminal colors: purely for UX, comment out if causing problems decor = $(shell [ -t 0 ] && printf "\033[38;5;2m====" || printf "====") endDecor = $(shell [ -t 0 ] && printf "====\033[0m" || printf "====") debug: DEFINES=-DTRACE debug: all asan: CFLAGS=-fsanitize=address -static-libasan -g -march=native -Wall -O2 -g -fstack-protector-strong -fpic -Werror=format-security -Werror=implicit-function-declaration -Wl, -z,noexecstack ifeq ($(ARCH),x86_64) CFLAGS+=-fcf-protection=full endif ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),armv7-a) else CFLAGS+=-fstack-clash-protection endif asan: LDFLAGS+= -fsanitize=address -static-libasan asan: all AR ?= ar aux_/kdf_hkdf_sha256.o: aux_/kdf_hkdf_sha256.c $(CC) $(CPPFLAGS) $(CFLAGS) -fvisibility=hidden $(INCLUDES) -o $@ -c $^ liboprf.$(SOEXT): liboprf_merged.o noise_xk/liboprf-noiseXK.$(SOEXT) $(info $(decor) Build dynamic release library: $@ $(endDecor)) $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -shared $(SOFLAGS) -o $@ liboprf_merged.o $(LDFLAGS) $(LIBS) $(OBJCOPY) --only-keep-debug $@ $@.debug $(STRIP) --strip-debug --strip-unneeded $@ $(OBJCOPY) --add-gnu-debuglink=$@.debug $@ liboprf.$(SOEXT).debug: liboprf.$(SOEXT) liboprf-corrupt-dkg.$(SOEXT): $(SOURCES) noise_xk/liboprf-noiseXK.$(SOEXT) $(info $(decor) Build unit test library: $@ $(endDecor)) $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -DUNITTEST -DUNITTEST_CORRUPT -fPIC -shared $(SOFLAGS) -o $@ $(SOURCES) $(LDFLAGS) $(LIBS) liboprf_merged.o: $(OBJECTS) $(info $(decor) Merge object files $(endDecor)) $(LD) -r -o $@ $^ liboprf_merged_localized.o: liboprf_merged.o $(info $(decor) Localize symbols $(endDecor)) objcopy --localize-hidden $^ $@ liboprf_release.$(STATICEXT): liboprf_merged_localized.o $(info $(decor) Build static release library: $@ $(endDecor)) $(AR) rcs $@ $^ liboprf.$(STATICEXT): $(OBJECTS) $(AR) rcs $@ $^ noise_xk/liboprf-noiseXK.$(SOEXT): $(info $(decor) Compile vendorized noiseXK library: $@ $(endDecor)) make -C noise_xk all noise_xk/liboprf-noiseXK.$(STATICEXT): $(info $(decor) Compile vendorized noiseXK library: $@ $(endDecor)) make -C noise_xk all liboprf.pc: echo "prefix=$(PREFIX)" >liboprf.pc cat ../liboprf.pc >>liboprf.pc clean: rm -f *.o liboprf.$(SOEXT) liboprf.$(STATICEXT) liboprf-corrupt-dkg.$(SOEXT) rm -f aux_/*.o make -C tests clean make -C noise_xk clean install: install-oprf install-noiseXK install-oprf: $(DESTDIR)$(PREFIX)/lib/liboprf.$(SOEXT) \ $(DESTDIR)$(PREFIX)/$(DEBUGDIR)/liboprf.$(SOEXT).debug \ $(DESTDIR)$(PREFIX)/lib/liboprf.$(STATICEXT) \ $(DESTDIR)$(PREFIX)/lib/pkgconfig/liboprf.pc \ $(DESTDIR)$(PREFIX)/include/oprf/oprf.h \ $(DESTDIR)$(PREFIX)/include/oprf/toprf.h \ $(DESTDIR)$(PREFIX)/include/oprf/toprf-update.h \ $(DESTDIR)$(PREFIX)/include/oprf/dkg.h \ $(DESTDIR)$(PREFIX)/include/oprf/tp-dkg.h \ $(DESTDIR)$(PREFIX)/include/oprf/stp-dkg.h \ $(DESTDIR)$(PREFIX)/include/oprf/utils.h install-noiseXK: make -C noise_xk install uninstall: uninstall-oprf uninstall-noiseXK uninstall-oprf: @ \ if [ ! '$(strip $(DESTDIR)$(PREFIX))' ]; then echo 'liboprf: DESTDIR-PREFIX is empty!' && exit 1; fi; \ if [ ! -d '$(strip $(DESTDIR)$(PREFIX))' ]; then echo 'liboprf: DESTDIR-PREFIX is not a folder' && exit 1; fi; rm -vf '$(DESTDIR)$(PREFIX)/lib/liboprf.$(SOEXT)' '$(DESTDIR)$(PREFIX)/lib/liboprf.$(STATICEXT)' rm -vf '$(DESTDIR)$(PREFIX)/include/oprf/oprf.h' '$(DESTDIR)$(PREFIX)/include/oprf/toprf.h' rm -vf '$(DESTDIR)$(PREFIX)/include/oprf/dkg.h' '$(DESTDIR)$(PREFIX)/include/oprf/toprf-update.h' rm -vf '$(DESTDIR)$(PREFIX)/include/oprf/utils.h' '$(DESTDIR)$(PREFIX)/lib/pkgconfig/liboprf.pc' rm -vf '$(DESTDIR)$(PREFIX)/include/oprf/tp-dkg.h' '$(DESTDIR)$(PREFIX)/include/oprf/stp-dkg.h' rm -vf '$(DESTDIR)$(PREFIX)/$(DEBUGDIR)/liboprf.$(SOEXT).debug' rmdir -v '$(DESTDIR)$(PREFIX)/include/oprf/' uninstall-noiseXK: make -C noise_xk uninstall $(DESTDIR)$(PREFIX)/lib/liboprf.$(SOEXT): liboprf.$(SOEXT) mkdir -p $(DESTDIR)$(PREFIX)/lib cp $< $@.$(SOVER) ln -sf $@.$(SOVER) $@ $(DESTDIR)$(PREFIX)/lib/liboprf.$(STATICEXT): liboprf_release.$(STATICEXT) mkdir -p $(DESTDIR)$(PREFIX)/lib cp $< $@ $(DESTDIR)$(PREFIX)/lib/pkgconfig/liboprf.pc: liboprf.pc mkdir -p $(DESTDIR)$(PREFIX)/lib/pkgconfig cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/oprf.h: oprf.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/toprf.h: toprf.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/toprf-update.h: toprf-update.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/dkg.h: dkg.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/tp-dkg.h: tp-dkg.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/stp-dkg.h: stp-dkg.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/utils.h: utils.h mkdir -p $(DESTDIR)$(PREFIX)/include/oprf cp $< $@ $(DESTDIR)$(PREFIX)/$(DEBUGDIR)/liboprf.$(SOEXT).debug: liboprf.$(SOEXT).debug mkdir -p $(DESTDIR)$(PREFIX)/$(DEBUGDIR) cp $< $@ test: liboprf-corrupt-dkg.$(SOEXT) liboprf.$(STATICEXT) noise_xk/liboprf-noiseXK.$(STATICEXT) make -C tests tests make -C noise_xk test %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $(INCLUDES) -c $< -o $@ PHONY: clean liboprf-0.9.4/src/mpmult.c000066400000000000000000000355511514673400200154450ustar00rootroot00000000000000#include #include #include #include "toprf.h" #include "dkg-vss.h" #include "dkg.h" #ifdef UNIT_TEST #include "utils.h" #endif /** Implements the Simple-Mult algorithm from page 5 fig. 2 of "Simplified VSS and Fast-track Multiparty Computations with Applications to Threshold Cryptography" by Gennaro, Rabin, Rabin, 1998. */ static int cmp(const uint8_t a[crypto_core_ristretto255_SCALARBYTES], const uint8_t b[crypto_core_ristretto255_SCALARBYTES]) { // non-const time! but its ok, this operates on the vandermonde matrix, no secrets involved for(int i=crypto_core_ristretto255_SCALARBYTES-1;i>=0;i--) { if(a[i]>b[i]) return 1; if(a[i]0) {// a[i][j] > c1 memcpy(c1,&a[i][j],crypto_core_ristretto255_SCALARBYTES); } } memcpy(&c[i],c1,crypto_core_ristretto255_SCALARBYTES); } uint8_t k=0; for(uint8_t j=0;j pi1? if(cmp(pi0,pi1)>0) {// pi0 > pi1 memcpy(pi1,pi0,crypto_core_ristretto255_SCALARBYTES); k=i; } } // swap index[j] and index[k] uint8_t prev_index_j=index[j]; index[j] = index[k]; index[k] = prev_index_j; for(uint8_t i=j+1; i=0; j--) { memcpy(&x[j][i], &b[index[j]][i], crypto_core_ristretto255_SCALARBYTES); for(int k = j+1; kindex!=b->index || peersvalue, b->value); //dump(ab, sizeof ab, "ab"); toprf_create_shares(ab, peers, threshold, shares); //for(unsigned j=0;jindex=peer; uint8_t tmp[crypto_core_ristretto255_SCALARBYTES]; for(unsigned i=0;ivalue, share->value, tmp); } //dump(share->value, sizeof share->value, "share"); } static int vsps_check(const uint8_t t, const uint8_t A[t][crypto_core_ristretto255_BYTES], const uint8_t lambda[t+1][t+1][crypto_core_ristretto255_SCALARBYTES], const uint8_t delta_exp[t+1][crypto_core_ristretto255_SCALARBYTES], uint8_t v[crypto_core_ristretto255_BYTES]) { // calculates Ξ (A_i ^ Ξ”_i), where i=1..t+1, Ξ”_i = Ξ£(Ξ»_ji * Ξ΄^j, j= 0..t // v = 0 memset(v, 0,crypto_core_ristretto255_BYTES); for(int i=0;i<=t;i++) { uint8_t delta_i[crypto_core_ristretto255_SCALARBYTES]={0}; for(int j=0;j<=t;j++) { // calculate Ξ»_ji * Ξ΄^j uint8_t tmp[crypto_core_ristretto255_SCALARBYTES]; #ifdef UNIT_TEST dump(lambda[j][i], crypto_core_ristretto255_SCALARBYTES, "vdm[%d,%d]", j, i); dump(delta_exp[j], crypto_core_ristretto255_SCALARBYTES, "d^%d", j); #endif crypto_core_ristretto255_scalar_mul(tmp, lambda[j][i], delta_exp[j]); // Ξ”_i = sum_(j=0..t) (Ξ»_ji * Ξ΄^j) crypto_core_ristretto255_scalar_add(delta_i, delta_i, tmp); } #ifdef UNIT_TEST dump(delta_i,sizeof delta_i, "Ξ”%d", i); #endif uint8_t tmp[crypto_core_ristretto255_BYTES]; // A_i ^ Ξ”_i if(0!=crypto_scalarmult_ristretto255(tmp, delta_i, A[i])) return 1; #ifdef UNIT_TEST dump(tmp, crypto_scalarmult_ristretto255_BYTES, "A%d^Ξ”%d", i, i); #endif // Ξ , but we are in an additive group crypto_core_ristretto255_add(v, v, tmp); } return 0; } int toprf_mpc_vsps_check(const uint8_t t, const uint8_t A[t*2][crypto_core_ristretto255_BYTES]) { uint8_t indexes[t+1]; // p8para3L2: A0..At & At+1..A2t+1 // left-hand side of the equation (1) for(uint8_t i=0;i<=t;i++) indexes[i]=i; // left side of equation Ξ  i:=1..t, which is a typo? should be 0..t uint8_t lambda[t+1][t+1][crypto_core_ristretto255_SCALARBYTES]; invertedVDMmatrix(t+1,indexes,lambda); #ifdef UNIT_TEST if(liboprf_log_file!=NULL && liboprf_debug) fprintf(liboprf_log_file,"vdm1\n"); for(int i=0;i #include #include "toprf.h" #include "dkg.h" /** * @brief Computes the inverse of a Vandermonde matrix * * Given a list of dealer indices, this function generates the * corresponding Vandermonde matrix and computes its inverse, storing * the result in `inverted`. * * @param[in] dealers Number of dealers (matrix dimension) * @param[in] indexes Array of indices corresponding to each dealer * @param[out] inverted Output inverted Vandermonde matrix */ void invertedVDMmatrix(const uint8_t dealers, const uint8_t indexes[dealers], uint8_t inverted[dealers][dealers][crypto_core_ristretto255_SCALARBYTES]); /** * @brief Phase 1 of multiparty threshold multiplication. * * Performs the multiplication of two shares, `a` and `b`. * * @param[in] a One share held by the dealer contributing to the * multiplication * @param[in] b Another share held by the dealer contributing to * the multiplication * @param[in] peers The number of peers participating in the * computation. This should equal the number of peers * holding shares of `a` and `b`. * @param[in] threshold The number of peers minimum necessary to * reconstruct either of the input or the result * shares. Should equal the threshold for the `a` and `b` * values. * @param[out] shares Output array of shares of a*b, one for each peer * * @return 0 on success, non-zero on error */ int toprf_mpc_mul_start(const uint8_t _a[TOPRF_Share_BYTES], const uint8_t _b[TOPRF_Share_BYTES], const uint8_t peers, const uint8_t threshold, uint8_t shares[peers][TOPRF_Share_BYTES]); /** * @brief Phase 2 of multiparty threshold multiplication. * * Each peer calls this function to finalize their share of a*b, * using all shares from phase 1 and the inverted Vandermonde matrix. * * @param[in] dealers Number of dealers * @param[in] indexes Indices of the participating dealers * @param[in] peer Index of the current peer computing their share * @param[in] shares All shares from phase 1 for this participant * @param[out] share Output share of a*b for this participant */ void toprf_mpc_mul_finish(const uint8_t dealers, const uint8_t indexes[dealers], const uint8_t peer, const uint8_t shares[dealers][TOPRF_Share_BYTES], uint8_t _share[TOPRF_Share_BYTES]); /** * @brief Checks the correctness of a set of commitments * * @param[in] t Degree of the polynomials + 1 (threshold) * @param[in] A Array of commitments to check * * @return 0 if the check passes, non-zero otherwise */ int toprf_mpc_vsps_check(const uint8_t t, const uint8_t A[t*2][crypto_core_ristretto255_BYTES]); /** * @brief Step 1 of the Fast-Track Multiplication (FT-Mult) protocol * * Each player shares a value (Ξ»_iΞ±_iΞ²_i) using VSS, producing shares and commitments * for the next phase. FT-Mult is defined in Fig. 5 of "Simplified VSS and Fast-track * Multiparty Computations with Applications to Threshold Cryptography" by R. Gennaro, M. O. * Rabin, and T. Rabin, PODC 1998. * * @param[in] dealers Number of participants acting as dealers (always 2t+1) * @param[in] n Number of parties receiving shares (must be more or equal 2t+1) * @param[in] t Threshold for reconstruction * @param[in] self Index of the current participant * @param[in] alpha Share of secret `a` (and its blinding factor) * @param[in] beta Share of secret `b` (and its blinding factor) * @param[in] lambdas Lagrange coefficients * @param[out] ci_shares Output array of shares for each participant * @param[out] ci_commitments Output array of commitments * @param[out] ci_tau Output blinding factor for the commitment * * @return 0 on success, non-zero on error */ int toprf_mpc_ftmult_step1(const uint8_t dealers, const uint8_t n, const uint8_t t, const uint8_t self, const TOPRF_Share alpha[2], const TOPRF_Share beta[2], const uint8_t lambdas[dealers][crypto_core_ristretto255_SCALARBYTES], TOPRF_Share ci_shares[n][2], uint8_t ci_commitments[n][crypto_core_ristretto255_BYTES], uint8_t ci_tau[crypto_core_ristretto255_SCALARBYTES]); /** * @brief Computes zero-knowledge (ZK) commitments for fast-track * multiplication * * Generates commitments for use in the zero-knowledge proof of correct * multiplication. * * @param[in] B_i Commitment to the value being proved * @param[out] d Random scalar for the proof * @param[out] s Random scalar for the proof * @param[out] x Random scalar for the proof * @param[out] s_1 Random scalar for the proof * @param[out] s_2 Random scalar for the proof * @param[out] zk_commitments Output array of three commitments * * @return 0 on success, non-zero on error */ int toprf_mpc_ftmult_zk_commitments(const uint8_t B_i[crypto_core_ristretto255_BYTES], uint8_t d[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t s[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t x[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t s_1[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t s_2[crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t zk_commitments[3][crypto_scalarmult_ristretto255_BYTES]); #endif // THMULT_H liboprf-0.9.4/src/noise_xk/000077500000000000000000000000001514673400200155715ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/.gitignore000066400000000000000000000001041514673400200175540ustar00rootroot00000000000000liboprf-noiseXK.a liboprf-noiseXK.so liboprf-noiseXK.so.0 xk-ex *.o liboprf-0.9.4/src/noise_xk/README.md000066400000000000000000000005471514673400200170560ustar00rootroot00000000000000libsodiumized version of `XK_25519_ChaChaPoly_BLAKE2b` extracted from https://github.com/Inria-Prosecco/noise-star git revision `38ad46c4e1adc048f8b9002c2265a96b961eb702` karmel directory contains the necessary includes https://github.com/project-everest/hacl-star git version `e5620ceb7c8a4996520d693f597872806dc0a1d3` see noise-star.patch for all changes liboprf-0.9.4/src/noise_xk/example/000077500000000000000000000000001514673400200172245ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/example/authorized_keys000066400000000000000000000005441514673400200223630ustar00rootroot00000000000000cWGtl4rxhY8S1uFh2Gfyi4d4B1BBoxKXpX5oAqsn3SQ= stf wLjHJ6njZIpaAuxQ2mK1OjLhYkHu0GGjm/HFfSi5iyM= alice auL719iakN0Uh9X1XSjsNgmMSYrbLUQHRJmjKuuqRHc= robert LvujMt01+k/1YQ19tIyLXdnqZlabtyPQv8+EZLKeBVA= rob kQH8j5UPJLcQfDEDGYS3NavVobgiov9dHVZ2v5JMGgo= robby NHBPri2k+EhTY+RKaKAlTXpxuQ7mKSxeRZ1stVeDLy0= robbie pN7KA3MPHU4Y9tmMcgsWjhYUuz1Kalk6KtAasr17+2w= bobbie liboprf-0.9.4/src/noise_xk/example/makefile000066400000000000000000000017351514673400200207320ustar00rootroot00000000000000LDFLAGS=-lsodium -loprf-noiseXK SOURCES=xk-ex.c ../../utils.c INCLUDES=-I../.. -I../include -I ../include/karmel -I ../include/karmel/minimal CFLAGS ?= -Wall -Wextra -Werror -std=c11 -Wno-unused-variable \ -Wno-unknown-warning-option -Wno-unused-but-set-variable \ -Wno-unused-parameter -Wno-infinite-recursion -fPIC \ -g -fwrapv -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM \ -O2 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \ -fasynchronous-unwind-tables -fpic -Werror=format-security \ -Werror=implicit-function-declaration -Wl,-z,defs -Wl,-z,relro \ -ftrapv -Wl,-z,noexecstack ARCH := $(shell uname -m) ifeq ($(ARCH),x86_64) CFLAGS+=-fcf-protection=full endif ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),parisc64) else CFLAGS+=-fstack-clash-protection endif all: xk-ex xk-ex: $(SOURCES) $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LDFLAGS) -L.. AR ?= ar test: xk-ex LD_LIBRARY_PATH=.. ./xk-ex clean: rm -rf *.o xk-ex liboprf-0.9.4/src/noise_xk/example/xk-ex.c000066400000000000000000000256631514673400200204400ustar00rootroot00000000000000#include #include #include #include #include "XK.h" #include "utils.h" #include #include typedef Noise_XK_device_t device; typedef Noise_XK_session_t session; typedef Noise_XK_peer_t peer; typedef Noise_XK_encap_message_t encap_message; typedef Noise_XK_rcode rcode; typedef uint32_t peer_id; // Using XK with: // - DH: Curve25519 // - AEAD: ChaChaPoly // - Hash: SHA256 #define AEAD_KEY_SIZE 32 #define DH_KEY_SIZE 32 #define HASH_SIZE 32 #define PSK_SIZE 32 #define RETURN_IF_ERROR(e, msg) if (!(e)) { printf("Error: %s\n", msg); return 1; } // Symmetric key used by Alice for serialization/deserialization uint8_t alice_srlz_key[AEAD_KEY_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // Alice's DH keys uint8_t alice_spriv[DH_KEY_SIZE] = { 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 }; uint8_t alice_spub[DH_KEY_SIZE] = { 0 }; // Symmetric key used by Bob for serialization/deserialization uint8_t bob_srlz_key[AEAD_KEY_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; // Bob's DH keys uint8_t bob_spriv[DH_KEY_SIZE] = { 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 0x85, 0x52 }; uint8_t bob_spub[DH_KEY_SIZE] = { 0 }; extern int liboprf_debug; static int load_authkeys(const char *path, device *dev) { FILE *stream; char *line = NULL; size_t len = 0; ssize_t nread; int ret = 0; stream = fopen(path, "r"); if (stream == NULL) { perror("fopen authorized_keys file"); return 1; } while ((nread = getline(&line, &len, stream)) != -1) { int i; for(i=0;i 0) free(cipher_msg); if (plain_msg_len > 0) free(plain_msg); // # Step 2: Send an empty message from Bob to Alice. // Very similar to step 1. // ## Bob: generate the message encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_ZERO, 0, NULL); res = Noise_XK_session_write(encap_msg, bob_session, &cipher_msg_len, &cipher_msg); RETURN_IF_ERROR(Noise_XK_rcode_is_success(res), "Send message 1"); Noise_XK_encap_message_p_free(encap_msg); // ## Alice: read the message fprintf(stderr, "len of msg2: %d\n", cipher_msg_len); res = Noise_XK_session_read(&encap_msg, alice_session, cipher_msg_len, cipher_msg); RETURN_IF_ERROR(Noise_XK_rcode_is_success(res), "Receive message 1"); RETURN_IF_ERROR( Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_ZERO, encap_msg), "Unpack message 1"); Noise_XK_encap_message_p_free(encap_msg); if (cipher_msg_len > 0) free(cipher_msg); if (plain_msg_len > 0) free(plain_msg); // # Step 3 : Send a confidential message from Alice to Bob // By now, Alice should have reached the best security level. // Send a secret message, and request the highest confidentiality // guarantees. // ## Alice: generate the message // We request strong forward secrecy encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_STRONG_FORWARD_SECRECY, 11, (uint8_t*) "Hello Bob!"); //encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_STRONG_FORWARD_SECRECY, 0, NULL); res = Noise_XK_session_write(encap_msg, alice_session, &cipher_msg_len, &cipher_msg); RETURN_IF_ERROR(Noise_XK_rcode_is_success(res), "Send message 2"); Noise_XK_encap_message_p_free(encap_msg); // ## Bob: read the message // We request the highest authentication guarantees (known sender, no KCI). fprintf(stderr, "len of msg3: %d\n", cipher_msg_len); res = Noise_XK_session_read(&encap_msg, bob_session, cipher_msg_len, cipher_msg); RETURN_IF_ERROR(Noise_XK_rcode_is_success(res), "Receive message 2"); // digging out alices spub: peer_id peer_id = Noise_XK_session_get_peer_id(bob_session); Noise_XK_peer_t *peer = Noise_XK_device_lookup_peer_by_id(bob_device, peer_id); uint8_t pubkey[32]; Noise_XK_peer_get_static(pubkey, peer); dump(pubkey, sizeof pubkey, "peers spub: "); RETURN_IF_ERROR( Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_KNOWN_SENDER_NO_KCI, encap_msg), "Unpack message 2"); Noise_XK_encap_message_p_free(encap_msg); if (cipher_msg_len > 0) free(cipher_msg); printf("%s\n",plain_msg); if (plain_msg_len > 0) free(plain_msg); // # Step 4: Send a confidential message from Bob to Alice. // Same for Bob: we can now send messages with the highest confidentiality level. // ## Bob: generate the message (request max confidentiality) encap_msg = Noise_XK_pack_message_with_conf_level(NOISE_XK_CONF_STRONG_FORWARD_SECRECY, 11, (uint8_t*) "Hello Alice!"); res = Noise_XK_session_write(encap_msg, bob_session, &cipher_msg_len, &cipher_msg); RETURN_IF_ERROR(Noise_XK_rcode_is_success(res), "Send message 3"); Noise_XK_encap_message_p_free(encap_msg); // ## Alice: read the message (request max authentication) res = Noise_XK_session_read(&encap_msg, alice_session, cipher_msg_len, cipher_msg); RETURN_IF_ERROR(Noise_XK_rcode_is_success(res), "Receive message 3"); RETURN_IF_ERROR( Noise_XK_unpack_message_with_auth_level(&plain_msg_len, &plain_msg, NOISE_XK_AUTH_KNOWN_SENDER_NO_KCI, encap_msg), "Unpack message 3"); Noise_XK_encap_message_p_free(encap_msg); if (cipher_msg_len > 0) free(cipher_msg); if (plain_msg_len > 0) free(plain_msg); /* * Cleanup */ // Free the sessions, then the devices Noise_XK_session_free(alice_session); Noise_XK_session_free(bob_session); Noise_XK_device_free(alice_device); Noise_XK_device_free(bob_device); printf("Success!\n"); return 0; } liboprf-0.9.4/src/noise_xk/include/000077500000000000000000000000001514673400200172145ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/include/Noise_XK.h000066400000000000000000000052261514673400200210510ustar00rootroot00000000000000/** This file was automatically generated */ #ifndef __Noise_XK_H #define __Noise_XK_H #include #include "krml/internal/target.h" #include "krml/internal/types.h" #ifndef WITH_HACL #include #else // WITH_SODIUM #include "Hacl.h" #endif // WITH_SODIUM #define Noise_XK_CSuccess 0 #define Noise_XK_CIncorrect_transition 1 #define Noise_XK_CPremessage 2 #define Noise_XK_CNo_key 3 #define Noise_XK_CAlready_key 4 #define Noise_XK_CRs_rejected_by_policy 5 #define Noise_XK_CRs_not_certified 6 #define Noise_XK_CAlready_peer 7 #define Noise_XK_CPeer_conflict 8 #define Noise_XK_CUnknown_peer_id 9 #define Noise_XK_CInput_size 10 #define Noise_XK_CDH_error 11 #define Noise_XK_CDecrypt_error 12 #define Noise_XK_CSaturated_nonce 13 #define Noise_XK_CEphemeral_generation 14 #define Noise_XK_CSecurity_level 15 typedef uint8_t Noise_XK_error_code; bool Noise_XK_lbytes_eq(uint32_t len, uint8_t *b1, uint8_t *b2); typedef struct Noise_XK_sized_buffer_s { uint32_t size; uint8_t *buffer; } Noise_XK_sized_buffer; uint64_t Noise_XK_bytes_to_nonce(uint8_t *n8); #define Noise_XK_Handshake_read 0 #define Noise_XK_Handshake_write 1 #define Noise_XK_Transport 2 typedef uint8_t Noise_XK_status; typedef uint8_t *Noise_XK_noise_string; typedef Noise_XK_noise_string *Noise_XK_hstring; Noise_XK_error_code Noise_XK_dh_secret_to_public(uint8_t *dest, uint8_t *priv); Noise_XK_error_code Noise_XK_dh(uint8_t *dest, uint8_t *priv, uint8_t *pub); void Noise_XK_aead_encrypt( uint8_t *key, uint64_t nonce, uint32_t aad_len, uint8_t *aad, uint32_t plen, uint8_t *plain, uint8_t *cipher ); Noise_XK_error_code Noise_XK_aead_decrypt( uint8_t *key, uint64_t nonce, uint32_t aad_len, uint8_t *aad, uint32_t plen, uint8_t *plain, uint8_t *cipher ); void Noise_XK_hash(uint8_t *output, uint32_t inlen, uint8_t *input); void Noise_XK_mix_hash(uint8_t *hash1, uint32_t inlen, uint8_t *input); void Noise_XK_hmac(uint8_t *output, uint32_t keylen, uint8_t *key, uint32_t datalen, uint8_t *data); void Noise_XK_kdf( uint8_t *hash1, uint32_t keylen, uint8_t *key, uint8_t *dst1, uint8_t *dst2, uint8_t *dst3 ); void Noise_XK_mix_psk(uint8_t *psk, uint8_t *st_cs_k, uint8_t *st_ck, uint8_t *st_h); void Noise_XK_encrypt_and_hash( uint32_t msg_len, uint8_t *msg, uint8_t *cipher, uint8_t *st_cs_k, uint8_t *st_h, uint64_t nonce ); Noise_XK_error_code Noise_XK_decrypt_and_hash( uint32_t msg_len, uint8_t *msg, uint8_t *cipher, uint8_t *st_cs_k, uint8_t *st_h, uint64_t nonce ); Noise_XK_error_code Noise_XK_mix_dh(uint8_t *sec, uint8_t *pub, uint8_t *cipher_key, uint8_t *ck, uint8_t *hash1); #define __Noise_XK_H_DEFINED #endif liboprf-0.9.4/src/noise_xk/include/XK.h000066400000000000000000000345471514673400200177240ustar00rootroot00000000000000/** This file was automatically generated */ #ifndef __XK_H #define __XK_H #include #include "krml/internal/target.h" #include "krml/internal/types.h" #ifdef WITH_HACL #include "Hacl.h" #endif // WITH_SODIUM #include "Noise_XK.h" #define Noise_XK_Success 0 #define Noise_XK_Error 1 #define Noise_XK_Stuck 2 typedef uint8_t Noise_XK_rcode_tags; typedef struct Noise_XK_rcode_s { Noise_XK_rcode_tags tag; union { Noise_XK_error_code case_Error; Noise_XK_error_code case_Stuck; } val; } Noise_XK_rcode; bool Noise_XK_uu___is_Success(Noise_XK_rcode projectee); bool Noise_XK_uu___is_Error(Noise_XK_rcode projectee); Noise_XK_error_code Noise_XK___proj__Error__item___0(Noise_XK_rcode projectee); bool Noise_XK_uu___is_Stuck(Noise_XK_rcode projectee); Noise_XK_error_code Noise_XK___proj__Stuck__item___0(Noise_XK_rcode projectee); typedef uint8_t Noise_XK_conf_level_t; typedef uint8_t Noise_XK_auth_level_t; #define NOISE_XK_AUTH_ZERO ((uint8_t)0U) #define NOISE_XK_AUTH_KNOWN_SENDER ((uint8_t)1U) #define NOISE_XK_AUTH_KNOWN_SENDER_NO_KCI ((uint8_t)2U) #define NOISE_XK_MAX_AUTH_LEVEL ((uint8_t)2U) #define NOISE_XK_CONF_ZERO ((uint8_t)0U) #define NOISE_XK_CONF_KNOWN_RECEIVER ((uint8_t)2U) #define NOISE_XK_CONF_KNOWN_RECEIVER_NON_REPLAYABLE ((uint8_t)3U) #define NOISE_XK_CONF_STRONG_FORWARD_SECRECY ((uint8_t)5U) #define NOISE_XK_MAX_CONF_LEVEL ((uint8_t)5U) #define Noise_XK_Auth_level 0 #define Noise_XK_Conf_level 1 #define Noise_XK_No_level 2 typedef uint8_t Noise_XK_ac_level_t_tags; typedef struct Noise_XK_ac_level_t_s { Noise_XK_ac_level_t_tags tag; union { uint8_t case_Auth_level; uint8_t case_Conf_level; } val; } Noise_XK_ac_level_t; bool Noise_XK_uu___is_Auth_level(Noise_XK_ac_level_t projectee); uint8_t Noise_XK___proj__Auth_level__item__l(Noise_XK_ac_level_t projectee); bool Noise_XK_uu___is_Conf_level(Noise_XK_ac_level_t projectee); uint8_t Noise_XK___proj__Conf_level__item__l(Noise_XK_ac_level_t projectee); bool Noise_XK_uu___is_No_level(Noise_XK_ac_level_t projectee); typedef struct Noise_XK_encap_message_t_s Noise_XK_encap_message_t; typedef Noise_XK_encap_message_t *Noise_XK_encap_message_p_or_null; Noise_XK_encap_message_t *Noise_XK___proj__Mkencap_message_p_or_null__item__emp(Noise_XK_encap_message_t *projectee); bool Noise_XK_encap_message_p_is_null(Noise_XK_encap_message_t *emp); typedef Noise_XK_encap_message_t *Noise_XK_encap_message_p; void Noise_XK_encap_message_p_free(Noise_XK_encap_message_t *emp); Noise_XK_encap_message_t *Noise_XK_pack_message_with_conf_level( uint8_t requested_conf_level, uint32_t msg_len, uint8_t *msg ); Noise_XK_encap_message_t *Noise_XK_pack_message(uint32_t msg_len, uint8_t *msg); bool Noise_XK_unpack_message_with_auth_level( uint32_t *out_msg_len, uint8_t **out_msg, uint8_t requested_auth_level, Noise_XK_encap_message_t *emp ); bool Noise_XK_unpack_message( uint32_t *out_msg_len, uint8_t **out_msg, Noise_XK_encap_message_t *emp ); void Noise_XK_unsafe_unpack_message( Noise_XK_ac_level_t *out_ac_level, uint32_t *out_msg_len, uint8_t **out_msg, Noise_XK_encap_message_t *emp ); extern Prims_int Noise_XK_num_pattern_messages; bool Noise_XK_rcode_is_success(Noise_XK_rcode c); bool Noise_XK_rcode_is_error(Noise_XK_rcode c); bool Noise_XK_rcode_is_stuck(Noise_XK_rcode c); /******************************************************************************* An instanciation of the NoiseAPI for the XK pattern. This instanciation uses the following features: * uint32 for the sessions and peers counters/unique identifiers * we don't accept unknown remote static keys: all remote keys should have been registered in the device by adding the proper peers. * device/session/peer names are null-terminated strings of ANSI char *******************************************************************************/ typedef Noise_XK_status Noise_XK_status0; #define Noise_XK_IMS_Handshake 0 #define Noise_XK_IMS_Transport 1 typedef uint8_t Noise_XK_init_state_t_tags; typedef struct Noise_XK_init_state_t_s Noise_XK_init_state_t; typedef struct Noise_XK_peer_t_s Noise_XK_peer_t; typedef struct Noise_XK_cell_s Noise_XK_cell; typedef struct Noise_XK_cell_s { Noise_XK_cell *next; Noise_XK_peer_t *data; } Noise_XK_cell; typedef struct Noise_XK_device_t_s Noise_XK_device_t; typedef struct Noise_XK_resp_state_t_s Noise_XK_resp_state_t; #define Noise_XK_DS_Initiator 0 #define Noise_XK_DS_Responder 1 typedef uint8_t Noise_XK_session_t_tags; typedef struct Noise_XK_session_t_s Noise_XK_session_t; typedef Noise_XK_session_t Noise_XK_session_t0; typedef Noise_XK_session_t *Noise_XK_session_p; typedef Noise_XK_device_t Noise_XK_device_t0; typedef Noise_XK_device_t *Noise_XK_device_p; typedef Noise_XK_peer_t Noise_XK_peer_t0; typedef Noise_XK_peer_t *Noise_XK_peer_p; /* Create a device. Parameters: * `prlg`: Prologue for session initialization * `info`: Device name * `sk`: (if present) symmetric key used to serialize/deserialize private data * `spriv`: (if present) static private key May fail and return NULL if provided unvalid keys. */ Noise_XK_device_t *Noise_XK_device_create( uint32_t prlg_len, uint8_t *prlg, uint8_t *info, uint8_t *sk, uint8_t *spriv ); /* Create a device. Takes as arguments a symmetric key `sk` for secret data serialization/ deserialization, and an encrypted static private key `spriv`. The device name `info` is used as authentication data to encrypt/decrypt the device private key. May fail and return NULL if provided unvalid keys. */ Noise_XK_device_t *Noise_XK_device_create_from_secret( uint32_t prlg_len, uint8_t *prlg, uint8_t *info, uint8_t *sk, uint8_t *spriv ); /* Free a device. Take care to free the device **AFTER** having freed all the sessions created from this device. */ void Noise_XK_device_free(Noise_XK_device_t *dvp); /* Encrypt and derialize a device's secret. Uses the device symmetric key to encrypt the device's secret key. Uses a randomly generated nonce together with the device name as authentication data. */ void Noise_XK_serialize_device_secret(uint32_t *outlen, uint8_t **out, Noise_XK_device_t *dvp); /* Add a peer to the device and return a pointer to the newly created peer. May fail and return NULL if the device already contains a peer with the same public static key. Note that the peer is owned by the device: we don't provide any way of freeing it on the user side, and it might be invalidated after a removal operation. For this reason, we advise to immediately use the returned pointer (to retrieve the peer id for instance), then forget it. */ Noise_XK_peer_t *Noise_XK_device_add_peer(Noise_XK_device_t *dvp, uint8_t *pinfo, uint8_t *rs); /* Remove a peer designated by its unique identifier. */ void Noise_XK_device_remove_peer(Noise_XK_device_t *dvp, uint32_t pid); /* Encrypt and serialize a peer's key(s). Uses the device symmetric key to encrypt the peer's key(s). Uses a randomly generated nonce together with the peer name as authentication data. */ void Noise_XK_serialize_peer_secret( uint32_t *outlen, uint8_t **out, Noise_XK_device_t *dvp, Noise_XK_peer_t *peer ); /* Decrypt and deserialize a peer's secret data and add it to the device. */ Noise_XK_peer_t *Noise_XK_deserialize_peer_secret( Noise_XK_device_t *dvp, uint8_t *peer_name, uint32_t inlen, uint8_t *enc_keys ); /* Lookup a peer by using its unique identifier. Return NULL is no peer was found. Note that the peer is owned by the device: we don't provide any way of freeing it on the user side, and it might be invalidated after a removal operation. For this reason, we advise to immediately use the returned pointer (to retrieve the peer name, etc.), then forget it. */ Noise_XK_peer_t *Noise_XK_device_lookup_peer_by_id(Noise_XK_device_t *dvp, uint32_t id); /* Lookup a peer by using its static public key. Return NULL is no peer was found. Note that the peer is owned by the device: we don't provide any way of freeing it on the user side, and it might be invalidated after a removal operation. For this reason, we advise to immediately use the returned pointer (to retrieve the peer name, etc.), then forget it. */ Noise_XK_peer_t *Noise_XK_device_lookup_peer_by_static(Noise_XK_device_t *dvp, uint8_t *s); /* Copy the peer information to the user provided pointer. */ void Noise_XK_device_get_info(Noise_XK_noise_string *out, Noise_XK_device_t *dvp); /* Return the current value of the sessions counter. The device keeps track of the number of sessions created so far, in order to give them unique identifiers. */ uint32_t Noise_XK_device_get_sessions_counter(Noise_XK_device_t *dvp); /* Return true if the sessions counter is saturated. It is not possible to create any more sessions if the counter is saturated. */ bool Noise_XK_device_sessions_counter_is_saturated(Noise_XK_device_t *dvp); /* Return the current value of the peers counter. The device keeps track of the number of peers created so far, in order to give them unique identifiers. */ uint32_t Noise_XK_device_get_peers_counter(Noise_XK_device_t *dvp); /* Return true if the peers counter is saturated. It is not possible to add any more peers to the device if the counter is saturated. */ bool Noise_XK_device_peers_counter_is_saturated(Noise_XK_device_t *dvp); /* Copy the device static private key to the user provided buffer. */ void Noise_XK_device_get_static_priv(uint8_t *out, Noise_XK_device_t *dvp); /* Copy the device static public key to the user provided buffer. */ void Noise_XK_device_get_static_pub(uint8_t *out, Noise_XK_device_t *dvp); /* Return the unique peer identifier. */ uint32_t Noise_XK_peer_get_id(Noise_XK_peer_t *pp); /* Copy the peer information to the user provided pointer. */ void Noise_XK_peer_get_info(Noise_XK_noise_string *out, Noise_XK_peer_t *pp); /* Copy the peer static public key to the user provided buffer. */ void Noise_XK_peer_get_static(uint8_t *out, Noise_XK_peer_t *pp); /* Create an initiator session. May fail and return NULL in case of invalid keys, unknown peer, etc. */ Noise_XK_session_t *Noise_XK_session_create_initiator(Noise_XK_device_t *dvp, uint32_t pid); /* Create a responder session. May fail and return NULL in case of invalid keys, unknown peer, etc. */ Noise_XK_session_t *Noise_XK_session_create_responder(Noise_XK_device_t *dvp); /* Free a session. Be sure to free all sessions before freeing the device used to create those sessions. */ void Noise_XK_session_free(Noise_XK_session_t *sn); /* Write a message with the current session. If successful, this function will allocate a buffer of the proper length in `*out` and will write the length of this buffer in `*out_len`. Note that using `out` and `out_len` is always safe: if the function fails, it will set `*outlen` to 0 and `*out` to NULL. */ Noise_XK_rcode Noise_XK_session_write( Noise_XK_encap_message_t *payload, Noise_XK_session_t *sn_p, uint32_t *out_len, uint8_t **out ); /* Read a message with the current session. If successful, this function will allocate a an encapsulated message in `*payload_out`. Note that using `payload_out` is always safe: if the function fails, it will set `*payload_out` to NULL. */ Noise_XK_rcode Noise_XK_session_read( Noise_XK_encap_message_t **payload_out, Noise_XK_session_t *sn_p, uint32_t inlen, uint8_t *input ); /* Compute the length of the next message, given a payload length. Note that the function may fail, if the length of the message is too long for example (though very unlikely). You thus need to check the returned value. Also note that the length of the next message is always equal to: payload length + a value depending only on the current step. */ bool Noise_XK_session_compute_next_message_len( uint32_t *out, Noise_XK_session_t *sn, uint32_t payload_len ); /* Return the current status. */ Noise_XK_status Noise_XK_session_get_status(Noise_XK_session_t *sn); /* Copy the session hash to the user provided buffer. Note that the session hash is always public. Using the session hash might be pertinent once the session has reached the transport phase. */ void Noise_XK_session_get_hash(uint8_t *out, Noise_XK_session_t *sn); /* Return the session unique identifier. */ uint32_t Noise_XK_session_get_id(Noise_XK_session_t *sn); /* Copy the session information to the user provided pointer. */ void Noise_XK_session_get_info(Noise_XK_noise_string *out, Noise_XK_session_t *sn); /* Return the session's peer unique identifier. The remote may be unknown, in which case the returned id will be 0. Note that you can safely use the returned peer id without testing it, because all the functions taking peer ids as parameters were written to correctly manipulate 0. In particular, looking up id 0 will return NULL, and trying to create a session with peer id 0 will cleanly fail by also returning NULL. */ uint32_t Noise_XK_session_get_peer_id(Noise_XK_session_t *sn); /* Copy the session peer information, if known, to the user provided pointer. The remote may be unknown yet, in which case there is no peer information in the device and the function will return false. */ bool Noise_XK_session_get_peer_info(Noise_XK_noise_string *out, Noise_XK_session_t *sn); /* Return true if this session has reached the maximum security level for this pattern. Once the maximum security level is reached, it is not possible to have better confidentiality/authentication guarantees for the payloads sent/received with this session. Note that the guarantees provided by the maximum reachable level vary with the pattern, which must thus be carefully chosen. In order to reach the maximum level, the session must have finished the handshake. Moreover, in case the session sends the last handshake message, it must wait for the first transport message from the remote: otherwise, we have no way to know whether the remote was itself able to finish the handshake. */ bool Noise_XK_session_reached_max_security(Noise_XK_session_t *snp); /* DO NOT use this: for tests and benchmarks only */ Noise_XK_session_t *Noise_XK__session_create_initiator_with_ephemeral( Noise_XK_device_t *dvp, uint8_t *epriv, uint8_t *epub, uint32_t pid ); /* DO NOT use this: for tests and benchmarks only */ Noise_XK_session_t *Noise_XK__session_create_responder_with_ephemeral( Noise_XK_device_t *dvp, uint8_t *epriv, uint8_t *epub ); #define __XK_H_DEFINED #endif liboprf-0.9.4/src/noise_xk/include/karmel/000077500000000000000000000000001514673400200204675ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/include/karmel/README.txt000066400000000000000000000004221514673400200221630ustar00rootroot00000000000000contains snapshot of e5620ceb7c8a4996520d693f597872806dc0a1d3 from https://github.com/project-everest/hacl-star cp -r $(HACL_ROOT)/dist/karamel/krmllib/dist/minimal karmel cp -r $(HACL_ROOT)/dist/karamel/include/krml karmel/ cp -r ${HACL_ROOT}/dist/karamel/include karmel liboprf-0.9.4/src/noise_xk/include/karmel/krml/000077500000000000000000000000001514673400200214345ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/include/karmel/krml/c_endianness.h000066400000000000000000000004641514673400200242420ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __KRML_ENDIAN_H #define __KRML_ENDIAN_H #ifdef __GNUC__ #warning "c_endianness.h is deprecated, include lowstar_endianness.h instead" #endif #include "lowstar_endianness.h" #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/fstar_int.h000066400000000000000000000045651514673400200236100ustar00rootroot00000000000000#ifndef __FSTAR_INT_H #define __FSTAR_INT_H #include "internal/types.h" /* * Arithmetic Shift Right operator * * In all C standards, a >> b is implementation-defined when a has a signed * type and a negative value. See e.g. 6.5.7 in * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf * * GCC, MSVC, and Clang implement a >> b as an arithmetic shift. * * GCC: https://gcc.gnu.org/onlinedocs/gcc-9.1.0/gcc/Integers-implementation.html#Integers-implementation * MSVC: https://docs.microsoft.com/en-us/cpp/cpp/left-shift-and-right-shift-operators-input-and-output?view=vs-2019#right-shifts * Clang: tested that Clang 7, 8 and 9 compile this to an arithmetic shift * * We implement arithmetic shift right simply as >> in these compilers * and bail out in others. */ #if !(defined(_MSC_VER) || defined(__GNUC__) || (defined(__clang__) && (__clang_major__ >= 7))) static inline int8_t FStar_Int8_shift_arithmetic_right(int8_t a, uint32_t b) { do { KRML_HOST_EPRINTF("Could not identify compiler so could not provide an implementation of signed arithmetic shift right.\n"); KRML_HOST_EXIT(255); } while (0); } static inline int16_t FStar_Int16_shift_arithmetic_right(int16_t a, uint32_t b) { do { KRML_HOST_EPRINTF("Could not identify compiler so could not provide an implementation of signed arithmetic shift right.\n"); KRML_HOST_EXIT(255); } while (0); } static inline int32_t FStar_Int32_shift_arithmetic_right(int32_t a, uint32_t b) { do { KRML_HOST_EPRINTF("Could not identify compiler so could not provide an implementation of signed arithmetic shift right.\n"); KRML_HOST_EXIT(255); } while (0); } static inline int64_t FStar_Int64_shift_arithmetic_right(int64_t a, uint32_t b) { do { KRML_HOST_EPRINTF("Could not identify compiler so could not provide an implementation of signed arithmetic shift right.\n"); KRML_HOST_EXIT(255); } while (0); } #else static inline int8_t FStar_Int8_shift_arithmetic_right(int8_t a, uint32_t b) { return (a >> b); } static inline int16_t FStar_Int16_shift_arithmetic_right(int16_t a, uint32_t b) { return (a >> b); } static inline int32_t FStar_Int32_shift_arithmetic_right(int32_t a, uint32_t b) { return (a >> b); } static inline int64_t FStar_Int64_shift_arithmetic_right(int64_t a, uint32_t b) { return (a >> b); } #endif /* !(defined(_MSC_VER) ... ) */ #endif /* __FSTAR_INT_H */ liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/000077500000000000000000000000001514673400200232505ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/builtin.h000066400000000000000000000007731514673400200250760ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __KRML_BUILTIN_H #define __KRML_BUILTIN_H /* For alloca, when using KaRaMeL's -falloca */ #if (defined(_WIN32) || defined(_WIN64)) # include #elif (defined(sun)) # include #endif /* If some globals need to be initialized before the main, then karamel will * generate and try to link last a function with this type: */ void krmlinit_globals(void); #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/callconv.h000066400000000000000000000015221514673400200252220ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __KRML_CALLCONV_H #define __KRML_CALLCONV_H /******************************************************************************/ /* Some macros to ease compatibility (TODO: move to miTLS) */ /******************************************************************************/ /* We want to generate __cdecl safely without worrying about it being undefined. * When using MSVC, these are always defined. When using MinGW, these are * defined too. They have no meaning for other platforms, so we define them to * be empty macros in other situations. */ #ifndef _MSC_VER #ifndef __cdecl #define __cdecl #endif #ifndef __stdcall #define __stdcall #endif #ifndef __fastcall #define __fastcall #endif #endif #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/compat.h000066400000000000000000000024371514673400200247120ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef KRML_COMPAT_H #define KRML_COMPAT_H #include /* A series of macros that define C implementations of types that are not Low*, * to facilitate porting programs to Low*. */ typedef struct { uint32_t length; const char *data; } FStar_Bytes_bytes; typedef int32_t Prims_pos, Prims_nat, Prims_nonzero, Prims_int, krml_checked_int_t; #define RETURN_OR(x) \ do { \ int64_t __ret = x; \ if (__ret < INT32_MIN || INT32_MAX < __ret) { \ KRML_HOST_PRINTF( \ "Prims.{int,nat,pos} integer overflow at %s:%d\n", __FILE__, \ __LINE__); \ KRML_HOST_EXIT(252); \ } \ return (int32_t)__ret; \ } while (0) #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/debug.h000066400000000000000000000057471514673400200245240ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __KRML_DEBUG_H #define __KRML_DEBUG_H #include #include "krml/internal/target.h" /******************************************************************************/ /* Debugging helpers - intended only for KaRaMeL developers */ /******************************************************************************/ /* In support of "-wasm -d force-c": we might need this function to be * forward-declared, because the dependency on WasmSupport appears very late, * after SimplifyWasm, and sadly, after the topological order has been done. */ void WasmSupport_check_buffer_size(uint32_t s); /* A series of GCC atrocities to trace function calls (karamel's [-d c-calls] * option). Useful when trying to debug, say, Wasm, to compare traces. */ /* clang-format off */ #ifdef __GNUC__ #define KRML_FORMAT(X) _Generic((X), \ uint8_t : "0x%08" PRIx8, \ uint16_t: "0x%08" PRIx16, \ uint32_t: "0x%08" PRIx32, \ uint64_t: "0x%08" PRIx64, \ int8_t : "0x%08" PRIx8, \ int16_t : "0x%08" PRIx16, \ int32_t : "0x%08" PRIx32, \ int64_t : "0x%08" PRIx64, \ default : "%s") #define KRML_FORMAT_ARG(X) _Generic((X), \ uint8_t : X, \ uint16_t: X, \ uint32_t: X, \ uint64_t: X, \ int8_t : X, \ int16_t : X, \ int32_t : X, \ int64_t : X, \ default : "unknown") /* clang-format on */ # define KRML_DEBUG_RETURN(X) \ ({ \ __auto_type _ret = (X); \ KRML_HOST_PRINTF("returning: "); \ KRML_HOST_PRINTF(KRML_FORMAT(_ret), KRML_FORMAT_ARG(_ret)); \ KRML_HOST_PRINTF(" \n"); \ _ret; \ }) #endif #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/target.h000066400000000000000000000325021514673400200247110ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __KRML_TARGET_H #define __KRML_TARGET_H #include #include #include #include #include #include #include /* Since KaRaMeL emits the inline keyword unconditionally, we follow the * guidelines at https://gcc.gnu.org/onlinedocs/gcc/Inline.html and make this * __inline__ to ensure the code compiles with -std=c90 and earlier. */ #ifdef __GNUC__ # define inline __inline__ #endif /******************************************************************************/ /* Macros that KaRaMeL will generate. */ /******************************************************************************/ /* For "bare" targets that do not have a C stdlib, the user might want to use * [-add-early-include '"mydefinitions.h"'] and override these. */ #ifndef KRML_HOST_PRINTF # define KRML_HOST_PRINTF printf #endif #if ( \ (defined __STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ (!(defined KRML_HOST_EPRINTF))) # define KRML_HOST_EPRINTF(...) fprintf(stderr, __VA_ARGS__) #elif !(defined KRML_HOST_EPRINTF) && defined(_MSC_VER) # define KRML_HOST_EPRINTF(...) fprintf(stderr, __VA_ARGS__) #endif #ifndef KRML_HOST_EXIT # define KRML_HOST_EXIT exit #endif #ifndef KRML_HOST_MALLOC # define KRML_HOST_MALLOC malloc #endif #ifndef KRML_HOST_CALLOC # define KRML_HOST_CALLOC calloc #endif #ifndef KRML_HOST_FREE # define KRML_HOST_FREE free #endif #ifndef KRML_HOST_IGNORE # define KRML_HOST_IGNORE(x) (void)(x) #endif #ifndef KRML_MAYBE_UNUSED_VAR # define KRML_MAYBE_UNUSED_VAR(x) KRML_HOST_IGNORE(x) #endif #ifndef KRML_MAYBE_UNUSED # if defined(__GNUC__) # define KRML_MAYBE_UNUSED __attribute__((unused)) # else # define KRML_MAYBE_UNUSED # endif #endif #ifndef KRML_NOINLINE # if defined(_MSC_VER) # define KRML_NOINLINE __declspec(noinline) # elif defined (__GNUC__) # define KRML_NOINLINE __attribute__((noinline,unused)) # else # define KRML_NOINLINE # warning "The KRML_NOINLINE macro is not defined for this toolchain!" # warning "The compiler may defeat side-channel resistance with optimizations." # warning "Please locate target.h and try to fill it out with a suitable definition for this compiler." # endif #endif #ifndef KRML_PRE_ALIGN # ifdef _MSC_VER # define KRML_PRE_ALIGN(X) __declspec(align(X)) # else # define KRML_PRE_ALIGN(X) # endif #endif #ifndef KRML_POST_ALIGN # ifdef _MSC_VER # define KRML_POST_ALIGN(X) # else # define KRML_POST_ALIGN(X) __attribute__((aligned(X))) # endif #endif /* MinGW-W64 does not support C11 aligned_alloc, but it supports * MSVC's _aligned_malloc. */ #ifndef KRML_ALIGNED_MALLOC # ifdef __MINGW32__ # include <_mingw.h> # endif # if ( \ defined(_MSC_VER) || \ (defined(__MINGW32__) && defined(__MINGW64_VERSION_MAJOR))) # define KRML_ALIGNED_MALLOC(X, Y) _aligned_malloc(Y, X) # else # define KRML_ALIGNED_MALLOC(X, Y) aligned_alloc(X, Y) # endif #endif /* Since aligned allocations with MinGW-W64 are done with * _aligned_malloc (see above), such pointers must be freed with * _aligned_free. */ #ifndef KRML_ALIGNED_FREE # ifdef __MINGW32__ # include <_mingw.h> # endif # if ( \ defined(_MSC_VER) || \ (defined(__MINGW32__) && defined(__MINGW64_VERSION_MAJOR))) # define KRML_ALIGNED_FREE(X) _aligned_free(X) # else # define KRML_ALIGNED_FREE(X) free(X) # endif #endif #ifndef KRML_HOST_TIME # include /* Prims_nat not yet in scope */ inline static int32_t krml_time(void) { return (int32_t)time(NULL); } # define KRML_HOST_TIME krml_time #endif /* In statement position, exiting is easy. */ #define KRML_EXIT \ do { \ KRML_HOST_PRINTF("Unimplemented function at %s:%d\n", __FILE__, __LINE__); \ KRML_HOST_EXIT(254); \ } while (0) /* In expression position, use the comma-operator and a malloc to return an * expression of the right size. KaRaMeL passes t as the parameter to the macro. */ #define KRML_EABORT(t, msg) \ (KRML_HOST_PRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, msg), \ KRML_HOST_EXIT(255), *((t *)KRML_HOST_MALLOC(sizeof(t)))) /* In FStar.Buffer.fst, the size of arrays is uint32_t, but it's a number of * *elements*. Do an ugly, run-time check (some of which KaRaMeL can eliminate). */ #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4)) # define _KRML_CHECK_SIZE_PRAGMA \ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") #else # define _KRML_CHECK_SIZE_PRAGMA #endif #define KRML_CHECK_SIZE(size_elt, sz) \ do { \ _KRML_CHECK_SIZE_PRAGMA \ if (((size_t)(sz)) > ((size_t)(SIZE_MAX / (size_elt)))) { \ KRML_HOST_PRINTF( \ "Maximum allocatable size exceeded, aborting before overflow at " \ "%s:%d\n", \ __FILE__, __LINE__); \ KRML_HOST_EXIT(253); \ } \ } while (0) #if defined(_MSC_VER) && _MSC_VER < 1900 # define KRML_HOST_SNPRINTF(buf, sz, fmt, arg) \ _snprintf_s(buf, sz, _TRUNCATE, fmt, arg) #else # define KRML_HOST_SNPRINTF(buf, sz, fmt, arg) snprintf(buf, sz, fmt, arg) #endif #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4)) # define KRML_DEPRECATED(x) __attribute__((deprecated(x))) #elif defined(__GNUC__) /* deprecated attribute is not defined in GCC < 4.5. */ # define KRML_DEPRECATED(x) #elif defined(_MSC_VER) # define KRML_DEPRECATED(x) __declspec(deprecated(x)) #endif /* Macros for prettier unrolling of loops */ #define KRML_LOOP1(i, n, x) { \ x \ i += n; \ (void) i; \ } #define KRML_LOOP2(i, n, x) \ KRML_LOOP1(i, n, x) \ KRML_LOOP1(i, n, x) #define KRML_LOOP3(i, n, x) \ KRML_LOOP2(i, n, x) \ KRML_LOOP1(i, n, x) #define KRML_LOOP4(i, n, x) \ KRML_LOOP2(i, n, x) \ KRML_LOOP2(i, n, x) #define KRML_LOOP5(i, n, x) \ KRML_LOOP4(i, n, x) \ KRML_LOOP1(i, n, x) #define KRML_LOOP6(i, n, x) \ KRML_LOOP4(i, n, x) \ KRML_LOOP2(i, n, x) #define KRML_LOOP7(i, n, x) \ KRML_LOOP4(i, n, x) \ KRML_LOOP3(i, n, x) #define KRML_LOOP8(i, n, x) \ KRML_LOOP4(i, n, x) \ KRML_LOOP4(i, n, x) #define KRML_LOOP9(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP1(i, n, x) #define KRML_LOOP10(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP2(i, n, x) #define KRML_LOOP11(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP3(i, n, x) #define KRML_LOOP12(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP4(i, n, x) #define KRML_LOOP13(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP5(i, n, x) #define KRML_LOOP14(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP6(i, n, x) #define KRML_LOOP15(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP7(i, n, x) #define KRML_LOOP16(i, n, x) \ KRML_LOOP8(i, n, x) \ KRML_LOOP8(i, n, x) #define KRML_UNROLL_FOR(i, z, n, k, x) \ do { \ uint32_t i = z; \ KRML_LOOP##n(i, k, x) \ } while (0) #define KRML_ACTUAL_FOR(i, z, n, k, x) \ do { \ for (uint32_t i = z; i < n; i += k) { \ x \ } \ } while (0) #ifndef KRML_UNROLL_MAX # define KRML_UNROLL_MAX 16 #endif /* 1 is the number of loop iterations, i.e. (n - z)/k as evaluated by krml */ #if 0 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR0(i, z, n, k, x) #else # define KRML_MAYBE_FOR0(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 1 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR1(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 1, k, x) #else # define KRML_MAYBE_FOR1(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 2 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR2(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 2, k, x) #else # define KRML_MAYBE_FOR2(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 3 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR3(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 3, k, x) #else # define KRML_MAYBE_FOR3(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 4 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR4(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 4, k, x) #else # define KRML_MAYBE_FOR4(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 5 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR5(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 5, k, x) #else # define KRML_MAYBE_FOR5(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 6 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR6(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 6, k, x) #else # define KRML_MAYBE_FOR6(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 7 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR7(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 7, k, x) #else # define KRML_MAYBE_FOR7(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 8 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR8(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 8, k, x) #else # define KRML_MAYBE_FOR8(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 9 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR9(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 9, k, x) #else # define KRML_MAYBE_FOR9(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 10 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR10(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 10, k, x) #else # define KRML_MAYBE_FOR10(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 11 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR11(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 11, k, x) #else # define KRML_MAYBE_FOR11(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 12 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR12(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 12, k, x) #else # define KRML_MAYBE_FOR12(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 13 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR13(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 13, k, x) #else # define KRML_MAYBE_FOR13(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 14 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR14(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 14, k, x) #else # define KRML_MAYBE_FOR14(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 15 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR15(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 15, k, x) #else # define KRML_MAYBE_FOR15(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #if 16 <= KRML_UNROLL_MAX # define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_UNROLL_FOR(i, z, 16, k, x) #else # define KRML_MAYBE_FOR16(i, z, n, k, x) KRML_ACTUAL_FOR(i, z, n, k, x) #endif #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/types.h000066400000000000000000000070221514673400200245660ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef KRML_TYPES_H #define KRML_TYPES_H #include #include #include #include /* Types which are either abstract, meaning that have to be implemented in C, or * which are models, meaning that they are swapped out at compile-time for * hand-written C types (in which case they're marked as noextract). */ typedef uint64_t FStar_UInt64_t, FStar_UInt64_t_; typedef int64_t FStar_Int64_t, FStar_Int64_t_; typedef uint32_t FStar_UInt32_t, FStar_UInt32_t_; typedef int32_t FStar_Int32_t, FStar_Int32_t_; typedef uint16_t FStar_UInt16_t, FStar_UInt16_t_; typedef int16_t FStar_Int16_t, FStar_Int16_t_; typedef uint8_t FStar_UInt8_t, FStar_UInt8_t_; typedef int8_t FStar_Int8_t, FStar_Int8_t_; /* Only useful when building krmllib, because it's in the dependency graph of * FStar.Int.Cast. */ typedef uint64_t FStar_UInt63_t, FStar_UInt63_t_; typedef int64_t FStar_Int63_t, FStar_Int63_t_; typedef double FStar_Float_float; typedef uint32_t FStar_Char_char; typedef FILE *FStar_IO_fd_read, *FStar_IO_fd_write; typedef void *FStar_Dyn_dyn; typedef const char *C_String_t, *C_String_t_, *C_Compat_String_t, *C_Compat_String_t_; typedef int exit_code; typedef FILE *channel; typedef unsigned long long TestLib_cycles; typedef uint64_t FStar_Date_dateTime, FStar_Date_timeSpan; /* Now Prims.string is no longer illegal with the new model in LowStar.Printf; * it's operations that produce Prims_string which are illegal. Bring the * definition into scope by default. */ typedef const char *Prims_string; #if (defined(_MSC_VER) && defined(_M_X64) && !defined(__clang__)) #define IS_MSVC64 1 #endif /* This code makes a number of assumptions and should be refined. In particular, * it assumes that: any non-MSVC amd64 compiler supports int128. Maybe it would * be easier to just test for defined(__SIZEOF_INT128__) only? */ #if (defined(__x86_64__) || \ defined(__x86_64) || \ defined(__aarch64__) || \ (defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)) || \ defined(__s390x__) || \ (defined(_MSC_VER) && defined(_M_X64) && defined(__clang__)) || \ (defined(__mips__) && defined(__LP64__)) || \ (defined(__riscv) && __riscv_xlen == 64) || \ defined(__SIZEOF_INT128__)) #define HAS_INT128 1 #endif /* The uint128 type is a special case since we offer several implementations of * it, depending on the compiler and whether the user wants the verified * implementation or not. */ #if !defined(KRML_VERIFIED_UINT128) && defined(IS_MSVC64) # include typedef __m128i FStar_UInt128_uint128; #elif !defined(KRML_VERIFIED_UINT128) && defined(HAS_INT128) typedef unsigned __int128 FStar_UInt128_uint128; #else typedef struct FStar_UInt128_uint128_s { uint64_t low; uint64_t high; } FStar_UInt128_uint128; #endif /* The former is defined once, here (otherwise, conflicts for test-c89. The * latter is for internal use. */ typedef FStar_UInt128_uint128 FStar_UInt128_t, uint128_t; #include "krml/lowstar_endianness.h" #endif /* Avoid a circular loop: if this header is included via FStar_UInt8_16_32_64, * then don't bring the uint128 definitions into scope. */ #ifndef __FStar_UInt_8_16_32_64_H #if !defined(KRML_VERIFIED_UINT128) && defined(IS_MSVC64) #include "fstar_uint128_msvc.h" #elif !defined(KRML_VERIFIED_UINT128) && defined(HAS_INT128) #include "fstar_uint128_gcc64.h" #else #include "FStar_UInt128_Verified.h" #include "fstar_uint128_struct_endianness.h" #endif #endif liboprf-0.9.4/src/noise_xk/include/karmel/krml/internal/wasmsupport.h000066400000000000000000000003551514673400200260300ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ /* This file is automatically included when compiling with -wasm -d force-c */ #define WasmSupport_check_buffer_size(X) liboprf-0.9.4/src/noise_xk/include/karmel/krml/lowstar_endianness.h000066400000000000000000000171151514673400200255140ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __LOWSTAR_ENDIANNESS_H #define __LOWSTAR_ENDIANNESS_H #include #include /******************************************************************************/ /* Implementing C.fst (part 2: endian-ness macros) */ /******************************************************************************/ /* ... for Linux */ #if defined(__linux__) || defined(__CYGWIN__) || defined (__USE_SYSTEM_ENDIAN_H__) || defined(__GLIBC__) # include /* ... for OSX */ #elif defined(__APPLE__) # include # define htole64(x) OSSwapHostToLittleInt64(x) # define le64toh(x) OSSwapLittleToHostInt64(x) # define htobe64(x) OSSwapHostToBigInt64(x) # define be64toh(x) OSSwapBigToHostInt64(x) # define htole16(x) OSSwapHostToLittleInt16(x) # define le16toh(x) OSSwapLittleToHostInt16(x) # define htobe16(x) OSSwapHostToBigInt16(x) # define be16toh(x) OSSwapBigToHostInt16(x) # define htole32(x) OSSwapHostToLittleInt32(x) # define le32toh(x) OSSwapLittleToHostInt32(x) # define htobe32(x) OSSwapHostToBigInt32(x) # define be32toh(x) OSSwapBigToHostInt32(x) /* ... for Solaris */ #elif defined(__sun__) # include # define htole64(x) LE_64(x) # define le64toh(x) LE_64(x) # define htobe64(x) BE_64(x) # define be64toh(x) BE_64(x) # define htole16(x) LE_16(x) # define le16toh(x) LE_16(x) # define htobe16(x) BE_16(x) # define be16toh(x) BE_16(x) # define htole32(x) LE_32(x) # define le32toh(x) LE_32(x) # define htobe32(x) BE_32(x) # define be32toh(x) BE_32(x) /* ... for the BSDs */ #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) # include #elif defined(__OpenBSD__) # include /* ... for Windows (MSVC)... not targeting XBOX 360! */ #elif defined(_MSC_VER) # include # define htobe16(x) _byteswap_ushort(x) # define htole16(x) (x) # define be16toh(x) _byteswap_ushort(x) # define le16toh(x) (x) # define htobe32(x) _byteswap_ulong(x) # define htole32(x) (x) # define be32toh(x) _byteswap_ulong(x) # define le32toh(x) (x) # define htobe64(x) _byteswap_uint64(x) # define htole64(x) (x) # define be64toh(x) _byteswap_uint64(x) # define le64toh(x) (x) /* ... for Windows (GCC-like, e.g. mingw or clang) */ #elif (defined(_WIN32) || defined(_WIN64) || defined(__EMSCRIPTEN__)) && \ (defined(__GNUC__) || defined(__clang__)) # define htobe16(x) __builtin_bswap16(x) # define htole16(x) (x) # define be16toh(x) __builtin_bswap16(x) # define le16toh(x) (x) # define htobe32(x) __builtin_bswap32(x) # define htole32(x) (x) # define be32toh(x) __builtin_bswap32(x) # define le32toh(x) (x) # define htobe64(x) __builtin_bswap64(x) # define htole64(x) (x) # define be64toh(x) __builtin_bswap64(x) # define le64toh(x) (x) /* ... generic big-endian fallback code */ /* ... AIX doesn't have __BYTE_ORDER__ (with XLC compiler) & is always big-endian */ #elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || defined(_AIX) /* byte swapping code inspired by: * https://github.com/rweather/arduinolibs/blob/master/libraries/Crypto/utility/EndianUtil.h * */ # define htobe32(x) (x) # define be32toh(x) (x) # define htole32(x) \ (__extension__({ \ uint32_t _temp = (x); \ ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ })) # define le32toh(x) (htole32((x))) # define htobe64(x) (x) # define be64toh(x) (x) # define htole64(x) \ (__extension__({ \ uint64_t __temp = (x); \ uint32_t __low = htobe32((uint32_t)__temp); \ uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ (((uint64_t)__low) << 32) | __high; \ })) # define le64toh(x) (htole64((x))) /* ... generic little-endian fallback code */ #elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define htole32(x) (x) # define le32toh(x) (x) # define htobe32(x) \ (__extension__({ \ uint32_t _temp = (x); \ ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ })) # define be32toh(x) (htobe32((x))) # define htole64(x) (x) # define le64toh(x) (x) # define htobe64(x) \ (__extension__({ \ uint64_t __temp = (x); \ uint32_t __low = htobe32((uint32_t)__temp); \ uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ (((uint64_t)__low) << 32) | __high; \ })) # define be64toh(x) (htobe64((x))) /* ... couldn't determine endian-ness of the target platform */ #else # error "Please define __BYTE_ORDER__!" #endif /* defined(__linux__) || ... */ /* Loads and stores. These avoid undefined behavior due to unaligned memory * accesses, via memcpy. */ inline static uint16_t load16(uint8_t *b) { uint16_t x; memcpy(&x, b, 2); return x; } inline static uint32_t load32(uint8_t *b) { uint32_t x; memcpy(&x, b, 4); return x; } inline static uint64_t load64(uint8_t *b) { uint64_t x; memcpy(&x, b, 8); return x; } inline static void store16(uint8_t *b, uint16_t i) { memcpy(b, &i, 2); } inline static void store32(uint8_t *b, uint32_t i) { memcpy(b, &i, 4); } inline static void store64(uint8_t *b, uint64_t i) { memcpy(b, &i, 8); } /* Legacy accessors so that this header can serve as an implementation of * C.Endianness */ #define load16_le(b) (le16toh(load16(b))) #define store16_le(b, i) (store16(b, htole16(i))) #define load16_be(b) (be16toh(load16(b))) #define store16_be(b, i) (store16(b, htobe16(i))) #define load32_le(b) (le32toh(load32(b))) #define store32_le(b, i) (store32(b, htole32(i))) #define load32_be(b) (be32toh(load32(b))) #define store32_be(b, i) (store32(b, htobe32(i))) #define load64_le(b) (le64toh(load64(b))) #define store64_le(b, i) (store64(b, htole64(i))) #define load64_be(b) (be64toh(load64(b))) #define store64_be(b, i) (store64(b, htobe64(i))) /* Co-existence of LowStar.Endianness and FStar.Endianness generates name * conflicts, because of course both insist on having no prefixes. Until a * prefix is added, or until we truly retire FStar.Endianness, solve this issue * in an elegant way. */ #define load16_le0 load16_le #define store16_le0 store16_le #define load16_be0 load16_be #define store16_be0 store16_be #define load32_le0 load32_le #define store32_le0 store32_le #define load32_be0 load32_be #define store32_be0 store32_be #define load64_le0 load64_le #define store64_le0 store64_le #define load64_be0 load64_be #define store64_be0 store64_be #define load128_le0 load128_le #define store128_le0 store128_le #define load128_be0 load128_be #define store128_be0 store128_be #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/000077500000000000000000000000001514673400200221155ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/include/karmel/minimal/FStar_UInt128.h000066400000000000000000000051511514673400200245010ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __FStar_UInt128_H #define __FStar_UInt128_H #include #include #include "krml/internal/compat.h" #include "krml/lowstar_endianness.h" #include "krml/internal/types.h" #include "krml/internal/target.h" static inline FStar_UInt128_uint128 FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a); static inline FStar_UInt128_uint128 FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s); static inline FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s); static inline bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); static inline FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a); static inline uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a); static inline FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y); static inline FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y); #define __FStar_UInt128_H_DEFINED #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/FStar_UInt128_Verified.h000066400000000000000000000222071514673400200263170ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __FStar_UInt128_Verified_H #define __FStar_UInt128_Verified_H #include "FStar_UInt_8_16_32_64.h" #include #include #include "krml/internal/types.h" #include "krml/internal/target.h" static inline uint64_t FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b) { return (a ^ ((a ^ b) | ((a - b) ^ b))) >> 63U; } static inline uint64_t FStar_UInt128_carry(uint64_t a, uint64_t b) { return FStar_UInt128_constant_time_carry(a, b); } static inline FStar_UInt128_uint128 FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low + b.low; lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low + b.low; lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low + b.low; lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low - b.low; lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low - b.low; lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_sub_mod_impl(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low - b.low; lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return FStar_UInt128_sub_mod_impl(a, b); } static inline FStar_UInt128_uint128 FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low & b.low; lit.high = a.high & b.high; return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low ^ b.low; lit.high = a.high ^ b.high; return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low | b.low; lit.high = a.high | b.high; return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a) { FStar_UInt128_uint128 lit; lit.low = ~a.low; lit.high = ~a.high; return lit; } static uint32_t FStar_UInt128_u32_64 = 64U; static inline uint64_t FStar_UInt128_add_u64_shift_left(uint64_t hi, uint64_t lo, uint32_t s) { return (hi << s) + (lo >> (FStar_UInt128_u32_64 - s)); } static inline uint64_t FStar_UInt128_add_u64_shift_left_respec(uint64_t hi, uint64_t lo, uint32_t s) { return FStar_UInt128_add_u64_shift_left(hi, lo, s); } static inline FStar_UInt128_uint128 FStar_UInt128_shift_left_small(FStar_UInt128_uint128 a, uint32_t s) { if (s == 0U) { return a; } else { FStar_UInt128_uint128 lit; lit.low = a.low << s; lit.high = FStar_UInt128_add_u64_shift_left_respec(a.high, a.low, s); return lit; } } static inline FStar_UInt128_uint128 FStar_UInt128_shift_left_large(FStar_UInt128_uint128 a, uint32_t s) { FStar_UInt128_uint128 lit; lit.low = 0ULL; lit.high = a.low << (s - FStar_UInt128_u32_64); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s) { if (s < FStar_UInt128_u32_64) { return FStar_UInt128_shift_left_small(a, s); } else { return FStar_UInt128_shift_left_large(a, s); } } static inline uint64_t FStar_UInt128_add_u64_shift_right(uint64_t hi, uint64_t lo, uint32_t s) { return (lo >> s) + (hi << (FStar_UInt128_u32_64 - s)); } static inline uint64_t FStar_UInt128_add_u64_shift_right_respec(uint64_t hi, uint64_t lo, uint32_t s) { return FStar_UInt128_add_u64_shift_right(hi, lo, s); } static inline FStar_UInt128_uint128 FStar_UInt128_shift_right_small(FStar_UInt128_uint128 a, uint32_t s) { if (s == 0U) { return a; } else { FStar_UInt128_uint128 lit; lit.low = FStar_UInt128_add_u64_shift_right_respec(a.high, a.low, s); lit.high = a.high >> s; return lit; } } static inline FStar_UInt128_uint128 FStar_UInt128_shift_right_large(FStar_UInt128_uint128 a, uint32_t s) { FStar_UInt128_uint128 lit; lit.low = a.high >> (s - FStar_UInt128_u32_64); lit.high = 0ULL; return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s) { if (s < FStar_UInt128_u32_64) { return FStar_UInt128_shift_right_small(a, s); } else { return FStar_UInt128_shift_right_large(a, s); } } static inline bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.low == b.low && a.high == b.high; } static inline bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high > b.high || (a.high == b.high && a.low > b.low); } static inline bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high < b.high || (a.high == b.high && a.low < b.low); } static inline bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high > b.high || (a.high == b.high && a.low >= b.low); } static inline bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high < b.high || (a.high == b.high && a.low <= b.low); } static inline FStar_UInt128_uint128 FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); lit.high = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)); lit.high = (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)); return lit; } static inline FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a) { FStar_UInt128_uint128 lit; lit.low = a; lit.high = 0ULL; return lit; } static inline uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a) { return a.low; } static inline uint64_t FStar_UInt128_u64_mod_32(uint64_t a) { return a & 0xffffffffULL; } static uint32_t FStar_UInt128_u32_32 = 32U; static inline uint64_t FStar_UInt128_u32_combine(uint64_t hi, uint64_t lo) { return lo + (hi << FStar_UInt128_u32_32); } static inline FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y) { FStar_UInt128_uint128 lit; lit.low = FStar_UInt128_u32_combine((x >> FStar_UInt128_u32_32) * (uint64_t)y + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32), FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * (uint64_t)y)); lit.high = ((x >> FStar_UInt128_u32_32) * (uint64_t)y + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32)) >> FStar_UInt128_u32_32; return lit; } static inline uint64_t FStar_UInt128_u32_combine_(uint64_t hi, uint64_t lo) { return lo + (hi << FStar_UInt128_u32_32); } static inline FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y) { FStar_UInt128_uint128 lit; lit.low = FStar_UInt128_u32_combine_(FStar_UInt128_u64_mod_32(x) * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32((x >> FStar_UInt128_u32_32) * FStar_UInt128_u64_mod_32(y) + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32)), FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y))); lit.high = (x >> FStar_UInt128_u32_32) * (y >> FStar_UInt128_u32_32) + (((x >> FStar_UInt128_u32_32) * FStar_UInt128_u64_mod_32(y) + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32)) >> FStar_UInt128_u32_32) + ((FStar_UInt128_u64_mod_32(x) * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32((x >> FStar_UInt128_u32_32) * FStar_UInt128_u64_mod_32(y) + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32))) >> FStar_UInt128_u32_32); return lit; } #define __FStar_UInt128_Verified_H_DEFINED #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/FStar_UInt_8_16_32_64.h000066400000000000000000000135021514673400200256170ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __FStar_UInt_8_16_32_64_H #define __FStar_UInt_8_16_32_64_H #include #include #include "krml/internal/compat.h" #include "krml/lowstar_endianness.h" #include "krml/internal/types.h" #include "krml/internal/target.h" extern krml_checked_int_t FStar_UInt64_n; extern bool FStar_UInt64_uu___is_Mk(uint64_t projectee); extern krml_checked_int_t FStar_UInt64___proj__Mk__item__v(uint64_t projectee); extern krml_checked_int_t FStar_UInt64_v(uint64_t x); extern uint64_t FStar_UInt64_uint_to_t(krml_checked_int_t x); extern uint64_t FStar_UInt64_zero; extern uint64_t FStar_UInt64_one; extern uint64_t FStar_UInt64_minus(uint64_t a); extern uint32_t FStar_UInt64_n_minus_one; static KRML_NOINLINE uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) { uint64_t x = a ^ b; uint64_t minus_x = ~x + 1ULL; uint64_t x_or_minus_x = x | minus_x; uint64_t xnx = x_or_minus_x >> 63U; return xnx - 1ULL; } static KRML_NOINLINE uint64_t FStar_UInt64_gte_mask(uint64_t a, uint64_t b) { uint64_t x = a; uint64_t y = b; uint64_t x_xor_y = x ^ y; uint64_t x_sub_y = x - y; uint64_t x_sub_y_xor_y = x_sub_y ^ y; uint64_t q = x_xor_y | x_sub_y_xor_y; uint64_t x_xor_q = x ^ q; uint64_t x_xor_q_ = x_xor_q >> 63U; return x_xor_q_ - 1ULL; } extern Prims_string FStar_UInt64_to_string(uint64_t uu___); extern Prims_string FStar_UInt64_to_string_hex(uint64_t uu___); extern Prims_string FStar_UInt64_to_string_hex_pad(uint64_t uu___); extern uint64_t FStar_UInt64_of_string(Prims_string uu___); extern krml_checked_int_t FStar_UInt32_n; extern bool FStar_UInt32_uu___is_Mk(uint32_t projectee); extern krml_checked_int_t FStar_UInt32___proj__Mk__item__v(uint32_t projectee); extern krml_checked_int_t FStar_UInt32_v(uint32_t x); extern uint32_t FStar_UInt32_uint_to_t(krml_checked_int_t x); extern uint32_t FStar_UInt32_zero; extern uint32_t FStar_UInt32_one; extern uint32_t FStar_UInt32_minus(uint32_t a); extern uint32_t FStar_UInt32_n_minus_one; static KRML_NOINLINE uint32_t FStar_UInt32_eq_mask(uint32_t a, uint32_t b) { uint32_t x = a ^ b; uint32_t minus_x = ~x + 1U; uint32_t x_or_minus_x = x | minus_x; uint32_t xnx = x_or_minus_x >> 31U; return xnx - 1U; } static KRML_NOINLINE uint32_t FStar_UInt32_gte_mask(uint32_t a, uint32_t b) { uint32_t x = a; uint32_t y = b; uint32_t x_xor_y = x ^ y; uint32_t x_sub_y = x - y; uint32_t x_sub_y_xor_y = x_sub_y ^ y; uint32_t q = x_xor_y | x_sub_y_xor_y; uint32_t x_xor_q = x ^ q; uint32_t x_xor_q_ = x_xor_q >> 31U; return x_xor_q_ - 1U; } extern Prims_string FStar_UInt32_to_string(uint32_t uu___); extern Prims_string FStar_UInt32_to_string_hex(uint32_t uu___); extern Prims_string FStar_UInt32_to_string_hex_pad(uint32_t uu___); extern uint32_t FStar_UInt32_of_string(Prims_string uu___); extern krml_checked_int_t FStar_UInt16_n; extern bool FStar_UInt16_uu___is_Mk(uint16_t projectee); extern krml_checked_int_t FStar_UInt16___proj__Mk__item__v(uint16_t projectee); extern krml_checked_int_t FStar_UInt16_v(uint16_t x); extern uint16_t FStar_UInt16_uint_to_t(krml_checked_int_t x); extern uint16_t FStar_UInt16_zero; extern uint16_t FStar_UInt16_one; extern uint16_t FStar_UInt16_minus(uint16_t a); extern uint32_t FStar_UInt16_n_minus_one; static KRML_NOINLINE uint16_t FStar_UInt16_eq_mask(uint16_t a, uint16_t b) { uint16_t x = (uint32_t)a ^ (uint32_t)b; uint16_t minus_x = (uint32_t)~x + 1U; uint16_t x_or_minus_x = (uint32_t)x | (uint32_t)minus_x; uint16_t xnx = (uint32_t)x_or_minus_x >> 15U; return (uint32_t)xnx - 1U; } static KRML_NOINLINE uint16_t FStar_UInt16_gte_mask(uint16_t a, uint16_t b) { uint16_t x = a; uint16_t y = b; uint16_t x_xor_y = (uint32_t)x ^ (uint32_t)y; uint16_t x_sub_y = (uint32_t)x - (uint32_t)y; uint16_t x_sub_y_xor_y = (uint32_t)x_sub_y ^ (uint32_t)y; uint16_t q = (uint32_t)x_xor_y | (uint32_t)x_sub_y_xor_y; uint16_t x_xor_q = (uint32_t)x ^ (uint32_t)q; uint16_t x_xor_q_ = (uint32_t)x_xor_q >> 15U; return (uint32_t)x_xor_q_ - 1U; } extern Prims_string FStar_UInt16_to_string(uint16_t uu___); extern Prims_string FStar_UInt16_to_string_hex(uint16_t uu___); extern Prims_string FStar_UInt16_to_string_hex_pad(uint16_t uu___); extern uint16_t FStar_UInt16_of_string(Prims_string uu___); extern krml_checked_int_t FStar_UInt8_n; extern bool FStar_UInt8_uu___is_Mk(uint8_t projectee); extern krml_checked_int_t FStar_UInt8___proj__Mk__item__v(uint8_t projectee); extern krml_checked_int_t FStar_UInt8_v(uint8_t x); extern uint8_t FStar_UInt8_uint_to_t(krml_checked_int_t x); extern uint8_t FStar_UInt8_zero; extern uint8_t FStar_UInt8_one; extern uint8_t FStar_UInt8_minus(uint8_t a); extern uint32_t FStar_UInt8_n_minus_one; static KRML_NOINLINE uint8_t FStar_UInt8_eq_mask(uint8_t a, uint8_t b) { uint8_t x = (uint32_t)a ^ (uint32_t)b; uint8_t minus_x = (uint32_t)~x + 1U; uint8_t x_or_minus_x = (uint32_t)x | (uint32_t)minus_x; uint8_t xnx = (uint32_t)x_or_minus_x >> 7U; return (uint32_t)xnx - 1U; } static KRML_NOINLINE uint8_t FStar_UInt8_gte_mask(uint8_t a, uint8_t b) { uint8_t x = a; uint8_t y = b; uint8_t x_xor_y = (uint32_t)x ^ (uint32_t)y; uint8_t x_sub_y = (uint32_t)x - (uint32_t)y; uint8_t x_sub_y_xor_y = (uint32_t)x_sub_y ^ (uint32_t)y; uint8_t q = (uint32_t)x_xor_y | (uint32_t)x_sub_y_xor_y; uint8_t x_xor_q = (uint32_t)x ^ (uint32_t)q; uint8_t x_xor_q_ = (uint32_t)x_xor_q >> 7U; return (uint32_t)x_xor_q_ - 1U; } extern Prims_string FStar_UInt8_to_string(uint8_t uu___); extern Prims_string FStar_UInt8_to_string_hex(uint8_t uu___); extern Prims_string FStar_UInt8_to_string_hex_pad(uint8_t uu___); extern uint8_t FStar_UInt8_of_string(Prims_string uu___); typedef uint8_t FStar_UInt8_byte; #define __FStar_UInt_8_16_32_64_H_DEFINED #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/LowStar_Endianness.h000066400000000000000000000012471514673400200260340ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef __LowStar_Endianness_H #define __LowStar_Endianness_H #include #include #include "krml/internal/compat.h" #include "krml/lowstar_endianness.h" #include "krml/internal/types.h" #include "krml/internal/target.h" static inline void store128_le(uint8_t *x0, FStar_UInt128_uint128 x1); static inline FStar_UInt128_uint128 load128_le(uint8_t *x0); static inline void store128_be(uint8_t *x0, FStar_UInt128_uint128 x1); static inline FStar_UInt128_uint128 load128_be(uint8_t *x0); #define __LowStar_Endianness_H_DEFINED #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/Makefile.basic000066400000000000000000000026061514673400200246410ustar00rootroot00000000000000# A basic Makefile that KaRaMeL copies in the output directory; this is not # guaranteed to work and will only work well for very simple projects. This # Makefile uses: # - the custom C files passed to your krml invocation # - the custom C flags passed to your krml invocation # - the -o option passed to your krml invocation include Makefile.include ifeq (,$(KRML_HOME)) $(error please define KRML_HOME to point to the root of your KaRaMeL git checkout) endif CFLAGS += -I. -I $(KRML_HOME)/include -I $(KRML_HOME)/krmllib/dist/minimal CFLAGS += -Wall -Wextra -Werror -std=c11 \ -Wno-unknown-warning-option \ -Wno-infinite-recursion \ -g -fwrapv -D_BSD_SOURCE -D_DEFAULT_SOURCE ifeq ($(OS),Windows_NT) CFLAGS += -D__USE_MINGW_ANSI_STDIO else CFLAGS += -fPIC endif CFLAGS += $(USER_CFLAGS) SOURCES += $(ALL_C_FILES) $(USER_C_FILES) ifneq (,$(BLACKLIST)) SOURCES := $(filter-out $(BLACKLIST),$(SOURCES)) endif OBJS += $(patsubst %.c,%.o,$(SOURCES)) all: $(USER_TARGET) $(USER_TARGET): $(OBJS) AR ?= ar %.a: $(AR) cr $@ $^ %.exe: $(CC) $(CFLAGS) -o $@ $^ $(KRML_HOME)/krmllib/dist/generic/libkrmllib.a %.so: $(CC) $(CFLAGS) -shared -o $@ $^ %.d: %.c @set -e; rm -f $@; \ $(CC) -MM -MG $(CFLAGS) $< > $@.$$$$; \ sed 's,\($(notdir $*)\)\.o[ :]*,$(dir $@)\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ include $(patsubst %.c,%.d,$(SOURCES)) clean: rm -rf *.o *.d $(USER_TARGET) liboprf-0.9.4/src/noise_xk/include/karmel/minimal/Makefile.include000066400000000000000000000002311514673400200251730ustar00rootroot00000000000000USER_TARGET=libkrmllib.a USER_CFLAGS= USER_C_FILES=fstar_uint128.c ALL_C_FILES= ALL_H_FILES=FStar_UInt128.h FStar_UInt_8_16_32_64.h LowStar_Endianness.h liboprf-0.9.4/src/noise_xk/include/karmel/minimal/fstar_uint128_gcc64.h000066400000000000000000000111161514673400200256650ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ /******************************************************************************/ /* Machine integers (128-bit arithmetic) */ /******************************************************************************/ /* This header contains two things. * * First, an implementation of 128-bit arithmetic suitable for 64-bit GCC and * Clang, i.e. all the operations from FStar.UInt128. * * Second, 128-bit operations from C.Endianness (or LowStar.Endianness), * suitable for any compiler and platform (via a series of ifdefs). This second * part is unfortunate, and should be fixed by moving {load,store}128_{be,le} to * FStar.UInt128 to avoid a maze of preprocessor guards and hand-written code. * */ /* This file is used for both the minimal and generic krmllib distributions. As * such, it assumes that the machine integers have been bundled the exact same * way in both cases. */ #ifndef FSTAR_UINT128_GCC64 #define FSTAR_UINT128_GCC64 #include "FStar_UInt128.h" #include "FStar_UInt_8_16_32_64.h" #include "LowStar_Endianness.h" /* GCC + using native unsigned __int128 support */ inline static uint128_t load128_le(uint8_t *b) { uint128_t l = (uint128_t)load64_le(b); uint128_t h = (uint128_t)load64_le(b + 8); return (h << 64 | l); } inline static void store128_le(uint8_t *b, uint128_t n) { store64_le(b, (uint64_t)n); store64_le(b + 8, (uint64_t)(n >> 64)); } inline static uint128_t load128_be(uint8_t *b) { uint128_t h = (uint128_t)load64_be(b); uint128_t l = (uint128_t)load64_be(b + 8); return (h << 64 | l); } inline static void store128_be(uint8_t *b, uint128_t n) { store64_be(b, (uint64_t)(n >> 64)); store64_be(b + 8, (uint64_t)n); } inline static uint128_t FStar_UInt128_add(uint128_t x, uint128_t y) { return x + y; } inline static uint128_t FStar_UInt128_mul(uint128_t x, uint128_t y) { return x * y; } inline static uint128_t FStar_UInt128_add_mod(uint128_t x, uint128_t y) { return x + y; } inline static uint128_t FStar_UInt128_sub(uint128_t x, uint128_t y) { return x - y; } inline static uint128_t FStar_UInt128_sub_mod(uint128_t x, uint128_t y) { return x - y; } inline static uint128_t FStar_UInt128_logand(uint128_t x, uint128_t y) { return x & y; } inline static uint128_t FStar_UInt128_logor(uint128_t x, uint128_t y) { return x | y; } inline static uint128_t FStar_UInt128_logxor(uint128_t x, uint128_t y) { return x ^ y; } inline static uint128_t FStar_UInt128_lognot(uint128_t x) { return ~x; } inline static uint128_t FStar_UInt128_shift_left(uint128_t x, uint32_t y) { return x << y; } inline static uint128_t FStar_UInt128_shift_right(uint128_t x, uint32_t y) { return x >> y; } inline static uint128_t FStar_UInt128_uint64_to_uint128(uint64_t x) { return (uint128_t)x; } inline static uint64_t FStar_UInt128_uint128_to_uint64(uint128_t x) { return (uint64_t)x; } inline static uint128_t FStar_UInt128_mul_wide(uint64_t x, uint64_t y) { return ((uint128_t) x) * y; } inline static uint128_t FStar_UInt128_eq_mask(uint128_t x, uint128_t y) { uint64_t mask = FStar_UInt64_eq_mask((uint64_t)(x >> 64), (uint64_t)(y >> 64)) & FStar_UInt64_eq_mask((uint64_t)x, (uint64_t)y); return ((uint128_t)mask) << 64 | mask; } inline static uint128_t FStar_UInt128_gte_mask(uint128_t x, uint128_t y) { uint64_t mask = (FStar_UInt64_gte_mask(x >> 64, y >> 64) & ~(FStar_UInt64_eq_mask(x >> 64, y >> 64))) | (FStar_UInt64_eq_mask(x >> 64, y >> 64) & FStar_UInt64_gte_mask((uint64_t)x, (uint64_t)y)); return ((uint128_t)mask) << 64 | mask; } inline static uint64_t FStar_UInt128___proj__Mkuint128__item__low(uint128_t x) { return (uint64_t) x; } inline static uint64_t FStar_UInt128___proj__Mkuint128__item__high(uint128_t x) { return (uint64_t) (x >> 64); } inline static uint128_t FStar_UInt128_add_underspec(uint128_t x, uint128_t y) { return x + y; } inline static uint128_t FStar_UInt128_sub_underspec(uint128_t x, uint128_t y) { return x - y; } inline static bool FStar_UInt128_eq(uint128_t x, uint128_t y) { return x == y; } inline static bool FStar_UInt128_gt(uint128_t x, uint128_t y) { return x > y; } inline static bool FStar_UInt128_lt(uint128_t x, uint128_t y) { return x < y; } inline static bool FStar_UInt128_gte(uint128_t x, uint128_t y) { return x >= y; } inline static bool FStar_UInt128_lte(uint128_t x, uint128_t y) { return x <= y; } inline static uint128_t FStar_UInt128_mul32(uint64_t x, uint32_t y) { return (uint128_t) x * (uint128_t) y; } #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/fstar_uint128_msvc.h000066400000000000000000000362401514673400200257340ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ /* This file was generated by KaRaMeL * then hand-edited to use MSVC intrinsics KaRaMeL invocation: * C:\users\barrybo\mitls2c\karamel\_build\src\Karamel.native -minimal -fnouint128 C:/users/barrybo/mitls2c/FStar/ulib/FStar.UInt128.fst -tmpdir ../secure_api/out/runtime_switch/uint128 -skip-compilation -add-include "krmllib0.h" -drop FStar.Int.Cast.Full -bundle FStar.UInt128=FStar.*,Prims * F* version: 15104ff8 * KaRaMeL version: 318b7fa8 */ #ifndef FSTAR_UINT128_MSVC #define FSTAR_UINT128_MSVC #include "krml/internal/types.h" #include "FStar_UInt128.h" #include "FStar_UInt_8_16_32_64.h" #ifndef _MSC_VER # error This file only works with the MSVC compiler #endif /* JP: need to rip out HAS_OPTIMIZED since the header guards in types.h are now * done properly and only include this file when we know for sure we are on * 64-bit MSVC. */ #if defined(_M_X64) && !defined(KRML_VERIFIED_UINT128) #define HAS_OPTIMIZED 1 #else #define HAS_OPTIMIZED 0 #endif // Define .low and .high in terms of the __m128i fields, to reduce // the amount of churn in this file. #if HAS_OPTIMIZED #include #include #define low m128i_u64[0] #define high m128i_u64[1] #endif inline static FStar_UInt128_uint128 load128_le(uint8_t *b) { #if HAS_OPTIMIZED return _mm_loadu_si128((__m128i *)b); #else FStar_UInt128_uint128 lit; lit.low = load64_le(b); lit.high = load64_le(b + 8); return lit; #endif } inline static void store128_le(uint8_t *b, FStar_UInt128_uint128 n) { store64_le(b, n.low); store64_le(b + 8, n.high); } inline static FStar_UInt128_uint128 load128_be(uint8_t *b) { uint64_t l = load64_be(b + 8); uint64_t h = load64_be(b); #if HAS_OPTIMIZED return _mm_set_epi64x(h, l); #else FStar_UInt128_uint128 lit; lit.low = l; lit.high = h; return lit; #endif } inline static void store128_be(uint8_t *b, uint128_t n) { store64_be(b, n.high); store64_be(b + 8, n.low); } inline static uint64_t FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b) { return (a ^ (a ^ b | a - b ^ b)) >> (uint32_t)63U; } inline static uint64_t FStar_UInt128_carry(uint64_t a, uint64_t b) { return FStar_UInt128_constant_time_carry(a, b); } inline static FStar_UInt128_uint128 FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED uint64_t l, h; unsigned char carry = _addcarry_u64(0, a.low, b.low, &l); // low/CF = a.low+b.low+0 _addcarry_u64(carry, a.high, b.high, &h); // high = a.high+b.high+CF return _mm_set_epi64x(h, l); #else FStar_UInt128_uint128 lit; lit.low = a.low + b.low; lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return FStar_UInt128_add(a, b); #else FStar_UInt128_uint128 lit; lit.low = a.low + b.low; lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low; return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return FStar_UInt128_add(a, b); #else FStar_UInt128_uint128 lit; lit.low = a.low + b.low; lit.high = a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low); return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED uint64_t l, h; unsigned char borrow = _subborrow_u64(0, a.low, b.low, &l); _subborrow_u64(borrow, a.high, b.high, &h); return _mm_set_epi64x(h, l); #else FStar_UInt128_uint128 lit; lit.low = a.low - b.low; lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return FStar_UInt128_sub(a, b); #else FStar_UInt128_uint128 lit; lit.low = a.low - b.low; lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_sub_mod_impl(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { FStar_UInt128_uint128 lit; lit.low = a.low - b.low; lit.high = a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low); return lit; } inline static FStar_UInt128_uint128 FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return FStar_UInt128_sub(a, b); #else return FStar_UInt128_sub_mod_impl(a, b); #endif } inline static FStar_UInt128_uint128 FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return _mm_and_si128(a, b); #else FStar_UInt128_uint128 lit; lit.low = a.low & b.low; lit.high = a.high & b.high; return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return _mm_xor_si128(a, b); #else FStar_UInt128_uint128 lit; lit.low = a.low ^ b.low; lit.high = a.high ^ b.high; return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED return _mm_or_si128(a, b); #else FStar_UInt128_uint128 lit; lit.low = a.low | b.low; lit.high = a.high | b.high; return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a) { #if HAS_OPTIMIZED return _mm_andnot_si128(a, a); #else FStar_UInt128_uint128 lit; lit.low = ~a.low; lit.high = ~a.high; return lit; #endif } static const uint32_t FStar_UInt128_u32_64 = (uint32_t)64U; inline static uint64_t FStar_UInt128_add_u64_shift_left(uint64_t hi, uint64_t lo, uint32_t s) { return (hi << s) + (lo >> FStar_UInt128_u32_64 - s); } inline static uint64_t FStar_UInt128_add_u64_shift_left_respec(uint64_t hi, uint64_t lo, uint32_t s) { return FStar_UInt128_add_u64_shift_left(hi, lo, s); } inline static FStar_UInt128_uint128 FStar_UInt128_shift_left_small(FStar_UInt128_uint128 a, uint32_t s) { if (s == (uint32_t)0U) return a; else { FStar_UInt128_uint128 lit; lit.low = a.low << s; lit.high = FStar_UInt128_add_u64_shift_left_respec(a.high, a.low, s); return lit; } } inline static FStar_UInt128_uint128 FStar_UInt128_shift_left_large(FStar_UInt128_uint128 a, uint32_t s) { FStar_UInt128_uint128 lit; lit.low = (uint64_t)0U; lit.high = a.low << s - FStar_UInt128_u32_64; return lit; } inline static FStar_UInt128_uint128 FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s) { #if HAS_OPTIMIZED if (s == 0) { return a; } else if (s < FStar_UInt128_u32_64) { uint64_t l = a.low << s; uint64_t h = __shiftleft128(a.low, a.high, (unsigned char)s); return _mm_set_epi64x(h, l); } else { return _mm_set_epi64x(a.low << (s - FStar_UInt128_u32_64), 0); } #else if (s < FStar_UInt128_u32_64) return FStar_UInt128_shift_left_small(a, s); else return FStar_UInt128_shift_left_large(a, s); #endif } inline static uint64_t FStar_UInt128_add_u64_shift_right(uint64_t hi, uint64_t lo, uint32_t s) { return (lo >> s) + (hi << FStar_UInt128_u32_64 - s); } inline static uint64_t FStar_UInt128_add_u64_shift_right_respec(uint64_t hi, uint64_t lo, uint32_t s) { return FStar_UInt128_add_u64_shift_right(hi, lo, s); } inline static FStar_UInt128_uint128 FStar_UInt128_shift_right_small(FStar_UInt128_uint128 a, uint32_t s) { if (s == (uint32_t)0U) return a; else { FStar_UInt128_uint128 lit; lit.low = FStar_UInt128_add_u64_shift_right_respec(a.high, a.low, s); lit.high = a.high >> s; return lit; } } inline static FStar_UInt128_uint128 FStar_UInt128_shift_right_large(FStar_UInt128_uint128 a, uint32_t s) { FStar_UInt128_uint128 lit; lit.low = a.high >> s - FStar_UInt128_u32_64; lit.high = (uint64_t)0U; return lit; } inline static FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s) { #if HAS_OPTIMIZED if (s == 0) { return a; } else if (s < FStar_UInt128_u32_64) { uint64_t l = __shiftright128(a.low, a.high, (unsigned char)s); uint64_t h = a.high >> s; return _mm_set_epi64x(h, l); } else { return _mm_set_epi64x(0, a.high >> (s - FStar_UInt128_u32_64)); } #else if (s < FStar_UInt128_u32_64) return FStar_UInt128_shift_right_small(a, s); else return FStar_UInt128_shift_right_large(a, s); #endif } inline static bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.low == b.low && a.high == b.high; } inline static bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high > b.high || a.high == b.high && a.low > b.low; } inline static bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high < b.high || a.high == b.high && a.low < b.low; } inline static bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high > b.high || a.high == b.high && a.low >= b.low; } inline static bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { return a.high < b.high || a.high == b.high && a.low <= b.low; } inline static FStar_UInt128_uint128 FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED // PCMPW to produce 4 32-bit values, all either 0x0 or 0xffffffff __m128i r32 = _mm_cmpeq_epi32(a, b); // Shuffle 3,2,1,0 into 2,3,0,1 (swapping dwords inside each half) __m128i s32 = _mm_shuffle_epi32(r32, _MM_SHUFFLE(2, 3, 0, 1)); // Bitwise and to compute (3&2),(2&3),(1&0),(0&1) __m128i ret64 = _mm_and_si128(r32, s32); // Swap the two 64-bit values to form s64 __m128i s64 = _mm_shuffle_epi32(ret64, _MM_SHUFFLE(1, 0, 3, 2)); // 3,2,1,0 -> 1,0,3,2 // And them together return _mm_and_si128(ret64, s64); #else FStar_UInt128_uint128 lit; lit.low = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); lit.high = FStar_UInt64_eq_mask(a.low, b.low) & FStar_UInt64_eq_mask(a.high, b.high); return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) { #if HAS_OPTIMIZED && 0 // ge - compare 3,2,1,0 for >= and generating 0 or 0xffffffff for each // eq - compare 3,2,1,0 for == and generating 0 or 0xffffffff for each // slot 0 = ge0 | (eq0 & ge1) | (eq0 & eq1 & ge2) | (eq0 & eq1 & eq2 & ge3) // then splat slot 0 to 3,2,1,0 __m128i gt = _mm_cmpgt_epi32(a, b); __m128i eq = _mm_cmpeq_epi32(a, b); __m128i ge = _mm_or_si128(gt, eq); __m128i ge0 = ge; __m128i eq0 = eq; __m128i ge1 = _mm_srli_si128(ge, 4); // shift ge from 3,2,1,0 to 0x0,3,2,1 __m128i t1 = _mm_and_si128(eq0, ge1); __m128i ret = _mm_or_si128(ge, t1); // ge0 | (eq0 & ge1) is now in 0 __m128i eq1 = _mm_srli_si128(eq, 4); // shift eq from 3,2,1,0 to 0x0,3,2,1 __m128i ge2 = _mm_srli_si128(ge1, 4); // shift original ge from 3,2,1,0 to 0x0,0x0,3,2 __m128i t2 = _mm_and_si128(eq0, _mm_and_si128(eq1, ge2)); // t2 = (eq0 & eq1 & ge2) ret = _mm_or_si128(ret, t2); __m128i eq2 = _mm_srli_si128(eq1, 4); // shift eq from 3,2,1,0 to 0x0,00,00,3 __m128i ge3 = _mm_srli_si128(ge2, 4); // shift original ge from 3,2,1,0 to 0x0,0x0,0x0,3 __m128i t3 = _mm_and_si128( eq0, _mm_and_si128( eq1, _mm_and_si128(eq2, ge3))); // t3 = (eq0 & eq1 & eq2 & ge3) ret = _mm_or_si128(ret, t3); return _mm_shuffle_epi32( ret, _MM_SHUFFLE(0, 0, 0, 0)); // the result is in 0. Shuffle into all dwords. #else FStar_UInt128_uint128 lit; lit.low = FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high) | FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low); lit.high = FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high) | FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low); return lit; #endif } inline static FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a) { #if HAS_OPTIMIZED return _mm_set_epi64x(0, a); #else FStar_UInt128_uint128 lit; lit.low = a; lit.high = (uint64_t)0U; return lit; #endif } inline static uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a) { return a.low; } inline static uint64_t FStar_UInt128_u64_mod_32(uint64_t a) { return a & (uint64_t)0xffffffffU; } static uint32_t FStar_UInt128_u32_32 = (uint32_t)32U; inline static uint64_t FStar_UInt128_u32_combine(uint64_t hi, uint64_t lo) { return lo + (hi << FStar_UInt128_u32_32); } inline static FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y) { #if HAS_OPTIMIZED uint64_t l, h; l = _umul128(x, (uint64_t)y, &h); return _mm_set_epi64x(h, l); #else FStar_UInt128_uint128 lit; lit.low = FStar_UInt128_u32_combine( (x >> FStar_UInt128_u32_32) * (uint64_t)y + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32), FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * (uint64_t)y)); lit.high = (x >> FStar_UInt128_u32_32) * (uint64_t)y + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32) >> FStar_UInt128_u32_32; return lit; #endif } /* Note: static headers bring scope collision issues when they define types! * Because now client (karamel-generated) code will include this header and * there might be type collisions if the client code uses quadruples of uint64s. * So, we cannot use the karamel-generated name. */ typedef struct K_quad_s { uint64_t fst; uint64_t snd; uint64_t thd; uint64_t f3; } K_quad; inline static K_quad FStar_UInt128_mul_wide_impl_t_(uint64_t x, uint64_t y) { K_quad tmp; tmp.fst = FStar_UInt128_u64_mod_32(x); tmp.snd = FStar_UInt128_u64_mod_32( FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y)); tmp.thd = x >> FStar_UInt128_u32_32; tmp.f3 = (x >> FStar_UInt128_u32_32) * FStar_UInt128_u64_mod_32(y) + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32); return tmp; } static uint64_t FStar_UInt128_u32_combine_(uint64_t hi, uint64_t lo) { return lo + (hi << FStar_UInt128_u32_32); } inline static FStar_UInt128_uint128 FStar_UInt128_mul_wide_impl(uint64_t x, uint64_t y) { K_quad scrut = FStar_UInt128_mul_wide_impl_t_(x, y); uint64_t u1 = scrut.fst; uint64_t w3 = scrut.snd; uint64_t x_ = scrut.thd; uint64_t t_ = scrut.f3; FStar_UInt128_uint128 lit; lit.low = FStar_UInt128_u32_combine_( u1 * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32(t_), w3); lit.high = x_ * (y >> FStar_UInt128_u32_32) + (t_ >> FStar_UInt128_u32_32) + (u1 * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32(t_) >> FStar_UInt128_u32_32); return lit; } inline static FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y) { #if HAS_OPTIMIZED uint64_t l, h; l = _umul128(x, y, &h); return _mm_set_epi64x(h, l); #else return FStar_UInt128_mul_wide_impl(x, y); #endif } #undef low #undef high #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/fstar_uint128_struct_endianness.h000066400000000000000000000031611514673400200305130ustar00rootroot00000000000000/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. Licensed under the Apache 2.0 License. */ #ifndef FSTAR_UINT128_STRUCT_ENDIANNESS_H #define FSTAR_UINT128_STRUCT_ENDIANNESS_H /* Hand-written implementation of endianness-related uint128 functions * for the extracted uint128 implementation */ /* Access 64-bit fields within the int128. */ #define HIGH64_OF(x) ((x)->high) #define LOW64_OF(x) ((x)->low) /* A series of definitions written using pointers. */ inline static void load128_le_(uint8_t *b, uint128_t *r) { LOW64_OF(r) = load64_le(b); HIGH64_OF(r) = load64_le(b + 8); } inline static void store128_le_(uint8_t *b, uint128_t *n) { store64_le(b, LOW64_OF(n)); store64_le(b + 8, HIGH64_OF(n)); } inline static void load128_be_(uint8_t *b, uint128_t *r) { HIGH64_OF(r) = load64_be(b); LOW64_OF(r) = load64_be(b + 8); } inline static void store128_be_(uint8_t *b, uint128_t *n) { store64_be(b, HIGH64_OF(n)); store64_be(b + 8, LOW64_OF(n)); } #ifndef KRML_NOSTRUCT_PASSING inline static uint128_t load128_le(uint8_t *b) { uint128_t r; load128_le_(b, &r); return r; } inline static void store128_le(uint8_t *b, uint128_t n) { store128_le_(b, &n); } inline static uint128_t load128_be(uint8_t *b) { uint128_t r; load128_be_(b, &r); return r; } inline static void store128_be(uint8_t *b, uint128_t n) { store128_be_(b, &n); } #else /* !defined(KRML_STRUCT_PASSING) */ # define print128 print128_ # define load128_le load128_le_ # define store128_le store128_le_ # define load128_be load128_be_ # define store128_be store128_be_ #endif /* KRML_STRUCT_PASSING */ #endif liboprf-0.9.4/src/noise_xk/include/karmel/minimal/libkrmllib.def000066400000000000000000000003261514673400200247210ustar00rootroot00000000000000LIBRARY libkrmllib EXPORTS FStar_UInt64_eq_mask FStar_UInt64_gte_mask FStar_UInt32_eq_mask FStar_UInt32_gte_mask FStar_UInt16_eq_mask FStar_UInt16_gte_mask FStar_UInt8_eq_mask FStar_UInt8_gte_mask liboprf-0.9.4/src/noise_xk/include/noise_private.h000066400000000000000000000036441514673400200222430ustar00rootroot00000000000000#ifndef noise_xk_private_h #define noise_xk_private_h #include #include "XK.h" #undef Noise_XK_init_state_t_s typedef struct Noise_XK_init_state_t_s { Noise_XK_init_state_t_tags tag; union { struct { uint32_t step; uint8_t *cipher_key; uint8_t *chaining_key; uint8_t *h; uint8_t *spriv; uint8_t *spub; uint8_t *epriv; uint8_t *epub; uint8_t *rs; uint8_t *re; } case_IMS_Handshake; struct { uint8_t *h; bool recv_transport_message; uint8_t *send_key; uint64_t send_nonce; uint8_t *receive_key; uint64_t receive_nonce; } case_IMS_Transport; } val; } Noise_XK_init_state_t; #undef Noise_XK_resp_state_t_s typedef struct Noise_XK_resp_state_t_s { Noise_XK_init_state_t_tags tag; union { struct { uint32_t step; uint8_t *cipher_key; uint8_t *chaining_key; uint8_t *h; uint8_t *spriv; uint8_t *spub; uint8_t *epriv; uint8_t *epub; uint8_t *rs; uint8_t *re; } case_IMS_Handshake; struct { uint8_t *h; uint8_t *send_key; uint64_t send_nonce; uint8_t *receive_key; uint64_t receive_nonce; } case_IMS_Transport; } val; } Noise_XK_resp_state_t; #undef Noise_XK_session_t_s typedef struct Noise_XK_session_t_s { Noise_XK_session_t_tags tag; union { struct { Noise_XK_init_state_t state; uint32_t id; Noise_XK_noise_string *info; uint8_t *spriv; uint8_t *spub; uint32_t pid; Noise_XK_noise_string *pinfo; Noise_XK_device_t *dv; } case_DS_Initiator; struct { Noise_XK_resp_state_t state; uint32_t id; Noise_XK_noise_string *info; uint8_t *spriv; uint8_t *spub; uint32_t pid; Noise_XK_noise_string *pinfo; Noise_XK_device_t *dv; } case_DS_Responder; } val; } Noise_XK_session_t; #endif // noise_xk_private_h liboprf-0.9.4/src/noise_xk/makefile000066400000000000000000000047721514673400200173030ustar00rootroot00000000000000PREFIX?=/usr/local LIBS=-lsodium SOURCES=src/Noise_XK.c src/XK.c INCLUDES= -Iinclude -I include/karmel -I include/karmel/minimal CFLAGS ?= -Wall -Wextra -Werror -std=c11 -Wno-unused-variable \ -Wno-unknown-warning-option -Wno-unused-but-set-variable \ -Wno-unused-parameter -Wno-infinite-recursion -Wno-maybe-uninitialized \ -fwrapv -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM \ -O2 -fstack-protector-strong -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \ -fasynchronous-unwind-tables -fpic \ -Werror=format-security -Werror=implicit-function-declaration \ -ftrapv CC?=gcc SOEXT?=so STATICEXT?=a SOVER=0 UNAME := $(if $(UNAME),$(UNAME),$(shell uname -s)) ARCH := $(if $(ARCH),$(ARCH),$(shell uname -m)) ifeq ($(UNAME),Darwin) SOEXT=dylib SOFLAGS=-Wl,-install_name,$(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(SOEXT) else ifeq ($(UNAME),Linux) CFLAGS += -Wl,--error-unresolved-symbols -Wl,-z,defs -Wl,-z,relro -Wl,-z,noexecstack SOEXT=so SOFLAGS=-Wl,-soname,liboprf-noiseXK.$(SOEXT).$(SOVER) endif ifeq ($(ARCH),x86_64) CFLAGS+=-fcf-protection=full endif ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),armv7-a) else CFLAGS+=-fstack-clash-protection endif endif OBJS += $(patsubst %.c,%.o,$(SOURCES)) android: CFLAGS+=-I$(SODIUM) -I$(SODIUM)/sodium android: LDFLAGS+=-L. android: liboprf-noiseXK.$(SOEXT) all: liboprf-noiseXK.$(STATICEXT) liboprf-noiseXK.$(SOEXT) AR ?= ar %.$(STATICEXT): $(OBJS) $(AR) rcs $@ $^ %.$(SOEXT): $(OBJS) $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -shared $(SOFLAGS) $(INCLUDES) -o $@ $^ $(LDFLAGS) $(LIBS) clean: rm -rf *.so *.a src/*.o make -C example clean liboprf-noiseXK.$(SOEXT).$(SOVER): liboprf-noiseXK.$(SOEXT) ln -sf $^ $@ install: $(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(SOEXT) $(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(STATICEXT) $(DESTDIR)$(PREFIX)/include/oprf/noiseXK uninstall: $(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(SOEXT) $(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(STATICEXT) $(DESTDIR)$(PREFIX)/include/oprf/noiseXK rm -rf $^ $(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(SOEXT): liboprf-noiseXK.$(SOEXT) mkdir -p $(DESTDIR)$(PREFIX)/lib/ cp $< $@.$(SOVER) ln -sf $@.$(SOVER) $@ $(DESTDIR)$(PREFIX)/lib/liboprf-noiseXK.$(STATICEXT): liboprf-noiseXK.$(STATICEXT) mkdir -p $(DESTDIR)$(PREFIX)/lib/ cp $< $@ $(DESTDIR)$(PREFIX)/include/oprf/noiseXK: include cp -r $< $@ test: liboprf-noiseXK.$(SOEXT).$(SOVER) make -C example test %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $(INCLUDES) -c $< -o $@ liboprf-0.9.4/src/noise_xk/noise-star.patch000066400000000000000000000354411514673400200207050ustar00rootroot00000000000000diff --git a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.basic b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.basic index 1698d85..c4b7fef 100644 --- a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.basic +++ b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.basic @@ -29,6 +29,7 @@ OBJS += $(patsubst %.c,%.o,$(SOURCES)) all: $(USER_TARGET) $(USER_TARGET): $(OBJS) + cc $(CFLAGS) $^ xk-ex.c -o $@ $(LDFLAGS) AR ?= ar diff --git a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.include b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.include index ea66194..024d749 100644 --- a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.include +++ b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Makefile.include @@ -1,5 +1,8 @@ -USER_TARGET=libnoiseapi.a -USER_CFLAGS= +LDFLAGS=-lsodium +#LDFLAGS=-L$(HACL_HOME)/dist/gcc-compatible -levercrypt +USER_TARGET=libnoiseapi.a xk-ex +USER_CFLAGS= -DWITH_SODIUM +#USER_CFLAGS= USER_C_FILES= ALL_C_FILES=Noise_XK.c XK.c ALL_H_FILES=Hacl.h Noise_XK.h XK.h diff --git a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.c b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.c index 746f704..c953f01 100644 --- a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.c +++ b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.c @@ -1,6 +1,5 @@ /** This file was automatically generated */ - #include "Noise_XK.h" inline bool Noise_XK_lbytes_eq(uint32_t len, uint8_t *b1, uint8_t *b2) @@ -28,13 +27,22 @@ uint64_t Noise_XK_bytes_to_nonce(uint8_t *n8) Noise_XK_error_code Noise_XK_dh_secret_to_public(uint8_t *dest, uint8_t *priv) { +#ifdef WITH_SODIUM + crypto_scalarmult_base(dest, priv); +#else // WITH_SODIUM Hacl_Curve25519_64_secret_to_public(dest, priv); +#endif // WITH_SODIUM return Noise_XK_CSuccess; } Noise_XK_error_code Noise_XK_dh(uint8_t *dest, uint8_t *priv, uint8_t *pub) { +#ifdef WITH_SODIUM + bool b = crypto_scalarmult(dest, priv, pub) == 0; +#else // WITH_SODIUM bool b = Hacl_Curve25519_64_ecdh(dest, priv, pub); +#endif // WITH_SODIUM + if (b) return Noise_XK_CSuccess; else @@ -52,6 +60,9 @@ Noise_XK_aead_encrypt( uint8_t *cipher ) { +#ifdef WITH_SODIUM + crypto_aead_chacha20poly1305_encrypt(cipher, NULL, plain, plen, aad, aad_len, NULL, (uint8_t*) &nonce, key); +#else uint8_t n12[12U] = { 0U }; uint8_t *nonce12_end = n12 + (uint32_t)4U; store64_le(nonce12_end, nonce); @@ -59,6 +70,7 @@ Noise_XK_aead_encrypt( uint8_t *tag = cipher + plen; Hacl_Chacha20Poly1305_32_aead_encrypt(key, n12, aad_len, aad, plen, plain, output, tag); Lib_Memzero0_memzero(n12, (uint32_t)12U * sizeof (n12[0U])); +#endif // WITH_SODIUM } Noise_XK_error_code @@ -72,6 +84,18 @@ Noise_XK_aead_decrypt( uint8_t *cipher ) { +#ifdef WITH_SODIUM + uint32_t + r = crypto_aead_chacha20poly1305_decrypt(plain, // plaintext + NULL, // *plen + NULL, // nsec + cipher, + plen + crypto_aead_chacha20poly1305_ABYTES, + aad, + aad_len, + (uint8_t*) &nonce, + key); +#else // WITH_SODIUM uint8_t n12[12U] = { 0U }; uint8_t *nonce12_end = n12 + (uint32_t)4U; store64_le(nonce12_end, nonce); @@ -80,6 +104,7 @@ Noise_XK_aead_decrypt( uint32_t r = Hacl_Chacha20Poly1305_32_aead_decrypt(key, n12, aad_len, aad, plen, plain, output, tag); Lib_Memzero0_memzero(n12, (uint32_t)12U * sizeof (n12[0U])); +#endif // WITH_SODIUM if (r == (uint32_t)0U) return Noise_XK_CSuccess; else @@ -88,11 +113,22 @@ Noise_XK_aead_decrypt( void Noise_XK_hash(uint8_t *output, uint32_t inlen, uint8_t *input) { +#ifdef WITH_SODIUM + crypto_generichash(output, (uint32_t)64U, input, inlen, NULL, 0); +#else // WITH_SODIUM Hacl_Blake2b_32_blake2b((uint32_t)64U, output, inlen, input, (uint32_t)0U, NULL); +#endif // WITH_SODIUM } void Noise_XK_mix_hash(uint8_t *hash1, uint32_t inlen, uint8_t *input) { +#ifdef WITH_SODIUM + crypto_generichash_state state; + crypto_generichash_init(&state, NULL, 0, 64); + crypto_generichash_update(&state, hash1, 64); + crypto_generichash_update(&state, input, inlen); + crypto_generichash_final(&state, hash1, 64); +#else // WITH_SODIUM KRML_CHECK_SIZE(sizeof (uint8_t), Hacl_Streaming_Blake2_blocks_state_len(Spec_Blake2_Blake2B, Hacl_Impl_Blake2_Core_M32)); uint8_t @@ -121,12 +157,17 @@ void Noise_XK_mix_hash(uint8_t *hash1, uint32_t inlen, uint8_t *input) Hacl_Streaming_Blake2_blake2b_32_no_key_update(s0, hash1, (uint32_t)64U); Hacl_Streaming_Blake2_blake2b_32_no_key_update(s0, input, inlen); Hacl_Streaming_Blake2_blake2b_32_no_key_finish(s0, hash1); +#endif // WITH_SODIUM } void Noise_XK_hmac(uint8_t *output, uint32_t keylen, uint8_t *key, uint32_t datalen, uint8_t *data) { +#ifdef WITH_SODIUM + crypto_generichash(output, 64, data, datalen, key, keylen); +#else // WITH_SODIUM Hacl_HMAC_compute_blake2b_32(output, key, keylen, data, datalen); +#endif // WITH_SODIUM } void @@ -162,8 +203,13 @@ Noise_XK_kdf( } } } +#ifdef WITH_SODIUM + sodium_memzero(output, (uint32_t)65U * sizeof (output[0U])); + sodium_memzero(secret, (uint32_t)64U * sizeof (secret[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(output, (uint32_t)65U * sizeof (output[0U])); Lib_Memzero0_memzero(secret, (uint32_t)64U * sizeof (secret[0U])); +#endif // WITH_SODIUM } void Noise_XK_mix_psk(uint8_t *psk, uint8_t *st_cs_k, uint8_t *st_ck, uint8_t *st_h) @@ -172,9 +218,17 @@ void Noise_XK_mix_psk(uint8_t *psk, uint8_t *st_cs_k, uint8_t *st_ck, uint8_t *s uint8_t temp_k[64U] = { 0U }; Noise_XK_kdf(st_ck, (uint32_t)32U, psk, st_ck, temp_hash, temp_k); memcpy(st_cs_k, temp_k, (uint32_t)32U * sizeof (uint8_t)); +#ifdef WITH_SODIUM + sodium_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); +#endif // WITH_SODIUM Noise_XK_mix_hash(st_h, (uint32_t)64U, temp_hash); +#ifdef WITH_SODIUM + sodium_memzero(temp_hash, (uint32_t)64U * sizeof (temp_hash[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_hash, (uint32_t)64U * sizeof (temp_hash[0U])); +#endif // WITH_SODIUM } void @@ -224,13 +278,21 @@ Noise_XK_mix_dh(uint8_t *sec, uint8_t *pub, uint8_t *cipher_key, uint8_t *ck, ui uint8_t temp_k[64U] = { 0U }; Noise_XK_kdf(ck, (uint32_t)32U, dh_key, ck, temp_k, NULL); memcpy(cipher_key, temp_k, (uint32_t)32U * sizeof (uint8_t)); +#ifdef WITH_SODIUM + sodium_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); +#endif // WITH_SODIUM r2 = Noise_XK_CSuccess; } else r2 = r1; Noise_XK_error_code r = r2; +#ifdef WITH_SODIUM + sodium_memzero(dh_key, (uint32_t)32U * sizeof (dh_key[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(dh_key, (uint32_t)32U * sizeof (dh_key[0U])); +#endif // WITH_SODIUM return r; } diff --git a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.h b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.h index b92393c..f99a332 100644 --- a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.h +++ b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/Noise_XK.h @@ -8,7 +8,11 @@ #include "krml/internal/types.h" +#ifdef WITH_SODIUM +#include +#else // WITH_SODIUM #include "Hacl.h" +#endif // WITH_SODIUM #define Noise_XK_CSuccess 0 #define Noise_XK_CIncorrect_transition 1 diff --git a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/XK.c b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/XK.c index bd4c689..63f42c7 100644 --- a/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/XK.c +++ b/noise-all/api-XK/XK_25519_ChaChaPoly_BLAKE2b/XK.c @@ -754,7 +754,11 @@ void Noise_XK_serialize_device_secret(uint32_t *outlen, uint8_t **out, Noise_XK_ uint8_t *name_raw = scrut.snd; uint8_t *n8 = outb; uint8_t *c = outb + (uint32_t)8U; +#ifdef WITH_SODIUM + randombytes_buf(n8, (uint32_t)8U); +#else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(n8, (uint32_t)8U); +#endif // WITH_SODIUM uint64_t n = Noise_XK_bytes_to_nonce(n8); Noise_XK_aead_encrypt(dv.dv_sk, n, name_raw_len, name_raw, (uint32_t)32U, dv.dv_spriv, c); out[0U] = outb; @@ -995,7 +999,11 @@ void Noise_XK_device_remove_peer(Noise_XK_device_t *dvp, uint32_t pid) if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(p.p_info); +#ifdef WITH_SODIUM + sodium_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); +#endif // WITH_SODIUM KRML_HOST_FREE(p.p_s); KRML_HOST_FREE(c1.data); KRML_HOST_FREE(c01.next); @@ -1011,7 +1019,11 @@ void Noise_XK_device_remove_peer(Noise_XK_device_t *dvp, uint32_t pid) if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(p.p_info); +#ifdef WITH_SODIUM + sodium_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); +#endif // WITH_SODIUM KRML_HOST_FREE(p.p_s); KRML_HOST_FREE(elem1); } @@ -1077,7 +1089,11 @@ Noise_XK_serialize_peer_secret( uint8_t *name_raw = scrut.snd; uint8_t *n8 = outb; uint8_t *c = outb + (uint32_t)8U; +#ifdef WITH_SODIUM + randombytes_buf(n8, (uint32_t)8U); +#else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(n8, (uint32_t)8U); +#endif // WITH_SODIUM uint64_t n = Noise_XK_bytes_to_nonce(n8); Noise_XK_aead_encrypt(dv.dv_sk, n, name_raw_len, name_raw, (uint32_t)32U, concat_keys, c); out[0U] = outb; @@ -1634,7 +1650,11 @@ Noise_XK_session_t *Noise_XK_session_create_initiator(Noise_XK_device_t *dvp, ui { uint8_t epriv[32U] = { 0U }; uint8_t epub[32U] = { 0U }; +#ifdef WITH_SODIUM + randombytes_buf(epriv, (uint32_t)32U); +#else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(epriv, (uint32_t)32U); +#endif // WITH_SODIUM Noise_XK_error_code res0 = Noise_XK_dh_secret_to_public(epub, epriv); Noise_XK_session_t *res; switch (res0) @@ -1929,8 +1949,13 @@ Noise_XK_session_t *Noise_XK_session_create_initiator(Noise_XK_device_t *dvp, ui res = NULL; } } +#ifdef WITH_SODIUM + sodium_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); + sodium_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); Lib_Memzero0_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); +#endif // WITH_SODIUM Noise_XK_session_t *res1 = res; return res1; } @@ -1944,7 +1969,11 @@ Noise_XK_session_t *Noise_XK_session_create_responder(Noise_XK_device_t *dvp) { uint8_t epriv[32U] = { 0U }; uint8_t epub[32U] = { 0U }; +#ifdef WITH_SODIUM + randombytes_buf(epriv, (uint32_t)32U); +#else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(epriv, (uint32_t)32U); +#endif // WITH_SODIUM Noise_XK_error_code res0 = Noise_XK_dh_secret_to_public(epub, epriv); Noise_XK_session_t *res; switch (res0) @@ -2134,8 +2163,13 @@ Noise_XK_session_t *Noise_XK_session_create_responder(Noise_XK_device_t *dvp) res = NULL; } } +#ifdef WITH_SODIUM + sodium_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); + sodium_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); +#else //WITH_SODIUM Lib_Memzero0_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); Lib_Memzero0_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); +#endif //WITH_SODIUM Noise_XK_session_t *res1 = res; return res1; } @@ -2520,8 +2554,13 @@ state_handshake_write( Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); +#ifdef WITH_SODIUM + sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); + sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); @@ -2721,8 +2760,13 @@ state_handshake_write( Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); +#ifdef WITH_SODIUM + sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); + sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); @@ -3190,8 +3234,13 @@ state_handshake_read( Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); +#ifdef WITH_SODIUM + sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); + sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); @@ -3647,8 +3696,13 @@ state_handshake_read( Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); +#ifdef WITH_SODIUM + sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); + sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); +#endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); liboprf-0.9.4/src/noise_xk/src/000077500000000000000000000000001514673400200163605ustar00rootroot00000000000000liboprf-0.9.4/src/noise_xk/src/Noise_XK.c000066400000000000000000000251221514673400200202050ustar00rootroot00000000000000/** This file was automatically generated */ #include "Noise_XK.h" inline bool Noise_XK_lbytes_eq(uint32_t len, uint8_t *b1, uint8_t *b2) { uint8_t accp = (uint8_t)0U; for (uint32_t i = (uint32_t)0U; i < len; i++) { uint8_t x1 = b1[i]; uint8_t x2 = b2[i]; uint8_t diff = x1 ^ x2; uint8_t acc = accp; uint8_t acc_ = diff | acc; accp = acc_; } uint8_t r = accp; return r == (uint8_t)0U; } uint64_t Noise_XK_bytes_to_nonce(uint8_t *n8) { uint64_t u = load64_le(n8); uint64_t nonce = u; return nonce; } Noise_XK_error_code Noise_XK_dh_secret_to_public(uint8_t *dest, uint8_t *priv) { #ifndef WITH_HACL crypto_scalarmult_base(dest, priv); #else // WITH_SODIUM Hacl_Curve25519_64_secret_to_public(dest, priv); #endif // WITH_SODIUM return Noise_XK_CSuccess; } Noise_XK_error_code Noise_XK_dh(uint8_t *dest, uint8_t *priv, uint8_t *pub) { #ifndef WITH_HACL bool b = crypto_scalarmult(dest, priv, pub) == 0; #else // WITH_SODIUM bool b = Hacl_Curve25519_64_ecdh(dest, priv, pub); #endif // WITH_SODIUM if (b) return Noise_XK_CSuccess; else return Noise_XK_CDH_error; } void Noise_XK_aead_encrypt( uint8_t *key, uint64_t nonce, uint32_t aad_len, uint8_t *aad, uint32_t plen, uint8_t *plain, uint8_t *cipher ) { uint8_t n12[12U] = { 0U }; uint8_t *nonce12_end = n12 + (uint32_t)4U; store64_le(nonce12_end, nonce); #ifndef WITH_HACL crypto_aead_chacha20poly1305_ietf_encrypt(cipher, NULL, plain, plen, aad, aad_len, NULL, n12, key); sodium_memzero(n12, (uint32_t)12U * sizeof (n12[0U])); #else uint8_t *output = cipher; uint8_t *tag = cipher + plen; Hacl_Chacha20Poly1305_32_aead_encrypt(key, n12, aad_len, aad, plen, plain, output, tag); Lib_Memzero0_memzero(n12, (uint32_t)12U * sizeof (n12[0U])); #endif // WITH_SODIUM } Noise_XK_error_code Noise_XK_aead_decrypt( uint8_t *key, uint64_t nonce, uint32_t aad_len, uint8_t *aad, uint32_t plen, uint8_t *plain, uint8_t *cipher ) { uint8_t n12[12U] = { 0U }; uint8_t *nonce12_end = n12 + (uint32_t)4U; store64_le(nonce12_end, nonce); #ifndef WITH_HACL uint32_t r = crypto_aead_chacha20poly1305_ietf_decrypt(plain, NULL, // *plen NULL, // nsec cipher, plen + crypto_aead_chacha20poly1305_ABYTES, aad, aad_len, n12, key); sodium_memzero(n12, (uint32_t)12U * sizeof (n12[0U])); #else // WITH_SODIUM uint8_t *output = cipher; uint8_t *tag = cipher + plen; uint32_t r = Hacl_Chacha20Poly1305_32_aead_decrypt(key, n12, aad_len, aad, plen, plain, output, tag); Lib_Memzero0_memzero(n12, (uint32_t)12U * sizeof (n12[0U])); #endif // WITH_SODIUM if (r == (uint32_t)0U) return Noise_XK_CSuccess; else return Noise_XK_CDecrypt_error; } void Noise_XK_hash(uint8_t *output, uint32_t inlen, uint8_t *input) { #ifndef WITH_HACL crypto_generichash(output, (uint32_t)64U, input, inlen, NULL, 0); #else // WITH_SODIUM Hacl_Blake2b_32_blake2b((uint32_t)64U, output, inlen, input, (uint32_t)0U, NULL); #endif // WITH_SODIUM } void Noise_XK_mix_hash(uint8_t *hash1, uint32_t inlen, uint8_t *input) { #ifndef WITH_HACL crypto_generichash_state state; crypto_generichash_init(&state, NULL, 0, 64); crypto_generichash_update(&state, hash1, 64); crypto_generichash_update(&state, input, inlen); crypto_generichash_final(&state, hash1, 64); #else // WITH_SODIUM KRML_CHECK_SIZE(sizeof (uint8_t), Hacl_Streaming_Blake2_blocks_state_len(Spec_Blake2_Blake2B, Hacl_Impl_Blake2_Core_M32)); uint8_t buf[Hacl_Streaming_Blake2_blocks_state_len(Spec_Blake2_Blake2B, Hacl_Impl_Blake2_Core_M32)]; memset(buf, 0U, Hacl_Streaming_Blake2_blocks_state_len(Spec_Blake2_Blake2B, Hacl_Impl_Blake2_Core_M32) * sizeof (uint8_t)); KRML_CHECK_SIZE(sizeof (uint64_t), (uint32_t)4U * (uint32_t)4U); uint64_t wv[(uint32_t)4U * (uint32_t)4U]; memset(wv, 0U, (uint32_t)4U * (uint32_t)4U * sizeof (uint64_t)); KRML_CHECK_SIZE(sizeof (uint64_t), (uint32_t)4U * (uint32_t)4U); uint64_t b[(uint32_t)4U * (uint32_t)4U]; memset(b, 0U, (uint32_t)4U * (uint32_t)4U * sizeof (uint64_t)); Hacl_Streaming_Blake2_blake2b_32_block_state block_state = { .fst = wv, .snd = b }; Hacl_Streaming_Blake2_blake2b_32_state s = { .block_state = block_state, .buf = buf, .total_len = (uint64_t)0U }; Hacl_Streaming_Blake2_blake2b_32_state p = s; Hacl_Blake2b_32_blake2b_init(block_state.fst, block_state.snd, (uint32_t)0U, NULL, (uint32_t)64U); Hacl_Streaming_Blake2_blake2b_32_state *s0 = &p; Hacl_Streaming_Blake2_blake2b_32_no_key_update(s0, hash1, (uint32_t)64U); Hacl_Streaming_Blake2_blake2b_32_no_key_update(s0, input, inlen); Hacl_Streaming_Blake2_blake2b_32_no_key_finish(s0, hash1); #endif // WITH_SODIUM } #ifndef WITH_HACL #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5C static void Noise_XK_hashstate_xor_key(uint8_t *key, size_t key_len, uint8_t value) { while (key_len > 0) { *key++ ^= value; --key_len; } } #endif // WITH_SODIUM void Noise_XK_hmac(uint8_t *output, uint32_t keylen, uint8_t *key, uint32_t datalen, uint8_t *data) { #ifndef WITH_HACL size_t hash_len = 64; size_t block_len = 128; uint8_t key_block[block_len]; crypto_generichash_state state; /* Format the key for the inner hashing context */ if (keylen <= block_len) { memcpy(key_block, key, keylen); memset(key_block + keylen, 0, block_len - keylen); } else { crypto_generichash_blake2b_init(&state, NULL, 0, crypto_generichash_blake2b_BYTES_MAX); crypto_generichash_blake2b_update(&state, key, keylen); crypto_generichash_blake2b_final(&state, key_block, crypto_generichash_blake2b_BYTES_MAX); memset(key_block + hash_len, 0, block_len - hash_len); } Noise_XK_hashstate_xor_key(key_block, block_len, HMAC_IPAD); /* Calculate the inner hash */ crypto_generichash_blake2b_init(&state, NULL, 0, crypto_generichash_blake2b_BYTES_MAX); crypto_generichash_blake2b_update(&state, key_block, block_len); crypto_generichash_blake2b_update(&state, data, datalen); crypto_generichash_blake2b_final(&state, output, crypto_generichash_blake2b_BYTES_MAX); /* Format the key for the outer hashing context */ Noise_XK_hashstate_xor_key(key_block, block_len, HMAC_IPAD ^ HMAC_OPAD); /* Calculate the outer hash */ crypto_generichash_blake2b_init(&state, NULL, 0, crypto_generichash_blake2b_BYTES_MAX); crypto_generichash_blake2b_update(&state, key_block, block_len); crypto_generichash_blake2b_update(&state, output, hash_len); crypto_generichash_blake2b_final(&state, output, crypto_generichash_blake2b_BYTES_MAX); /* Clean up and exit */ sodium_memzero(key_block,sizeof key_block); #else // WITH_SODIUM Hacl_HMAC_compute_blake2b_32(output, key, keylen, data, datalen); #endif // WITH_SODIUM } void Noise_XK_kdf( uint8_t *hash1, uint32_t keylen, uint8_t *key, uint8_t *dst1, uint8_t *dst2, uint8_t *dst3 ) { uint8_t output[65U] = { 0U }; uint8_t secret[64U] = { 0U }; uint8_t *output_hash = output; uint8_t *output1 = output; Noise_XK_hmac(secret, (uint32_t)64U, hash1, keylen, key); if (!(dst1 == NULL)) { output[0U] = (uint8_t)1U; Noise_XK_hmac(output_hash, (uint32_t)64U, secret, (uint32_t)1U, output1); memcpy(dst1, output_hash, (uint32_t)64U * sizeof (uint8_t)); if (!(dst2 == NULL)) { output[64U] = (uint8_t)2U; Noise_XK_hmac(output_hash, (uint32_t)64U, secret, (uint32_t)65U, output); memcpy(dst2, output_hash, (uint32_t)64U * sizeof (uint8_t)); if (!(dst3 == NULL)) { output[64U] = (uint8_t)3U; Noise_XK_hmac(output_hash, (uint32_t)64U, secret, (uint32_t)65U, output); memcpy(dst3, output_hash, (uint32_t)64U * sizeof (uint8_t)); } } } #ifndef WITH_HACL sodium_memzero(output, (uint32_t)65U * sizeof (output[0U])); sodium_memzero(secret, (uint32_t)64U * sizeof (secret[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(output, (uint32_t)65U * sizeof (output[0U])); Lib_Memzero0_memzero(secret, (uint32_t)64U * sizeof (secret[0U])); #endif // WITH_SODIUM } void Noise_XK_mix_psk(uint8_t *psk, uint8_t *st_cs_k, uint8_t *st_ck, uint8_t *st_h) { uint8_t temp_hash[64U] = { 0U }; uint8_t temp_k[64U] = { 0U }; Noise_XK_kdf(st_ck, (uint32_t)32U, psk, st_ck, temp_hash, temp_k); memcpy(st_cs_k, temp_k, (uint32_t)32U * sizeof (uint8_t)); #ifndef WITH_HACL sodium_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); #endif // WITH_SODIUM Noise_XK_mix_hash(st_h, (uint32_t)64U, temp_hash); #ifndef WITH_HACL sodium_memzero(temp_hash, (uint32_t)64U * sizeof (temp_hash[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_hash, (uint32_t)64U * sizeof (temp_hash[0U])); #endif // WITH_SODIUM } void Noise_XK_encrypt_and_hash( uint32_t msg_len, uint8_t *msg, uint8_t *cipher, uint8_t *st_cs_k, uint8_t *st_h, uint64_t nonce ) { Noise_XK_aead_encrypt(st_cs_k, nonce, (uint32_t)64U, st_h, msg_len, msg, cipher); uint32_t cipher_len = msg_len + (uint32_t)16U; Noise_XK_mix_hash(st_h, cipher_len, cipher); } Noise_XK_error_code Noise_XK_decrypt_and_hash( uint32_t msg_len, uint8_t *msg, uint8_t *cipher, uint8_t *st_cs_k, uint8_t *st_h, uint64_t nonce ) { Noise_XK_error_code r1 = Noise_XK_aead_decrypt(st_cs_k, nonce, (uint32_t)64U, st_h, msg_len, msg, cipher); if (r1 == Noise_XK_CSuccess) { Noise_XK_mix_hash(st_h, msg_len + (uint32_t)16U, cipher); return Noise_XK_CSuccess; } else return r1; } Noise_XK_error_code Noise_XK_mix_dh(uint8_t *sec, uint8_t *pub, uint8_t *cipher_key, uint8_t *ck, uint8_t *hash1) { uint8_t dh_key[32U] = { 0U }; Noise_XK_error_code r1 = Noise_XK_dh(dh_key, sec, pub); Noise_XK_error_code r2; if (r1 == Noise_XK_CSuccess) { uint8_t temp_k[64U] = { 0U }; Noise_XK_kdf(ck, (uint32_t)32U, dh_key, ck, temp_k, NULL); memcpy(cipher_key, temp_k, (uint32_t)32U * sizeof (uint8_t)); #ifndef WITH_HACL sodium_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_k, (uint32_t)64U * sizeof (temp_k[0U])); #endif // WITH_SODIUM r2 = Noise_XK_CSuccess; } else r2 = r1; Noise_XK_error_code r = r2; #ifndef WITH_HACL sodium_memzero(dh_key, (uint32_t)32U * sizeof (dh_key[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(dh_key, (uint32_t)32U * sizeof (dh_key[0U])); #endif // WITH_SODIUM return r; } liboprf-0.9.4/src/noise_xk/src/XK.c000066400000000000000000006163531514673400200170640ustar00rootroot00000000000000/** This file was automatically generated */ #include "XK.h" bool Noise_XK_uu___is_Success(Noise_XK_rcode projectee) { if (projectee.tag == Noise_XK_Success) return true; else return false; } bool Noise_XK_uu___is_Error(Noise_XK_rcode projectee) { if (projectee.tag == Noise_XK_Error) return true; else return false; } Noise_XK_error_code Noise_XK___proj__Error__item___0(Noise_XK_rcode projectee) { if (projectee.tag == Noise_XK_Error) return projectee.val.case_Error; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } bool Noise_XK_uu___is_Stuck(Noise_XK_rcode projectee) { if (projectee.tag == Noise_XK_Stuck) return true; else return false; } Noise_XK_error_code Noise_XK___proj__Stuck__item___0(Noise_XK_rcode projectee) { if (projectee.tag == Noise_XK_Stuck) return projectee.val.case_Stuck; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } bool Noise_XK_uu___is_Auth_level(Noise_XK_ac_level_t projectee) { if (projectee.tag == Noise_XK_Auth_level) return true; else return false; } uint8_t Noise_XK___proj__Auth_level__item__l(Noise_XK_ac_level_t projectee) { if (projectee.tag == Noise_XK_Auth_level) return projectee.val.case_Auth_level; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } bool Noise_XK_uu___is_Conf_level(Noise_XK_ac_level_t projectee) { if (projectee.tag == Noise_XK_Conf_level) return true; else return false; } uint8_t Noise_XK___proj__Conf_level__item__l(Noise_XK_ac_level_t projectee) { if (projectee.tag == Noise_XK_Conf_level) return projectee.val.case_Conf_level; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } bool Noise_XK_uu___is_No_level(Noise_XK_ac_level_t projectee) { if (projectee.tag == Noise_XK_No_level) return true; else return false; } typedef struct Noise_XK_encap_message_t_s { Noise_XK_ac_level_t em_ac_level; uint32_t em_message_len; uint8_t *em_message; } Noise_XK_encap_message_t; Noise_XK_encap_message_t *Noise_XK___proj__Mkencap_message_p_or_null__item__emp(Noise_XK_encap_message_t *projectee) { return projectee; } bool Noise_XK_encap_message_p_is_null(Noise_XK_encap_message_t *emp) { return emp == NULL; } void Noise_XK_encap_message_p_free(Noise_XK_encap_message_t *emp) { Noise_XK_encap_message_t em = emp[0U]; if (!(em.em_message == NULL)) KRML_HOST_FREE(em.em_message); KRML_HOST_FREE(emp); } Noise_XK_encap_message_t *Noise_XK_pack_message_with_conf_level( uint8_t requested_conf_level, uint32_t msg_len, uint8_t *msg ) { uint8_t *msg_; if (msg_len > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), msg_len); uint8_t *o = KRML_HOST_CALLOC(msg_len, sizeof (uint8_t)); memcpy(o, msg, msg_len * sizeof (uint8_t)); msg_ = o; } else msg_ = NULL; KRML_CHECK_SIZE(sizeof (Noise_XK_encap_message_t), (uint32_t)1U); Noise_XK_encap_message_t *emp_p = KRML_HOST_MALLOC(sizeof (Noise_XK_encap_message_t)); emp_p[0U] = ( (Noise_XK_encap_message_t){ .em_ac_level = { .tag = Noise_XK_Conf_level, .val = { .case_Conf_level = requested_conf_level } }, .em_message_len = msg_len, .em_message = msg_ } ); return emp_p; } Noise_XK_encap_message_t *Noise_XK_pack_message(uint32_t msg_len, uint8_t *msg) { return Noise_XK_pack_message_with_conf_level(NOISE_XK_MAX_CONF_LEVEL, msg_len, msg); } bool Noise_XK_unpack_message_with_auth_level( uint32_t *out_msg_len, uint8_t **out_msg, uint8_t requested_auth_level, Noise_XK_encap_message_t *emp ) { Noise_XK_encap_message_t em = emp[0U]; bool ok; if (em.em_message_len == (uint32_t)0U) ok = true; else if (em.em_ac_level.tag == Noise_XK_Auth_level) { uint8_t l = em.em_ac_level.val.case_Auth_level; ok = l >= requested_auth_level; } else ok = false; if (ok) { uint8_t *msg; if (em.em_message_len > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), em.em_message_len); uint8_t *o = KRML_HOST_CALLOC(em.em_message_len, sizeof (uint8_t)); memcpy(o, em.em_message, em.em_message_len * sizeof (uint8_t)); msg = o; } else msg = NULL; out_msg_len[0U] = em.em_message_len; out_msg[0U] = msg; return true; } else { out_msg[0U] = NULL; return false; } } bool Noise_XK_unpack_message( uint32_t *out_msg_len, uint8_t **out_msg, Noise_XK_encap_message_t *emp ) { return Noise_XK_unpack_message_with_auth_level(out_msg_len, out_msg, NOISE_XK_MAX_AUTH_LEVEL, emp); } void Noise_XK_unsafe_unpack_message( Noise_XK_ac_level_t *out_ac_level, uint32_t *out_msg_len, uint8_t **out_msg, Noise_XK_encap_message_t *emp ) { Noise_XK_encap_message_t em = emp[0U]; uint8_t *msg; if (em.em_message_len > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), em.em_message_len); uint8_t *o = KRML_HOST_CALLOC(em.em_message_len, sizeof (uint8_t)); memcpy(o, em.em_message, em.em_message_len * sizeof (uint8_t)); msg = o; } else msg = NULL; out_ac_level[0U] = em.em_ac_level; out_msg_len[0U] = em.em_message_len; out_msg[0U] = msg; } Prims_int Noise_XK_num_pattern_messages = (krml_checked_int_t)3; bool Noise_XK_rcode_is_success(Noise_XK_rcode c) { if (c.tag == Noise_XK_Success) return true; else return false; } bool Noise_XK_rcode_is_error(Noise_XK_rcode c) { if (c.tag == Noise_XK_Error) return true; else return false; } bool Noise_XK_rcode_is_stuck(Noise_XK_rcode c) { if (c.tag == Noise_XK_Stuck) return true; else return false; } #include "noise_private.h" typedef struct Noise_XK_peer_t_s { uint32_t p_id; Noise_XK_noise_string *p_info; uint8_t *p_s; } Noise_XK_peer_t; typedef Noise_XK_peer_t *peer_p; typedef Noise_XK_cell **t___Impl_Noise_API_Instances_X1N_25519_AESGCM_BLAKE2b_peer_p; typedef struct Noise_XK_device_t_s { Noise_XK_noise_string *dv_info; uint8_t *dv_sk; uint8_t *dv_spriv; uint8_t *dv_spub; Noise_XK_sized_buffer dv_prologue; uint32_t dv_states_counter; Noise_XK_cell **dv_peers; uint32_t dv_peers_counter; } Noise_XK_device_t; typedef Noise_XK_device_t *device_p; typedef Noise_XK_session_t *session_p; /* Create a device. Parameters: * `prlg`: Prologue for session initialization * `info`: Device name * `sk`: (if present) symmetric key used to serialize/deserialize private data * `spriv`: (if present) static private key May fail and return NULL if provided unvalid keys. */ Noise_XK_device_t *Noise_XK_device_create( uint32_t prlg_len, uint8_t *prlg, uint8_t *info, uint8_t *sk, uint8_t *spriv ) { uint8_t *o0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o0, sk, (uint32_t)32U * sizeof (uint8_t)); uint8_t *sk_ = o0; uint8_t *o1 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o1, spriv, (uint32_t)32U * sizeof (uint8_t)); uint8_t *spriv_ = o1; uint8_t *spub_ = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); Noise_XK_error_code res = Noise_XK_dh_secret_to_public(spub_, spriv_); switch (res) { case Noise_XK_CSuccess: { uint8_t *prlg_; if (prlg_len > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), prlg_len); uint8_t *o = KRML_HOST_CALLOC(prlg_len, sizeof (uint8_t)); memcpy(o, prlg, prlg_len * sizeof (uint8_t)); prlg_ = o; } else prlg_ = NULL; Noise_XK_sized_buffer prlg_1 = { .size = prlg_len, .buffer = prlg_ }; bool b = info == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = info[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = info[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = info[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = info[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = info[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = out_str; Noise_XK_noise_string *info_ = out_ptr; KRML_CHECK_SIZE(sizeof (Noise_XK_cell *), (uint32_t)1U); Noise_XK_cell **ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_cell *)); ptr[0U] = NULL; Noise_XK_cell **peers = ptr; Noise_XK_device_t dv = { .dv_info = info_, .dv_sk = sk_, .dv_spriv = spriv_, .dv_spub = spub_, .dv_prologue = prlg_1, .dv_states_counter = (uint32_t)1U, .dv_peers = peers, .dv_peers_counter = (uint32_t)1U }; KRML_CHECK_SIZE(sizeof (Noise_XK_device_t), (uint32_t)1U); Noise_XK_device_t *dvp = KRML_HOST_MALLOC(sizeof (Noise_XK_device_t)); dvp[0U] = dv; return dvp; } default: { return NULL; } } } typedef struct __uint32_t__uint8_t__s { uint32_t fst; uint8_t *snd; } __uint32_t__uint8_t_; /* Create a device. Takes as arguments a symmetric key `sk` for secret data serialization/ deserialization, and an encrypted static private key `spriv`. The device name `info` is used as authentication data to encrypt/decrypt the device private key. May fail and return NULL if provided unvalid keys. */ Noise_XK_device_t *Noise_XK_device_create_from_secret( uint32_t prlg_len, uint8_t *prlg, uint8_t *info, uint8_t *sk, uint8_t *spriv ) { uint8_t *spriv_ = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); __uint32_t__uint8_t_ scrut; if (info == NULL) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c = info[i0]; bool cond = c != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = info[i0]; cond = c != (uint8_t)0U; } uint32_t l = ip; if (l == (uint32_t)0U) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint8_t *s = info; scrut = ((__uint32_t__uint8_t_){ .fst = l, .snd = s }); } } uint32_t name_raw_len = scrut.fst; uint8_t *name_raw = scrut.snd; uint8_t *n8 = spriv; uint8_t *c0 = spriv + (uint32_t)8U; uint64_t n0 = Noise_XK_bytes_to_nonce(n8); Noise_XK_error_code res0 = Noise_XK_aead_decrypt(sk, n0, name_raw_len, name_raw, (uint32_t)32U, spriv_, c0); if (!(res0 == Noise_XK_CSuccess)) { KRML_HOST_FREE(spriv_); return NULL; } else { uint8_t *o0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o0, sk, (uint32_t)32U * sizeof (uint8_t)); uint8_t *sk_ = o0; uint8_t *spub_ = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); Noise_XK_error_code res1 = Noise_XK_dh_secret_to_public(spub_, spriv_); uint8_t *prlg_; if (prlg_len > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), prlg_len); uint8_t *o = KRML_HOST_CALLOC(prlg_len, sizeof (uint8_t)); memcpy(o, prlg, prlg_len * sizeof (uint8_t)); prlg_ = o; } else prlg_ = NULL; Noise_XK_sized_buffer prlg_1 = { .size = prlg_len, .buffer = prlg_ }; bool b = info == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = info[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = info[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = info[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = info[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = info[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = out_str; Noise_XK_noise_string *info_ = out_ptr; KRML_CHECK_SIZE(sizeof (Noise_XK_cell *), (uint32_t)1U); Noise_XK_cell **ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_cell *)); ptr[0U] = NULL; Noise_XK_cell **peers = ptr; Noise_XK_device_t dv = { .dv_info = info_, .dv_sk = sk_, .dv_spriv = spriv_, .dv_spub = spub_, .dv_prologue = prlg_1, .dv_states_counter = (uint32_t)1U, .dv_peers = peers, .dv_peers_counter = (uint32_t)1U }; KRML_CHECK_SIZE(sizeof (Noise_XK_device_t), (uint32_t)1U); Noise_XK_device_t *dvp = KRML_HOST_MALLOC(sizeof (Noise_XK_device_t)); dvp[0U] = dv; return dvp; } } static void free___Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____( Noise_XK_cell *l ) { if (!(l == NULL)) { free___Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____((*l).next); KRML_HOST_FREE(l); } } static void free__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____( Noise_XK_cell **pl ) { free___Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____(*pl); *pl = NULL; } /* Free a device. Take care to free the device **AFTER** having freed all the sessions created from this device. */ void Noise_XK_device_free(Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; uint8_t *str = dv.dv_info[0U]; if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(dv.dv_info); free__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____(dv.dv_peers); KRML_HOST_FREE(dv.dv_peers); KRML_HOST_FREE(dv.dv_spriv); KRML_HOST_FREE(dv.dv_spub); if (!(dv.dv_prologue.buffer == NULL)) KRML_HOST_FREE(dv.dv_prologue.buffer); KRML_HOST_FREE(dvp); } /* Encrypt and derialize a device's secret. Uses the device symmetric key to encrypt the device's secret key. Uses a randomly generated nonce together with the device name as authentication data. */ void Noise_XK_serialize_device_secret(uint32_t *outlen, uint8_t **out, Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; uint8_t *outb = KRML_HOST_CALLOC((uint32_t)56U, sizeof (uint8_t)); uint8_t *name = dv.dv_info[0U]; __uint32_t__uint8_t_ scrut; if (name == NULL) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c = name[i0]; bool cond = c != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = name[i0]; cond = c != (uint8_t)0U; } uint32_t l = ip; if (l == (uint32_t)0U) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint8_t *s = name; scrut = ((__uint32_t__uint8_t_){ .fst = l, .snd = s }); } } uint32_t name_raw_len = scrut.fst; uint8_t *name_raw = scrut.snd; uint8_t *n8 = outb; uint8_t *c = outb + (uint32_t)8U; #ifndef WITH_HACL randombytes_buf(n8, (uint32_t)8U); #else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(n8, (uint32_t)8U); #endif // WITH_SODIUM uint64_t n = Noise_XK_bytes_to_nonce(n8); Noise_XK_aead_encrypt(dv.dv_sk, n, name_raw_len, name_raw, (uint32_t)32U, dv.dv_spriv, c); out[0U] = outb; outlen[0U] = (uint32_t)56U; } static void push__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____( Noise_XK_cell **pl, Noise_XK_peer_t *x ) { Noise_XK_cell *l = *pl; Noise_XK_cell c = { .next = l, .data = x }; KRML_CHECK_SIZE(sizeof (Noise_XK_cell), (uint32_t)1U); Noise_XK_cell *pc = KRML_HOST_MALLOC(sizeof (Noise_XK_cell)); pc[0U] = c; *pl = pc; } /* Add a peer to the device and return a pointer to the newly created peer. May fail and return NULL if the device already contains a peer with the same public static key. Note that the peer is owned by the device: we don't provide any way of freeing it on the user side, and it might be invalidated after a removal operation. For this reason, we advise to immediately use the returned pointer (to retrieve the peer id for instance), then forget it. */ Noise_XK_peer_t *Noise_XK_device_add_peer(Noise_XK_device_t *dvp, uint8_t *pinfo, uint8_t *rs) { Noise_XK_device_t dv = dvp[0U]; uint32_t pcounter = dv.dv_peers_counter; bool b1 = pcounter == (uint32_t)4294967295U; Noise_XK_cell *llt = *dv.dv_peers; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, rs); bool b1 = b; b0 = !b1; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, rs); bool b1 = b0; b = !b1; } cond = b; } Noise_XK_cell *llt1 = *&lltp; Noise_XK_peer_t *res; if (llt1 == NULL) res = NULL; else { Noise_XK_cell c = *llt1; res = c.data; } bool b2 = !(res == NULL); if (b1 || b2) return NULL; else { Noise_XK_noise_string *info1 = dv.dv_info; uint8_t *sk1 = dv.dv_sk; uint8_t *spriv1 = dv.dv_spriv; uint8_t *spub1 = dv.dv_spub; Noise_XK_sized_buffer prologue1 = dv.dv_prologue; uint32_t scounter1 = dv.dv_states_counter; Noise_XK_cell **peers1 = dv.dv_peers; uint32_t pcounter1 = dv.dv_peers_counter; uint8_t *rs1 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(rs1, rs, (uint32_t)32U * sizeof (uint8_t)); bool b = pinfo == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = pinfo[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = pinfo[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = pinfo[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = pinfo[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = pinfo[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = out_str; Noise_XK_noise_string *pinfo1 = out_ptr; Noise_XK_peer_t x_ = { .p_id = pcounter1, .p_info = pinfo1, .p_s = rs1 }; KRML_CHECK_SIZE(sizeof (Noise_XK_peer_t), (uint32_t)1U); Noise_XK_peer_t *xp_ = KRML_HOST_MALLOC(sizeof (Noise_XK_peer_t)); xp_[0U] = x_; Noise_XK_peer_t *x = xp_; push__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____(peers1, x); Noise_XK_peer_t *pp = x; dvp[0U] = ( (Noise_XK_device_t){ .dv_info = info1, .dv_sk = sk1, .dv_spriv = spriv1, .dv_spub = spub1, .dv_prologue = prologue1, .dv_states_counter = scounter1, .dv_peers = peers1, .dv_peers_counter = pcounter1 + (uint32_t)1U } ); Noise_XK_peer_t *pp0 = pp; return pp0; } } static Noise_XK_peer_t *pop__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____( Noise_XK_cell **pl ) { Noise_XK_cell *l = *pl; Noise_XK_peer_t *r1 = (*l).data; Noise_XK_cell *next = (*l).next; *pl = next; KRML_HOST_FREE(l); return r1; } /* Remove a peer designated by its unique identifier. */ void Noise_XK_device_remove_peer(Noise_XK_device_t *dvp, uint32_t pid) { if (!(pid == (uint32_t)0U)) { Noise_XK_device_t dv = dvp[0U]; Noise_XK_cell *llt = *dv.dv_peers; if (!(llt == NULL)) { Noise_XK_cell c0 = *llt; Noise_XK_peer_t x = c0.data[0U]; if (x.p_id != pid) { Noise_XK_cell *llt1 = *dv.dv_peers; Noise_XK_cell *lltp = llt1; Noise_XK_cell *llt20 = lltp; Noise_XK_cell *next = llt20->next; bool b0; if (next == NULL) b0 = false; else { Noise_XK_cell c = next[0U]; Noise_XK_peer_t x = c.data[0U]; b0 = x.p_id != pid; } bool cond = b0; while (cond) { Noise_XK_cell *llt2 = lltp; Noise_XK_cell c0 = llt2[0U]; lltp = c0.next; Noise_XK_cell *llt20 = lltp; Noise_XK_cell *next = llt20->next; bool b; if (next == NULL) b = false; else { Noise_XK_cell c = next[0U]; Noise_XK_peer_t x = c.data[0U]; b = x.p_id != pid; } cond = b; } Noise_XK_cell *llt2 = *&lltp; Noise_XK_cell c01 = *llt2; if (!(c01.next == NULL)) { Noise_XK_cell c1 = *c01.next; llt2[0U] = ((Noise_XK_cell){ .next = c1.next, .data = c01.data }); Noise_XK_peer_t p = c1.data[0U]; uint8_t *str = p.p_info[0U]; if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(p.p_info); #ifndef WITH_HACL sodium_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); #endif // WITH_SODIUM KRML_HOST_FREE(p.p_s); KRML_HOST_FREE(c1.data); KRML_HOST_FREE(c01.next); } } else { Noise_XK_peer_t *elem1 = pop__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____(dv.dv_peers); Noise_XK_peer_t p = elem1[0U]; uint8_t *str = p.p_info[0U]; if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(p.p_info); #ifndef WITH_HACL sodium_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(p.p_s, (uint32_t)32U * sizeof (p.p_s[0U])); #endif // WITH_SODIUM KRML_HOST_FREE(p.p_s); KRML_HOST_FREE(elem1); } } } } /* Encrypt and serialize a peer's key(s). Uses the device symmetric key to encrypt the peer's key(s). Uses a randomly generated nonce together with the peer name as authentication data. */ void Noise_XK_serialize_peer_secret( uint32_t *outlen, uint8_t **out, Noise_XK_device_t *dvp, Noise_XK_peer_t *peer ) { if (peer == NULL) { outlen[0U] = (uint32_t)0U; out[0U] = NULL; } else { Noise_XK_device_t dv = dvp[0U]; Noise_XK_peer_t p = peer[0U]; uint8_t *concat_keys = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *outb = KRML_HOST_CALLOC((uint32_t)56U, sizeof (uint8_t)); memcpy(concat_keys, p.p_s, (uint32_t)32U * sizeof (uint8_t)); uint8_t *name = p.p_info[0U]; __uint32_t__uint8_t_ scrut; if (name == NULL) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c = name[i0]; bool cond = c != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = name[i0]; cond = c != (uint8_t)0U; } uint32_t l = ip; if (l == (uint32_t)0U) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint8_t *s = name; scrut = ((__uint32_t__uint8_t_){ .fst = l, .snd = s }); } } uint32_t name_raw_len = scrut.fst; uint8_t *name_raw = scrut.snd; uint8_t *n8 = outb; uint8_t *c = outb + (uint32_t)8U; #ifndef WITH_HACL randombytes_buf(n8, (uint32_t)8U); #else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(n8, (uint32_t)8U); #endif // WITH_SODIUM uint64_t n = Noise_XK_bytes_to_nonce(n8); Noise_XK_aead_encrypt(dv.dv_sk, n, name_raw_len, name_raw, (uint32_t)32U, concat_keys, c); out[0U] = outb; outlen[0U] = (uint32_t)56U; KRML_HOST_FREE(concat_keys); } } /* Decrypt and deserialize a peer's secret data and add it to the device. */ Noise_XK_peer_t *Noise_XK_deserialize_peer_secret( Noise_XK_device_t *dvp, uint8_t *peer_name, uint32_t inlen, uint8_t *enc_keys ) { Noise_XK_device_t dv = dvp[0U]; if ((uint32_t)56U != inlen) return NULL; else { uint8_t *concat_keys = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); __uint32_t__uint8_t_ scrut; if (peer_name == NULL) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c = peer_name[i0]; bool cond = c != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = peer_name[i0]; cond = c != (uint8_t)0U; } uint32_t l = ip; if (l == (uint32_t)0U) scrut = ((__uint32_t__uint8_t_){ .fst = (uint32_t)0U, .snd = NULL }); else { uint8_t *s = peer_name; scrut = ((__uint32_t__uint8_t_){ .fst = l, .snd = s }); } } uint32_t name_raw_len = scrut.fst; uint8_t *name_raw = scrut.snd; uint8_t *n8 = enc_keys; uint8_t *c0 = enc_keys + (uint32_t)8U; uint64_t n0 = Noise_XK_bytes_to_nonce(n8); Noise_XK_error_code res = Noise_XK_aead_decrypt(dv.dv_sk, n0, name_raw_len, name_raw, (uint32_t)32U, concat_keys, c0); if (res == Noise_XK_CSuccess) { uint8_t *p_s = concat_keys; Noise_XK_device_t dv1 = dvp[0U]; uint32_t pcounter = dv1.dv_peers_counter; bool b1 = pcounter == (uint32_t)4294967295U; Noise_XK_cell *llt = *dv1.dv_peers; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, p_s); bool b1 = b; b0 = !b1; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, p_s); bool b1 = b0; b = !b1; } cond = b; } Noise_XK_cell *llt1 = *&lltp; Noise_XK_peer_t *res1; if (llt1 == NULL) res1 = NULL; else { Noise_XK_cell c = *llt1; res1 = c.data; } bool b2 = !(res1 == NULL); Noise_XK_peer_t *peer; if (b1 || b2) peer = NULL; else { Noise_XK_noise_string *info1 = dv1.dv_info; uint8_t *sk1 = dv1.dv_sk; uint8_t *spriv1 = dv1.dv_spriv; uint8_t *spub1 = dv1.dv_spub; Noise_XK_sized_buffer prologue1 = dv1.dv_prologue; uint32_t scounter1 = dv1.dv_states_counter; Noise_XK_cell **peers1 = dv1.dv_peers; uint32_t pcounter1 = dv1.dv_peers_counter; uint8_t *rs = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(rs, p_s, (uint32_t)32U * sizeof (uint8_t)); bool b = peer_name == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = peer_name[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = peer_name[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = peer_name[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = peer_name[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = peer_name[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = out_str; Noise_XK_noise_string *pinfo = out_ptr; Noise_XK_peer_t x_ = { .p_id = pcounter1, .p_info = pinfo, .p_s = rs }; KRML_CHECK_SIZE(sizeof (Noise_XK_peer_t), (uint32_t)1U); Noise_XK_peer_t *xp_ = KRML_HOST_MALLOC(sizeof (Noise_XK_peer_t)); xp_[0U] = x_; Noise_XK_peer_t *x = xp_; push__Impl_Noise_API_Device_raw_peer_p_or_null_raw_Impl_Noise_API_Device_raw_peer_t_raw_uint32_t_Impl_Noise_String_hstring__uint8_t____(peers1, x); Noise_XK_peer_t *pp = x; dvp[0U] = ( (Noise_XK_device_t){ .dv_info = info1, .dv_sk = sk1, .dv_spriv = spriv1, .dv_spub = spub1, .dv_prologue = prologue1, .dv_states_counter = scounter1, .dv_peers = peers1, .dv_peers_counter = pcounter1 + (uint32_t)1U } ); Noise_XK_peer_t *pp0 = pp; peer = pp0; } KRML_HOST_FREE(concat_keys); return peer; } else { KRML_HOST_FREE(concat_keys); return NULL; } } } /* Lookup a peer by using its unique identifier. Return NULL is no peer was found. Note that the peer is owned by the device: we don't provide any way of freeing it on the user side, and it might be invalidated after a removal operation. For this reason, we advise to immediately use the returned pointer (to retrieve the peer name, etc.), then forget it. */ Noise_XK_peer_t *Noise_XK_device_lookup_peer_by_id(Noise_XK_device_t *dvp, uint32_t id) { Noise_XK_device_t dv = dvp[0U]; if (id == (uint32_t)0U) return NULL; else { Noise_XK_cell *llt = *dv.dv_peers; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = x.p_id == id; b0 = !b; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = x.p_id == id; b = !b0; } cond = b; } Noise_XK_cell *llt1 = *&lltp; if (llt1 == NULL) return NULL; else { Noise_XK_cell c = *llt1; return c.data; } } } /* Lookup a peer by using its static public key. Return NULL is no peer was found. Note that the peer is owned by the device: we don't provide any way of freeing it on the user side, and it might be invalidated after a removal operation. For this reason, we advise to immediately use the returned pointer (to retrieve the peer name, etc.), then forget it. */ Noise_XK_peer_t *Noise_XK_device_lookup_peer_by_static(Noise_XK_device_t *dvp, uint8_t *s) { Noise_XK_device_t dv = dvp[0U]; Noise_XK_cell *llt = *dv.dv_peers; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, s); bool b1 = b; b0 = !b1; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, s); bool b1 = b0; b = !b1; } cond = b; } Noise_XK_cell *llt1 = *&lltp; if (llt1 == NULL) return NULL; else { Noise_XK_cell c = *llt1; return c.data; } } /* Copy the peer information to the user provided pointer. */ void Noise_XK_device_get_info(Noise_XK_noise_string *out, Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; uint8_t *input_str = dv.dv_info[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } out[0U] = out_str; } /* Return the current value of the sessions counter. The device keeps track of the number of sessions created so far, in order to give them unique identifiers. */ uint32_t Noise_XK_device_get_sessions_counter(Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; return dv.dv_states_counter; } /* Return true if the sessions counter is saturated. It is not possible to create any more sessions if the counter is saturated. */ bool Noise_XK_device_sessions_counter_is_saturated(Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; uint32_t cnt = dv.dv_states_counter; return cnt == (uint32_t)4294967295U; } /* Return the current value of the peers counter. The device keeps track of the number of peers created so far, in order to give them unique identifiers. */ uint32_t Noise_XK_device_get_peers_counter(Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; return dv.dv_peers_counter; } /* Return true if the peers counter is saturated. It is not possible to add any more peers to the device if the counter is saturated. */ bool Noise_XK_device_peers_counter_is_saturated(Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; uint32_t cnt = dv.dv_peers_counter; return cnt == (uint32_t)4294967295U; } /* Copy the device static private key to the user provided buffer. */ void Noise_XK_device_get_static_priv(uint8_t *out, Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; memcpy(out, dv.dv_spriv, (uint32_t)32U * sizeof (uint8_t)); } /* Copy the device static public key to the user provided buffer. */ void Noise_XK_device_get_static_pub(uint8_t *out, Noise_XK_device_t *dvp) { Noise_XK_device_t dv = dvp[0U]; memcpy(out, dv.dv_spub, (uint32_t)32U * sizeof (uint8_t)); } /* Return the unique peer identifier. */ uint32_t Noise_XK_peer_get_id(Noise_XK_peer_t *pp) { Noise_XK_peer_t p = pp[0U]; return p.p_id; } /* Copy the peer information to the user provided pointer. */ void Noise_XK_peer_get_info(Noise_XK_noise_string *out, Noise_XK_peer_t *pp) { Noise_XK_peer_t p = pp[0U]; uint8_t *input_str = p.p_info[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } out[0U] = out_str; } /* Copy the peer static public key to the user provided buffer. */ void Noise_XK_peer_get_static(uint8_t *out, Noise_XK_peer_t *pp) { Noise_XK_peer_t p = pp[0U]; memcpy(out, p.p_s, (uint32_t)32U * sizeof (uint8_t)); } typedef struct _________________s { } ________________; #define Res 0 #define Fail 1 typedef uint8_t result_session_t_tags; typedef struct result_session_t_s { result_session_t_tags tag; union { Noise_XK_session_t case_Res; Noise_XK_error_code case_Fail; } val; } result_session_t; typedef struct ______________s { } _____________; typedef struct ________s { } _______; /* Create an initiator session. May fail and return NULL in case of invalid keys, unknown peer, etc. */ Noise_XK_session_t *Noise_XK_session_create_initiator(Noise_XK_device_t *dvp, uint32_t pid) { uint8_t epriv[32U] = { 0U }; uint8_t epub[32U] = { 0U }; #ifndef WITH_HACL randombytes_buf(epriv, (uint32_t)32U); #else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(epriv, (uint32_t)32U); #endif // WITH_SODIUM Noise_XK_error_code res0 = Noise_XK_dh_secret_to_public(epub, epriv); Noise_XK_session_t *res; switch (res0) { case Noise_XK_CSuccess: { Noise_XK_device_t dv = dvp[0U]; result_session_t res10; if (dv.dv_states_counter == (uint32_t)4294967295U) res10 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else { Noise_XK_device_t dv1 = dvp[0U]; Noise_XK_peer_t *peer_ptr; if (pid == (uint32_t)0U) peer_ptr = NULL; else { Noise_XK_cell *llt = *dv1.dv_peers; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = x.p_id == pid; b0 = !b; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = x.p_id == pid; b = !b0; } cond = b; } Noise_XK_cell *llt1 = *&lltp; Noise_XK_peer_t *res1; if (llt1 == NULL) res1 = NULL; else { Noise_XK_cell c = *llt1; res1 = c.data; } peer_ptr = res1; } bool p_is_null = peer_ptr == NULL; if (p_is_null) res10 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CUnknown_peer_id } }); else { uint8_t *o0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o0, dv.dv_spriv, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spriv = o0; uint8_t *o = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o, dv.dv_spub, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spub = o; uint8_t *str0 = dv.dv_info[0U]; bool b0 = str0 == NULL; uint8_t *out_str0; if (b0) out_str0 = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = str0[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = str0[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str0 = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = str0[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = str0[n]; out_str[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = str0[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str[n] = (uint8_t)0U; uint8_t *out_str1 = out_str; out_str0 = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = out_str0; Noise_XK_noise_string *st_info = out_ptr; Noise_XK_peer_t peer = peer_ptr[0U]; uint8_t *str = peer.p_info[0U]; bool b = str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr0 = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr0[0U] = out_str; Noise_XK_noise_string *st_pinfo = out_ptr0; uint8_t *rs = peer.p_s; dvp[0U] = ( (Noise_XK_device_t){ .dv_info = dv.dv_info, .dv_sk = dv.dv_sk, .dv_spriv = dv.dv_spriv, .dv_spub = dv.dv_spub, .dv_prologue = dv.dv_prologue, .dv_states_counter = dv.dv_states_counter + (uint32_t)1U, .dv_peers = dv.dv_peers, .dv_peers_counter = dv.dv_peers_counter } ); uint8_t *st_k = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_ck0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_h0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_spriv1 = st_spriv; uint8_t *st_spub1 = st_spub; uint8_t *st_epriv0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_epub0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_rs0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_re = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); Noise_XK_init_state_t st = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)0U, .cipher_key = st_k, .chaining_key = st_ck0, .h = st_h0, .spriv = st_spriv1, .spub = st_spub1, .epriv = st_epriv0, .epub = st_epub0, .rs = st_rs0, .re = st_re } } }; uint8_t pname[33U] = { (uint8_t)78U, (uint8_t)111U, (uint8_t)105U, (uint8_t)115U, (uint8_t)101U, (uint8_t)95U, (uint8_t)88U, (uint8_t)75U, (uint8_t)95U, (uint8_t)50U, (uint8_t)53U, (uint8_t)53U, (uint8_t)49U, (uint8_t)57U, (uint8_t)95U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)80U, (uint8_t)111U, (uint8_t)108U, (uint8_t)121U, (uint8_t)95U, (uint8_t)66U, (uint8_t)76U, (uint8_t)65U, (uint8_t)75U, (uint8_t)69U, (uint8_t)50U, (uint8_t)98U }; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_rs = st.val.case_IMS_Handshake.rs; uint8_t *st_epub = st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = st.val.case_IMS_Handshake.epriv; uint8_t *st_h = st.val.case_IMS_Handshake.h; uint8_t *st_ck = st.val.case_IMS_Handshake.chaining_key; if ((uint32_t)33U <= (uint32_t)64U) memcpy(st_h, pname, (uint32_t)33U * sizeof (uint8_t)); else Noise_XK_hash(st_h, (uint32_t)33U, pname); memcpy(st_ck, st_h, (uint32_t)64U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, dv.dv_prologue.size, dv.dv_prologue.buffer); memcpy(st_epriv, epriv, (uint32_t)32U * sizeof (uint8_t)); memcpy(st_epub, epub, (uint32_t)32U * sizeof (uint8_t)); memcpy(st_rs, rs, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, (uint32_t)32U, rs); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } Noise_XK_init_state_t st0 = st; result_session_t res1 = { .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st0, .id = dv.dv_states_counter, .info = st_info, .spriv = st_spriv, .spub = st_spub, .pid = pid, .pinfo = st_pinfo, .dv = dvp } } } } }; res10 = res1; } } Noise_XK_session_t *res1; if (res10.tag == Fail) res1 = NULL; else if (res10.tag == Res) { Noise_XK_session_t st = res10.val.case_Res; KRML_CHECK_SIZE(sizeof (Noise_XK_session_t), (uint32_t)1U); Noise_XK_session_t *ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_session_t)); ptr[0U] = st; res1 = ptr; } else res1 = KRML_EABORT(Noise_XK_session_t *, "unreachable (pattern matches are exhaustive in F*)"); res = res1; break; } default: { res = NULL; } } #ifndef WITH_HACL sodium_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); sodium_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); Lib_Memzero0_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); #endif // WITH_SODIUM Noise_XK_session_t *res1 = res; return res1; } /* Create a responder session. May fail and return NULL in case of invalid keys, unknown peer, etc. */ Noise_XK_session_t *Noise_XK_session_create_responder(Noise_XK_device_t *dvp) { uint8_t epriv[32U] = { 0U }; uint8_t epub[32U] = { 0U }; #ifndef WITH_HACL randombytes_buf(epriv, (uint32_t)32U); #else // WITH_SODIUM Lib_RandomBuffer_System_crypto_random(epriv, (uint32_t)32U); #endif // WITH_SODIUM Noise_XK_error_code res0 = Noise_XK_dh_secret_to_public(epub, epriv); Noise_XK_session_t *res; switch (res0) { case Noise_XK_CSuccess: { Noise_XK_device_t dv = dvp[0U]; result_session_t res10; if (dv.dv_states_counter == (uint32_t)4294967295U) res10 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else { uint8_t *o0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o0, dv.dv_spriv, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spriv = o0; uint8_t *o = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o, dv.dv_spub, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spub = o; uint8_t *str = dv.dv_info[0U]; bool b = str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr0 = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr0[0U] = out_str; Noise_XK_noise_string *st_info = out_ptr0; KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = NULL; Noise_XK_noise_string *st_pinfo = out_ptr; dvp[0U] = ( (Noise_XK_device_t){ .dv_info = dv.dv_info, .dv_sk = dv.dv_sk, .dv_spriv = dv.dv_spriv, .dv_spub = dv.dv_spub, .dv_prologue = dv.dv_prologue, .dv_states_counter = dv.dv_states_counter + (uint32_t)1U, .dv_peers = dv.dv_peers, .dv_peers_counter = dv.dv_peers_counter } ); uint8_t *st_k = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_ck0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_h0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_spriv1 = st_spriv; uint8_t *st_spub10 = st_spub; uint8_t *st_epriv0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_epub0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_rs = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_re = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); Noise_XK_resp_state_t st = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)0U, .cipher_key = st_k, .chaining_key = st_ck0, .h = st_h0, .spriv = st_spriv1, .spub = st_spub10, .epriv = st_epriv0, .epub = st_epub0, .rs = st_rs, .re = st_re } } }; uint8_t pname[33U] = { (uint8_t)78U, (uint8_t)111U, (uint8_t)105U, (uint8_t)115U, (uint8_t)101U, (uint8_t)95U, (uint8_t)88U, (uint8_t)75U, (uint8_t)95U, (uint8_t)50U, (uint8_t)53U, (uint8_t)53U, (uint8_t)49U, (uint8_t)57U, (uint8_t)95U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)80U, (uint8_t)111U, (uint8_t)108U, (uint8_t)121U, (uint8_t)95U, (uint8_t)66U, (uint8_t)76U, (uint8_t)65U, (uint8_t)75U, (uint8_t)69U, (uint8_t)50U, (uint8_t)98U }; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_epub = st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = st.val.case_IMS_Handshake.epriv; uint8_t *st_spub1 = st.val.case_IMS_Handshake.spub; uint8_t *st_h = st.val.case_IMS_Handshake.h; uint8_t *st_ck = st.val.case_IMS_Handshake.chaining_key; if ((uint32_t)33U <= (uint32_t)64U) memcpy(st_h, pname, (uint32_t)33U * sizeof (uint8_t)); else Noise_XK_hash(st_h, (uint32_t)33U, pname); memcpy(st_ck, st_h, (uint32_t)64U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, dv.dv_prologue.size, dv.dv_prologue.buffer); memcpy(st_epriv, epriv, (uint32_t)32U * sizeof (uint8_t)); memcpy(st_epub, epub, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, (uint32_t)32U, st_spub1); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } Noise_XK_resp_state_t st0 = st; result_session_t res1 = { .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st0, .id = dv.dv_states_counter, .info = st_info, .spriv = st_spriv, .spub = st_spub, .pid = (uint32_t)0U, .pinfo = st_pinfo, .dv = dvp } } } } }; res10 = res1; } Noise_XK_session_t *res1; if (res10.tag == Fail) res1 = NULL; else if (res10.tag == Res) { Noise_XK_session_t st = res10.val.case_Res; KRML_CHECK_SIZE(sizeof (Noise_XK_session_t), (uint32_t)1U); Noise_XK_session_t *ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_session_t)); ptr[0U] = st; res1 = ptr; } else res1 = KRML_EABORT(Noise_XK_session_t *, "unreachable (pattern matches are exhaustive in F*)"); res = res1; break; } default: { res = NULL; } } #ifndef WITH_HACL sodium_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); sodium_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); #else //WITH_SODIUM Lib_Memzero0_memzero(epriv, (uint32_t)32U * sizeof (epriv[0U])); Lib_Memzero0_memzero(epub, (uint32_t)32U * sizeof (epub[0U])); #endif //WITH_SODIUM Noise_XK_session_t *res1 = res; return res1; } /* Free a session. Be sure to free all sessions before freeing the device used to create those sessions. */ void Noise_XK_session_free(Noise_XK_session_t *sn) { Noise_XK_session_t st = sn[0U]; if (st.tag == Noise_XK_DS_Initiator) { Noise_XK_noise_string *pinfo = st.val.case_DS_Initiator.pinfo; uint8_t *spub = st.val.case_DS_Initiator.spub; uint8_t *spriv = st.val.case_DS_Initiator.spriv; Noise_XK_noise_string *info = st.val.case_DS_Initiator.info; Noise_XK_init_state_t state = st.val.case_DS_Initiator.state; if (state.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re = state.val.case_IMS_Handshake.re; uint8_t *st_rs = state.val.case_IMS_Handshake.rs; uint8_t *st_epub = state.val.case_IMS_Handshake.epub; uint8_t *st_epriv = state.val.case_IMS_Handshake.epriv; uint8_t *st_h = state.val.case_IMS_Handshake.h; uint8_t *st_ck = state.val.case_IMS_Handshake.chaining_key; uint8_t *st_k = state.val.case_IMS_Handshake.cipher_key; KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck); KRML_HOST_FREE(st_h); KRML_HOST_FREE(st_epriv); KRML_HOST_FREE(st_epub); KRML_HOST_FREE(st_rs); KRML_HOST_FREE(st_re); } else if (state.tag == Noise_XK_IMS_Transport) { uint8_t *receive_key = state.val.case_IMS_Transport.receive_key; uint8_t *send_key = state.val.case_IMS_Transport.send_key; uint8_t *st_h = state.val.case_IMS_Transport.h; KRML_HOST_FREE(st_h); KRML_HOST_FREE(send_key); KRML_HOST_FREE(receive_key); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } uint8_t *str = info[0U]; if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(info); uint8_t *str0 = pinfo[0U]; if (!(str0 == NULL)) KRML_HOST_FREE(str0); KRML_HOST_FREE(pinfo); KRML_HOST_FREE(spriv); KRML_HOST_FREE(spub); } else if (st.tag == Noise_XK_DS_Responder) { Noise_XK_noise_string *pinfo = st.val.case_DS_Responder.pinfo; uint8_t *spub = st.val.case_DS_Responder.spub; uint8_t *spriv = st.val.case_DS_Responder.spriv; Noise_XK_noise_string *info = st.val.case_DS_Responder.info; Noise_XK_resp_state_t state = st.val.case_DS_Responder.state; if (state.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re = state.val.case_IMS_Handshake.re; uint8_t *st_rs = state.val.case_IMS_Handshake.rs; uint8_t *st_epub = state.val.case_IMS_Handshake.epub; uint8_t *st_epriv = state.val.case_IMS_Handshake.epriv; uint8_t *st_h = state.val.case_IMS_Handshake.h; uint8_t *st_ck = state.val.case_IMS_Handshake.chaining_key; uint8_t *st_k = state.val.case_IMS_Handshake.cipher_key; KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck); KRML_HOST_FREE(st_h); KRML_HOST_FREE(st_epriv); KRML_HOST_FREE(st_epub); KRML_HOST_FREE(st_rs); KRML_HOST_FREE(st_re); } else if (state.tag == Noise_XK_IMS_Transport) { uint8_t *receive_key = state.val.case_IMS_Transport.receive_key; uint8_t *send_key = state.val.case_IMS_Transport.send_key; uint8_t *st_h = state.val.case_IMS_Transport.h; KRML_HOST_FREE(st_h); KRML_HOST_FREE(send_key); KRML_HOST_FREE(receive_key); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } uint8_t *str = info[0U]; if (!(str == NULL)) KRML_HOST_FREE(str); KRML_HOST_FREE(info); uint8_t *str0 = pinfo[0U]; if (!(str0 == NULL)) KRML_HOST_FREE(str0); KRML_HOST_FREE(pinfo); KRML_HOST_FREE(spriv); KRML_HOST_FREE(spub); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } KRML_HOST_FREE(sn); } typedef struct result_init_state_t_s { result_session_t_tags tag; union { Noise_XK_init_state_t case_Res; Noise_XK_error_code case_Fail; } val; } result_init_state_t; typedef struct result_resp_state_t_s { result_session_t_tags tag; union { Noise_XK_resp_state_t case_Res; Noise_XK_error_code case_Fail; } val; } result_resp_state_t; static Noise_XK_error_code state_handshake_write( uint32_t payload_len, uint8_t *payload, Noise_XK_session_t *dst_p, uint32_t outlen, uint8_t *out ) { Noise_XK_session_t *dst_p1 = dst_p; Noise_XK_session_t *stp = dst_p1; Noise_XK_session_t dst = stp[0U]; result_session_t res0; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dst_dv = dst.val.case_DS_Initiator.dv; Noise_XK_noise_string *dst_pinfo = dst.val.case_DS_Initiator.pinfo; uint32_t dst_pid = dst.val.case_DS_Initiator.pid; uint8_t *dst_spub = dst.val.case_DS_Initiator.spub; uint8_t *dst_spriv = dst.val.case_DS_Initiator.spriv; Noise_XK_noise_string *dst_info = dst.val.case_DS_Initiator.info; uint32_t dst_id = dst.val.case_DS_Initiator.id; Noise_XK_init_state_t dst_st = dst.val.case_DS_Initiator.state; if (dst_st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re = dst_st.val.case_IMS_Handshake.re; uint8_t *st_rs = dst_st.val.case_IMS_Handshake.rs; uint8_t *st_epub = dst_st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = dst_st.val.case_IMS_Handshake.epriv; uint8_t *st_spub = dst_st.val.case_IMS_Handshake.spub; uint8_t *st_spriv = dst_st.val.case_IMS_Handshake.spriv; uint8_t *st_h = dst_st.val.case_IMS_Handshake.h; uint8_t *st_ck = dst_st.val.case_IMS_Handshake.chaining_key; uint8_t *st_cipher = dst_st.val.case_IMS_Handshake.cipher_key; uint32_t st_step = dst_st.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)3U) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else if (!(true == (st_step % (uint32_t)2U == (uint32_t)0U))) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else { result_init_state_t res1; if (st_step == (uint32_t)0U) { result_init_state_t res; if (!(payload_len <= (uint32_t)4294967215U && outlen == (uint32_t)48U + payload_len)) res = ((result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else { uint32_t pat_outlen = (uint32_t)32U; uint8_t *pat_out = out; uint8_t *payload_out = out + pat_outlen; uint8_t *tk_out = pat_out; Noise_XK_mix_hash(st_h, (uint32_t)32U, st_epub); memcpy(tk_out, st_epub, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_error_code r0 = Noise_XK_mix_dh(st_epriv, st_rs, st_cipher, st_ck, st_h); Noise_XK_error_code r2 = r0; Noise_XK_error_code r1 = r2; Noise_XK_error_code r; if (!(r1 == Noise_XK_CSuccess)) r = r1; else { Noise_XK_encrypt_and_hash(payload_len, payload, payload_out, st_cipher, st_h, (uint64_t)0U); r = Noise_XK_CSuccess; } Noise_XK_error_code res0 = r; if (res0 == Noise_XK_CSuccess) res = ( (result_init_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = st_step + (uint32_t)1U, .cipher_key = st_cipher, .chaining_key = st_ck, .h = st_h, .spriv = st_spriv, .spub = st_spub, .epriv = st_epriv, .epub = st_epub, .rs = st_rs, .re = st_re } } } } } ); else switch (res0) { case Noise_XK_CDH_error: { res = ( (result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CDH_error } } ); break; } default: { KRML_HOST_EPRINTF("KaRaMeL incomplete match at %s:%d\n", __FILE__, __LINE__); KRML_HOST_EXIT(253U); } } } result_init_state_t res0 = res; res1 = res0; } else { result_init_state_t res; if (!(payload_len <= (uint32_t)4294967215U && outlen == (uint32_t)64U + payload_len)) res = ((result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else { uint32_t pat_outlen = (uint32_t)48U; uint8_t *pat_out = out; uint8_t *payload_out = out + pat_outlen; uint8_t *tk_out = pat_out; Noise_XK_encrypt_and_hash((uint32_t)32U, st_spub, tk_out, st_cipher, st_h, (uint64_t)1U); Noise_XK_error_code r0 = Noise_XK_mix_dh(st_spriv, st_re, st_cipher, st_ck, st_h); Noise_XK_error_code r2 = r0; Noise_XK_error_code r1 = r2; Noise_XK_error_code r; if (!(r1 == Noise_XK_CSuccess)) r = r1; else { Noise_XK_encrypt_and_hash(payload_len, payload, payload_out, st_cipher, st_h, (uint64_t)0U); r = Noise_XK_CSuccess; } Noise_XK_error_code res0 = r; if (res0 == Noise_XK_CSuccess) res = ( (result_init_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = st_step + (uint32_t)1U, .cipher_key = st_cipher, .chaining_key = st_ck, .h = st_h, .spriv = st_spriv, .spub = st_spub, .epriv = st_epriv, .epub = st_epub, .rs = st_rs, .re = st_re } } } } } ); else switch (res0) { case Noise_XK_CDH_error: { res = ( (result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CDH_error } } ); break; } default: { KRML_HOST_EPRINTF("KaRaMeL incomplete match at %s:%d\n", __FILE__, __LINE__); KRML_HOST_EXIT(253U); } } } result_init_state_t res0 = res; res1 = res0; } if (res1.tag == Fail) { Noise_XK_error_code e = res1.val.case_Fail; res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (res1.tag == Res) { Noise_XK_init_state_t st1 = res1.val.case_Res; Noise_XK_session_t ite; if (st_step == (uint32_t)2U) { Noise_XK_init_state_t st11; if (st1.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re1 = st1.val.case_IMS_Handshake.re; uint8_t *st_rs1 = st1.val.case_IMS_Handshake.rs; uint8_t *st_epub1 = st1.val.case_IMS_Handshake.epub; uint8_t *st_epriv1 = st1.val.case_IMS_Handshake.epriv; uint8_t *st_h1 = st1.val.case_IMS_Handshake.h; uint8_t *st_ck1 = st1.val.case_IMS_Handshake.chaining_key; uint8_t *st_k = st1.val.case_IMS_Handshake.cipher_key; uint8_t *k1 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *k2 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t temp_k1[64U] = { 0U }; uint8_t temp_k2[64U] = { 0U }; Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); #ifndef WITH_HACL sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); KRML_HOST_FREE(st_epub1); KRML_HOST_FREE(st_rs1); KRML_HOST_FREE(st_re1); st11 = ( (Noise_XK_init_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = st_h1, .recv_transport_message = false, .send_key = k1, .send_nonce = (uint64_t)0U, .receive_key = k2, .receive_nonce = (uint64_t)0U } } } ); } else st11 = KRML_EABORT(Noise_XK_init_state_t, "unreachable (pattern matches are exhaustive in F*)"); ite = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st11, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = dst_pid, .pinfo = dst_pinfo, .dv = dst_dv } } } ); } else ite = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st1, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = dst_pid, .pinfo = dst_pinfo, .dv = dst_dv } } } ); res0 = ((result_session_t){ .tag = Res, .val = { .case_Res = ite } }); } else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } } else if (dst_st.tag == Noise_XK_IMS_Transport) res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dst_dv = dst.val.case_DS_Responder.dv; Noise_XK_noise_string *dst_pinfo = dst.val.case_DS_Responder.pinfo; uint32_t dst_pid = dst.val.case_DS_Responder.pid; uint8_t *dst_spub = dst.val.case_DS_Responder.spub; uint8_t *dst_spriv = dst.val.case_DS_Responder.spriv; Noise_XK_noise_string *dst_info = dst.val.case_DS_Responder.info; uint32_t dst_id = dst.val.case_DS_Responder.id; Noise_XK_resp_state_t dst_st = dst.val.case_DS_Responder.state; if (dst_st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re = dst_st.val.case_IMS_Handshake.re; uint8_t *st_rs = dst_st.val.case_IMS_Handshake.rs; uint8_t *st_epub = dst_st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = dst_st.val.case_IMS_Handshake.epriv; uint8_t *st_spub = dst_st.val.case_IMS_Handshake.spub; uint8_t *st_spriv = dst_st.val.case_IMS_Handshake.spriv; uint8_t *st_h = dst_st.val.case_IMS_Handshake.h; uint8_t *st_ck = dst_st.val.case_IMS_Handshake.chaining_key; uint8_t *st_cipher = dst_st.val.case_IMS_Handshake.cipher_key; uint32_t st_step = dst_st.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)3U) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else if (!(false == (st_step % (uint32_t)2U == (uint32_t)0U))) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else { result_resp_state_t res; if (!(payload_len <= (uint32_t)4294967215U && outlen == (uint32_t)48U + payload_len)) res = ((result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else { uint32_t pat_outlen = (uint32_t)32U; uint8_t *pat_out = out; uint8_t *payload_out = out + pat_outlen; uint8_t *tk_out = pat_out; Noise_XK_mix_hash(st_h, (uint32_t)32U, st_epub); memcpy(tk_out, st_epub, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_error_code r0 = Noise_XK_mix_dh(st_epriv, st_re, st_cipher, st_ck, st_h); Noise_XK_error_code r2 = r0; Noise_XK_error_code r1 = r2; Noise_XK_error_code r; if (!(r1 == Noise_XK_CSuccess)) r = r1; else { Noise_XK_encrypt_and_hash(payload_len, payload, payload_out, st_cipher, st_h, (uint64_t)0U); r = Noise_XK_CSuccess; } Noise_XK_error_code res0 = r; if (res0 == Noise_XK_CSuccess) res = ( (result_resp_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = st_step + (uint32_t)1U, .cipher_key = st_cipher, .chaining_key = st_ck, .h = st_h, .spriv = st_spriv, .spub = st_spub, .epriv = st_epriv, .epub = st_epub, .rs = st_rs, .re = st_re } } } } } ); else switch (res0) { case Noise_XK_CDH_error: { res = ( (result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CDH_error } } ); break; } default: { KRML_HOST_EPRINTF("KaRaMeL incomplete match at %s:%d\n", __FILE__, __LINE__); KRML_HOST_EXIT(253U); } } } result_resp_state_t res1 = res; result_resp_state_t res2 = res1; if (res2.tag == Fail) { Noise_XK_error_code e = res2.val.case_Fail; res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (res2.tag == Res) { Noise_XK_resp_state_t st1 = res2.val.case_Res; Noise_XK_session_t ite; if (st_step == (uint32_t)2U) { Noise_XK_resp_state_t st11; if (st1.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re1 = st1.val.case_IMS_Handshake.re; uint8_t *st_rs1 = st1.val.case_IMS_Handshake.rs; uint8_t *st_epub1 = st1.val.case_IMS_Handshake.epub; uint8_t *st_epriv1 = st1.val.case_IMS_Handshake.epriv; uint8_t *st_h1 = st1.val.case_IMS_Handshake.h; uint8_t *st_ck1 = st1.val.case_IMS_Handshake.chaining_key; uint8_t *st_k = st1.val.case_IMS_Handshake.cipher_key; uint8_t *k1 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *k2 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t temp_k1[64U] = { 0U }; uint8_t temp_k2[64U] = { 0U }; Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); #ifndef WITH_HACL sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); KRML_HOST_FREE(st_epub1); KRML_HOST_FREE(st_rs1); KRML_HOST_FREE(st_re1); st11 = ( (Noise_XK_resp_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = st_h1, .send_key = k2, .send_nonce = (uint64_t)0U, .receive_key = k1, .receive_nonce = (uint64_t)0U } } } ); } else st11 = KRML_EABORT(Noise_XK_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); ite = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st11, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = dst_pid, .pinfo = dst_pinfo, .dv = dst_dv } } } ); } else ite = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st1, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = dst_pid, .pinfo = dst_pinfo, .dv = dst_dv } } } ); res0 = ((result_session_t){ .tag = Res, .val = { .case_Res = ite } }); } else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } } else if (dst_st.tag == Noise_XK_IMS_Transport) res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); if (res0.tag == Fail) { Noise_XK_error_code e = res0.val.case_Fail; Noise_XK_session_t dst1 = dst_p1[0U]; if (dst1.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dv = dst1.val.case_DS_Initiator.dv; Noise_XK_noise_string *pinfo = dst1.val.case_DS_Initiator.pinfo; uint32_t pid = dst1.val.case_DS_Initiator.pid; uint8_t *spub = dst1.val.case_DS_Initiator.spub; uint8_t *spriv = dst1.val.case_DS_Initiator.spriv; Noise_XK_noise_string *info = dst1.val.case_DS_Initiator.info; uint32_t id = dst1.val.case_DS_Initiator.id; Noise_XK_init_state_t st = dst1.val.case_DS_Initiator.state; Noise_XK_init_state_t ite; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *re = st.val.case_IMS_Handshake.re; uint8_t *rs = st.val.case_IMS_Handshake.rs; uint8_t *epub = st.val.case_IMS_Handshake.epub; uint8_t *epriv = st.val.case_IMS_Handshake.epriv; uint8_t *spub1 = st.val.case_IMS_Handshake.spub; uint8_t *spriv1 = st.val.case_IMS_Handshake.spriv; uint8_t *h3 = st.val.case_IMS_Handshake.h; uint8_t *ck = st.val.case_IMS_Handshake.chaining_key; uint8_t *k = st.val.case_IMS_Handshake.cipher_key; ite = ( (Noise_XK_init_state_t){ .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)4U, .cipher_key = k, .chaining_key = ck, .h = h3, .spriv = spriv1, .spub = spub1, .epriv = epriv, .epub = epub, .rs = rs, .re = re } } } ); } else if (st.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = st.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = st.val.case_IMS_Transport.receive_key; uint64_t send_nonce = st.val.case_IMS_Transport.send_nonce; uint8_t *send_key = st.val.case_IMS_Transport.send_key; bool recv_tpt_msg = st.val.case_IMS_Transport.recv_transport_message; uint8_t *h3 = st.val.case_IMS_Transport.h; ite = ( (Noise_XK_init_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h3, .recv_transport_message = recv_tpt_msg, .send_key = send_key, .send_nonce = send_nonce, .receive_key = receive_key, .receive_nonce = receive_nonce } } } ); } else ite = KRML_EABORT(Noise_XK_init_state_t, "unreachable (pattern matches are exhaustive in F*)"); dst_p1[0U] = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = ite, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid, .pinfo = pinfo, .dv = dv } } } ); } else if (dst1.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dv = dst1.val.case_DS_Responder.dv; Noise_XK_noise_string *pinfo = dst1.val.case_DS_Responder.pinfo; uint32_t pid = dst1.val.case_DS_Responder.pid; uint8_t *spub = dst1.val.case_DS_Responder.spub; uint8_t *spriv = dst1.val.case_DS_Responder.spriv; Noise_XK_noise_string *info = dst1.val.case_DS_Responder.info; uint32_t id = dst1.val.case_DS_Responder.id; Noise_XK_resp_state_t st = dst1.val.case_DS_Responder.state; Noise_XK_resp_state_t ite; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *re = st.val.case_IMS_Handshake.re; uint8_t *rs = st.val.case_IMS_Handshake.rs; uint8_t *epub = st.val.case_IMS_Handshake.epub; uint8_t *epriv = st.val.case_IMS_Handshake.epriv; uint8_t *spub1 = st.val.case_IMS_Handshake.spub; uint8_t *spriv1 = st.val.case_IMS_Handshake.spriv; uint8_t *h3 = st.val.case_IMS_Handshake.h; uint8_t *ck = st.val.case_IMS_Handshake.chaining_key; uint8_t *k = st.val.case_IMS_Handshake.cipher_key; ite = ( (Noise_XK_resp_state_t){ .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)4U, .cipher_key = k, .chaining_key = ck, .h = h3, .spriv = spriv1, .spub = spub1, .epriv = epriv, .epub = epub, .rs = rs, .re = re } } } ); } else if (st.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = st.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = st.val.case_IMS_Transport.receive_key; uint64_t send_nonce = st.val.case_IMS_Transport.send_nonce; uint8_t *send_key = st.val.case_IMS_Transport.send_key; uint8_t *h3 = st.val.case_IMS_Transport.h; ite = ( (Noise_XK_resp_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h3, .send_key = send_key, .send_nonce = send_nonce, .receive_key = receive_key, .receive_nonce = receive_nonce } } } ); } else ite = KRML_EABORT(Noise_XK_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); dst_p1[0U] = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = ite, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid, .pinfo = pinfo, .dv = dv } } } ); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } return e; } else if (res0.tag == Res) { Noise_XK_session_t dst1 = res0.val.case_Res; dst_p1[0U] = dst1; return Noise_XK_CSuccess; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } #define Res0 0 #define Fail0 1 typedef uint8_t result_unit_error_tags; typedef struct result_unit_error_s { result_unit_error_tags tag; Noise_XK_error_code v; } result_unit_error; static Noise_XK_error_code state_handshake_read( uint32_t payload_outlen, uint8_t *payload_out, Noise_XK_session_t *dst_p, uint32_t inlen, uint8_t *input ) { Noise_XK_session_t dst = dst_p[0U]; uint32_t pid; if (dst.tag == Noise_XK_DS_Initiator) pid = dst.val.case_DS_Initiator.pid; else if (dst.tag == Noise_XK_DS_Responder) pid = dst.val.case_DS_Responder.pid; else pid = KRML_EABORT(uint32_t, "unreachable (pattern matches are exhaustive in F*)"); uint32_t *pid_ptr = KRML_HOST_MALLOC(sizeof (uint32_t)); pid_ptr[0U] = pid; result_session_t res0; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dst_dv = dst.val.case_DS_Initiator.dv; Noise_XK_noise_string *dst_pinfo = dst.val.case_DS_Initiator.pinfo; uint32_t dst_pid = dst.val.case_DS_Initiator.pid; uint8_t *dst_spub = dst.val.case_DS_Initiator.spub; uint8_t *dst_spriv = dst.val.case_DS_Initiator.spriv; Noise_XK_noise_string *dst_info = dst.val.case_DS_Initiator.info; uint32_t dst_id = dst.val.case_DS_Initiator.id; Noise_XK_init_state_t dst_st = dst.val.case_DS_Initiator.state; if (dst_st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re = dst_st.val.case_IMS_Handshake.re; uint8_t *st_rs = dst_st.val.case_IMS_Handshake.rs; uint8_t *st_epub = dst_st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = dst_st.val.case_IMS_Handshake.epriv; uint8_t *st_spub = dst_st.val.case_IMS_Handshake.spub; uint8_t *st_spriv = dst_st.val.case_IMS_Handshake.spriv; uint8_t *st_h = dst_st.val.case_IMS_Handshake.h; uint8_t *st_ck = dst_st.val.case_IMS_Handshake.chaining_key; uint8_t *st_cipher = dst_st.val.case_IMS_Handshake.cipher_key; uint32_t st_step = dst_st.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)3U) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else if (!(true == (st_step % (uint32_t)2U == (uint32_t)1U))) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else { Noise_XK_error_code r0; if (!(payload_outlen <= (uint32_t)4294967215U && inlen == (uint32_t)48U + payload_outlen)) r0 = Noise_XK_CInput_size; else { uint8_t *msg_input = input; uint8_t *payload_input = input + (uint32_t)32U; uint8_t *tk_input = msg_input; Noise_XK_mix_hash(st_h, (uint32_t)32U, tk_input); memcpy(st_re, tk_input, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_error_code r20 = Noise_XK_mix_dh(st_epriv, st_re, st_cipher, st_ck, st_h); Noise_XK_error_code r1 = r20; Noise_XK_error_code r; if (!(r1 == Noise_XK_CSuccess)) r = r1; else { Noise_XK_error_code r2 = Noise_XK_decrypt_and_hash(payload_outlen, payload_out, payload_input, st_cipher, st_h, (uint64_t)0U); r = r2; } Noise_XK_error_code r2 = r; Noise_XK_error_code res = r2; if (res == Noise_XK_CSuccess) r0 = Noise_XK_CSuccess; else r0 = res; } Noise_XK_error_code r1 = r0; result_init_state_t r; switch (r1) { case Noise_XK_CSuccess: { r = ( (result_init_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = st_step + (uint32_t)1U, .cipher_key = st_cipher, .chaining_key = st_ck, .h = st_h, .spriv = st_spriv, .spub = st_spub, .epriv = st_epriv, .epub = st_epub, .rs = st_rs, .re = st_re } } } } } ); break; } default: { r = ((result_init_state_t){ .tag = Fail, .val = { .case_Fail = r1 } }); } } result_init_state_t res1 = r; result_init_state_t res2 = res1; result_session_t res; if (res2.tag == Fail) { Noise_XK_error_code e = res2.val.case_Fail; res = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (res2.tag == Res) { Noise_XK_init_state_t st1 = res2.val.case_Res; if (!(st_step == (uint32_t)2U)) res = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st1, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = dst_pid, .pinfo = dst_pinfo, .dv = dst_dv } } } } } ); else { uint32_t pid1 = pid_ptr[0U]; res = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st1, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = pid1, .pinfo = dst_pinfo, .dv = dst_dv } } } } } ); } } else res = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); if (res.tag == Fail) { Noise_XK_error_code e = res.val.case_Fail; res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (res.tag == Res) { Noise_XK_session_t dst1 = res.val.case_Res; Noise_XK_session_t dst2; if (dst1.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dv = dst1.val.case_DS_Initiator.dv; Noise_XK_noise_string *pinfo = dst1.val.case_DS_Initiator.pinfo; uint32_t pid1 = dst1.val.case_DS_Initiator.pid; uint8_t *spub = dst1.val.case_DS_Initiator.spub; uint8_t *spriv = dst1.val.case_DS_Initiator.spriv; Noise_XK_noise_string *info = dst1.val.case_DS_Initiator.info; uint32_t id = dst1.val.case_DS_Initiator.id; Noise_XK_init_state_t st = dst1.val.case_DS_Initiator.state; if (st_step == (uint32_t)2U) { Noise_XK_init_state_t st1; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re1 = st.val.case_IMS_Handshake.re; uint8_t *st_rs1 = st.val.case_IMS_Handshake.rs; uint8_t *st_epub1 = st.val.case_IMS_Handshake.epub; uint8_t *st_epriv1 = st.val.case_IMS_Handshake.epriv; uint8_t *st_h1 = st.val.case_IMS_Handshake.h; uint8_t *st_ck1 = st.val.case_IMS_Handshake.chaining_key; uint8_t *st_k = st.val.case_IMS_Handshake.cipher_key; uint8_t *k1 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *k2 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t temp_k1[64U] = { 0U }; uint8_t temp_k2[64U] = { 0U }; Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); #ifndef WITH_HACL sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); KRML_HOST_FREE(st_epub1); KRML_HOST_FREE(st_rs1); KRML_HOST_FREE(st_re1); st1 = ( (Noise_XK_init_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = st_h1, .recv_transport_message = false, .send_key = k1, .send_nonce = (uint64_t)0U, .receive_key = k2, .receive_nonce = (uint64_t)0U } } } ); } else st1 = KRML_EABORT(Noise_XK_init_state_t, "unreachable (pattern matches are exhaustive in F*)"); dst2 = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st1, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid1, .pinfo = pinfo, .dv = dv } } } ); } else dst2 = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid1, .pinfo = pinfo, .dv = dv } } } ); } else dst2 = KRML_EABORT(Noise_XK_session_t, "unreachable (pattern matches are exhaustive in F*)"); res0 = ((result_session_t){ .tag = Res, .val = { .case_Res = dst2 } }); } else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } } else if (dst_st.tag == Noise_XK_IMS_Transport) res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dst_dv = dst.val.case_DS_Responder.dv; Noise_XK_noise_string *dst_pinfo = dst.val.case_DS_Responder.pinfo; uint32_t dst_pid = dst.val.case_DS_Responder.pid; uint8_t *dst_spub = dst.val.case_DS_Responder.spub; uint8_t *dst_spriv = dst.val.case_DS_Responder.spriv; Noise_XK_noise_string *dst_info = dst.val.case_DS_Responder.info; uint32_t dst_id = dst.val.case_DS_Responder.id; Noise_XK_resp_state_t dst_st = dst.val.case_DS_Responder.state; if (dst_st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re = dst_st.val.case_IMS_Handshake.re; uint8_t *st_rs = dst_st.val.case_IMS_Handshake.rs; uint8_t *st_epub = dst_st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = dst_st.val.case_IMS_Handshake.epriv; uint8_t *st_spub = dst_st.val.case_IMS_Handshake.spub; uint8_t *st_spriv = dst_st.val.case_IMS_Handshake.spriv; uint8_t *st_h = dst_st.val.case_IMS_Handshake.h; uint8_t *st_ck = dst_st.val.case_IMS_Handshake.chaining_key; uint8_t *st_cipher = dst_st.val.case_IMS_Handshake.cipher_key; uint32_t st_step = dst_st.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)3U) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else if (!(false == (st_step % (uint32_t)2U == (uint32_t)1U))) res0 = ( (result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } } ); else { Noise_XK_device_t dv0 = dst_dv[0U]; result_resp_state_t res1; if (st_step == (uint32_t)0U) { Noise_XK_error_code r0; if (!(payload_outlen <= (uint32_t)4294967215U && inlen == (uint32_t)48U + payload_outlen)) r0 = Noise_XK_CInput_size; else { uint8_t *msg_input = input; uint8_t *payload_input = input + (uint32_t)32U; uint8_t *tk_input = msg_input; Noise_XK_mix_hash(st_h, (uint32_t)32U, tk_input); memcpy(st_re, tk_input, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_error_code r20 = Noise_XK_mix_dh(st_spriv, st_re, st_cipher, st_ck, st_h); Noise_XK_error_code r1 = r20; Noise_XK_error_code r; if (!(r1 == Noise_XK_CSuccess)) r = r1; else { Noise_XK_error_code r2 = Noise_XK_decrypt_and_hash(payload_outlen, payload_out, payload_input, st_cipher, st_h, (uint64_t)0U); r = r2; } Noise_XK_error_code r2 = r; Noise_XK_error_code res = r2; if (res == Noise_XK_CSuccess) r0 = Noise_XK_CSuccess; else r0 = res; } Noise_XK_error_code r1 = r0; result_resp_state_t r; switch (r1) { case Noise_XK_CSuccess: { r = ( (result_resp_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = st_step + (uint32_t)1U, .cipher_key = st_cipher, .chaining_key = st_ck, .h = st_h, .spriv = st_spriv, .spub = st_spub, .epriv = st_epriv, .epub = st_epub, .rs = st_rs, .re = st_re } } } } } ); break; } default: { r = ((result_resp_state_t){ .tag = Fail, .val = { .case_Fail = r1 } }); } } result_resp_state_t res = r; res1 = res; } else { result_unit_error r0; if (!(payload_outlen <= (uint32_t)4294967215U && inlen == (uint32_t)64U + payload_outlen)) r0 = ((result_unit_error){ .tag = Fail0, .v = Noise_XK_CInput_size }); else { uint8_t *msg1 = input; uint8_t *msg2 = input + (uint32_t)48U; Noise_XK_error_code r1 = Noise_XK_decrypt_and_hash((uint32_t)32U, st_rs, msg1, st_cipher, st_h, (uint64_t)1U); Noise_XK_error_code r3 = r1; Noise_XK_error_code r10 = r3; if (r10 == Noise_XK_CSuccess) { Noise_XK_cell **peers1 = dv0.dv_peers; Noise_XK_cell *llt = *peers1; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, st_rs); b0 = !b; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = Noise_XK_lbytes_eq((uint32_t)32U, x.p_s, st_rs); b = !b0; } cond = b; } Noise_XK_cell *llt1 = *&lltp; Noise_XK_peer_t *res; if (llt1 == NULL) res = NULL; else { Noise_XK_cell c = *llt1; res = c.data; } Noise_XK_peer_t *peer_ptr = res; bool b1; if (!(peer_ptr == NULL)) { Noise_XK_peer_t peer = peer_ptr[0U]; uint8_t *input_str = peer.p_info[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } dst_pinfo[0U] = out_str; pid_ptr[0U] = peer.p_id; b1 = true; } else { pid_ptr[0U] = (uint32_t)0U; b1 = false; } bool r1 = b1; if (r1) { uint8_t *payload_input = msg2; Noise_XK_error_code r11 = Noise_XK_mix_dh(st_epriv, st_rs, st_cipher, st_ck, st_h); Noise_XK_error_code r; if (!(r11 == Noise_XK_CSuccess)) r = r11; else { Noise_XK_error_code r2 = Noise_XK_decrypt_and_hash(payload_outlen, payload_out, payload_input, st_cipher, st_h, (uint64_t)0U); r = r2; } Noise_XK_error_code r1 = r; Noise_XK_error_code r2 = r1; if (r2 == Noise_XK_CSuccess) r0 = ((result_unit_error){ .tag = Res0 }); else r0 = ((result_unit_error){ .tag = Fail0, .v = r2 }); } else r0 = ((result_unit_error){ .tag = Fail0, .v = Noise_XK_CRs_rejected_by_policy }); } else r0 = ((result_unit_error){ .tag = Fail0, .v = r10 }); } result_resp_state_t res; if (r0.tag == Res0) res = ( (result_resp_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = st_step + (uint32_t)1U, .cipher_key = st_cipher, .chaining_key = st_ck, .h = st_h, .spriv = st_spriv, .spub = st_spub, .epriv = st_epriv, .epub = st_epub, .rs = st_rs, .re = st_re } } } } } ); else if (r0.tag == Fail0) { Noise_XK_error_code e = r0.v; res = ((result_resp_state_t){ .tag = Fail, .val = { .case_Fail = e } }); } else res = KRML_EABORT(result_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); res1 = res; } result_session_t res; if (res1.tag == Fail) { Noise_XK_error_code e = res1.val.case_Fail; res = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (res1.tag == Res) { Noise_XK_resp_state_t st1 = res1.val.case_Res; if (!(st_step == (uint32_t)2U)) res = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st1, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = dst_pid, .pinfo = dst_pinfo, .dv = dst_dv } } } } } ); else { uint32_t pid1 = pid_ptr[0U]; res = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st1, .id = dst_id, .info = dst_info, .spriv = dst_spriv, .spub = dst_spub, .pid = pid1, .pinfo = dst_pinfo, .dv = dst_dv } } } } } ); } } else res = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); if (res.tag == Fail) { Noise_XK_error_code e = res.val.case_Fail; res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (res.tag == Res) { Noise_XK_session_t dst1 = res.val.case_Res; Noise_XK_session_t dst2; if (dst1.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dv = dst1.val.case_DS_Responder.dv; Noise_XK_noise_string *pinfo = dst1.val.case_DS_Responder.pinfo; uint32_t pid1 = dst1.val.case_DS_Responder.pid; uint8_t *spub = dst1.val.case_DS_Responder.spub; uint8_t *spriv = dst1.val.case_DS_Responder.spriv; Noise_XK_noise_string *info = dst1.val.case_DS_Responder.info; uint32_t id = dst1.val.case_DS_Responder.id; Noise_XK_resp_state_t st = dst1.val.case_DS_Responder.state; if (st_step == (uint32_t)2U) { Noise_XK_resp_state_t st1; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_re1 = st.val.case_IMS_Handshake.re; uint8_t *st_rs1 = st.val.case_IMS_Handshake.rs; uint8_t *st_epub1 = st.val.case_IMS_Handshake.epub; uint8_t *st_epriv1 = st.val.case_IMS_Handshake.epriv; uint8_t *st_h1 = st.val.case_IMS_Handshake.h; uint8_t *st_ck1 = st.val.case_IMS_Handshake.chaining_key; uint8_t *st_k = st.val.case_IMS_Handshake.cipher_key; uint8_t *k1 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *k2 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t temp_k1[64U] = { 0U }; uint8_t temp_k2[64U] = { 0U }; Noise_XK_kdf(st_ck1, (uint32_t)0U, NULL, temp_k1, temp_k2, NULL); memcpy(k1, temp_k1, (uint32_t)32U * sizeof (uint8_t)); memcpy(k2, temp_k2, (uint32_t)32U * sizeof (uint8_t)); #ifndef WITH_HACL sodium_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); sodium_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #else // WITH_SODIUM Lib_Memzero0_memzero(temp_k1, (uint32_t)64U * sizeof (temp_k1[0U])); Lib_Memzero0_memzero(temp_k2, (uint32_t)64U * sizeof (temp_k2[0U])); #endif // WITH_SODIUM KRML_HOST_FREE(st_k); KRML_HOST_FREE(st_ck1); KRML_HOST_FREE(st_epriv1); KRML_HOST_FREE(st_epub1); KRML_HOST_FREE(st_rs1); KRML_HOST_FREE(st_re1); st1 = ( (Noise_XK_resp_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = st_h1, .send_key = k2, .send_nonce = (uint64_t)0U, .receive_key = k1, .receive_nonce = (uint64_t)0U } } } ); } else st1 = KRML_EABORT(Noise_XK_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); dst2 = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st1, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid1, .pinfo = pinfo, .dv = dv } } } ); } else dst2 = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid1, .pinfo = pinfo, .dv = dv } } } ); } else dst2 = KRML_EABORT(Noise_XK_session_t, "unreachable (pattern matches are exhaustive in F*)"); res0 = ((result_session_t){ .tag = Res, .val = { .case_Res = dst2 } }); } else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } } else if (dst_st.tag == Noise_XK_IMS_Transport) res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else res0 = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_FREE(pid_ptr); if (res0.tag == Fail) { Noise_XK_error_code e = res0.val.case_Fail; Noise_XK_session_t dst1 = dst_p[0U]; if (dst1.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dv = dst1.val.case_DS_Initiator.dv; Noise_XK_noise_string *pinfo = dst1.val.case_DS_Initiator.pinfo; uint32_t pid1 = dst1.val.case_DS_Initiator.pid; uint8_t *spub = dst1.val.case_DS_Initiator.spub; uint8_t *spriv = dst1.val.case_DS_Initiator.spriv; Noise_XK_noise_string *info = dst1.val.case_DS_Initiator.info; uint32_t id = dst1.val.case_DS_Initiator.id; Noise_XK_init_state_t st = dst1.val.case_DS_Initiator.state; Noise_XK_init_state_t ite; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *re = st.val.case_IMS_Handshake.re; uint8_t *rs = st.val.case_IMS_Handshake.rs; uint8_t *epub = st.val.case_IMS_Handshake.epub; uint8_t *epriv = st.val.case_IMS_Handshake.epriv; uint8_t *spub1 = st.val.case_IMS_Handshake.spub; uint8_t *spriv1 = st.val.case_IMS_Handshake.spriv; uint8_t *h4 = st.val.case_IMS_Handshake.h; uint8_t *ck = st.val.case_IMS_Handshake.chaining_key; uint8_t *k = st.val.case_IMS_Handshake.cipher_key; ite = ( (Noise_XK_init_state_t){ .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)4U, .cipher_key = k, .chaining_key = ck, .h = h4, .spriv = spriv1, .spub = spub1, .epriv = epriv, .epub = epub, .rs = rs, .re = re } } } ); } else if (st.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = st.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = st.val.case_IMS_Transport.receive_key; uint64_t send_nonce = st.val.case_IMS_Transport.send_nonce; uint8_t *send_key = st.val.case_IMS_Transport.send_key; bool recv_tpt_msg = st.val.case_IMS_Transport.recv_transport_message; uint8_t *h4 = st.val.case_IMS_Transport.h; ite = ( (Noise_XK_init_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h4, .recv_transport_message = recv_tpt_msg, .send_key = send_key, .send_nonce = send_nonce, .receive_key = receive_key, .receive_nonce = receive_nonce } } } ); } else ite = KRML_EABORT(Noise_XK_init_state_t, "unreachable (pattern matches are exhaustive in F*)"); dst_p[0U] = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = ite, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid1, .pinfo = pinfo, .dv = dv } } } ); } else if (dst1.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dv = dst1.val.case_DS_Responder.dv; Noise_XK_noise_string *pinfo = dst1.val.case_DS_Responder.pinfo; uint32_t pid1 = dst1.val.case_DS_Responder.pid; uint8_t *spub = dst1.val.case_DS_Responder.spub; uint8_t *spriv = dst1.val.case_DS_Responder.spriv; Noise_XK_noise_string *info = dst1.val.case_DS_Responder.info; uint32_t id = dst1.val.case_DS_Responder.id; Noise_XK_resp_state_t st = dst1.val.case_DS_Responder.state; Noise_XK_resp_state_t ite; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *re = st.val.case_IMS_Handshake.re; uint8_t *rs = st.val.case_IMS_Handshake.rs; uint8_t *epub = st.val.case_IMS_Handshake.epub; uint8_t *epriv = st.val.case_IMS_Handshake.epriv; uint8_t *spub1 = st.val.case_IMS_Handshake.spub; uint8_t *spriv1 = st.val.case_IMS_Handshake.spriv; uint8_t *h4 = st.val.case_IMS_Handshake.h; uint8_t *ck = st.val.case_IMS_Handshake.chaining_key; uint8_t *k = st.val.case_IMS_Handshake.cipher_key; ite = ( (Noise_XK_resp_state_t){ .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)4U, .cipher_key = k, .chaining_key = ck, .h = h4, .spriv = spriv1, .spub = spub1, .epriv = epriv, .epub = epub, .rs = rs, .re = re } } } ); } else if (st.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = st.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = st.val.case_IMS_Transport.receive_key; uint64_t send_nonce = st.val.case_IMS_Transport.send_nonce; uint8_t *send_key = st.val.case_IMS_Transport.send_key; uint8_t *h4 = st.val.case_IMS_Transport.h; ite = ( (Noise_XK_resp_state_t){ .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h4, .send_key = send_key, .send_nonce = send_nonce, .receive_key = receive_key, .receive_nonce = receive_nonce } } } ); } else ite = KRML_EABORT(Noise_XK_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); dst_p[0U] = ( (Noise_XK_session_t){ .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = ite, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid1, .pinfo = pinfo, .dv = dv } } } ); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } return e; } else if (res0.tag == Res) { Noise_XK_session_t dst1 = res0.val.case_Res; dst_p[0U] = dst1; return Noise_XK_CSuccess; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } static Noise_XK_error_code state_transport_write( uint32_t plen, uint8_t *p, uint32_t clen, uint8_t *c, Noise_XK_session_t *dst_p ) { Noise_XK_session_t dst = dst_p[0U]; result_session_t r; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dv = dst.val.case_DS_Initiator.dv; Noise_XK_noise_string *pinfo = dst.val.case_DS_Initiator.pinfo; uint32_t pid = dst.val.case_DS_Initiator.pid; uint8_t *spub = dst.val.case_DS_Initiator.spub; uint8_t *spriv = dst.val.case_DS_Initiator.spriv; Noise_XK_noise_string *info = dst.val.case_DS_Initiator.info; uint32_t id = dst.val.case_DS_Initiator.id; Noise_XK_init_state_t state = dst.val.case_DS_Initiator.state; bool ite; if (state.tag == Noise_XK_IMS_Handshake) ite = true; else ite = false; if (!ite) { result_init_state_t scrut; if (state.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = state.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = state.val.case_IMS_Transport.receive_key; uint64_t send_nonce = state.val.case_IMS_Transport.send_nonce; uint8_t *send_key = state.val.case_IMS_Transport.send_key; bool recv_tpt_msg = state.val.case_IMS_Transport.recv_transport_message; uint8_t *h = state.val.case_IMS_Transport.h; if (!(plen <= (uint32_t)4294967279U && clen == plen + (uint32_t)16U)) scrut = ((result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else if (send_nonce >= (uint64_t)18446744073709551615U) scrut = ( (result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CSaturated_nonce } } ); else { Noise_XK_aead_encrypt(send_key, send_nonce, (uint32_t)0U, NULL, plen, p, c); scrut = ( (result_init_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h, .recv_transport_message = recv_tpt_msg, .send_key = send_key, .send_nonce = send_nonce + (uint64_t)1U, .receive_key = receive_key, .receive_nonce = receive_nonce } } } } } ); } } else scrut = KRML_EABORT(result_init_state_t, "unreachable (pattern matches are exhaustive in F*)"); if (scrut.tag == Fail) { Noise_XK_error_code e = scrut.val.case_Fail; r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (scrut.tag == Res) { Noise_XK_init_state_t state_ = scrut.val.case_Res; r = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = state_, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid, .pinfo = pinfo, .dv = dv } } } } } ); } else r = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dv = dst.val.case_DS_Responder.dv; Noise_XK_noise_string *pinfo = dst.val.case_DS_Responder.pinfo; uint32_t pid = dst.val.case_DS_Responder.pid; uint8_t *spub = dst.val.case_DS_Responder.spub; uint8_t *spriv = dst.val.case_DS_Responder.spriv; Noise_XK_noise_string *info = dst.val.case_DS_Responder.info; uint32_t id = dst.val.case_DS_Responder.id; Noise_XK_resp_state_t state = dst.val.case_DS_Responder.state; bool ite; if (state.tag == Noise_XK_IMS_Handshake) ite = true; else ite = false; if (!ite) { result_resp_state_t scrut; if (state.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = state.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = state.val.case_IMS_Transport.receive_key; uint64_t send_nonce = state.val.case_IMS_Transport.send_nonce; uint8_t *send_key = state.val.case_IMS_Transport.send_key; uint8_t *h = state.val.case_IMS_Transport.h; if (!(plen <= (uint32_t)4294967279U && clen == plen + (uint32_t)16U)) scrut = ((result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else if (send_nonce >= (uint64_t)18446744073709551615U) scrut = ( (result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CSaturated_nonce } } ); else { Noise_XK_aead_encrypt(send_key, send_nonce, (uint32_t)0U, NULL, plen, p, c); scrut = ( (result_resp_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h, .send_key = send_key, .send_nonce = send_nonce + (uint64_t)1U, .receive_key = receive_key, .receive_nonce = receive_nonce } } } } } ); } } else scrut = KRML_EABORT(result_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); if (scrut.tag == Fail) { Noise_XK_error_code e = scrut.val.case_Fail; r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (scrut.tag == Res) { Noise_XK_resp_state_t state_ = scrut.val.case_Res; r = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = state_, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid, .pinfo = pinfo, .dv = dv } } } } } ); } else r = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); } else r = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); if (r.tag == Fail) return r.val.case_Fail; else if (r.tag == Res) { Noise_XK_session_t dst_ = r.val.case_Res; dst_p[0U] = dst_; return Noise_XK_CSuccess; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } static Noise_XK_error_code state_transport_read( uint32_t plen, uint8_t *p, uint32_t clen, uint8_t *c, Noise_XK_session_t *dst_p ) { Noise_XK_session_t dst = dst_p[0U]; result_session_t r; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_device_t *dv = dst.val.case_DS_Initiator.dv; Noise_XK_noise_string *pinfo = dst.val.case_DS_Initiator.pinfo; uint32_t pid = dst.val.case_DS_Initiator.pid; uint8_t *spub = dst.val.case_DS_Initiator.spub; uint8_t *spriv = dst.val.case_DS_Initiator.spriv; Noise_XK_noise_string *info = dst.val.case_DS_Initiator.info; uint32_t id = dst.val.case_DS_Initiator.id; Noise_XK_init_state_t state = dst.val.case_DS_Initiator.state; bool ite; if (state.tag == Noise_XK_IMS_Handshake) ite = true; else ite = false; if (!ite) { result_init_state_t scrut; if (state.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = state.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = state.val.case_IMS_Transport.receive_key; uint64_t send_nonce = state.val.case_IMS_Transport.send_nonce; uint8_t *send_key = state.val.case_IMS_Transport.send_key; uint8_t *h = state.val.case_IMS_Transport.h; if (!(plen <= (uint32_t)4294967279U && clen == plen + (uint32_t)16U)) scrut = ((result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else if (receive_nonce >= (uint64_t)18446744073709551615U) scrut = ( (result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CSaturated_nonce } } ); else switch (Noise_XK_aead_decrypt(receive_key, receive_nonce, (uint32_t)0U, NULL, plen, p, c)) { case Noise_XK_CDecrypt_error: { scrut = ( (result_init_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CDecrypt_error } } ); break; } case Noise_XK_CSuccess: { scrut = ( (result_init_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h, .recv_transport_message = true, .send_key = send_key, .send_nonce = send_nonce, .receive_key = receive_key, .receive_nonce = receive_nonce + (uint64_t)1U } } } } } ); break; } default: { KRML_HOST_EPRINTF("KaRaMeL incomplete match at %s:%d\n", __FILE__, __LINE__); KRML_HOST_EXIT(253U); } } } else scrut = KRML_EABORT(result_init_state_t, "unreachable (pattern matches are exhaustive in F*)"); if (scrut.tag == Fail) { Noise_XK_error_code e = scrut.val.case_Fail; r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (scrut.tag == Res) { Noise_XK_init_state_t state_ = scrut.val.case_Res; r = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = state_, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid, .pinfo = pinfo, .dv = dv } } } } } ); } else r = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_device_t *dv = dst.val.case_DS_Responder.dv; Noise_XK_noise_string *pinfo = dst.val.case_DS_Responder.pinfo; uint32_t pid = dst.val.case_DS_Responder.pid; uint8_t *spub = dst.val.case_DS_Responder.spub; uint8_t *spriv = dst.val.case_DS_Responder.spriv; Noise_XK_noise_string *info = dst.val.case_DS_Responder.info; uint32_t id = dst.val.case_DS_Responder.id; Noise_XK_resp_state_t state = dst.val.case_DS_Responder.state; bool ite; if (state.tag == Noise_XK_IMS_Handshake) ite = true; else ite = false; if (!ite) { result_resp_state_t scrut; if (state.tag == Noise_XK_IMS_Transport) { uint64_t receive_nonce = state.val.case_IMS_Transport.receive_nonce; uint8_t *receive_key = state.val.case_IMS_Transport.receive_key; uint64_t send_nonce = state.val.case_IMS_Transport.send_nonce; uint8_t *send_key = state.val.case_IMS_Transport.send_key; uint8_t *h = state.val.case_IMS_Transport.h; if (!(plen <= (uint32_t)4294967279U && clen == plen + (uint32_t)16U)) scrut = ((result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CInput_size } }); else if (receive_nonce >= (uint64_t)18446744073709551615U) scrut = ( (result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CSaturated_nonce } } ); else switch (Noise_XK_aead_decrypt(receive_key, receive_nonce, (uint32_t)0U, NULL, plen, p, c)) { case Noise_XK_CDecrypt_error: { scrut = ( (result_resp_state_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CDecrypt_error } } ); break; } case Noise_XK_CSuccess: { scrut = ( (result_resp_state_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_IMS_Transport, .val = { .case_IMS_Transport = { .h = h, .send_key = send_key, .send_nonce = send_nonce, .receive_key = receive_key, .receive_nonce = receive_nonce + (uint64_t)1U } } } } } ); break; } default: { KRML_HOST_EPRINTF("KaRaMeL incomplete match at %s:%d\n", __FILE__, __LINE__); KRML_HOST_EXIT(253U); } } } else scrut = KRML_EABORT(result_resp_state_t, "unreachable (pattern matches are exhaustive in F*)"); if (scrut.tag == Fail) { Noise_XK_error_code e = scrut.val.case_Fail; r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = e } }); } else if (scrut.tag == Res) { Noise_XK_resp_state_t state_ = scrut.val.case_Res; r = ( (result_session_t){ .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = state_, .id = id, .info = info, .spriv = spriv, .spub = spub, .pid = pid, .pinfo = pinfo, .dv = dv } } } } } ); } else r = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); } else r = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); } else r = KRML_EABORT(result_session_t, "unreachable (pattern matches are exhaustive in F*)"); if (r.tag == Fail) return r.val.case_Fail; else if (r.tag == Res) { Noise_XK_session_t dst_ = r.val.case_Res; dst_p[0U] = dst_; return Noise_XK_CSuccess; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } #define None 0 #define Some 1 typedef uint8_t option__uint32_t_tags; typedef struct option__uint32_t_s { option__uint32_t_tags tag; uint32_t v; } option__uint32_t; /* Write a message with the current session. If successful, this function will allocate a buffer of the proper length in `*out` and will write the length of this buffer in `*out_len`. Note that using `out` and `out_len` is always safe: if the function fails, it will set `*outlen` to 0 and `*out` to NULL. */ Noise_XK_rcode Noise_XK_session_write( Noise_XK_encap_message_t *payload, Noise_XK_session_t *sn_p, uint32_t *out_len, uint8_t **out ) { Noise_XK_session_t *sn_p1 = sn_p; Noise_XK_session_t *snp = sn_p1; Noise_XK_session_t sn = snp[0U]; if (sn.tag == Noise_XK_DS_Initiator) { Noise_XK_init_state_t sn_state = sn.val.case_DS_Initiator.state; if (sn_state.tag == Noise_XK_IMS_Transport) { bool recv_tpt_msg = sn_state.val.case_IMS_Transport.recv_transport_message; Noise_XK_encap_message_t encap_payload = payload[0U]; bool next_length_ok; if (encap_payload.em_message_len <= (uint32_t)4294967279U) { out_len[0U] = encap_payload.em_message_len + (uint32_t)16U; next_length_ok = true; } else next_length_ok = false; if (next_length_ok) { bool sec_ok; if (encap_payload.em_message_len == (uint32_t)0U) sec_ok = true; else { uint8_t clevel; if (recv_tpt_msg) clevel = (uint8_t)5U; else clevel = (uint8_t)5U; if (encap_payload.em_ac_level.tag == Noise_XK_Conf_level) { uint8_t req_level = encap_payload.em_ac_level.val.case_Conf_level; sec_ok = (req_level >= (uint8_t)2U && clevel >= req_level) || (req_level == (uint8_t)1U && (clevel == req_level || clevel >= (uint8_t)3U)) || req_level == (uint8_t)0U; } else sec_ok = false; } if (sec_ok) { uint32_t outlen = out_len[0U]; KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *out1 = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); Noise_XK_error_code res = state_transport_write(encap_payload.em_message_len, encap_payload.em_message, outlen, out1, sn_p1); if (res == Noise_XK_CSuccess) { out[0U] = out1; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; KRML_HOST_FREE(out1); out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = e } }); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CSecurity_level } } ); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } }); } } else if (sn_state.tag == Noise_XK_IMS_Handshake) { uint32_t st_step = sn_state.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)4U) { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = Noise_XK_CIncorrect_transition } } ); } else { Noise_XK_encap_message_t encap_payload = payload[0U]; option__uint32_t scrut; if ((uint32_t)0U == st_step) if (encap_payload.em_message_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = encap_payload.em_message_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)1U == st_step) if (encap_payload.em_message_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = encap_payload.em_message_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)2U == st_step) if (encap_payload.em_message_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = encap_payload.em_message_len + (uint32_t)64U }); else scrut = ((option__uint32_t){ .tag = None }); else scrut = ((option__uint32_t){ .tag = None }); bool next_length_ok; if (scrut.tag == Some) { uint32_t l = scrut.v; out_len[0U] = l; next_length_ok = true; } else next_length_ok = false; if (next_length_ok) { bool sec_ok; if (encap_payload.em_message_len == (uint32_t)0U) sec_ok = true; else { uint8_t clevel; if (st_step == (uint32_t)0U) clevel = (uint8_t)2U; else if (st_step == (uint32_t)1U) clevel = (uint8_t)1U; else clevel = (uint8_t)5U; if (encap_payload.em_ac_level.tag == Noise_XK_Conf_level) { uint8_t req_level = encap_payload.em_ac_level.val.case_Conf_level; sec_ok = (req_level >= (uint8_t)2U && clevel >= req_level) || (req_level == (uint8_t)1U && (clevel == req_level || clevel >= (uint8_t)3U)) || req_level == (uint8_t)0U; } else sec_ok = false; } if (sec_ok) { uint32_t outlen = out_len[0U]; KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *out1 = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); Noise_XK_error_code res = state_handshake_write(encap_payload.em_message_len, encap_payload.em_message, sn_p1, outlen, out1); if (res == Noise_XK_CSuccess) { out[0U] = out1; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; KRML_HOST_FREE(out1); out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = e } }); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CSecurity_level } } ); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } } ); } } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else if (sn.tag == Noise_XK_DS_Responder) { Noise_XK_resp_state_t sn_state = sn.val.case_DS_Responder.state; if (sn_state.tag == Noise_XK_IMS_Transport) { Noise_XK_encap_message_t encap_payload = payload[0U]; bool next_length_ok; if (encap_payload.em_message_len <= (uint32_t)4294967279U) { out_len[0U] = encap_payload.em_message_len + (uint32_t)16U; next_length_ok = true; } else next_length_ok = false; if (next_length_ok) { bool sec_ok; if (encap_payload.em_message_len == (uint32_t)0U) sec_ok = true; else { uint8_t clevel = (uint8_t)5U; if (encap_payload.em_ac_level.tag == Noise_XK_Conf_level) { uint8_t req_level = encap_payload.em_ac_level.val.case_Conf_level; sec_ok = (req_level >= (uint8_t)2U && clevel >= req_level) || (req_level == (uint8_t)1U && (clevel == req_level || clevel >= (uint8_t)3U)) || req_level == (uint8_t)0U; } else sec_ok = false; } if (sec_ok) { uint32_t outlen = out_len[0U]; KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *out1 = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); Noise_XK_error_code res = state_transport_write(encap_payload.em_message_len, encap_payload.em_message, outlen, out1, sn_p1); if (res == Noise_XK_CSuccess) { out[0U] = out1; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; KRML_HOST_FREE(out1); out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = e } }); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CSecurity_level } } ); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } }); } } else if (sn_state.tag == Noise_XK_IMS_Handshake) { uint32_t st_step = sn_state.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)4U) { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = Noise_XK_CIncorrect_transition } } ); } else { Noise_XK_encap_message_t encap_payload = payload[0U]; option__uint32_t scrut; if ((uint32_t)0U == st_step) if (encap_payload.em_message_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = encap_payload.em_message_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)1U == st_step) if (encap_payload.em_message_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = encap_payload.em_message_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)2U == st_step) if (encap_payload.em_message_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = encap_payload.em_message_len + (uint32_t)64U }); else scrut = ((option__uint32_t){ .tag = None }); else scrut = ((option__uint32_t){ .tag = None }); bool next_length_ok; if (scrut.tag == Some) { uint32_t l = scrut.v; out_len[0U] = l; next_length_ok = true; } else next_length_ok = false; if (next_length_ok) { bool sec_ok; if (encap_payload.em_message_len == (uint32_t)0U) sec_ok = true; else { uint8_t clevel; if (st_step == (uint32_t)0U) clevel = (uint8_t)2U; else if (st_step == (uint32_t)1U) clevel = (uint8_t)1U; else clevel = (uint8_t)5U; if (encap_payload.em_ac_level.tag == Noise_XK_Conf_level) { uint8_t req_level = encap_payload.em_ac_level.val.case_Conf_level; sec_ok = (req_level >= (uint8_t)2U && clevel >= req_level) || (req_level == (uint8_t)1U && (clevel == req_level || clevel >= (uint8_t)3U)) || req_level == (uint8_t)0U; } else sec_ok = false; } if (sec_ok) { uint32_t outlen = out_len[0U]; KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *out1 = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); Noise_XK_error_code res = state_handshake_write(encap_payload.em_message_len, encap_payload.em_message, sn_p1, outlen, out1); if (res == Noise_XK_CSuccess) { out[0U] = out1; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; KRML_HOST_FREE(out1); out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = e } }); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CSecurity_level } } ); } } else { out_len[0U] = (uint32_t)0U; out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } } ); } } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Read a message with the current session. If successful, this function will allocate a an encapsulated message in `*payload_out`. Note that using `payload_out` is always safe: if the function fails, it will set `*payload_out` to NULL. */ Noise_XK_rcode Noise_XK_session_read( Noise_XK_encap_message_t **payload_out, Noise_XK_session_t *sn_p, uint32_t inlen, uint8_t *input ) { Noise_XK_session_t *sn_p1 = sn_p; Noise_XK_session_t *snp = sn_p1; Noise_XK_session_t sn = snp[0U]; if (sn.tag == Noise_XK_DS_Initiator) { Noise_XK_init_state_t sn_state = sn.val.case_DS_Initiator.state; if (sn_state.tag == Noise_XK_IMS_Transport) { option__uint32_t scrut; if (inlen >= (uint32_t)16U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)16U }); else scrut = ((option__uint32_t){ .tag = None }); if (scrut.tag == Some) { uint32_t outlen = scrut.v; uint8_t *out; if (outlen > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *buf = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); out = buf; } else out = NULL; Noise_XK_error_code res = state_transport_read(outlen, out, inlen, input, sn_p1); if (res == Noise_XK_CSuccess) { KRML_CHECK_SIZE(sizeof (Noise_XK_encap_message_t), (uint32_t)1U); Noise_XK_encap_message_t *em_ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_encap_message_t)); em_ptr[0U] = ( (Noise_XK_encap_message_t){ .em_ac_level = { .tag = Noise_XK_Auth_level, .val = { .case_Auth_level = (uint8_t)2U } }, .em_message_len = outlen, .em_message = out } ); Noise_XK_encap_message_t *emp = em_ptr; payload_out[0U] = emp; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; if (!(out == NULL)) KRML_HOST_FREE(out); payload_out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = e } }); } } else { payload_out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } }); } } else if (sn_state.tag == Noise_XK_IMS_Handshake) { uint32_t st_step = sn_state.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)4U) { payload_out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = Noise_XK_CIncorrect_transition } } ); } else if (st_step % (uint32_t)2U == (uint32_t)1U && st_step < (uint32_t)3U) { option__uint32_t scrut; if ((uint32_t)0U == st_step) if (inlen >= (uint32_t)48U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)1U == st_step) if (inlen >= (uint32_t)48U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)2U == st_step) if (inlen >= (uint32_t)64U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)64U }); else scrut = ((option__uint32_t){ .tag = None }); else scrut = ((option__uint32_t){ .tag = None }); if (scrut.tag == Some) { uint32_t outlen = scrut.v; uint8_t alevel; if (st_step == (uint32_t)0U) alevel = (uint8_t)0U; else if (st_step == (uint32_t)1U) alevel = (uint8_t)2U; else alevel = (uint8_t)2U; uint8_t *out; if (outlen > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *buf = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); out = buf; } else out = NULL; Noise_XK_error_code res = state_handshake_read(outlen, out, sn_p1, inlen, input); if (res == Noise_XK_CSuccess) { KRML_CHECK_SIZE(sizeof (Noise_XK_encap_message_t), (uint32_t)1U); Noise_XK_encap_message_t *em_ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_encap_message_t)); em_ptr[0U] = ( (Noise_XK_encap_message_t){ .em_ac_level = { .tag = Noise_XK_Auth_level, .val = { .case_Auth_level = alevel } }, .em_message_len = outlen, .em_message = out } ); Noise_XK_encap_message_t *emp = em_ptr; payload_out[0U] = emp; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; if (!(out == NULL)) KRML_HOST_FREE(out); payload_out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = e } }); } } else { payload_out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } } ); } } else { payload_out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CIncorrect_transition } } ); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else if (sn.tag == Noise_XK_DS_Responder) { Noise_XK_resp_state_t sn_state = sn.val.case_DS_Responder.state; if (sn_state.tag == Noise_XK_IMS_Transport) { option__uint32_t scrut; if (inlen >= (uint32_t)16U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)16U }); else scrut = ((option__uint32_t){ .tag = None }); if (scrut.tag == Some) { uint32_t outlen = scrut.v; uint8_t *out; if (outlen > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *buf = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); out = buf; } else out = NULL; Noise_XK_error_code res = state_transport_read(outlen, out, inlen, input, sn_p1); if (res == Noise_XK_CSuccess) { KRML_CHECK_SIZE(sizeof (Noise_XK_encap_message_t), (uint32_t)1U); Noise_XK_encap_message_t *em_ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_encap_message_t)); em_ptr[0U] = ( (Noise_XK_encap_message_t){ .em_ac_level = { .tag = Noise_XK_Auth_level, .val = { .case_Auth_level = (uint8_t)2U } }, .em_message_len = outlen, .em_message = out } ); Noise_XK_encap_message_t *emp = em_ptr; payload_out[0U] = emp; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; if (!(out == NULL)) KRML_HOST_FREE(out); payload_out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = e } }); } } else { payload_out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } }); } } else if (sn_state.tag == Noise_XK_IMS_Handshake) { uint32_t st_step = sn_state.val.case_IMS_Handshake.step; if (st_step >= (uint32_t)4U) { payload_out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = Noise_XK_CIncorrect_transition } } ); } else if (st_step % (uint32_t)2U == (uint32_t)0U && st_step < (uint32_t)3U) { option__uint32_t scrut; if ((uint32_t)0U == st_step) if (inlen >= (uint32_t)48U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)1U == st_step) if (inlen >= (uint32_t)48U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)2U == st_step) if (inlen >= (uint32_t)64U) scrut = ((option__uint32_t){ .tag = Some, .v = inlen - (uint32_t)64U }); else scrut = ((option__uint32_t){ .tag = None }); else scrut = ((option__uint32_t){ .tag = None }); if (scrut.tag == Some) { uint32_t outlen = scrut.v; uint8_t alevel; if (st_step == (uint32_t)0U) alevel = (uint8_t)0U; else if (st_step == (uint32_t)1U) alevel = (uint8_t)2U; else alevel = (uint8_t)2U; uint8_t *out; if (outlen > (uint32_t)0U) { KRML_CHECK_SIZE(sizeof (uint8_t), outlen); uint8_t *buf = KRML_HOST_CALLOC(outlen, sizeof (uint8_t)); out = buf; } else out = NULL; Noise_XK_error_code res = state_handshake_read(outlen, out, sn_p1, inlen, input); if (res == Noise_XK_CSuccess) { KRML_CHECK_SIZE(sizeof (Noise_XK_encap_message_t), (uint32_t)1U); Noise_XK_encap_message_t *em_ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_encap_message_t)); em_ptr[0U] = ( (Noise_XK_encap_message_t){ .em_ac_level = { .tag = Noise_XK_Auth_level, .val = { .case_Auth_level = alevel } }, .em_message_len = outlen, .em_message = out } ); Noise_XK_encap_message_t *emp = em_ptr; payload_out[0U] = emp; return ((Noise_XK_rcode){ .tag = Noise_XK_Success }); } else { Noise_XK_error_code e = res; if (!(out == NULL)) KRML_HOST_FREE(out); payload_out[0U] = NULL; return ((Noise_XK_rcode){ .tag = Noise_XK_Stuck, .val = { .case_Stuck = e } }); } } else { payload_out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CInput_size } } ); } } else { payload_out[0U] = NULL; return ( (Noise_XK_rcode){ .tag = Noise_XK_Error, .val = { .case_Error = Noise_XK_CIncorrect_transition } } ); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Compute the length of the next message, given a payload length. Note that the function may fail, if the length of the message is too long for example (though very unlikely). You thus need to check the returned value. Also note that the length of the next message is always equal to: payload length + a value depending only on the current step. */ bool Noise_XK_session_compute_next_message_len( uint32_t *out, Noise_XK_session_t *sn, uint32_t payload_len ) { Noise_XK_session_t dst = sn[0U]; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_init_state_t st = dst.val.case_DS_Initiator.state; if (st.tag == Noise_XK_IMS_Handshake) { uint32_t step = st.val.case_IMS_Handshake.step; option__uint32_t scrut; if ((uint32_t)0U == step) if (payload_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = payload_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)1U == step) if (payload_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = payload_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)2U == step) if (payload_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = payload_len + (uint32_t)64U }); else scrut = ((option__uint32_t){ .tag = None }); else scrut = ((option__uint32_t){ .tag = None }); if (scrut.tag == Some) { uint32_t l = scrut.v; out[0U] = l; return true; } else return false; } else if (st.tag == Noise_XK_IMS_Transport) if (payload_len <= (uint32_t)4294967279U) { out[0U] = payload_len + (uint32_t)16U; return true; } else return false; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_resp_state_t st = dst.val.case_DS_Responder.state; if (st.tag == Noise_XK_IMS_Handshake) { uint32_t step = st.val.case_IMS_Handshake.step; option__uint32_t scrut; if ((uint32_t)0U == step) if (payload_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = payload_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)1U == step) if (payload_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = payload_len + (uint32_t)48U }); else scrut = ((option__uint32_t){ .tag = None }); else if ((uint32_t)2U == step) if (payload_len <= (uint32_t)4294967215U) scrut = ((option__uint32_t){ .tag = Some, .v = payload_len + (uint32_t)64U }); else scrut = ((option__uint32_t){ .tag = None }); else scrut = ((option__uint32_t){ .tag = None }); if (scrut.tag == Some) { uint32_t l = scrut.v; out[0U] = l; return true; } else return false; } else if (st.tag == Noise_XK_IMS_Transport) if (payload_len <= (uint32_t)4294967279U) { out[0U] = payload_len + (uint32_t)16U; return true; } else return false; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Return the current status. */ Noise_XK_status Noise_XK_session_get_status(Noise_XK_session_t *sn) { Noise_XK_session_t dst = sn[0U]; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_init_state_t st = dst.val.case_DS_Initiator.state; if (st.tag == Noise_XK_IMS_Handshake) { uint32_t step = st.val.case_IMS_Handshake.step; if (step % (uint32_t)2U == (uint32_t)0U) return Noise_XK_Handshake_write; else return Noise_XK_Handshake_read; } else if (st.tag == Noise_XK_IMS_Transport) return Noise_XK_Transport; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_resp_state_t st = dst.val.case_DS_Responder.state; if (st.tag == Noise_XK_IMS_Handshake) { uint32_t step = st.val.case_IMS_Handshake.step; if (step % (uint32_t)2U == (uint32_t)0U) return Noise_XK_Handshake_read; else return Noise_XK_Handshake_write; } else if (st.tag == Noise_XK_IMS_Transport) return Noise_XK_Transport; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Copy the session hash to the user provided buffer. Note that the session hash is always public. Using the session hash might be pertinent once the session has reached the transport phase. */ void Noise_XK_session_get_hash(uint8_t *out, Noise_XK_session_t *sn) { Noise_XK_session_t dst = sn[0U]; uint8_t *h; if (dst.tag == Noise_XK_DS_Initiator) { Noise_XK_init_state_t st = dst.val.case_DS_Initiator.state; if (st.tag == Noise_XK_IMS_Handshake) h = st.val.case_IMS_Handshake.h; else if (st.tag == Noise_XK_IMS_Transport) h = st.val.case_IMS_Transport.h; else h = KRML_EABORT(uint8_t *, "unreachable (pattern matches are exhaustive in F*)"); } else if (dst.tag == Noise_XK_DS_Responder) { Noise_XK_resp_state_t st = dst.val.case_DS_Responder.state; if (st.tag == Noise_XK_IMS_Handshake) h = st.val.case_IMS_Handshake.h; else if (st.tag == Noise_XK_IMS_Transport) h = st.val.case_IMS_Transport.h; else h = KRML_EABORT(uint8_t *, "unreachable (pattern matches are exhaustive in F*)"); } else h = KRML_EABORT(uint8_t *, "unreachable (pattern matches are exhaustive in F*)"); memcpy(out, h, (uint32_t)64U * sizeof (uint8_t)); } /* Return the session unique identifier. */ uint32_t Noise_XK_session_get_id(Noise_XK_session_t *sn) { Noise_XK_session_t st = sn[0U]; if (st.tag == Noise_XK_DS_Initiator) return st.val.case_DS_Initiator.id; else if (st.tag == Noise_XK_DS_Responder) return st.val.case_DS_Responder.id; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Copy the session information to the user provided pointer. */ void Noise_XK_session_get_info(Noise_XK_noise_string *out, Noise_XK_session_t *sn) { Noise_XK_session_t st = sn[0U]; if (st.tag == Noise_XK_DS_Initiator) { Noise_XK_noise_string *info = st.val.case_DS_Initiator.info; uint8_t *input_str = info[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } out[0U] = out_str; } else if (st.tag == Noise_XK_DS_Responder) { Noise_XK_noise_string *info = st.val.case_DS_Responder.info; uint8_t *input_str = info[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } out[0U] = out_str; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Return the session's peer unique identifier. The remote may be unknown, in which case the returned id will be 0. Note that you can safely use the returned peer id without testing it, because all the functions taking peer ids as parameters were written to correctly manipulate 0. In particular, looking up id 0 will return NULL, and trying to create a session with peer id 0 will cleanly fail by also returning NULL. */ uint32_t Noise_XK_session_get_peer_id(Noise_XK_session_t *sn) { Noise_XK_session_t st = sn[0U]; if (st.tag == Noise_XK_DS_Initiator) return st.val.case_DS_Initiator.pid; else if (st.tag == Noise_XK_DS_Responder) return st.val.case_DS_Responder.pid; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Copy the session peer information, if known, to the user provided pointer. The remote may be unknown yet, in which case there is no peer information in the device and the function will return false. */ bool Noise_XK_session_get_peer_info(Noise_XK_noise_string *out, Noise_XK_session_t *sn) { Noise_XK_session_t st = sn[0U]; if (st.tag == Noise_XK_DS_Initiator) { Noise_XK_noise_string *pinfo = st.val.case_DS_Initiator.pinfo; uint32_t pid = st.val.case_DS_Initiator.pid; if (pid != (uint32_t)0U) { uint8_t *input_str = pinfo[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } out[0U] = out_str; return true; } else return false; } else if (st.tag == Noise_XK_DS_Responder) { Noise_XK_noise_string *pinfo = st.val.case_DS_Responder.pinfo; uint32_t pid = st.val.case_DS_Responder.pid; if (pid != (uint32_t)0U) { uint8_t *input_str = pinfo[0U]; bool b = input_str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = input_str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = input_str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = input_str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = input_str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } out[0U] = out_str; return true; } else return false; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* Return true if this session has reached the maximum security level for this pattern. Once the maximum security level is reached, it is not possible to have better confidentiality/authentication guarantees for the payloads sent/received with this session. Note that the guarantees provided by the maximum reachable level vary with the pattern, which must thus be carefully chosen. In order to reach the maximum level, the session must have finished the handshake. Moreover, in case the session sends the last handshake message, it must wait for the first transport message from the remote: otherwise, we have no way to know whether the remote was itself able to finish the handshake. */ bool Noise_XK_session_reached_max_security(Noise_XK_session_t *snp) { Noise_XK_session_t sn = snp[0U]; if (sn.tag == Noise_XK_DS_Initiator) { Noise_XK_init_state_t sn_state = sn.val.case_DS_Initiator.state; if (sn_state.tag == Noise_XK_IMS_Transport) return sn_state.val.case_IMS_Transport.recv_transport_message; else if (sn_state.tag == Noise_XK_IMS_Handshake) return false; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else if (sn.tag == Noise_XK_DS_Responder) { Noise_XK_resp_state_t sn_state = sn.val.case_DS_Responder.state; if (sn_state.tag == Noise_XK_IMS_Transport) return true; else if (sn_state.tag == Noise_XK_IMS_Handshake) return false; else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* DO NOT use this: for tests and benchmarks only */ Noise_XK_session_t *Noise_XK__session_create_initiator_with_ephemeral( Noise_XK_device_t *dvp, uint8_t *epriv, uint8_t *epub, uint32_t pid ) { Noise_XK_device_t dv = dvp[0U]; result_session_t res0; if (dv.dv_states_counter == (uint32_t)4294967295U) res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); else { Noise_XK_device_t dv1 = dvp[0U]; Noise_XK_peer_t *peer_ptr; if (pid == (uint32_t)0U) peer_ptr = NULL; else { Noise_XK_cell *llt = *dv1.dv_peers; Noise_XK_cell *lltp = llt; Noise_XK_cell *llt10 = lltp; bool b0; if (llt10 == NULL) b0 = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b = x.p_id == pid; b0 = !b; } bool cond = b0; while (cond) { Noise_XK_cell *llt1 = lltp; Noise_XK_cell c0 = llt1[0U]; lltp = c0.next; Noise_XK_cell *llt10 = lltp; bool b; if (llt10 == NULL) b = false; else { Noise_XK_cell c = llt10[0U]; Noise_XK_peer_t x = c.data[0U]; bool b0 = x.p_id == pid; b = !b0; } cond = b; } Noise_XK_cell *llt1 = *&lltp; Noise_XK_peer_t *res; if (llt1 == NULL) res = NULL; else { Noise_XK_cell c = *llt1; res = c.data; } peer_ptr = res; } bool p_is_null = peer_ptr == NULL; if (p_is_null) res0 = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CUnknown_peer_id } }); else { uint8_t *o0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o0, dv.dv_spriv, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spriv = o0; uint8_t *o = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o, dv.dv_spub, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spub = o; uint8_t *str0 = dv.dv_info[0U]; bool b0 = str0 == NULL; uint8_t *out_str0; if (b0) out_str0 = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = str0[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = str0[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str0 = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = str0[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = str0[n]; out_str[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = str0[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str[n] = (uint8_t)0U; uint8_t *out_str1 = out_str; out_str0 = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = out_str0; Noise_XK_noise_string *st_info = out_ptr; Noise_XK_peer_t peer = peer_ptr[0U]; uint8_t *str = peer.p_info[0U]; bool b = str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr0 = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr0[0U] = out_str; Noise_XK_noise_string *st_pinfo = out_ptr0; uint8_t *rs = peer.p_s; dvp[0U] = ( (Noise_XK_device_t){ .dv_info = dv.dv_info, .dv_sk = dv.dv_sk, .dv_spriv = dv.dv_spriv, .dv_spub = dv.dv_spub, .dv_prologue = dv.dv_prologue, .dv_states_counter = dv.dv_states_counter + (uint32_t)1U, .dv_peers = dv.dv_peers, .dv_peers_counter = dv.dv_peers_counter } ); uint8_t *st_k = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_ck0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_h0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_spriv1 = st_spriv; uint8_t *st_spub1 = st_spub; uint8_t *st_epriv0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_epub0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_rs0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_re = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); Noise_XK_init_state_t st = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)0U, .cipher_key = st_k, .chaining_key = st_ck0, .h = st_h0, .spriv = st_spriv1, .spub = st_spub1, .epriv = st_epriv0, .epub = st_epub0, .rs = st_rs0, .re = st_re } } }; uint8_t pname[33U] = { (uint8_t)78U, (uint8_t)111U, (uint8_t)105U, (uint8_t)115U, (uint8_t)101U, (uint8_t)95U, (uint8_t)88U, (uint8_t)75U, (uint8_t)95U, (uint8_t)50U, (uint8_t)53U, (uint8_t)53U, (uint8_t)49U, (uint8_t)57U, (uint8_t)95U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)80U, (uint8_t)111U, (uint8_t)108U, (uint8_t)121U, (uint8_t)95U, (uint8_t)66U, (uint8_t)76U, (uint8_t)65U, (uint8_t)75U, (uint8_t)69U, (uint8_t)50U, (uint8_t)98U }; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_rs = st.val.case_IMS_Handshake.rs; uint8_t *st_epub = st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = st.val.case_IMS_Handshake.epriv; uint8_t *st_h = st.val.case_IMS_Handshake.h; uint8_t *st_ck = st.val.case_IMS_Handshake.chaining_key; if ((uint32_t)33U <= (uint32_t)64U) memcpy(st_h, pname, (uint32_t)33U * sizeof (uint8_t)); else Noise_XK_hash(st_h, (uint32_t)33U, pname); memcpy(st_ck, st_h, (uint32_t)64U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, dv.dv_prologue.size, dv.dv_prologue.buffer); memcpy(st_epriv, epriv, (uint32_t)32U * sizeof (uint8_t)); memcpy(st_epub, epub, (uint32_t)32U * sizeof (uint8_t)); memcpy(st_rs, rs, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, (uint32_t)32U, rs); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } Noise_XK_init_state_t st0 = st; result_session_t res = { .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Initiator, .val = { .case_DS_Initiator = { .state = st0, .id = dv.dv_states_counter, .info = st_info, .spriv = st_spriv, .spub = st_spub, .pid = pid, .pinfo = st_pinfo, .dv = dvp } } } } }; res0 = res; } } if (res0.tag == Fail) return NULL; else if (res0.tag == Res) { Noise_XK_session_t st = res0.val.case_Res; KRML_CHECK_SIZE(sizeof (Noise_XK_session_t), (uint32_t)1U); Noise_XK_session_t *ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_session_t)); ptr[0U] = st; return ptr; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } /* DO NOT use this: for tests and benchmarks only */ Noise_XK_session_t *Noise_XK__session_create_responder_with_ephemeral( Noise_XK_device_t *dvp, uint8_t *epriv, uint8_t *epub ) { Noise_XK_device_t dv = dvp[0U]; result_session_t res; if (dv.dv_states_counter == (uint32_t)4294967295U) res = ((result_session_t){ .tag = Fail, .val = { .case_Fail = Noise_XK_CIncorrect_transition } }); else { uint8_t *o0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o0, dv.dv_spriv, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spriv = o0; uint8_t *o = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); memcpy(o, dv.dv_spub, (uint32_t)32U * sizeof (uint8_t)); uint8_t *st_spub = o; uint8_t *str = dv.dv_info[0U]; bool b = str == NULL; uint8_t *out_str; if (b) out_str = NULL; else { uint32_t ip = (uint32_t)0U; uint32_t i0 = ip; uint8_t c0 = str[i0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t i = ip; ip = i + (uint32_t)1U; uint32_t i0 = ip; uint8_t c = str[i0]; cond = c != (uint8_t)0U; } uint32_t len = ip; if (len == (uint32_t)0U) out_str = NULL; else { KRML_CHECK_SIZE(sizeof (uint8_t), len + (uint32_t)1U); uint8_t *out_str0 = KRML_HOST_CALLOC(len + (uint32_t)1U, sizeof (uint8_t)); uint32_t np = (uint32_t)0U; uint32_t n0 = np; uint8_t c0 = str[n0]; bool cond = c0 != (uint8_t)0U; while (cond) { uint32_t n = np; uint8_t c = str[n]; out_str0[n] = c; np = n + (uint32_t)1U; uint32_t n0 = np; uint8_t c0 = str[n0]; cond = c0 != (uint8_t)0U; } uint32_t n = np; out_str0[n] = (uint8_t)0U; uint8_t *out_str1 = out_str0; out_str = out_str1; } } KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr0 = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr0[0U] = out_str; Noise_XK_noise_string *st_info = out_ptr0; KRML_CHECK_SIZE(sizeof (uint8_t *), (uint32_t)1U); uint8_t **out_ptr = KRML_HOST_MALLOC(sizeof (uint8_t *)); out_ptr[0U] = NULL; Noise_XK_noise_string *st_pinfo = out_ptr; dvp[0U] = ( (Noise_XK_device_t){ .dv_info = dv.dv_info, .dv_sk = dv.dv_sk, .dv_spriv = dv.dv_spriv, .dv_spub = dv.dv_spub, .dv_prologue = dv.dv_prologue, .dv_states_counter = dv.dv_states_counter + (uint32_t)1U, .dv_peers = dv.dv_peers, .dv_peers_counter = dv.dv_peers_counter } ); uint8_t *st_k = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_ck0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_h0 = KRML_HOST_CALLOC((uint32_t)64U, sizeof (uint8_t)); uint8_t *st_spriv1 = st_spriv; uint8_t *st_spub10 = st_spub; uint8_t *st_epriv0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_epub0 = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_rs = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); uint8_t *st_re = KRML_HOST_CALLOC((uint32_t)32U, sizeof (uint8_t)); Noise_XK_resp_state_t st = { .tag = Noise_XK_IMS_Handshake, .val = { .case_IMS_Handshake = { .step = (uint32_t)0U, .cipher_key = st_k, .chaining_key = st_ck0, .h = st_h0, .spriv = st_spriv1, .spub = st_spub10, .epriv = st_epriv0, .epub = st_epub0, .rs = st_rs, .re = st_re } } }; uint8_t pname[33U] = { (uint8_t)78U, (uint8_t)111U, (uint8_t)105U, (uint8_t)115U, (uint8_t)101U, (uint8_t)95U, (uint8_t)88U, (uint8_t)75U, (uint8_t)95U, (uint8_t)50U, (uint8_t)53U, (uint8_t)53U, (uint8_t)49U, (uint8_t)57U, (uint8_t)95U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)67U, (uint8_t)104U, (uint8_t)97U, (uint8_t)80U, (uint8_t)111U, (uint8_t)108U, (uint8_t)121U, (uint8_t)95U, (uint8_t)66U, (uint8_t)76U, (uint8_t)65U, (uint8_t)75U, (uint8_t)69U, (uint8_t)50U, (uint8_t)98U }; if (st.tag == Noise_XK_IMS_Handshake) { uint8_t *st_epub = st.val.case_IMS_Handshake.epub; uint8_t *st_epriv = st.val.case_IMS_Handshake.epriv; uint8_t *st_spub1 = st.val.case_IMS_Handshake.spub; uint8_t *st_h = st.val.case_IMS_Handshake.h; uint8_t *st_ck = st.val.case_IMS_Handshake.chaining_key; if ((uint32_t)33U <= (uint32_t)64U) memcpy(st_h, pname, (uint32_t)33U * sizeof (uint8_t)); else Noise_XK_hash(st_h, (uint32_t)33U, pname); memcpy(st_ck, st_h, (uint32_t)64U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, dv.dv_prologue.size, dv.dv_prologue.buffer); memcpy(st_epriv, epriv, (uint32_t)32U * sizeof (uint8_t)); memcpy(st_epub, epub, (uint32_t)32U * sizeof (uint8_t)); Noise_XK_mix_hash(st_h, (uint32_t)32U, st_spub1); } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } Noise_XK_resp_state_t st0 = st; result_session_t res0 = { .tag = Res, .val = { .case_Res = { .tag = Noise_XK_DS_Responder, .val = { .case_DS_Responder = { .state = st0, .id = dv.dv_states_counter, .info = st_info, .spriv = st_spriv, .spub = st_spub, .pid = (uint32_t)0U, .pinfo = st_pinfo, .dv = dvp } } } } }; res = res0; } if (res.tag == Fail) return NULL; else if (res.tag == Res) { Noise_XK_session_t st = res.val.case_Res; KRML_CHECK_SIZE(sizeof (Noise_XK_session_t), (uint32_t)1U); Noise_XK_session_t *ptr = KRML_HOST_MALLOC(sizeof (Noise_XK_session_t)); ptr[0U] = st; return ptr; } else { KRML_HOST_EPRINTF("KaRaMeL abort at %s:%d\n%s\n", __FILE__, __LINE__, "unreachable (pattern matches are exhaustive in F*)"); KRML_HOST_EXIT(255U); } } liboprf-0.9.4/src/oprf.c000066400000000000000000000343351514673400200150740ustar00rootroot00000000000000/* @copyright 2022, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ #include #include #include #include #include "oprf.h" #include "utils.h" #include "toprf.h" #ifdef CFRG_TEST_VEC #ifdef CFRG_OPRF_TEST_VEC #include "tests/cfrg_oprf_test_vector_decl.h" #else #include "tests/cfrg_test_vector_decl.h" #endif #endif #define VOPRF "OPRFV1" /** * This function generates an OPRF private key. * * This is almost the KeyGen OPRF function defined in the RFC: since * this lib does not implement V oprf, we don't need a pubkey and so * we don't bother with all that is related. * * @param [out] kU - the per-user OPRF private key */ void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]) { #if (defined CFRG_TEST_VEC && defined oprf_key_len) memcpy(kU,oprf_key,oprf_key_len); #else crypto_core_ristretto255_scalar_random(kU); #endif } /** * This function computes the OPRF output using input x, N, and domain separation * tag info. * * This is the Finalize OPRF function defined in the RFC. * * @param [in] x - a value used to compute OPRF (the same value that * was used as input to be blinded) * @param [in] x_len - the length of param x in bytes * @param [in] N - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Unblind * @param [in] info - a domain separation tag * @param [in] info_len - the length of param info in bytes * @param [out] y - an OPRF output * @return The function returns 0 if everything is correct. */ int oprf_Finalize(const uint8_t *x, const uint16_t x_len, const uint8_t N[crypto_core_ristretto255_BYTES], uint8_t rwdU[OPRF_BYTES]) { // according to paper: hash(pwd||H0^k) // acccording to voprf IRTF CFRG specification: hash(htons(len(pwd))||pwd|| // htons(len(H0_k))||H0_k||| // htons(len("Finalize-"VOPRF"-\x00-ristretto255-SHA512"))||"Finalize-"VOPRF"-\x00-ristretto255-SHA512") crypto_hash_sha512_state state; if(-1==sodium_mlock(&state,sizeof state)) { return -1; } crypto_hash_sha512_init(&state); // pwd uint16_t size=htons(x_len); crypto_hash_sha512_update(&state, (uint8_t*) &size, 2); crypto_hash_sha512_update(&state, x, x_len); #if (defined TRACE || defined CFRG_TEST_VEC) dump(x,x_len,"finalize input"); #endif // H0_k size=htons(crypto_core_ristretto255_BYTES); crypto_hash_sha512_update(&state, (uint8_t*) &size, 2); crypto_hash_sha512_update(&state, N, crypto_core_ristretto255_BYTES); //const uint8_t DST[]="Finalize-"VOPRF"-\x00\x00\x01"; const uint8_t DST[]="Finalize"; const uint8_t DST_size=sizeof DST -1; //size=htons(DST_size); //crypto_hash_sha512_update(&state, (uint8_t*) &size, 2); crypto_hash_sha512_update(&state, DST, DST_size); crypto_hash_sha512_final(&state, rwdU); sodium_munlock(&state, sizeof state); return 0; } /* expand_loop 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) */ static void expand_loop(const uint8_t *b_0, const uint8_t *b_i, const uint8_t i, const uint8_t *dst_prime, const uint8_t dst_prime_len, uint8_t *b_ii) { uint8_t xored[crypto_hash_sha512_BYTES]; unsigned j; for(j=0;j 255 * 3. DST_prime = DST || I2OSP(len(DST), 1) * 4. Z_pad = I2OSP(0, r_in_bytes) * 5. l_i_b_str = I2OSP(len_in_bytes, 2) * 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime * 7. b_0 = H(msg_prime) * 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) * 9. for i in (2, ..., ell): * 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) * 11. uniform_bytes = b_1 || ... || b_ell * 12. return substr(uniform_bytes, 0, len_in_bytes) */ int oprf_expand_message_xmd(const uint8_t *msg, const uint16_t msg_len, const uint8_t *dst, const uint8_t dst_len, const uint8_t len_in_bytes, uint8_t *uniform_bytes) { // 1. ell = ceil(len_in_bytes / b_in_bytes) const unsigned ell = (len_in_bytes + crypto_hash_sha512_BYTES-1) / crypto_hash_sha512_BYTES; #ifdef TRACE fprintf(stderr, "ell %d\n", ell); dump(msg, msg_len, "msg"); dump(dst, dst_len, "dst"); #endif // 2. ABORT if ell > 255 if(ell>255) return -1; // 3. DST_prime = DST || I2OSP(len(DST), 1) if(dst_len==255) return -1; uint8_t dst_prime[dst_len+1]; memcpy(dst_prime, dst, dst_len); dst_prime[dst_len] = dst_len; #ifdef TRACE dump(dst_prime, sizeof dst_prime, "dst_prime"); #endif // 4. Z_pad = I2OSP(0, r_in_bytes) //const uint8_t r_in_bytes = 128; // for sha512 uint8_t z_pad[128 /*r_in_bytes*/] = {0}; // supress gcc error: variable-sized object may not be initialized #ifdef TRACE dump(z_pad, sizeof z_pad, "z_pad"); #endif // 5. l_i_b_str = I2OSP(len_in_bytes, 2) const uint16_t l_i_b = htons(len_in_bytes); const uint8_t *l_i_b_str = (const uint8_t*) &l_i_b; // 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime uint8_t msg_prime[sizeof z_pad + msg_len + sizeof l_i_b + 1 + sizeof dst_prime]; uint8_t *ptr = msg_prime; memcpy(ptr, z_pad, sizeof z_pad); ptr += sizeof z_pad; memcpy(ptr, msg, msg_len); ptr += msg_len; memcpy(ptr, l_i_b_str, sizeof l_i_b); ptr += sizeof l_i_b; *ptr = 0; ptr++; memcpy(ptr, dst_prime, sizeof dst_prime); #ifdef TRACE dump(msg_prime, sizeof msg_prime, "msg_prime"); #endif // 7. b_0 = H(msg_prime) uint8_t b_0[crypto_hash_sha512_BYTES]; crypto_hash_sha512(b_0, msg_prime, sizeof msg_prime); #ifdef TRACE dump(b_0, sizeof b_0, "b_0"); #endif // 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) uint8_t b_i[crypto_hash_sha512_BYTES]; crypto_hash_sha512_state state; crypto_hash_sha512_init(&state); crypto_hash_sha512_update(&state, b_0, sizeof b_0); crypto_hash_sha512_update(&state,(const uint8_t*) &"\x01", 1); crypto_hash_sha512_update(&state, dst_prime, (long long unsigned int) sizeof dst_prime); crypto_hash_sha512_final(&state, b_i); #ifdef TRACE dump(b_i, sizeof b_i, "b_1"); #endif // 9. for i in (2, ..., ell): unsigned left = len_in_bytes; uint8_t *out = uniform_bytes; unsigned clen = (left>sizeof b_i)?sizeof b_i:left; memcpy(out, b_i, clen); out+=clen; left-=clen; uint8_t b_ii[crypto_hash_sha512_BYTES]; for(uint8_t i=2;i<=ell;i+=2) { // 11. uniform_bytes = b_1 || ... || b_ell // 12. return substr(uniform_bytes, 0, len_in_bytes) // 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) expand_loop(b_0, b_i, i, dst_prime, (uint8_t) (sizeof dst_prime), b_ii); clen = (left>sizeof b_ii)?sizeof b_ii:left; memcpy(out, b_ii, clen); out+=clen; left-=clen; // unrolled next iteration so we don't have to swap b_i and b_ii expand_loop(b_0, b_ii, i+1, dst_prime, (uint8_t) (sizeof dst_prime), b_i); clen = (left>sizeof b_i)?sizeof b_i:left; memcpy(out, b_i, clen); out+=clen; left-=clen; } return 0; } /* hash-to-ristretto255 - as defined by https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/master/draft-irtf-cfrg-hash-to-curve.md#hashing-to-ristretto255-appx-ristretto255 * Steps: * -1. context-string = \x0 + htons(1) // contextString = I2OSP(modeBase(==0), 1) || I2OSP(suite.ID(==1), 2) * 0. dst="HashToGroup-OPRFV1-\x00-ristretto255-SHA512") * 1. uniform_bytes = expand_message(msg, DST, 64) * 2. P = ristretto255_map(uniform_bytes) * 3. return P */ int voprf_hash_to_group(const uint8_t *msg, const uint16_t msg_len, uint8_t p[crypto_core_ristretto255_BYTES]) { const uint8_t dst[] = "HashToGroup-"VOPRF"-\x00-ristretto255-SHA512"; const uint8_t dst_len = (sizeof dst) - 1; uint8_t uniform_bytes[crypto_core_ristretto255_HASHBYTES]={0}; if(0!=sodium_mlock(uniform_bytes,sizeof uniform_bytes)) { return -1; } if(0!=oprf_expand_message_xmd(msg, msg_len, dst, dst_len, crypto_core_ristretto255_HASHBYTES, uniform_bytes)) { sodium_munlock(uniform_bytes,sizeof uniform_bytes); return -1; } #if (defined TRACE || defined CFRG_TEST_VEC) dump(uniform_bytes, sizeof uniform_bytes, "uniform_bytes"); #endif crypto_core_ristretto255_from_hash(p, uniform_bytes); sodium_munlock(uniform_bytes,sizeof uniform_bytes); #if (defined TRACE || defined CFRG_TEST_VEC) dump(p, crypto_core_ristretto255_BYTES, "hashed-to-curve"); #endif return 0; } /** * This function converts input x into an element of the OPRF group, randomizes it * by some scalar r, producing blinded, and outputs (r, blinded). * * This is the Blind OPRF function defined in the RFC. * * @param [in] x - the value to blind (for OPAQUE, this is pwdU, the user's * password) * @param [in] x_len - the length of param x in bytes * @param [out] r - an OPRF scalar value used for randomization * @param [out] blinded - a serialized OPRF group element, a byte array of fixed length, * the blinded version of x, an input to oprf_Evaluate * @return The function returns 0 if everything is correct. */ int oprf_Blind(const uint8_t *x, const uint16_t x_len, uint8_t r[crypto_core_ristretto255_SCALARBYTES], uint8_t blinded[crypto_core_ristretto255_BYTES]) { #if (defined TRACE || defined CFRG_TEST_VEC) dump(x, x_len, "input"); #endif uint8_t H0[crypto_core_ristretto255_BYTES]; if(0!=sodium_mlock(H0,sizeof H0)) { return -1; } // sets Ξ± := (H^0(pw))^r if(0!=voprf_hash_to_group(x, x_len, H0)) return -1; #if (defined TRACE || defined CFRG_TEST_VEC) dump(H0,sizeof H0, "H0"); #endif // U picks r #ifdef CFRG_TEST_VEC static int vecidx=0; const unsigned char *rtest[2] = {blind_registration, blind_login}; const unsigned int rtest_len = 32; memcpy(r,rtest[vecidx++ % 2],rtest_len); #else crypto_core_ristretto255_scalar_random(r); #endif #ifdef TRACE dump(r, crypto_core_ristretto255_SCALARBYTES, "r"); #endif // H^0(pw)^r if (crypto_scalarmult_ristretto255(blinded, r, H0) != 0) { sodium_munlock(H0,sizeof H0); return -1; } sodium_munlock(H0,sizeof H0); #if (defined TRACE || defined CFRG_TEST_VEC) dump(blinded, crypto_core_ristretto255_BYTES, "blinded"); #endif return 0; } /** * This function evaluates input element blinded using private key k, yielding output * element Z. * * This is the Evaluate OPRF function defined in the RFC. * * @param [in] k - a private key (for OPAQUE, this is kU, the user's OPRF private * key) * @param [in] blinded - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Blind (for OPAQUE, this is the blinded pwdU, the user's * password) * @param [out] Z - a serialized OPRF group element, a byte array of fixed length, * an input to oprf_Unblind * @return The function returns 0 if everything is correct. */ int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], const uint8_t blinded[crypto_core_ristretto255_BYTES], uint8_t Z[crypto_core_ristretto255_BYTES]) { return crypto_scalarmult_ristretto255(Z, k, blinded); } /** * This function removes random scalar r from Z, yielding output N. * * This is the Unblind OPRF function defined in the RFC. * * @param [in] r - an OPRF scalar value used for randomization in oprf_Blind * @param [in] Z - a serialized OPRF group element, a byte array of fixed length, * an output of oprf_Evaluate * @param [out] N - a serialized OPRF group element with random scalar r removed, * a byte array of fixed length, an input to oprf_Finalize * @return The function returns 0 if everything is correct. */ int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES], const uint8_t Z[crypto_core_ristretto255_BYTES], uint8_t N[crypto_core_ristretto255_BYTES]) { #ifdef TRACE dump(r, crypto_core_ristretto255_SCALARBYTES, "r "); dump(Z, crypto_core_ristretto255_BYTES, "Z "); #endif // (a) Checks that Ξ² ∈ G βˆ— . If not, outputs (abort, sid , ssid ) and halts; if(crypto_core_ristretto255_is_valid_point(Z) != 1) return -1; // (b) Computes rw := H(pw, Ξ²^1/r ); // invert r = 1/r uint8_t ir[crypto_core_ristretto255_SCALARBYTES]; if(-1==sodium_mlock(ir, sizeof ir)) return -1; if (crypto_core_ristretto255_scalar_invert(ir, r) != 0) { sodium_munlock(ir, sizeof ir); return -1; } #ifdef TRACE dump((uint8_t*) ir, sizeof ir, "r^-1 "); #endif // H0 = Ξ²^(1/r) // beta^(1/r) = h(pwd)^k if (crypto_scalarmult_ristretto255(N, ir, Z) != 0) { sodium_munlock(ir, sizeof ir); return -1; } #ifdef TRACE dump(N, crypto_core_ristretto255_BYTES, "N "); #endif sodium_munlock(ir, sizeof ir); return 0; } liboprf-0.9.4/src/oprf.h000066400000000000000000000142661514673400200151020ustar00rootroot00000000000000#ifndef oprf_h #define oprf_h /** * @file oprf.h * @brief API for Oblivious Pseudorandom Function (OPRF) implementation * * SPDX-FileCopyrightText: 2025, Marsiske Stefan * SPDX-License-Identifier: LGPL-3.0-or-later * * This file provides the API for Oblivious Pseudorandom Functions (OPRFs) * using the Ristretto255 group. It includes functions for key generation, * blinding inputs, evaluating OPRFs, and unblinding results. * * This implementation is based on RFC 9497: Oblivious Pseudorandom * Functions (OPRFs) Using Prime-Order Groups * (https://www.rfc-editor.org/rfc/rfc9497.html). * * * This implementation also uses hashing techniques as defined in * RFC 9380: Hashing to Elliptic Curves * (https://www.rfc-editor.org/rfc/rfc9380). * */ #include #include #include "toprf.h" #define OPRF_BYTES 64 /** * @brief Generates an OPRF private key * * This is almost the `KeyGen` OPRF function defined in RFC 9497. * Since this library does not implement Verfiable OPRF (VOPRF) * functionality, no public key is needed, so steps related to that * are omitted. * * @param[out] kU The per-user OPRF private key */ void oprf_KeyGen(uint8_t kU[crypto_core_ristretto255_SCALARBYTES]); /** * @brief Computes the final OPRF output * * Implements the `Finalize` OPRF function defined in RFC 9497. * It hashes the input and the OPRF evaluation to produce the * final output for the client. * * @param[in] x A value used to compute OPRF (the same value that * was used as input to be blinded) * @param[in] x_len Length of input x in bytes. * @param[in] N Evaluated group element (output from `oprf_Unblind()`) * @param[out] rwdU Output buffer for the OPRF result */ int oprf_Finalize(const uint8_t *x, const uint16_t x_len, const uint8_t N[crypto_core_ristretto255_BYTES], uint8_t rwdU[OPRF_BYTES]); /** * @brief Blinds an input value for OPRF evaluation * * Implements the `Blind` OPRF function defined in RFC 9497. * This function converts the input into an OPRF group element and * randomizes it with a scalar value `r`. Both the scalar and blinded * element are returned. * * @param[in] x Input value to blind. E.g., the user's password in * OPAQUE (pwdU) * @param[in] x_len Length of the input value in bytes * @param[out] r Random scalar used to blind the input * @param[out] alpha Serialized OPRF group element, the blinded version * of `x`, used as input to `oprf_Evaluate()` * * @return 0 on success, non-zero on error */ int oprf_Blind(const uint8_t *x, const uint16_t x_len, uint8_t r[crypto_core_ristretto255_SCALARBYTES], uint8_t alpha[crypto_core_ristretto255_BYTES]); /** * @brief Evaluates a blinded input using the OPRF private key * * Implements the `Evaluate` OPRF function defined in RFC 9497. * This function is run by the server. It uses the server's private * key `k` to evaluate the client's blinded input, producing a group * element `beta` that the client can later unblind. * * @param[in] k OPRF private key E.g., the user's private key in OPAQUE * (kU) * @param[in] alpha Serialized OPRF group element, an output of * `oprf_Blind()`. For OPAQUE, this is the blinded user's * password, pwdU * @param[out] beta Serialized OPRF group element, used as input to * `oprf_Unblind()` * * @return 0 on success, non-zero on error */ int oprf_Evaluate(const uint8_t k[crypto_core_ristretto255_SCALARBYTES], const uint8_t alpha[crypto_core_ristretto255_BYTES], uint8_t beta[crypto_core_ristretto255_BYTES]); /** * @brief Unblinds an evaluated OPRF element * * Implements the `Unblind` OPRF function defined in RFC 9497. * This function removes the random scalar `r` from the evaluated * element `beta`, producing the unblinded output `N`. * * @param[in] r Scalar used to blind the input originally * @param[in] beta OPRF evaluation result from the server, an output of * `oprf_Evaluate()` * @param[out] N Serialized OPRF group element with random scalar `r` * remove, used as input to `oprf_Finalize()`. * * @return 0 on success, non-zero on error */ int oprf_Unblind(const uint8_t r[crypto_core_ristretto255_SCALARBYTES], const uint8_t beta[crypto_core_ristretto255_BYTES], uint8_t N[crypto_core_ristretto255_BYTES]); /** * @brief Hashes an input message to a point on the Ristretto255 curve * * Implements the `hash-to-curve` function defined in RFC 9380. * This function is needed for the OPRF implementation. * * @param[in] msg Input message to hash to a Ristretto255 point * @param[in] msg_len Length of the input message in bytes * @param[out] p The resulting Ristretto255 point * * @return 0 on success, non-zero on error */ int voprf_hash_to_group(const uint8_t *msg, const uint16_t msg_len, uint8_t p[crypto_core_ristretto255_BYTES]); /** * @brief Expands an input message to a uniformly random byte string * using a cryptographic hash function * * Implements `expand_message_xmd` as defined in RFC 9380. * * @param[in] msg The input message to expand * @param[in] msg_len The length of the input message in bytes * @param[in] dst Domain separation tag (DST) * @param[in] dst_len The length of the DST * @param[in] len_in_bytes Desired number of output bytes * @param[out] uniform_bytes Output buffer of length `len_in_bytes` to * receive the high entropy result * * @return 0 on success, non-zero on error */ int oprf_expand_message_xmd(const uint8_t *msg, const uint16_t msg_len, const uint8_t *dst, const uint8_t dst_len, const uint8_t len_in_bytes, uint8_t *uniform_bytes); #ifdef __EMSCRIPTEN__ /** * if compiling to webassembly, there is no sodium_m(un)?lock and thus we suppress that with the following */ // Per // https://emscripten.org/docs/compiling/Building-Projects.html#detecting-emscripten-in-preprocessor, // "The preprocessor define __EMSCRIPTEN__ is always defined when compiling // programs with Emscripten". For why we are replacing sodium_m(un)?lock, see // common.c for more details. #define sodium_mlock(a,l) (0) #define sodium_munlock(a,l) (0) #endif //__EMSCRIPTEN__ #endif liboprf-0.9.4/src/stp-dkg.c000066400000000000000000002555631514673400200155070ustar00rootroot00000000000000#include //htons #include "utils.h" #include "stp-dkg.h" #include "dkg-vss.h" #include "mpmult.h" #ifdef __ZEPHYR__ #include #endif /* @copyright 2025, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. SPDX-FileCopyrightText: 2024, Marsiske Stefan SPDX-License-Identifier: LGPL-3.0-or-later liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ /* This file implements a high-level DKG protocol facilitated by a semi-trusted orchestrator which connects to all participating peers in a star topology. The underlying algorithm is based on the FT-Joint-DL-VSS (fig 7.) from R. Gennaro, M. O. Rabin, and T. Rabin. "Simplified VSS and fact-track multiparty computations with applications to threshold cryptography" In B. A. Coan and Y. Afek, editors, 17th ACM PODC, pages 101–111. ACM, June / July 1998 */ #ifdef UNITTEST_CORRUPT static void corrupt_vsps_p1t1(STP_DKG_PeerState *ctx) { // deals shares with polynomial t+1 instead of 1 if(ctx->index!=1) return; (void)dkg_vss_share(ctx->n, ctx->t+1, NULL, (*ctx->k_commitments), (*ctx->k_shares), NULL); } static void corrupt_commitment_p2(STP_DKG_PeerState *ctx) { // corrupts the 1st commitment with the 2nd if(ctx->index!=2) return; memcpy((*ctx->k_commitments)[0], (*ctx->k_commitments)[1], crypto_core_ristretto255_BYTES); } static void corrupt_wrongshare_correct_commitment_p3(STP_DKG_PeerState *ctx) { // swaps the share and it's blinder, // recalculates commitment if(ctx->index!=3) return; TOPRF_Share tmp; // swap shares for p1 memcpy(&tmp, &(*ctx->k_shares)[0][0], sizeof tmp); memcpy(&(*ctx->k_shares)[0][0], &(*ctx->k_shares)[0][1], sizeof tmp); memcpy(&(*ctx->k_shares)[0][1], &tmp, sizeof tmp); dkg_vss_commit((*ctx->k_shares)[0][0].value,(*ctx->k_shares)[0][1].value,(*ctx->k_commitments)[0]); } static void corrupt_share_p4(STP_DKG_PeerState *ctx) { if(ctx->index!=4) return; (*ctx->k_shares)[0][0].value[2]^=0xff; // flip some bits } static void corrupt_false_accuse_p2p3(STP_DKG_PeerState *ctx, uint8_t *fails_len, uint8_t *fails) { if(ctx->index!=2) return; fails[(*fails_len)++]=3; } #endif // UNITTEST_CORRUPT size_t stp_dkg_peerstate_size(void) { return sizeof(STP_DKG_PeerState); } uint8_t stp_dkg_peerstate_n(const STP_DKG_PeerState *ctx) { return ctx->n; } uint8_t stp_dkg_peerstate_t(const STP_DKG_PeerState *ctx) { return ctx->t; } const uint8_t* stp_dkg_peerstate_sessionid(const STP_DKG_PeerState *ctx) { return ctx->sessionid; } const uint8_t* stp_dkg_peerstate_lt_sk(const STP_DKG_PeerState *ctx) { return ctx->sig_sk; } const uint8_t* stp_dkg_peerstate_share(const STP_DKG_PeerState *ctx) { return (const uint8_t*) &ctx->share; } const uint8_t* stp_dkg_peerstate_commitments(const STP_DKG_PeerState *ctx) { return (const uint8_t*) *ctx->k_commitments; } int stp_dkg_peerstate_step(const STP_DKG_PeerState *ctx) { return ctx->step; } size_t stp_dkg_stpstate_size(void) { return sizeof(STP_DKG_STPState); } uint8_t stp_dkg_stpstate_n(const STP_DKG_STPState *ctx) { return ctx->n; } uint8_t stp_dkg_stpstate_t(const STP_DKG_STPState *ctx) { return ctx->t; } size_t stp_dkg_stpstate_cheater_len(const STP_DKG_STPState *ctx) { return ctx->cheater_len; } const uint8_t* stp_dkg_stpstate_sessionid(const STP_DKG_STPState *ctx) { return ctx->sessionid; } const uint8_t* stp_dkg_stpstate_commitments(const STP_DKG_STPState *ctx) { return (const uint8_t*) *ctx->commitments; } int stp_dkg_stpstate_step(const STP_DKG_STPState *ctx) { return ctx->step; } static int toprf_send_msg(uint8_t* msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_sk, const uint8_t sessionid[dkg_sessionid_SIZE]) { return send_msg(msg_buf, msg_buf_len, MSG_TYPE_SEMI_TRUSTED | MSG_TYPE_DKG, 0, msgno, from, to, sig_sk, sessionid); } static int toprf_recv_msg(const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_pk, const uint8_t sessionid[dkg_sessionid_SIZE], const uint64_t ts_epsilon, uint64_t *last_ts) { return recv_msg(msg_buf, msg_buf_len, MSG_TYPE_SEMI_TRUSTED | MSG_TYPE_DKG, 0, msgno, from, to, sig_pk, sessionid, ts_epsilon, last_ts); } static void set_cheater(STP_DKG_Cheater *cheater, const int step, const int error, const uint8_t peer, const uint8_t other_peer) { cheater->step = step; cheater->error = error; cheater->peer = peer; cheater->other_peer=other_peer; } static STP_DKG_Cheater* stp_add_cheater(STP_DKG_STPState *ctx, const int error, const uint8_t peer, const uint8_t other_peer) { if(ctx->cheater_len >= ctx->cheater_max) return NULL; STP_DKG_Cheater *cheater = &(*ctx->cheaters)[ctx->cheater_len++]; set_cheater(cheater, ctx->step, error, peer, other_peer); return cheater; } static STP_DKG_Cheater* peer_add_cheater(STP_DKG_PeerState *ctx,const int error, const uint8_t peer, const uint8_t other_peer) { if(ctx->cheater_len >= ctx->cheater_max) return NULL; STP_DKG_Cheater *cheater = &(*ctx->cheaters)[ctx->cheater_len++]; set_cheater(cheater, ctx->step, error, peer, other_peer); return cheater; } static int stp_recv_msg(STP_DKG_STPState *ctx, const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to) { dkg_dump_msg(msg_buf, msg_buf_len, 0); int ret = toprf_recv_msg(msg_buf, msg_buf_len, msgno, from, to, (*ctx->sig_pks)[from], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[from-1]); if(0!=ret) { if(stp_add_cheater(ctx, 64+ret, from, to) == NULL) return STP_DKG_Err_CheatersFull; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"failed to validate msg %d from %d, err: %d\n"NORMAL, msgno, from, ret); return ret; } return 0; } static int peer_recv_msg(STP_DKG_PeerState *ctx, const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to) { dkg_dump_msg(msg_buf, msg_buf_len, ctx->index); int ret = toprf_recv_msg(msg_buf, msg_buf_len, msgno, from, to, (*ctx->sig_pks)[from], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[from-1]); if(0!=ret) { if(peer_add_cheater(ctx, 64+ret, from, to) == NULL) return STP_DKG_Err_CheatersFull; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed to validate msg %d from %d, err: %d\n"NORMAL, ctx->index, msgno, from, ret); return 1; } return 0; } static STP_DKG_Err stp_broadcast(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, const char *step_title, const uint8_t msg_count, // usually n, sometimes dealers const size_t msg_size, const uint8_t msgno, const STP_DKG_STP_Steps next_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] %s\x1b[0m\n", step_title); if(msg_count * msg_size != input_len) return STP_DKG_Err_ISize; if(sizeof(STP_DKG_Message) + input_len != output_len) return STP_DKG_Err_OSize; size_t cheaters = ctx->cheater_len; const uint8_t *ptr = input; uint8_t *wptr = ((STP_DKG_Message *) output)->data; for(uint8_t i=0;icheater_len>cheaters) return STP_DKG_Err_CheatersFound; if(0!=toprf_send_msg(output, output_len, msgno+1, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, output_len, 0); // add broadcast msg to transcript update_transcript(&ctx->transcript, output, output_len); ctx->step = next_step; return STP_DKG_Err_OK; } static STP_DKG_Err stp_route(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, const char *step_title, const uint8_t send_count, const uint8_t recv_count, const uint8_t msgno, const size_t msg_size, const STP_DKG_STP_Steps next_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] %s\x1b[0m\n", step_title); if(input_len != msg_size * send_count * recv_count) return STP_DKG_Err_ISize; if(input_len != output_len) return STP_DKG_Err_OSize; const uint8_t (*inputs)[send_count][recv_count][msg_size] = (const uint8_t (*)[send_count][recv_count][msg_size]) input; uint8_t *wptr = output; for(uint8_t i=0;isig_pks)[j+1], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[j]); if(0!=ret) { if(stp_add_cheater(ctx, 64+ret, j+1, i+1) == NULL) return STP_DKG_Err_CheatersFull; const STP_DKG_Message *msg = (const STP_DKG_Message*) (*inputs)[j][i]; fprintf(liboprf_log_file,"[x] msgno: %d, from: %d to: %d err: %d ", msg->msgno, msg->from, msg->to, ret); dump((*inputs)[j][i], msg_size, "msg"); continue; } memcpy(wptr, (*inputs)[j][i], msg_size); wptr+=msg_size; } } //if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; ctx->step = next_step; return STP_DKG_Err_OK; } static STP_DKG_Err unwrap_envelope(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, const uint8_t msgno, const uint8_t **contents) { // verify STP message envelope const STP_DKG_Message* msg = (const STP_DKG_Message*) input; dkg_dump_msg(input, input_len, ctx->index); int ret = toprf_recv_msg(input, input_len, msgno, 0, 0xff, ctx->stp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return STP_DKG_Err_BroadcastEnv+ret; // add broadcast msg to transcript update_transcript(&ctx->transcript, input, input_len); *contents = msg->data; return STP_DKG_Err_OK; } // todo test this static void handle_complaints(const uint8_t n, const uint8_t accuser, const char *type, const uint8_t fails_len, const uint8_t fails[], uint16_t *ctx_complaints_len, uint16_t *ctx_complaints, const uint8_t self, uint8_t *ctx_my_complaints_len, uint8_t *ctx_my_complaints) { // keep a copy all complaint pairs (complainer, complained) for(unsigned k=0;k n || fails[k] < 1) { //fails[k] has an invalid peer idx value. // todo cheater handling //if(stp_add_cheater(ctx, 7, i+1, msg->data[k+1]) == NULL) return 6; continue; } uint16_t pair=(uint16_t) ((accuser<<8) | fails[k]); int j=0; for(j=0;j<*ctx_complaints_len;j++) if(ctx_complaints[j]==pair) break; if(j<*ctx_complaints_len) { //already seen this accuser/accused pair. // todo cheater handling //if(stp_add_cheater(ctx, 18, 8, i+1, msg->data[k+1]) == NULL) return 6; continue; } ctx_complaints[(*ctx_complaints_len)++] = pair; if(self!=0 && fails[k] == self && ctx_my_complaints_len != NULL && ctx_my_complaints != NULL) { ctx_my_complaints[(*ctx_my_complaints_len)++] = accuser; } if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] peer %d failed to verify %s from peer %d!\x1b[0m\n", accuser, type, fails[k]); } } } static STP_DKG_Err ft_or_full_vsps(const uint8_t n, const uint8_t t, const uint8_t dealers, const uint8_t self, const uint8_t C_i[n][crypto_core_ristretto255_BYTES], const uint8_t (*C_ij)[n][n][crypto_core_ristretto255_BYTES], const char *ft_msg, const char *sub_msg, const char *no_sub_msg, uint8_t *fails_len, uint8_t fails[n]) { liboprf_debug=0; if(0!=toprf_mpc_vsps_check(t-1, C_i)) { if(liboprf_log_file!=NULL) fprintf(stderr, RED"[%d] %s\n"NORMAL, self, ft_msg); for(uint8_t i=0;in || t>=n || n>128 || n<2*t+1) return 1; if(proto_name_len<1) return 2; if(proto_name_len>1024) return 3; if(msg0_len != stpvssdkg_start_msg_SIZE) return 4; ctx->ts_epsilon = ts_epsilon; ctx->step = STP_DKG_STP_Send_Index; ctx->n = n; ctx->t = t; ctx->share_complaints_len = 0; ctx->cheater_len = 0; // dst hash(len(protoname) | "STP VSS DKG for protocol " | protoname | n | t) crypto_generichash_state dst_state; crypto_generichash_init(&dst_state, NULL, 0, crypto_generichash_BYTES); uint16_t len=htons((uint16_t) proto_name_len+20); // we have a guard above restricting to 1KB the proto_name_len crypto_generichash_update(&dst_state, (uint8_t*) &len, 2); crypto_generichash_update(&dst_state, (const uint8_t*) "STP VSS DKG for protocol ", 25); crypto_generichash_update(&dst_state, (const uint8_t*) proto_name, proto_name_len); crypto_generichash_update(&dst_state, &n, 1); crypto_generichash_update(&dst_state, &t, 1); uint8_t dst[crypto_generichash_BYTES]; crypto_generichash_final(&dst_state,dst,sizeof dst); // set sessionid nonce, we abuse this session_id field in the state // to temporarily store the session id nonce; which will later // become the real session_id after the other peers also contributed // their nonces randombytes_buf(&ctx->sessionid, sizeof ctx->sessionid); // a list of all long-term pubkeys ctx->sig_pks = sig_pks; // keep a copy of our long-term signing key memcpy(ctx->sig_sk, ltssk, crypto_sign_SECRETKEYBYTES); // data = {stp_lt_pks, dst, n, t} uint8_t *ptr = msg0->data; memcpy(ptr, (*sig_pks)[0], crypto_sign_PUBLICKEYBYTES); ptr+=crypto_sign_PUBLICKEYBYTES; memcpy(ptr, dst, sizeof dst); ptr+=sizeof dst; *ptr++ = n; *ptr++ = t; if(0!=toprf_send_msg((uint8_t*) msg0, stpvssdkg_start_msg_SIZE, stpvssdkg_stp_start_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 5; // init transcript crypto_generichash_init(&ctx->transcript, NULL, 0, crypto_generichash_BYTES); crypto_generichash_update(&ctx->transcript, (const uint8_t*) "stp vss dkg session transcript", 31); // feed msg0 into transcript update_transcript(&ctx->transcript, (uint8_t*) msg0, msg0_len); dkg_dump_msg((uint8_t*) msg0, stpvssdkg_start_msg_SIZE, 0); return 0; } void stp_dkg_stp_set_bufs(STP_DKG_STPState *ctx, uint8_t (*commitment_hashes)[][stp_dkg_commitment_HASHBYTES], uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint16_t (*share_complaints)[], STP_DKG_Cheater (*cheaters)[], const size_t cheater_max, uint64_t *last_ts) { ctx->share_complaints = share_complaints; ctx->cheaters = cheaters; memset(*cheaters, 0, cheater_max*sizeof(STP_DKG_Cheater)); ctx->commitment_hashes = commitment_hashes; ctx->share_macs = share_macs; ctx->commitments = commitments; ctx->cheater_max = cheater_max; ctx->last_ts = last_ts; #ifdef __ZEPHYR__ uint64_t now = (uint64_t) k_uptime_get(); #else uint64_t now = (uint64_t)time(NULL); #endif for(uint8_t i=0;in;i++) ctx->last_ts[i]=now; } STP_DKG_Err stp_dkg_start_peer(STP_DKG_PeerState *ctx, const uint64_t ts_epsilon, const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], const uint8_t noise_sks[crypto_scalarmult_SCALARBYTES], const STP_DKG_Message *msg0, uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[?] step 0.5 start peer\x1b[0m\n"); ctx->ts_epsilon = ts_epsilon; ctx->stp_last_ts = 0; ctx->keyloader_cb_arg = NULL; int ret = toprf_recv_msg((const uint8_t*) msg0, stpvssdkg_start_msg_SIZE, stpvssdkg_stp_start_msg, 0, 0xff, msg0->data, msg0->sessionid, ts_epsilon, &ctx->stp_last_ts); dkg_dump_msg((const uint8_t*) msg0, stpvssdkg_start_msg_SIZE, 0); if(0!=ret) return STP_DKG_Err_Env+ ret; // extract data from message // we abuse sessionid as a temporary storage for the nonce_stp value, until we have the final sessionid memcpy(ctx->sessionid, msg0->sessionid, sizeof ctx->sessionid); const uint8_t *ptr=msg0->data; memcpy(stp_ltpk,ptr,crypto_sign_PUBLICKEYBYTES); memcpy(ctx->stp_sig_pk,ptr,crypto_sign_PUBLICKEYBYTES); ptr+=crypto_sign_PUBLICKEYBYTES + crypto_generichash_BYTES; // also skip DST ctx->n = *ptr++; ctx->t = *ptr++; if(ctx->t < 2) return 1; if(ctx->t >= ctx->n) return 2; if(ctx->n > 128) return 3; ctx->share_complaints_len = 0; ctx->my_share_complaints_len = 0; ctx->cheater_len = 0; memcpy(ctx->sig_sk, lt_sk, crypto_sign_SECRETKEYBYTES); memcpy(ctx->noise_sk, noise_sks, crypto_scalarmult_SCALARBYTES); crypto_generichash_init(&ctx->transcript, NULL, 0, crypto_generichash_BYTES); crypto_generichash_update(&ctx->transcript, (const uint8_t*) "stp vss dkg session transcript", 31); // feed msg0 into transcript update_transcript(&ctx->transcript, (const uint8_t*) msg0, stpvssdkg_start_msg_SIZE); ctx->dev = NULL; ctx->step = STP_DKG_Peer_Broadcast_NPK_SIDNonce; return STP_DKG_Err_OK; } int stp_dkg_peer_set_bufs(STP_DKG_PeerState *ctx, uint8_t (*peerids)[][crypto_generichash_BYTES], Keyloader_CB keyloader_cb, void *keyloader_cb_arg, uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], Noise_XK_session_t *(*noise_outs)[], Noise_XK_session_t *(*noise_ins)[], TOPRF_Share (*k_shares)[][2], uint8_t (*encrypted_shares)[][TOPRF_Share_BYTES * 2 + noise_xk_handshake3_SIZE + crypto_secretbox_xchacha20poly1305_MACBYTES], uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*ki_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*k_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*commitments_hashes)[][stp_dkg_commitment_HASHBYTES], STP_DKG_Cheater (*cheaters)[], const size_t cheater_max, uint16_t *share_complaints, uint8_t *my_share_complaints, uint64_t *last_ts) { ctx->peerids = peerids; ctx->keyloader_cb = keyloader_cb; ctx->keyloader_cb_arg = keyloader_cb_arg; ctx->sig_pks = peers_sig_pks; ctx->peer_noise_pks = peers_noise_pks; ctx->noise_outs = noise_outs; ctx->noise_ins = noise_ins; ctx->k_shares = k_shares; ctx->encrypted_shares = encrypted_shares; ctx->share_macs = share_macs; ctx->ki_commitments = ki_commitments; ctx->k_commitments = k_commitments; ctx->commitments_hashes = commitments_hashes; ctx->share_complaints = share_complaints; ctx->my_share_complaints = my_share_complaints; ctx->cheaters = cheaters; ctx->cheater_max = cheater_max; ctx->last_ts = last_ts; for(uint8_t i=0;in;i++) ctx->last_ts[i]=0; return 0; } #define stp_dkg_stp_index_msg_SIZE(ctx) (sizeof(STP_DKG_Message) + ctx->n * crypto_generichash_BYTES) static int stp_init_send_indexes(STP_DKG_STPState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 0. assign peer indices\x1b[0m\n"); if(output_len!=ctx->n * stp_dkg_stp_index_msg_SIZE(ctx)) return 2; uint8_t (*pkhashes)[crypto_generichash_BYTES] = (uint8_t (*)[crypto_generichash_BYTES]) ((STP_DKG_Message*) output)->data; for(unsigned i=0;in;i++) { crypto_generichash(pkhashes[i],crypto_generichash_BYTES,(*ctx->sig_pks)[i+1],crypto_sign_PUBLICKEYBYTES,NULL,0); } uint8_t* ptr = output; for(uint8_t i=1;i<=ctx->n;i++,ptr+=stp_dkg_stp_index_msg_SIZE(ctx)) { memcpy(((STP_DKG_Message*) ptr)->data, pkhashes, ctx->n * crypto_generichash_BYTES); if(0!=toprf_send_msg(ptr, stp_dkg_stp_index_msg_SIZE(ctx), stpvssdkg_stp_index_msg, 0, i, ctx->sig_sk, ctx->sessionid)) return 3; } ctx->step = STP_DKG_STP_Broadcast_NPKs; return 0; } #define stp_dkg_peer_init1_msg_SIZE (sizeof(STP_DKG_Message) + dkg_sessionid_SIZE) static STP_DKG_Err peer_init1_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const DKG_Message *msg1=(const DKG_Message*) input; ctx->index=msg1->to; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] init1 send msg1 containing session nonce\x1b[0m\n", ctx->index); if(input_len != stp_dkg_stp_index_msg_SIZE(ctx)) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_init1_msg_SIZE) return STP_DKG_Err_OSize; dkg_dump_msg(input, input_len, ctx->index); int ret = toprf_recv_msg(input, stp_dkg_stp_index_msg_SIZE(ctx), stpvssdkg_stp_index_msg, 0, msg1->to, ctx->stp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return STP_DKG_Err_Env + ret; if(msg1->to > 128 || msg1->to < 1 || msg1->to > ctx->n) return STP_DKG_Err_Index; // todo remove peerids memcpy((*ctx->peerids), msg1->data, ctx->n * crypto_generichash_BYTES); if(ctx->keyloader_cb!=NULL) { const uint8_t (*peerids)[crypto_generichash_BYTES]=(const uint8_t (*)[crypto_generichash_BYTES]) msg1->data; for(unsigned i=0;in;i++) { if(0!=ctx->keyloader_cb(peerids[i],ctx->keyloader_cb_arg,(*ctx->sig_pks)[i+1],(*ctx->peer_noise_pks)[i])) { return 23; // todo } } } uint8_t *wptr = ((STP_DKG_Message *) output)->data; randombytes_buf(wptr, dkg_sessionid_SIZE); if(0!=toprf_send_msg(output, stp_dkg_peer_init1_msg_SIZE, stpvssdkg_peer_init1_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, output_len, ctx->index); ctx->step = STP_DKG_Peer_Rcv_NPK_SIDNonce; return STP_DKG_Err_OK; } static STP_DKG_Err stp_init2_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] init2 broadcast msg1 containing sessionid nonces\x1b[0m\n"); if(input_len != (stp_dkg_peer_init1_msg_SIZE) * ctx->n) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_init1_msg_SIZE * ctx->n + sizeof(STP_DKG_Message)) return STP_DKG_Err_OSize; crypto_generichash_state sid_hash_state; crypto_generichash_init(&sid_hash_state, NULL, 0, dkg_sessionid_SIZE); crypto_generichash_update(&sid_hash_state, ctx->sessionid, dkg_sessionid_SIZE); const uint8_t *ptr = input; uint8_t *wptr = ((STP_DKG_Message *) output)->data; for(uint8_t i=0;in;i++,ptr+=stp_dkg_peer_init1_msg_SIZE) { const uint8_t *dptr = ((const STP_DKG_Message*) ptr)->data; if(stp_recv_msg(ctx,ptr,stp_dkg_peer_init1_msg_SIZE,stpvssdkg_peer_init1_msg,i+1,0xff)) continue; // contribution to final session id crypto_generichash_update(&sid_hash_state, dptr, dkg_sessionid_SIZE); memcpy(wptr, ptr, stp_dkg_peer_init1_msg_SIZE); wptr+=stp_dkg_peer_init1_msg_SIZE; } if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; crypto_generichash_final(&sid_hash_state,ctx->sessionid,dkg_sessionid_SIZE); if(0!=toprf_send_msg(output, output_len, stpvssdkg_stp_bc_init1_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, output_len, 0); update_transcript(&ctx->transcript, output, output_len); ctx->step = STP_DKG_STP_Route_Noise_Handshakes1; return STP_DKG_Err_OK; } #define stp_dkg_peer_start_noise_msg_SIZE (sizeof(STP_DKG_Message) + noise_xk_handshake1_SIZE) static STP_DKG_Err peer_start_noise_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] noise1 receive peers session nonces, finalize sessionid, start noise sessions\x1b[0m\n", ctx->index); if(input_len != (stp_dkg_peer_init1_msg_SIZE) * ctx->n + sizeof(STP_DKG_Message)) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_start_noise_msg_SIZE * ctx->n) return STP_DKG_Err_OSize; const STP_DKG_Message* msg2 = (const STP_DKG_Message*) input; int ret = toprf_recv_msg(input, input_len, stpvssdkg_stp_bc_init1_msg, 0, 0xff, ctx->stp_sig_pk, msg2->sessionid, ctx->ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return STP_DKG_Err_BroadcastEnv+ret; update_transcript(&ctx->transcript, input, input_len); // create noise device uint8_t iname[15]; snprintf((char*) iname, sizeof iname, "toprf peer %02x", ctx->index); uint8_t dummy[32]={0}; // the following function needs a deserialization key, which we never use. ctx->dev = Noise_XK_device_create(13, (uint8_t*) "toprf p2p v0.1", iname, dummy, ctx->noise_sk); crypto_generichash_state sid_hash_state; crypto_generichash_init(&sid_hash_state, NULL, 0, dkg_sessionid_SIZE); crypto_generichash_update(&sid_hash_state, ctx->sessionid, dkg_sessionid_SIZE); const uint8_t *ptr = msg2->data; for(uint8_t i=0;in;i++, ptr+=stp_dkg_peer_init1_msg_SIZE) { const STP_DKG_Message* msg1 = (const STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_init1_msg_SIZE,stpvssdkg_peer_init1_msg,i+1,0xff)) continue; // extract peer noise pk crypto_generichash_update(&sid_hash_state, msg1->data, dkg_sessionid_SIZE); } if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; crypto_generichash_final(&sid_hash_state,ctx->sessionid,dkg_sessionid_SIZE); if(memcmp(ctx->sessionid, msg2->sessionid, dkg_sessionid_SIZE)!=0) { return STP_DKG_Err_InvSessionID; } uint8_t *wptr = output; for(uint8_t i=0;in;i++, wptr+=stp_dkg_peer_start_noise_msg_SIZE) { STP_DKG_Message *msg3 = (STP_DKG_Message *) wptr; uint8_t rname[15]; snprintf((char*) rname, sizeof rname, "toprf peer %02x", i+1); if(0!=dkg_init_noise_handshake(ctx->index, ctx->dev, (*ctx->peer_noise_pks)[i], rname, &(*ctx->noise_outs)[i], msg3->data)) return STP_DKG_Err_Noise; if(0!=toprf_send_msg(wptr, stp_dkg_peer_start_noise_msg_SIZE, stpvssdkg_peer_start_noise_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(wptr, stp_dkg_peer_start_noise_msg_SIZE, ctx->index); } ctx->step = STP_DKG_Peer_Noise_Handshake; return STP_DKG_Err_OK; } static STP_DKG_Err stp_route_start_noise_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_route(ctx, input, input_len, output, output_len, "noise1 route p2p noise handshakes to peers", ctx->n, ctx->n, stpvssdkg_peer_start_noise_msg, stp_dkg_peer_start_noise_msg_SIZE, STP_DKG_STP_Route_Noise_Handshakes2); } #define stp_dkg_peer_respond_noise_msg_SIZE (sizeof(STP_DKG_Message) + noise_xk_handshake2_SIZE) static STP_DKG_Err peer_respond_noise_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] noise2 receive session requests\x1b[0m\n", ctx->index); if(input_len != stp_dkg_peer_start_noise_msg_SIZE * ctx->n) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_respond_noise_msg_SIZE * ctx->n) return STP_DKG_Err_OSize; const uint8_t *ptr = input; uint8_t *wptr = output; for(uint8_t i=0;in;i++,ptr+=stp_dkg_peer_start_noise_msg_SIZE,wptr+=stp_dkg_peer_respond_noise_msg_SIZE) { STP_DKG_Message* msg3 = (STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_start_noise_msg_SIZE,stpvssdkg_peer_start_noise_msg,i+1,ctx->index)) continue; // respond to noise handshake request STP_DKG_Message *msg4 = (STP_DKG_Message *) wptr; uint8_t rname[15]; snprintf((char*) rname, sizeof rname, "toprf peer %02x", i+1); if(0!=dkg_respond_noise_handshake(ctx->index, ctx->dev, rname, &(*ctx->noise_ins)[i], msg3->data, msg4->data)) return STP_DKG_Err_Noise; if(0!=toprf_send_msg(wptr, stp_dkg_peer_respond_noise_msg_SIZE, stpvssdkg_peer_respond_noise_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(wptr, stp_dkg_peer_respond_noise_msg_SIZE, ctx->index); } if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; ctx->step=STP_DKG_Peer_Finish_Noise_Handshake; return STP_DKG_Err_OK; } static STP_DKG_Err stp_route_noise_respond_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_route(ctx, input, input_len, output, output_len, "noise2 route p2p noise handshakes to peers", ctx->n, ctx->n, stpvssdkg_peer_respond_noise_msg, stp_dkg_peer_respond_noise_msg_SIZE, STP_DKG_STP_Broadcast_DKG_Hash_Commitments); } #define stp_dkg_peer_start_dkg_msg_SIZE(ctx) (sizeof(STP_DKG_Message) + stp_dkg_commitment_HASHBYTES + ctx->n * crypto_auth_hmacsha256_BYTES) static STP_DKG_Err peer_dkg1_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg1 finish session handshake, start with dkg\x1b[0m\n", ctx->index); if(input_len != stp_dkg_peer_respond_noise_msg_SIZE * ctx->n) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_start_dkg_msg_SIZE(ctx)) return STP_DKG_Err_OSize; const uint8_t *ptr = input; for(uint8_t i=0;in;i++, ptr+=stp_dkg_peer_respond_noise_msg_SIZE) { STP_DKG_Message* msg4 = (STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_respond_noise_msg_SIZE,stpvssdkg_peer_respond_noise_msg,i+1,ctx->index)) continue; // process final step of noise handshake if(0!=dkg_finish_noise_handshake(ctx->index, ctx->dev, &(*ctx->noise_outs)[i], msg4->data)) return STP_DKG_Err_Noise; } if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; // start DKG STP_DKG_Message* msg5 = (STP_DKG_Message*) output; // we stash our commitments temporarily in k_commitments - they will be sent out in the next step if(dkg_vss_share(ctx->n, ctx->t, NULL, (*ctx->k_commitments), (*ctx->k_shares), NULL)) { return STP_DKG_Err_Share; } #ifdef UNITTEST_CORRUPT corrupt_vsps_p1t1(ctx); corrupt_commitment_p2(ctx); corrupt_wrongshare_correct_commitment_p3(ctx); corrupt_share_p4(ctx); #endif // UNITTEST_CORRUPT if(liboprf_log_file!=NULL) { dump((const uint8_t*) (*ctx->k_commitments), crypto_core_ristretto255_BYTES*ctx->n, "[%d] dealer commitments", ctx->index); } uint8_t *wptr = msg5->data; crypto_generichash(wptr, stp_dkg_commitment_HASHBYTES, (uint8_t*) (*ctx->k_commitments), crypto_core_ristretto255_BYTES*ctx->n, NULL, 0); wptr+=stp_dkg_commitment_HASHBYTES; uint8_t *dptr = (uint8_t*) (*ctx->encrypted_shares); for(uint8_t i=0;in;i++) { // we need to send an empty packet, so that the handshake completes // and we have a final symetric key, the key during the handshake changes, only // when the handshake completes does the key become static. // this is important, so that when there are complaints, we can disclose the key. uint8_t empty[1]={0}; // would love to do [0] but that is undefined c if(0!=dkg_noise_encrypt(empty, 0, dptr, noise_xk_handshake3_SIZE, &(*ctx->noise_outs)[i])) return STP_DKG_Err_NoiseEncrypt; dptr+=noise_xk_handshake3_SIZE; if(0!=dkg_noise_encrypt((uint8_t*) &(*ctx->k_shares)[i], TOPRF_Share_BYTES*2, dptr, stp_dkg_encrypted_share_SIZE, &(*ctx->noise_outs)[i])) return STP_DKG_Err_NoiseEncrypt; // we also need to use a key-commiting mac over the encrypted share, since poly1305 is not... // these we broadcast crypto_auth(wptr, dptr, stp_dkg_encrypted_share_SIZE, Noise_XK_session_get_key((*ctx->noise_outs)[i])); dptr+=TOPRF_Share_BYTES * 2 + crypto_secretbox_xchacha20poly1305_MACBYTES; wptr+=crypto_auth_hmacsha256_BYTES; } if(liboprf_log_file!=NULL) { dump(msg5->data+stp_dkg_commitment_HASHBYTES, ctx->n*crypto_auth_hmacsha256_BYTES, "[%d] share macs", ctx->index); } //broadcast dealer_commitments and share HMACS if(0!=toprf_send_msg(output, stp_dkg_peer_start_dkg_msg_SIZE(ctx), stpvssdkg_peer_dkg1_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, stp_dkg_peer_start_dkg_msg_SIZE(ctx), ctx->index); ctx->step = STP_DKG_Peer_Rcv_Commitments_Send_Commitments; return STP_DKG_Err_OK; } static STP_DKG_Err stp_dkg1_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { STP_DKG_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "dkg1 broadcast commitment commitments", ctx->n, stp_dkg_peer_start_dkg_msg_SIZE(ctx), stpvssdkg_peer_dkg1_msg, STP_DKG_STP_Broadcast_DKG_Commitments); if(ret != STP_DKG_Err_OK) return ret; const uint8_t *ptr = input; for(unsigned i=0;in;i++,ptr+=stp_dkg_peer_start_dkg_msg_SIZE(ctx)) { const DKG_Message* msg = (const DKG_Message*) ptr; memcpy((*ctx->commitment_hashes)[i], msg->data, stp_dkg_commitment_HASHBYTES); memcpy((*ctx->share_macs)[i*ctx->n], msg->data+stp_dkg_commitment_HASHBYTES, ctx->n*crypto_auth_hmacsha256_BYTES); } return ret; } #define stp_dkg_peer_dkg2_msg_SIZE(ctx) (sizeof(STP_DKG_Message) + crypto_core_ristretto255_BYTES * ctx->n) static STP_DKG_Err peer_dkg2_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg2 receive commitment hashes, broadcast commitments\x1b[0m\n", ctx->index); if(input_len != sizeof(STP_DKG_Message) + stp_dkg_peer_start_dkg_msg_SIZE(ctx) * ctx->n) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_dkg2_msg_SIZE(ctx)) return STP_DKG_Err_OSize; // verify STP message envelope const uint8_t *ptr; int ret = unwrap_envelope(ctx,input,input_len,stpvssdkg_stp_bc_dkg1_msg,&ptr); if(ret!=STP_DKG_Err_OK) return ret; for(uint8_t i=0;in;i++, ptr+=stp_dkg_peer_start_dkg_msg_SIZE(ctx)) { const STP_DKG_Message* msg5 = (const STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_start_dkg_msg_SIZE(ctx),stpvssdkg_peer_dkg1_msg,i+1,0xff)) continue; const uint8_t *dptr=msg5->data; // extract peer commitment hash memcpy((*ctx->commitments_hashes)[i], dptr, stp_dkg_commitment_HASHBYTES); // extract and store encrypted share mac dptr+=stp_dkg_commitment_HASHBYTES; memcpy((*ctx->share_macs)[i*ctx->n], dptr, crypto_auth_hmacsha256_BYTES*ctx->n); if(liboprf_log_file!=NULL) { dump((*ctx->commitments_hashes)[i], stp_dkg_commitment_HASHBYTES, "[%d] commitment hash [%d]", ctx->index, i+1); dump((*ctx->share_macs)[i*ctx->n], crypto_auth_hmacsha256_BYTES*ctx->n, "[%d] share macs [%d]", ctx->index, i+1); } } //if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; STP_DKG_Message* msg = (STP_DKG_Message*) output; // we stashed our commitments temporarily in k_commitments memcpy(msg->data, (*ctx->k_commitments), ctx->n * crypto_core_ristretto255_BYTES); //broadcast dealer_commitments if(0!=toprf_send_msg(output, stp_dkg_peer_dkg2_msg_SIZE(ctx), stpvssdkg_peer_dkg2_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, stp_dkg_peer_dkg2_msg_SIZE(ctx), ctx->index); ctx->step = STP_DKG_Peer_Rcv_Commitments_Send_Shares; return STP_DKG_Err_OK; } static STP_DKG_Err stp_dkg2_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { STP_DKG_Err ret = stp_broadcast(ctx, input, input_len, output, output_len, "dkg2 broadcast commitments dkg step 1", ctx->n, stp_dkg_peer_dkg2_msg_SIZE(ctx), stpvssdkg_peer_dkg2_msg, STP_DKG_STP_Route_Encrypted_Shares); if(ret!=STP_DKG_Err_OK) return ret; const uint8_t *ptr = input; // fixup step, that has already been advanced in the call to stp_broadcast above. uint8_t step = ctx->step; ctx->step = STP_DKG_STP_Broadcast_DKG_Commitments; uint8_t chash[stp_dkg_commitment_HASHBYTES]; uint8_t (*c)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES]) ctx->commitments; for(uint8_t i=0;in;i++,ptr+=stp_dkg_peer_dkg2_msg_SIZE(ctx)) { const DKG_Message* msg = (const DKG_Message*) ptr; // verify against commitment hashes crypto_generichash(chash, stp_dkg_commitment_HASHBYTES, msg->data, crypto_core_ristretto255_BYTES*ctx->n, NULL, 0); if(memcmp(chash, (*ctx->commitment_hashes)[i], stp_dkg_commitment_HASHBYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"f[!] failed to verify hash for commitments of dealer %d\n"NORMAL, i+1); if(stp_add_cheater(ctx, 4, i+1, 0) == NULL) { ctx->step=step; return STP_DKG_Err_CheatersFull; } } memcpy((*c)[i], msg->data, crypto_core_ristretto255_BYTES * ctx->n); } // calculate preliminary final commitments uint8_t kcom[ctx->n][crypto_core_ristretto255_BYTES]; for(unsigned i=0;in;i++) { memcpy(kcom[i], (*c)[0][i], crypto_scalarmult_ristretto255_BYTES); for(unsigned j=1;jn;j++) { crypto_core_ristretto255_add(kcom[i], kcom[i], (*c)[j][i]); } } uint8_t fails_len=0; uint8_t fails[ctx->n]; memset(fails,0,ctx->n); ret = ft_or_full_vsps(ctx->n, ctx->t, ctx->n, 0, kcom, c, "VSPS failed k during DKG, doing full VSPS check on all peers", "VSPS failed k", "ERROR, could not find any dealer commitments that fail the VSPS check", &fails_len, fails); if(ret!=STP_DKG_Err_OK) { ctx->step=step; return ret; } for(unsigned i=0;istep=step; return STP_DKG_Err_CheatersFull; } } if(ctx->n - fails_len < 2) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] less than 2 honest dealers: %d \n"NORMAL, ctx->n - fails_len); if(stp_add_cheater(ctx,2,0,0) == NULL) { ctx->step=step; return STP_DKG_Err_CheatersFull; } } if(fails_len >= ctx->t) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] more than t cheaters (t=%d, cheaters=%d)\n"NORMAL, ctx->t, fails_len); if(stp_add_cheater(ctx,3,fails_len,0) == NULL) { ctx->step=step; return STP_DKG_Err_CheatersFull; } } ctx->step=step; return ret; } #define stp_dkg_peer_dkg3_msg_SIZE (sizeof(STP_DKG_Message) /* header */ \ + noise_xk_handshake3_SIZE /* 4th&final noise handshake */ \ + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped k share */ \ + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped k blind */ \ + crypto_secretbox_xchacha20poly1305_MACBYTES /* mac of msg */ ) static STP_DKG_Err peer_dkg3_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg3 receive commitments & distribute shares via noise chans\x1b[0m\n", ctx->index); if(input_len != sizeof(STP_DKG_Message) + stp_dkg_peer_dkg2_msg_SIZE(ctx) * ctx->n) return STP_DKG_Err_ISize; if(output_len != ctx->n * stp_dkg_peer_dkg3_msg_SIZE) return STP_DKG_Err_OSize; // verify STP message envelope const uint8_t *ptr; int ret = unwrap_envelope(ctx,input,input_len,stpvssdkg_stp_bc_dkg2_msg,&ptr); if(ret!=STP_DKG_Err_OK) return ret; for(uint8_t i=0;in;i++, ptr+=stp_dkg_peer_dkg2_msg_SIZE(ctx)) { const STP_DKG_Message* msg5 = (const STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_dkg2_msg_SIZE(ctx),stpvssdkg_peer_dkg2_msg,i+1,0xff)) continue; // extract peer commitments memcpy((*ctx->ki_commitments)[i*ctx->n], msg5->data, crypto_core_ristretto255_BYTES * ctx->n); if(liboprf_log_file!=NULL) { dump((*ctx->ki_commitments)[i*ctx->n], crypto_core_ristretto255_BYTES*ctx->n, "[%d] k commitments [%d]", ctx->index, i+1); } // verify against commitment hashes uint8_t chash[stp_dkg_commitment_HASHBYTES]; crypto_generichash(chash, stp_dkg_commitment_HASHBYTES, (*ctx->ki_commitments)[i*ctx->n], crypto_core_ristretto255_BYTES*ctx->n, NULL, 0); if(memcmp(chash, (*ctx->commitments_hashes)[i], stp_dkg_commitment_HASHBYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"f[%d] failed to verify hash for commitments of dealer %d\n"NORMAL, ctx->index, i+1); if(peer_add_cheater(ctx, 1, i+1, 0) == NULL) return STP_DKG_Err_CheatersFull; } } // yes we abort here if the hash commitment fails. if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; // we could check VSPS here, but that would complicate msg size // calculation taking into account demoted dealers, so we do it // after the shares have been dealt. uint8_t *wptr = output; for(uint8_t i=0;in;i++, wptr+=stp_dkg_peer_dkg3_msg_SIZE) { STP_DKG_Message *msg7 = (STP_DKG_Message *) wptr; memcpy(msg7->data, (*ctx->encrypted_shares)[i], noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE); if(0!=toprf_send_msg(wptr, stp_dkg_peer_dkg3_msg_SIZE, stpvssdkg_peer_dkg3_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(wptr, stp_dkg_peer_dkg3_msg_SIZE, ctx->index); } ctx->step = STP_DKG_Peer_Verify_Commitments; return STP_DKG_Err_OK; } static STP_DKG_Err stp_dkg3_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_route(ctx, input, input_len, output, output_len, "dkg3 route shares to peers", ctx->n, ctx->n, stpvssdkg_peer_dkg3_msg, stp_dkg_peer_dkg3_msg_SIZE, STP_DKG_STP_Broadcast_Complaints); } #define stp_dkg_peer_verify_shares_msg_SIZE(ctx) (sizeof(STP_DKG_Message) + (size_t)(ctx->n + 1)) static STP_DKG_Err peer_verify_shares_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] verify1 DKG step 2 - receive shares, verify commitments\x1b[0m\n", ctx->index); if(input_len != stp_dkg_peer_dkg3_msg_SIZE * ctx->n) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_verify_shares_msg_SIZE(ctx)) return STP_DKG_Err_OSize; const uint8_t *ptr = input; for(uint8_t i=0;in;i++) { const STP_DKG_Message* msg = (const STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_dkg3_msg_SIZE,stpvssdkg_peer_dkg3_msg,i+1,ctx->index)) continue; // decrypt final empty handshake packet if(0!=dkg_noise_decrypt(msg->data, noise_xk_handshake3_SIZE, NULL, 0, &(*ctx->noise_ins)[i])) return STP_DKG_Err_NoiseDecrypt; if(0!=dkg_noise_decrypt(msg->data + noise_xk_handshake3_SIZE, TOPRF_Share_BYTES*2 + crypto_secretbox_xchacha20poly1305_MACBYTES, (uint8_t*) &(*ctx->k_shares)[i], TOPRF_Share_BYTES*2, &(*ctx->noise_ins)[i])) return STP_DKG_Err_NoiseDecrypt; ptr+=stp_dkg_peer_dkg3_msg_SIZE; } //if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; STP_DKG_Message* msg = (STP_DKG_Message*) output; uint8_t *fails_len = msg->data; uint8_t *fails = fails_len+1; memset(fails_len, 0, ctx->n+1); uint8_t (*c)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES]) ctx->ki_commitments; // verify that the shares match the commitment for(uint8_t i=0;in;i++) { if(0!=dkg_vss_verify_commitment((*c)[i][ctx->index-1],(*ctx->k_shares)[i])) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,"\x1b[0;31m[%d] failed to verify k commitments from %d!\x1b[0m\n", ctx->index, i+1); fails[(*fails_len)++]=i+1; } } #ifdef UNITTEST_CORRUPT corrupt_false_accuse_p2p3(ctx, fails_len, fails); #endif //UNITTEST_CORRUPT if(liboprf_log_file!=NULL && *fails_len>0) { fprintf(liboprf_log_file, RED"[%d] commitment fails#: %d -> ", ctx->index, *fails_len); for(unsigned i=0;i<*fails_len;i++) fprintf(liboprf_log_file, "%s%d", (i>0)?", ":"", fails[i]); fprintf(liboprf_log_file, NORMAL"\n"); } if(0!=toprf_send_msg(output, stp_dkg_peer_verify_shares_msg_SIZE(ctx), stpvssdkg_peer_verify_shares_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, stp_dkg_peer_verify_shares_msg_SIZE(ctx), ctx->index); ctx->step = STP_DKG_Peer_Handle_DKG_Complaints; return STP_DKG_Err_OK; } static STP_DKG_Err stp_complaint_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, const char* step_title, const uint8_t msg_count, const size_t msg_size, const uint8_t msgno, const STP_DKG_STP_Steps pass_step, const STP_DKG_STP_Steps fail_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] %s\x1b[0m\n", step_title); if(input_len != msg_size * msg_count) return STP_DKG_Err_ISize; if(sizeof(STP_DKG_Message) + input_len != output_len) return STP_DKG_Err_OSize; ctx->share_complaints_len = 0; const uint8_t *ptr = input; uint8_t *wptr = ((STP_DKG_Message *) output)->data; for(uint8_t i=0;ilen - sizeof(STP_DKG_Message) < msg->data[0]) return STP_DKG_Err_OOB; const uint8_t *fails_len = msg->data; const uint8_t *fails = msg->data+1; handle_complaints(msg_count, i+1, "share commitment", *fails_len, fails, &ctx->share_complaints_len, (*ctx->share_complaints), 0, 0, 0); memcpy(wptr, ptr, msg_size); wptr+=msg_size; } // if more than t^2 complaints are received the protocol also fails if(ctx->share_complaints_len >= ctx->t * ctx->t) { if(stp_add_cheater(ctx, 6, 0xfe, 0xfe) == NULL) return STP_DKG_Err_CheatersFull; return STP_DKG_Err_TooManyCheaters; } //if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; if(0!=toprf_send_msg(output, output_len, msgno+1, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, output_len, 0); // add broadcast msg to transcript update_transcript(&ctx->transcript, output, output_len); ctx->prev = ctx->step; if(ctx->share_complaints_len == 0) { ctx->step = pass_step; } else { dump((uint8_t*) (*ctx->share_complaints), ctx->share_complaints_len*sizeof(uint16_t), "[!] complaints"); ctx->step = fail_step; } return STP_DKG_Err_OK; } #define stp_dkg_stp_bc_verify_shares_msg_SIZE(ctx) (sizeof(STP_DKG_Message) + (stp_dkg_peer_verify_shares_msg_SIZE(ctx) * ctx->n)) static STP_DKG_Err stp_verify_shares_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_complaint_handler(ctx, input, input_len, output, output_len, "verify1 broadcast complaints of peers", ctx->n, stp_dkg_peer_verify_shares_msg_SIZE(ctx), stpvssdkg_peer_verify_shares_msg, STP_DKG_STP_Broadcast_DKG_Transcripts, STP_DKG_STP_Broadcast_DKG_Defenses); } static STP_DKG_Err peer_complaint_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, const char *step_title, const size_t msg_size, const uint8_t msgno, const STP_DKG_Peer_Steps pass_step, const STP_DKG_Peer_Steps fail_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] %s\x1b[0m\n", ctx->index, step_title); if(input_len != sizeof(STP_DKG_Message) + msg_size * ctx->n) return STP_DKG_Err_ISize; // verify STP message envelope const uint8_t *ptr; int ret = unwrap_envelope(ctx,input,input_len,msgno+1,&ptr); if(ret!=STP_DKG_Err_OK) return ret; ctx->share_complaints_len = 0; for(uint8_t i=0;in;i++, ptr+=msg_size) { const STP_DKG_Message* msg = (const STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,msg_size,msgno,i+1,0xff)) continue; if(msg->len - sizeof(STP_DKG_Message) < msg->data[0]) return STP_DKG_Err_OOB; const uint8_t *fails_len = msg->data; const uint8_t *fails = msg->data+1; handle_complaints(ctx->n, i+1, "share commitment", *fails_len, fails, &ctx->share_complaints_len, ctx->share_complaints, ctx->index, &ctx->my_share_complaints_len, ctx->my_share_complaints); } //if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; ctx->prev = ctx->step; if(ctx->share_complaints_len == 0) { ctx->step = pass_step; } else { dump((uint8_t*) ctx->share_complaints, ctx->share_complaints_len*sizeof(uint16_t), "[%d] share complaints", ctx->index); ctx->step = fail_step; } return STP_DKG_Err_OK; } static STP_DKG_Err peer_dkg_fork(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len) { return peer_complaint_handler(ctx, input, input_len, "verify2 receive complaints broadcast", stp_dkg_peer_verify_shares_msg_SIZE(ctx), stpvssdkg_peer_verify_shares_msg, STP_DKG_Peer_Finish_DKG, STP_DKG_Peer_Defend_DKG_Accusations); } static STP_DKG_Err peer_defend(STP_DKG_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] defend disclose share encryption key\x1b[0m\n", ctx->index); if(output_len != stp_dkg_peer_output_size(ctx)) return STP_DKG_Err_OSize; if(output_len == 0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] nothing to defend against, no message to send\n", ctx->index); } ctx->step = STP_DKG_Peer_Check_Shares; return 0; } // send out all shares that belong to peers that complained. STP_DKG_Message* msg = (STP_DKG_Message*) output; uint8_t *wptr = msg->data; for(int i=0;imy_share_complaints_len;i++) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;36m[%d] defending against complaint from %d\x1b[0m\n", ctx->index, ctx->my_share_complaints[i]); *wptr++ = ctx->my_share_complaints[i]; // reveal key for noise wrapped share sent previously memcpy(wptr, Noise_XK_session_get_key((*ctx->noise_outs)[ctx->my_share_complaints[i]-1]), dkg_noise_key_SIZE); wptr+=dkg_noise_key_SIZE; memcpy(wptr, (*ctx->encrypted_shares)[ctx->my_share_complaints[i]-1] + noise_xk_handshake3_SIZE, stp_dkg_encrypted_share_SIZE); wptr+=stp_dkg_encrypted_share_SIZE; } if(0!=toprf_send_msg(output, stp_dkg_peer_output_size(ctx), stpvssdkg_peer_share_key_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, stp_dkg_peer_output_size(ctx), ctx->index); ctx->step = STP_DKG_Peer_Check_Shares; return STP_DKG_Err_OK; } static STP_DKG_Err stp_broadcast_defenses(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] defense1 broadcast defenses\x1b[0m\n"); if(input_len != stp_dkg_stp_input_size(ctx)) return STP_DKG_Err_ISize; if(output_len != stp_dkg_stp_output_size(ctx)) return STP_DKG_Err_OSize; unsigned int ctr[ctx->n]; memset(ctr,0,sizeof(ctr)); for(int i=0;ishare_complaints_len;i++) { ctr[((*ctx->share_complaints)[i] & 0xff)-1]++; } uint8_t (*c)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES]) ctx->commitments; const uint8_t *ptr = input; uint8_t *wptr = ((STP_DKG_Message *) output)->data; size_t msg_size; for(uint8_t i=0;in;i++,ptr += msg_size) { if(ctr[i]==0) { msg_size = 0; continue; // no complaints against this peer } msg_size = sizeof(DKG_Message) + (1+dkg_noise_key_SIZE+stp_dkg_encrypted_share_SIZE) * ctr[i]; if(stp_recv_msg(ctx,ptr,msg_size,stpvssdkg_peer_share_key_msg,i+1,0xff)) continue; const STP_DKG_Message *msg = (const STP_DKG_Message *) ptr; const uint8_t *dptr = msg->data; for(unsigned j=0;jshare_macs)[(accused-1)*ctx->n+(accuser-1)], shares, stp_dkg_encrypted_share_SIZE, key)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,RED"[!] invalid HMAC on shares of accused: %d, by %d\n"NORMAL, accused, accuser); if(stp_add_cheater(ctx, 1, accused, accuser) == NULL) return STP_DKG_Err_CheatersFull; continue; } TOPRF_Share share[2]; Noise_XK_error_code res0 = Noise_XK_aead_decrypt((uint8_t*)key, 0, 0U, NULL, TOPRF_Share_BYTES*2, (uint8_t*) &share, (uint8_t*) shares); if (!(res0 == Noise_XK_CSuccess)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,RED"[!] failed to decrypt shares of accused: %d, by %d\n"NORMAL, accused, accuser); // share decryption failure if(stp_add_cheater(ctx, 2, accused, accuser) == NULL) return STP_DKG_Err_CheatersFull; continue; } if(share[0].index != accuser) { // invalid share index STP_DKG_Cheater* cheater = stp_add_cheater(ctx, 3, accused, accuser); if(cheater == NULL) return STP_DKG_Err_CheatersFull; cheater->invalid_index = share[0].index; continue; } if(0!=dkg_vss_verify_commitment((*c)[accused-1][accuser-1],share)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify commitment of accused %d by accuser %d!\x1b[0m\n", accused, accuser); STP_DKG_Cheater* cheater = stp_add_cheater(ctx, 4, accused, accuser); if(cheater == NULL) return STP_DKG_Err_CheatersFull; cheater->invalid_index = share[0].index; continue; } else { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,GREEN"[!] succeeded to verify commitment of accused %d by accuser %d!\x1b[0m\n", accused, accuser); if(stp_add_cheater(ctx, 5, accuser, accused) == NULL) return STP_DKG_Err_CheatersFull; } } memcpy(wptr, ptr, msg_size); wptr+=msg_size; } //if(ctx->cheater_len>0) return STP_DKG_Err_CheatersFound; if(0!=toprf_send_msg(output, output_len, stpvssdkg_stp_bc_key_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, output_len, 0); // add broadcast msg to transcript update_transcript(&ctx->transcript, output, output_len); ctx->step = STP_DKG_STP_Broadcast_DKG_Transcripts; return STP_DKG_Err_OK; } #define stp_dkg_peer_bc_transcript_msg_SIZE (sizeof(STP_DKG_Message) + crypto_generichash_BYTES + crypto_core_ristretto255_BYTES) static STP_DKG_Err peer_verify_vsps(STP_DKG_PeerState *ctx, uint8_t *output, const size_t output_len); static STP_DKG_Err peer_check_shares(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] verify3 disclosed shares\x1b[0m\n", ctx->index); if(input_len != stp_dkg_peer_input_size(ctx)) return STP_DKG_Err_ISize; if(output_len != stp_dkg_peer_bc_transcript_msg_SIZE) return STP_DKG_Err_OSize; // verify STP message envelope const uint8_t *ptr; int ret = unwrap_envelope(ctx,input,input_len,stpvssdkg_stp_bc_key_msg,&ptr); if(ret!=STP_DKG_Err_OK) return ret; unsigned int ctr[ctx->n]; memset(ctr,0,sizeof(ctr)); for(int i=0;ishare_complaints_len;i++) { ctr[(ctx->share_complaints[i] & 0xff)-1]++; ctx->share_complaints[i]=0; } ctx->share_complaints_len=0; uint8_t (*c)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES]) ctx->ki_commitments; size_t msg_size; for(uint8_t i=0;in;i++,ptr += msg_size) { if(ctr[i]==0) { msg_size = 0; continue; // no complaints against this peer } msg_size = sizeof(DKG_Message) + (1+dkg_noise_key_SIZE+stp_dkg_encrypted_share_SIZE) * ctr[i]; if(peer_recv_msg(ctx,ptr,msg_size,stpvssdkg_peer_share_key_msg,i+1,0xff)) continue; const STP_DKG_Message *msg = (const STP_DKG_Message *) ptr; const uint8_t *dptr = msg->data; for(unsigned j=0;jindex, accused, accuser); if(0!=crypto_auth_verify((*ctx->share_macs)[(accused-1)*ctx->n+(accuser-1)], shares, stp_dkg_encrypted_share_SIZE, key)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,RED"[%d] invalid HMAC on shares of accused: %d, by %d\n"NORMAL, ctx->index, accused, accuser); if(peer_add_cheater(ctx, 1, accused, accuser) == NULL) return STP_DKG_Err_CheatersFull; ctx->share_complaints[ctx->share_complaints_len++]=accused; continue; } TOPRF_Share share[2]; Noise_XK_error_code res0 = Noise_XK_aead_decrypt((uint8_t*)key, 0, 0U, NULL, TOPRF_Share_BYTES*2, (uint8_t*) &share, (uint8_t*) shares); if (!(res0 == Noise_XK_CSuccess)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,RED"[%d] failed to decrypt shares of accused: %d, by %d\n"NORMAL, ctx->index, accused, accuser); // share decryption failure if(peer_add_cheater(ctx, 2, accused, accuser) == NULL) return STP_DKG_Err_CheatersFull; ctx->share_complaints[ctx->share_complaints_len++]=accused; continue; } if(share[0].index != accuser) { // invalid share index STP_DKG_Cheater* cheater = peer_add_cheater(ctx, 3, accused, accuser); if(cheater == NULL) return STP_DKG_Err_CheatersFull; cheater->invalid_index = share[0].index; ctx->share_complaints[ctx->share_complaints_len++]=accused; continue; } if(0!=dkg_vss_verify_commitment((*c)[accused-1][accuser-1],share)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,"\x1b[0;31m[%d] failed to verify commitment of accused %d by accuser %d!\x1b[0m\n", ctx->index, accused, accuser); STP_DKG_Cheater* cheater = peer_add_cheater(ctx, 4, accused, accuser); if(cheater == NULL) return STP_DKG_Err_CheatersFull; cheater->invalid_index = share[0].index; ctx->share_complaints[ctx->share_complaints_len++]=accused; continue; } else { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,GREEN"[%d] succeeded to verify commitment of accused %d by accuser %d!\x1b[0m\n", ctx->index, accused, accuser); if(peer_add_cheater(ctx, 5, accuser, accused) == NULL) return STP_DKG_Err_CheatersFull; //ctx->share_complaints[ctx->share_complaints_len++]=accused; } } } return peer_verify_vsps(ctx, output, output_len); } static STP_DKG_Err peer_verify_vsps(STP_DKG_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] verify1 DKG step 2 - VSPS check commitments, calculate share and broadcast transcript and final commitment\x1b[0m\n", ctx->index); if(output_len != stp_dkg_peer_bc_transcript_msg_SIZE) return STP_DKG_Err_OSize; // 2. Players verify the VSPS property of the sum of the shared secrets by running // VSPS-Check on 𝓐_i,..,𝓐_n where // // 𝓐_j = Ξ  𝓐_i,j // i // // If this check fails the players run VSPS-Check on each individual // sharing from step 1. Any player that fails this check is disqualified. uint8_t (*c)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES]) ctx->ki_commitments; uint8_t (*kcom)[ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][crypto_core_ristretto255_BYTES]) ctx->k_commitments; for(unsigned i=0;in;i++) { memcpy((*kcom)[i], (*c)[0][i], crypto_scalarmult_ristretto255_BYTES); for(unsigned j=1;jn;j++) { crypto_core_ristretto255_add((*kcom)[i], (*kcom)[i], (*c)[j][i]); } } uint8_t fails_len=0; uint8_t fails[ctx->n]; memset(fails,0,ctx->n); STP_DKG_Err ret = ft_or_full_vsps(ctx->n, ctx->t, ctx->n, ctx->index, (*kcom), c, "VSPS failed k during DKG, doing full VSPS check on all peers", "VSPS failed k", "ERROR, could not find any dealer commitments that fail the VSPS check", &fails_len, fails); if(ret!=STP_DKG_Err_OK) return ret; if(ctx->n - fails_len < 2) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, RED"[%d] less than 2 honest dealers: %d \n"NORMAL, ctx->index, ctx->n - fails_len); if(peer_add_cheater(ctx, 6, 0, 0) == NULL) return STP_DKG_Err_CheatersFull; } return STP_DKG_Err_NotEnoughDealers; } if(fails_len >= ctx->t) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, RED"[%d] more than t cheaters (t=%d, cheaters=%d)\n"NORMAL, ctx->index, ctx->t, fails_len); if(peer_add_cheater(ctx, 7, fails_len, 0) == NULL) return STP_DKG_Err_CheatersFull; } return STP_DKG_Err_TooManyCheaters; } uint8_t qual[ctx->n+1]; uint8_t qual_len=0; for(uint8_t i=0;in;i++) { unsigned j,k; for(j=0;jshare_complaints_len;k++) { if(ctx->share_complaints[k]==i+1) break; } if(j>=fails_len) { if(k>=ctx->share_complaints_len) qual[qual_len++]=i+1; } else if(peer_add_cheater(ctx, 8, ctx->index, i+1) == NULL) return STP_DKG_Err_CheatersFull; } qual[qual_len]=0; if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] qual is: ", ctx->index); for(unsigned i=0;ishare[0].index=ctx->index; ctx->share[1].index=ctx->index; // finalize dkg if(0!=dkg_vss_finish(ctx->n,qual,(*ctx->k_shares),ctx->index,ctx->share, ctx->k_commitment)) return STP_DKG_Err_DKGFinish; STP_DKG_Message* msg20 = (STP_DKG_Message*) output; crypto_generichash_final(&ctx->transcript, msg20->data, crypto_generichash_BYTES); memcpy(ctx->final_transcript, msg20->data, crypto_generichash_BYTES); memcpy(msg20->data+crypto_generichash_BYTES, ctx->k_commitment, crypto_core_ristretto255_BYTES); if(0!=toprf_send_msg(output, stp_dkg_peer_bc_transcript_msg_SIZE, stpvssdkg_peer_bc_transcript_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, stp_dkg_peer_bc_transcript_msg_SIZE, ctx->index); ctx->step = STP_DKG_Peer_Confirm_Transcripts; return STP_DKG_Err_OK; } #define stp_dkg_stp_bc_transcript_msg_SIZE(ctx) (sizeof(STP_DKG_Message) + stp_dkg_peer_bc_transcript_msg_SIZE*ctx->n) static STP_DKG_Err stp_bc_transcript_handler(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] final1 broadcast DKG transcripts\x1b[0m\n"); if((stp_dkg_peer_bc_transcript_msg_SIZE * ctx->n) != input_len) return STP_DKG_Err_ISize; if(output_len != stp_dkg_stp_bc_transcript_msg_SIZE(ctx)) return STP_DKG_Err_OSize; uint8_t transcript_hash[crypto_generichash_BYTES]; crypto_generichash_final(&ctx->transcript, transcript_hash, crypto_generichash_BYTES); size_t cheaters = ctx->cheater_len; uint8_t *wptr = ((STP_DKG_Message *) output)->data; const uint8_t *ptr = input; for(uint8_t i=0;in;i++, ptr+=stp_dkg_peer_bc_transcript_msg_SIZE) { const STP_DKG_Message* msg = (const STP_DKG_Message*) ptr; if(stp_recv_msg(ctx,ptr,stp_dkg_peer_bc_transcript_msg_SIZE,stpvssdkg_peer_bc_transcript_msg,i+1,0xff)) continue; memcpy((*ctx->commitments)[i], msg->data + crypto_generichash_BYTES, crypto_core_ristretto255_BYTES); if(sodium_memcmp(transcript_hash, msg->data, sizeof(transcript_hash))!=0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify transcript from %d!\x1b[0m\n", i); } if(stp_add_cheater(ctx, 1, i+1, 0) == NULL) return STP_DKG_Err_CheatersFull; continue; } memcpy(wptr, ptr, stp_dkg_peer_bc_transcript_msg_SIZE); wptr+=stp_dkg_peer_bc_transcript_msg_SIZE; } if(ctx->cheater_len>cheaters) return STP_DKG_Err_CheatersFound; liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, *ctx->commitments)) { liboprf_debug=1; if(liboprf_log_file!=NULL) fprintf(stderr, RED"[!] result of DKG final commitments fail VSPS\n"NORMAL); if(stp_add_cheater(ctx, 2, 0, 0) == NULL) return STP_DKG_Err_CheatersFull; } liboprf_debug=1; if(0!=toprf_send_msg(output, output_len, stpvssdkg_stp_bc_transcript_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return STP_DKG_Err_Send; dkg_dump_msg(output, output_len, 0); ctx->step = STP_DKG_STP_Done; return STP_DKG_Err_OK; } static STP_DKG_Err peer_final_handler(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] finish receive and check final transcript\x1b[0m\n", ctx->index); if(input_len != stp_dkg_stp_bc_transcript_msg_SIZE(ctx)) return STP_DKG_Err_ISize; // verify STP message envelope const uint8_t *ptr; int ret = unwrap_envelope(ctx,input,input_len,stpvssdkg_stp_bc_transcript_msg,&ptr); if(ret!=STP_DKG_Err_OK) return ret; size_t cheaters = ctx->cheater_len; uint8_t (*kcom)[ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][crypto_core_ristretto255_BYTES]) ctx->k_commitments; for(uint8_t i=0;in;i++, ptr+=stp_dkg_peer_bc_transcript_msg_SIZE) { const STP_DKG_Message* msg = (const STP_DKG_Message*) ptr; if(peer_recv_msg(ctx,ptr,stp_dkg_peer_bc_transcript_msg_SIZE,stpvssdkg_peer_bc_transcript_msg,i+1,0xff)) continue; if(sodium_memcmp(ctx->final_transcript, msg->data, crypto_generichash_BYTES)!=0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify transcript from %d!\x1b[0m\n", i); } if(peer_add_cheater(ctx, 1, i+1, 0) == NULL) return STP_DKG_Err_CheatersFull; continue; } memcpy((*kcom)[i], msg->data + crypto_generichash_BYTES, crypto_core_ristretto255_BYTES); } if(ctx->cheater_len>cheaters) return STP_DKG_Err_CheatersFound; // in theory this should not be needed, and not fail. except for the // case when the dealer shares were corrupted after calculating a // correct commitment for them. liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, (*kcom))) { liboprf_debug=1; if(liboprf_log_file!=NULL) fprintf(stderr, RED"[%d] result of DKG commitments fail VSPS\n"NORMAL, ctx->index); if(peer_add_cheater(ctx, 2, 0, 0) == NULL) return STP_DKG_Err_CheatersFull; } liboprf_debug=1; ctx->step = STP_DKG_Peer_Done; return STP_DKG_Err_OK; } int stp_dkg_stp_not_done(const STP_DKG_STPState *stp) { return stp->stepstepn;i++) { if((*ctx->noise_ins)[i]!=NULL) Noise_XK_session_free((*ctx->noise_ins)[i]); if((*ctx->noise_outs)[i]!=NULL) Noise_XK_session_free((*ctx->noise_outs)[i]); } if(ctx->dev!=NULL) Noise_XK_device_free(ctx->dev); } size_t stp_dkg_stp_input_size(const STP_DKG_STPState *ctx) { size_t sizes[ctx->n]; //memset(sizes,0,sizeof sizes); if(stp_dkg_stp_input_sizes(ctx, sizes) == 1) { return sizes[0] * ctx->n; } else { size_t result=0; for(int i=0;in;i++) result+=sizes[i]; return result; } } int stp_dkg_stp_input_sizes(const STP_DKG_STPState *ctx, size_t *sizes) { size_t item=0; switch(ctx->step) { case STP_DKG_STP_Send_Index: { item = 0; break; } case STP_DKG_STP_Broadcast_NPKs: { item = stp_dkg_peer_init1_msg_SIZE; break; } case STP_DKG_STP_Route_Noise_Handshakes1: { item=stp_dkg_peer_start_noise_msg_SIZE * ctx->n; break; } case STP_DKG_STP_Route_Noise_Handshakes2: { item=stp_dkg_peer_respond_noise_msg_SIZE * ctx->n; break; } case STP_DKG_STP_Broadcast_DKG_Hash_Commitments: { item=stp_dkg_peer_start_dkg_msg_SIZE(ctx); break; } case STP_DKG_STP_Broadcast_DKG_Commitments: { item = stp_dkg_peer_dkg2_msg_SIZE(ctx); break; } case STP_DKG_STP_Route_Encrypted_Shares: { item = stp_dkg_peer_dkg3_msg_SIZE * ctx->n; break; } case STP_DKG_STP_Broadcast_Complaints: { item = stp_dkg_peer_verify_shares_msg_SIZE(ctx); break; } case STP_DKG_STP_Broadcast_DKG_Defenses: { uint8_t ctr[ctx->n]; memset(ctr,0,ctx->n); for(int i=0;ishare_complaints_len;i++) ctr[((*ctx->share_complaints)[i] & 0xff) - 1]++; for(int i=0;in;i++) { if(ctr[i]>0) { sizes[i]=sizeof(STP_DKG_Message) + (1+dkg_noise_key_SIZE+stp_dkg_encrypted_share_SIZE) * ctr[i]; } else { sizes[i]=0; } } return 0; } case STP_DKG_STP_Broadcast_DKG_Transcripts: { item = stp_dkg_peer_bc_transcript_msg_SIZE; break; } case STP_DKG_STP_Done: { item = 0; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] isize invalid stp step: %d\n", ctx->step); } } for(uint8_t i=0;in;i++) { sizes[i] = item; } return 1; } size_t stp_dkg_stp_output_size(const STP_DKG_STPState *ctx) { switch(ctx->step) { case STP_DKG_STP_Send_Index: return stp_dkg_stp_index_msg_SIZE(ctx) * ctx->n; case STP_DKG_STP_Broadcast_NPKs: return (stp_dkg_peer_init1_msg_SIZE) * ctx->n + sizeof(STP_DKG_Message); case STP_DKG_STP_Route_Noise_Handshakes1: return stp_dkg_peer_start_noise_msg_SIZE * ctx->n * ctx->n; case STP_DKG_STP_Route_Noise_Handshakes2: return stp_dkg_peer_respond_noise_msg_SIZE * ctx->n * ctx->n; case STP_DKG_STP_Broadcast_DKG_Hash_Commitments: return sizeof(STP_DKG_Message) + (stp_dkg_peer_start_dkg_msg_SIZE(ctx) * ctx->n); case STP_DKG_STP_Broadcast_DKG_Commitments: return sizeof(STP_DKG_Message) + (stp_dkg_peer_dkg2_msg_SIZE(ctx) * ctx->n); case STP_DKG_STP_Route_Encrypted_Shares: return stp_dkg_peer_dkg3_msg_SIZE * ctx->n * ctx->n; case STP_DKG_STP_Broadcast_Complaints: return stp_dkg_stp_bc_verify_shares_msg_SIZE(ctx); case STP_DKG_STP_Broadcast_DKG_Defenses: return sizeof(STP_DKG_Message) + stp_dkg_stp_input_size(ctx); case STP_DKG_STP_Broadcast_DKG_Transcripts: return stp_dkg_stp_bc_transcript_msg_SIZE(ctx); case STP_DKG_STP_Done: return 0; default: if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] osize invalid stp step: %d\n", ctx->step); } return 0; } int stp_dkg_stp_peer_msg(const STP_DKG_STPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len) { if(peer>=ctx->n) return -1; switch(ctx->prev) { case STP_DKG_STP_Send_Index: { *msg = base + peer * stp_dkg_stp_index_msg_SIZE(ctx); *len = stp_dkg_stp_index_msg_SIZE(ctx); break; } case STP_DKG_STP_Broadcast_NPKs: { *msg = base; *len = (stp_dkg_peer_init1_msg_SIZE) * ctx->n + sizeof(STP_DKG_Message); break; } case STP_DKG_STP_Route_Noise_Handshakes1: { *msg = base + peer * stp_dkg_peer_start_noise_msg_SIZE * ctx->n; *len = stp_dkg_peer_start_noise_msg_SIZE * ctx->n; break; } case STP_DKG_STP_Route_Noise_Handshakes2: { *msg = base + peer * stp_dkg_peer_respond_noise_msg_SIZE * ctx->n; *len = stp_dkg_peer_start_noise_msg_SIZE * ctx->n; break; } case STP_DKG_STP_Broadcast_DKG_Hash_Commitments: { *msg = base; *len = sizeof(STP_DKG_Message) + (stp_dkg_peer_start_dkg_msg_SIZE(ctx) * ctx->n); break; } case STP_DKG_STP_Broadcast_DKG_Commitments: { *msg = base; *len = sizeof(STP_DKG_Message) + (stp_dkg_peer_dkg2_msg_SIZE(ctx) * ctx->n); break; } case STP_DKG_STP_Route_Encrypted_Shares: { *msg = base + peer * stp_dkg_peer_dkg3_msg_SIZE * ctx->n; *len = stp_dkg_peer_dkg3_msg_SIZE * ctx->n; break; } case STP_DKG_STP_Broadcast_Complaints: { *msg = base; *len = stp_dkg_stp_bc_verify_shares_msg_SIZE(ctx); break; } case STP_DKG_STP_Broadcast_DKG_Defenses: { *msg = base; *len = sizeof(STP_DKG_Message); uint8_t ctr[ctx->n]; memset(ctr,0,ctx->n); for(int i=0;ishare_complaints_len;i++) ctr[((*ctx->share_complaints)[i] & 0xff) - 1]++; for(int i=0;in;i++) { if(ctr[i]>0) { *len+=sizeof(STP_DKG_Message) + (1+dkg_noise_key_SIZE+stp_dkg_encrypted_share_SIZE) * ctr[i]; } } break; } case STP_DKG_STP_Broadcast_DKG_Transcripts: { *msg = base; *len = stp_dkg_stp_bc_transcript_msg_SIZE(ctx); break; } case STP_DKG_STP_Done: { *msg = NULL; *len = 0; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid stp step in stp_dkg_stp_peer_msg\n"); return 1; } } if(base+base_size < *msg + *len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "buffer overread detected in stp_dkg_stp_peer_msg %ld, step %d\n", (base+base_size) - (*msg + *len), ctx->step); return 2; } return 0; } size_t stp_dkg_peer_input_size(const STP_DKG_PeerState *ctx) { switch(ctx->step) { case STP_DKG_Peer_Broadcast_NPK_SIDNonce: return stp_dkg_stp_index_msg_SIZE(ctx); case STP_DKG_Peer_Rcv_NPK_SIDNonce: return (stp_dkg_peer_init1_msg_SIZE) * ctx->n + sizeof(STP_DKG_Message); case STP_DKG_Peer_Noise_Handshake: return stp_dkg_peer_start_noise_msg_SIZE * ctx->n; case STP_DKG_Peer_Finish_Noise_Handshake: return stp_dkg_peer_respond_noise_msg_SIZE * ctx->n; case STP_DKG_Peer_Rcv_Commitments_Send_Commitments: return sizeof(STP_DKG_Message) + stp_dkg_peer_start_dkg_msg_SIZE(ctx) * ctx->n; case STP_DKG_Peer_Rcv_Commitments_Send_Shares: return sizeof(STP_DKG_Message) + (stp_dkg_peer_dkg2_msg_SIZE(ctx) * ctx->n); case STP_DKG_Peer_Verify_Commitments: return ctx->n * stp_dkg_peer_dkg3_msg_SIZE; case STP_DKG_Peer_Handle_DKG_Complaints: return stp_dkg_stp_bc_verify_shares_msg_SIZE(ctx); case STP_DKG_Peer_Defend_DKG_Accusations: return 0; case STP_DKG_Peer_Check_Shares: { uint8_t ctr[ctx->n]; memset(ctr,0,ctx->n); for(int i=0;ishare_complaints_len;i++) ctr[(ctx->share_complaints[i] & 0xff) - 1]++; size_t ret = sizeof(STP_DKG_Message); for(int i=0;in;i++) { if(ctr[i]>0) { ret+=sizeof(STP_DKG_Message) + (1+dkg_noise_key_SIZE+stp_dkg_encrypted_share_SIZE) * ctr[i]; } } return ret; } case STP_DKG_Peer_Finish_DKG: return 0; case STP_DKG_Peer_Confirm_Transcripts: return stp_dkg_stp_bc_transcript_msg_SIZE(ctx); case STP_DKG_Peer_Done: return 0; default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] invalid step\n", ctx->index); } } return 1; } size_t stp_dkg_peer_output_size(const STP_DKG_PeerState *ctx) { switch(ctx->step) { case STP_DKG_Peer_Broadcast_NPK_SIDNonce: return stp_dkg_peer_init1_msg_SIZE; case STP_DKG_Peer_Rcv_NPK_SIDNonce: return stp_dkg_peer_start_noise_msg_SIZE * ctx->n; case STP_DKG_Peer_Noise_Handshake: return stp_dkg_peer_respond_noise_msg_SIZE * ctx->n; case STP_DKG_Peer_Finish_Noise_Handshake: return stp_dkg_peer_start_dkg_msg_SIZE(ctx); case STP_DKG_Peer_Rcv_Commitments_Send_Commitments: return stp_dkg_peer_dkg2_msg_SIZE(ctx); case STP_DKG_Peer_Rcv_Commitments_Send_Shares: return ctx->n * stp_dkg_peer_dkg3_msg_SIZE; case STP_DKG_Peer_Verify_Commitments: return stp_dkg_peer_verify_shares_msg_SIZE(ctx); case STP_DKG_Peer_Handle_DKG_Complaints: return 0; case STP_DKG_Peer_Defend_DKG_Accusations: { if(ctx->my_share_complaints_len == 0 /* && ctx->my_vsps_complaints_len == 0*/) return 0; size_t res = sizeof(STP_DKG_Message); if(ctx->my_share_complaints_len > 0) { res += ctx->my_share_complaints_len * (1+dkg_noise_key_SIZE+stp_dkg_encrypted_share_SIZE); } //if(ctx->my_vsps_complaints_len > 0) { // res += ctx->my_vsps_complaints_len * (1+dkg_noise_key_SIZE); //} return res; } case STP_DKG_Peer_Check_Shares: return stp_dkg_peer_bc_transcript_msg_SIZE; case STP_DKG_Peer_Finish_DKG: return stp_dkg_peer_bc_transcript_msg_SIZE; case STP_DKG_Peer_Confirm_Transcripts: return 0; case STP_DKG_Peer_Done: return 0; default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] invalid step\n", ctx->index); } } return 1; } int stp_dkg_stp_next(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { int ret = 0; ctx->prev=ctx->step; switch(ctx->step) { case STP_DKG_STP_Send_Index: { ret = stp_init_send_indexes(ctx, output, output_len) ; break ; } case STP_DKG_STP_Broadcast_NPKs: { ret = stp_init2_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Route_Noise_Handshakes1: { ret = stp_route_start_noise_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Route_Noise_Handshakes2: { ret = stp_route_noise_respond_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Broadcast_DKG_Hash_Commitments: { ret = stp_dkg1_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Broadcast_DKG_Commitments: { ret = stp_dkg2_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Route_Encrypted_Shares: { ret = stp_dkg3_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Broadcast_Complaints: { ret = stp_verify_shares_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Broadcast_DKG_Defenses: { ret = stp_broadcast_defenses(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Broadcast_DKG_Transcripts: { ret = stp_bc_transcript_handler(ctx, input, input_len, output, output_len); break;} case STP_DKG_STP_Done: { ret = 0; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid step\n"); return 99; } } if(ret!=0) ctx->step=99; // so that not_done reports done return ret; } int stp_dkg_peer_next(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { int ret=0; ctx->prev=ctx->step; switch(ctx->step) { case STP_DKG_Peer_Broadcast_NPK_SIDNonce: { ret = peer_init1_handler(ctx, input, input_len, output, output_len) ; break; } case STP_DKG_Peer_Rcv_NPK_SIDNonce: { ret = peer_start_noise_handler(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Noise_Handshake: { ret = peer_respond_noise_handler(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Finish_Noise_Handshake: { ret = peer_dkg1_handler(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Rcv_Commitments_Send_Commitments: { ret = peer_dkg2_handler(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Rcv_Commitments_Send_Shares: { ret = peer_dkg3_handler(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Verify_Commitments: { ret = peer_verify_shares_handler(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Handle_DKG_Complaints: { ret = peer_dkg_fork(ctx, input, input_len); break; } case STP_DKG_Peer_Defend_DKG_Accusations: { ret = peer_defend(ctx, output, output_len); break; } case STP_DKG_Peer_Check_Shares: { ret = peer_check_shares(ctx, input, input_len, output, output_len); break; } case STP_DKG_Peer_Finish_DKG: { ret = peer_verify_vsps(ctx, output, output_len); break; } case STP_DKG_Peer_Confirm_Transcripts: { ret = peer_final_handler(ctx, input, input_len); break; } case STP_DKG_Peer_Done: { // we are done ret = 0; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] invalid step\n", ctx->index); ret = 99; } } if(ret!=0) ctx->step=99; // so that not_done reports done return ret; } uint8_t stp_dkg_stp_cheater_msg(const STP_DKG_Cheater *c, char *out, const size_t outlen) { if(c->error>65 && c->error<=71) { snprintf(out, outlen, "step %d message from peer %d for peer %d could not be validated: %s", c->step, c->peer, c->other_peer, dkg_recv_err(c->error & 0x3f)); return c->peer; } if(c->error>33 && c->error<=39) { snprintf(out, outlen, "step %d broadcast message from STP for peer %d could not be validated: %s", c->step, c->other_peer, dkg_recv_err(c->error & 0x1f)); return c->peer; } if(c->step==STP_DKG_STP_Broadcast_DKG_Commitments) { switch(c->error) { case 1: {snprintf(out, outlen, "failed VSPS check for dealer %d.", c->peer); return c->peer; } case 2: {snprintf(out, outlen, "less than 2 honest dealers."); return 0; } case 3: {snprintf(out, outlen, "more than t cheaters (%d)", c->peer); return 0; } case 4: {snprintf(out, outlen, "failed to verify hash for commitments of dealer %d", c->peer); return c->peer; } } } if(c->step==STP_DKG_STP_Broadcast_DKG_Defenses) { switch(c->error) { case 1: {snprintf(out, outlen, "invalid HMAC on shares of accused: %d, by %d.", c->peer, c->other_peer); return c->peer; } case 2: {snprintf(out, outlen, "failed to decrypt shares of accused: %d, by %d", c->peer, c->other_peer); return c->peer; } case 3: {snprintf(out, outlen, "accused peer %d sent an invalid share with index %d to complaining peer %d", c->peer, c->other_peer, c->invalid_index); return c->peer; } case 4: {snprintf(out, outlen, "failed to verify commitment of accused %d by accuser %d!", c->peer, c->other_peer); return c->peer; } case 5: {snprintf(out, outlen, "succeeded to verify commitment of accused %d by accuser %d", c->peer, c->other_peer); return c->other_peer; } } } if(c->step==STP_DKG_STP_Broadcast_DKG_Transcripts) { switch(c->error) { case 1: {snprintf(out, outlen, "failed to verify transcript from %d.", c->peer); return c->peer; } case 2: {snprintf(out, outlen, "result of DKG final commitments fail VSPS."); return 0; } } } snprintf(out, outlen, "step: %d, error: %d, peer: %d, other peer: %d", c->step, c->error, c->peer, c->other_peer); return 0; } uint8_t stp_dkg_peer_cheater_msg(const STP_DKG_Cheater *c, char *out, const size_t outlen) { if(c->error>65 && c->error<=71) { snprintf(out, outlen, "step %d message from peer %d for peer %d could not be validated: %s", c->step, c->peer, c->other_peer, dkg_recv_err(c->error & 0x3f)); return c->peer; } if(c->error>33 && c->error<=39) { snprintf(out, outlen, "step %d broadcast message from STP for peer %d could not be validated: %s", c->step, c->other_peer, dkg_recv_err(c->error & 0x1f)); return c->peer; } if(c->step==STP_DKG_Peer_Rcv_Commitments_Send_Shares && c->error==1) { snprintf(out, outlen, "failed to verify hash for commitments of dealer %d", c->step); return c->peer; } if(c->step==STP_DKG_Peer_Check_Shares) { switch(c->error) { case 1: {snprintf(out, outlen, "invalid HMAC on shares of accused: %d, by %d.", c->peer, c->other_peer); return c->peer; } case 2: {snprintf(out, outlen, "failed to decrypt shares of accused: %d, by %d", c->peer, c->other_peer); return c->peer; } case 3: {snprintf(out, outlen, "accused peer %d sent an invalid share with index %d to complaining peer %d", c->peer, c->other_peer, c->invalid_index); return c->peer; } case 4: {snprintf(out, outlen, "failed to verify commitment of accused %d by accuser %d!", c->peer, c->other_peer); return c->peer; } case 5: {snprintf(out, outlen, "succeeded to verify commitment of accused %d by accuser %d", c->peer, c->other_peer); return c->other_peer; } } } if(c->step==STP_DKG_STP_Broadcast_DKG_Transcripts || c->step==STP_DKG_Peer_Check_Shares) { switch(c->error) { case 6: {snprintf(out, outlen, "less than 2 honest dealers."); return 0; } case 7: {snprintf(out, outlen, "more than t cheaters (%d)", c->peer); return 0; } case 8: {snprintf(out, outlen, "failed VSPS check for dealer %d.", c->other_peer); return c->other_peer; } } } if(c->step==STP_DKG_Peer_Confirm_Transcripts) { switch(c->error) { case 1: {snprintf(out, outlen, "failed to verify transcript from %d.", c->peer); return c->peer; } case 2: {snprintf(out, outlen, "result of DKG final commitments fail VSPS."); return 0; } } } snprintf(out, outlen, "step: %d, error: %d, peer: %d, other peer: %d", c->step, c->error, c->peer, c->other_peer); return 0; } liboprf-0.9.4/src/stp-dkg.h000066400000000000000000001057661514673400200155130ustar00rootroot00000000000000#ifndef STP_DKG_H #define STP_DKG_H /** * @file stp_dkg.h * @brief API for the Semi-Trusted Party Distributed Key Generation * (STP-DKG) Protocol * * SPDX-FileCopyrightText: 2025, Marsiske Stefan * SPDX-License-Identifier: LGPL-3.0-or-later * * This API implements a Distributed Key Generation (DKG) protocol involving * two roles: the semi-trusted party (STP) and multiple peers. * * ## Protocol Overview * * The STP orchestrates the entire protocol, relaying and broadcasting * messages between peers. Communication between peers occurs only through * the STP. This way, the STP also acts as a broadcast medium which is an * essential part of all DKG protocols. * * The protocol consists of over 20 internal steps, but the API hides this * complexity behind a state-driven loop, which any user can call iteratively * while implementing the networking communication themselves. This simplifies * the network model and enables usage across different communication channels * like TCP/IP, Bluetooth, USB, UART, etc. * The STP must support all communication channels that its peers require, * while each peer only needs to support its chosen medium. * * This protocol is based on R. Gennaro, M. O. Rabin, and T. Rabin. * "Simplified VSS and fast-track multiparty computations * with applications to threshold cryptography", in B. A. Coan and * Y. Afek, editors, 17th ACM PODC, pages 101–111. ACM, June / July. * * The full specification is available in `/docs/stp-dkg.txt`. * * ## Common Protocol Flow * * Both the peers and the STP share a similar API schema: * * - For peers: * ``` * msg0 = read(); // from the STP * start_peer(state, ...); * peer_set_bufs(); * while (peer_not_done(state)) { * input = allocate_memory( dkg_peer_input_size(state) ) * output = allocate_memory( dkg_peer_output_size(state) ) * input = read() * res = peer_next_step(state, input, output) * if res!=0: fail&abort * msg = output * send(msg) * } * store share * peer_free(state); * ``` * * - For the STP: * ``` * start_stp(state, ...); * set_bufs(...); * send(msg0); // to all peers * while (!stp_done(state)) { * input = allocate_memory( dkg_stp_input_size(state) ) * output = allocate_memory( dkg_stp_output_size(state) ) * input = read() * res = stp_next_step(state, input, output) * if res!=0: fail&abort * dkg_stp_peer_msg(state, output, peer_index, msg) * } * ``` */ #include #include #include "dkg.h" #include "toprf.h" typedef DKG_Message STP_DKG_Message; typedef DKG_Cheater STP_DKG_Cheater; #define stp_dkg_commitment_HASHBYTES 32U #define stp_dkg_encrypted_share_SIZE (TOPRF_Share_BYTES * 2 + crypto_secretbox_xchacha20poly1305_MACBYTES) #define stpvssdkg_start_msg_SIZE ( sizeof(STP_DKG_Message) \ + crypto_generichash_BYTES/*dst*/ \ + 2 /* n&t */ \ + crypto_sign_PUBLICKEYBYTES ) /** * @enum STP_DKG_Err * @brief Error codes returned by the STP DKG protocol functions * * These error codes represent the various failures and exceptional * conditions that can occur during the execution of the STP-based * DKG protocol. */ typedef enum { STP_DKG_Err_OK = 0, STP_DKG_Err_ISize, STP_DKG_Err_OSize, STP_DKG_Err_OOB, STP_DKG_Err_Send, STP_DKG_Err_CheatersFound, STP_DKG_Err_CheatersFull, STP_DKG_Err_InvSessionID, STP_DKG_Err_Share, STP_DKG_Err_Noise, STP_DKG_Err_NoiseEncrypt, STP_DKG_Err_NoiseDecrypt, STP_DKG_Err_HMac, STP_DKG_Err_Index, STP_DKG_Err_NoSubVSPSFail, STP_DKG_Err_NotEnoughDealers, STP_DKG_Err_TooManyCheaters, STP_DKG_Err_DKGFinish, STP_DKG_Err_BroadcastEnv = 32, STP_DKG_Err_Env = 64 } STP_DKG_Err; /** * @enum STP_DKG_STP_Steps * @brief Steps executed by the STP in the DKG protocol * * This defines each logical step in the protocol that the STP must * execute to complete the DKG. */ typedef enum { STP_DKG_STP_Send_Index, STP_DKG_STP_Broadcast_NPKs, STP_DKG_STP_Route_Noise_Handshakes1, STP_DKG_STP_Route_Noise_Handshakes2, STP_DKG_STP_Broadcast_DKG_Hash_Commitments, STP_DKG_STP_Broadcast_DKG_Commitments, STP_DKG_STP_Route_Encrypted_Shares, STP_DKG_STP_Broadcast_Complaints, STP_DKG_STP_Broadcast_DKG_Defenses, STP_DKG_STP_Broadcast_DKG_Transcripts, STP_DKG_STP_Broadcast_DKG_Final_Commitments, STP_DKG_STP_Done } STP_DKG_STP_Steps; /** * @enum STP_DKG_Message_Type * @brief Message types used during the STP DKG protocol * * Each type represents a stage-specific message exchanged between * the STP and peers during the distributed key generation process. */ typedef enum { stpvssdkg_stp_start_msg, stpvssdkg_stp_index_msg, stpvssdkg_peer_init1_msg, stpvssdkg_stp_bc_init1_msg, stpvssdkg_peer_start_noise_msg, stpvssdkg_peer_respond_noise_msg, stpvssdkg_peer_dkg1_msg, stpvssdkg_stp_bc_dkg1_msg, stpvssdkg_peer_dkg2_msg, stpvssdkg_stp_bc_dkg2_msg, stpvssdkg_peer_dkg3_msg, stpvssdkg_stp_bc_dkg3_msg, stpvssdkg_peer_verify_shares_msg, stpvssdkg_stp_bc_verify_shares_msg, stpvssdkg_peer_share_key_msg, stpvssdkg_stp_bc_key_msg, stpvssdkg_peer_bc_transcript_msg, stpvssdkg_stp_bc_transcript_msg, } STP_DKG_Message_Type; /** * @struct STP_DKG_STPState * @brief State for the STP during the execution of the DKG protocol * * Some fields in this struct are internal variables and should not * be used. The following fields are useful and can be accessed by * users of the API: * * @var STP_DKG_STPState::n Total number of peers participating in * this protocol * * @var STP_DKG_STPState::t The threshold, the minimum number of * peers required to use the shared secret generated by this DKG * * @var STP_DKG_STPState::cheaters List of detected cheaters and protocol * violators at the end of a failed protocol run * * @var STP_DKG_STPState::cheater_len Length of the `cheaters` list */ typedef struct { STP_DKG_STP_Steps step; STP_DKG_STP_Steps prev; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t n; uint8_t t; uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t sig_sk[crypto_sign_SECRETKEYBYTES]; uint64_t *last_ts; uint64_t ts_epsilon; uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*commitment_hashes)[][stp_dkg_commitment_HASHBYTES]; uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES]; uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]; uint16_t share_complaints_len; uint16_t (*share_complaints)[]; size_t cheater_len; STP_DKG_Cheater (*cheaters)[]; size_t cheater_max; crypto_generichash_state transcript; } STP_DKG_STPState; /** * @brief Gets the size needed for allocation of a STP_DKG_STPState struct * * WARNING: if you use this to allocate space for the state struct, it * is essential to have this aligned at 32 bytes. * * @return The size in bytes required for STP_DKG_STPState */ size_t stp_dkg_stpstate_size(void); /** * @brief Gets the number of peers (`n`) participating in the protocol * from the STP state * * @param[in] ctx Pointer to an initialized STP_DKG_STPState struct * * @return The number of peers (`n`) */ uint8_t stp_dkg_stpstate_n(const STP_DKG_STPState *ctx); /** * @brief Gets the threshold (`t`) required for the DKG from the STP state * * @param[in] ctx Pointer to an initialized STP_DKG_STPState struct * * @return The threshold value (`t`) */ uint8_t stp_dkg_stpstate_t(const STP_DKG_STPState *ctx); /** * @brief Gets the number of cheaters detected in the protocol from * the STP state * * @param[in] ctx Pointer to an initialized STP_DKG_STPState struct * * @return The number of cheaters detected (length of the `cheaters` list) */ size_t stp_dkg_stpstate_cheater_len(const STP_DKG_STPState *ctx); /** * @brief Gets the session ID associated with the current STP state * * @param[in] ctx Pointer to an initialized STP_DKG_STPState struct * * @return Pointer to the session ID buffer */ const uint8_t *stp_dkg_stpstate_sessionid(const STP_DKG_STPState *ctx); /** * @brief Gets the current step number in the protocol * * @param[in] ctx Pointer to an initialized STP_DKG_STPState struct * * @return The current step as an integer */ int stp_dkg_stpstate_step(const STP_DKG_STPState *ctx); /* * Semi-Trusted Party functions */ /** * @brief Starts a new execution of a STP DKG protocol for the STP * * This function initializes the state of the STP and creates an * initial message containing the parameters for the peers. * * @param[in] ctx Pointer to a STP_DKG_STPState struct. This struct * will be initialized by this function * @param[in] ts_epsilon Maximum allowed message age in seconds before * it is considered stale and rejected. This value is used to * prevent replay attacks and enforce freshness. For small, * local setups (e.g., 2-out-of-3 participants), values as low * as 2–3 seconds may suffice. For large-scale deployments * (e.g., 126-out-of-127), this may need to be increased to * several hours * @param[in] n Number of peers participating in this execution * @param[in] t Threshold necessary to use the results of this DKG * @param[in] proto_name An array of bytes used as a domain separation tag * (DST). Set it to the name of your application * @param[in] proto_name_len The size of the array `proto_name`, to allow * non-zero terminated DSTs * @param[in] sig_pks Pointer to a (n+1)-element array of signing public * keys. The STP's public key must be at index 0. The rest of the * items must be in order * @param[in] ltssk STP’s private long-term signing key * @param[in] msg0_len Size of allocated memory for the output message, * `msg0`. Should be exactly `stpvssdkg_msg0_SIZE` long * @param[out] msg0 Output parameter, the message to be sent to peers * to initialize them * * @return 0 on success, non-zero on error **/ int stp_dkg_start_stp(STP_DKG_STPState *ctx, const uint64_t ts_epsilon, const uint8_t n, const uint8_t t, const char *proto_name, const size_t proto_name_len, uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES], const uint8_t ltssk[crypto_sign_SECRETKEYBYTES], const size_t msg0_len, STP_DKG_Message *msg0); /** * @brief Sets all the variable sized buffers in the STP DKG state * * This function sets all the variable-sized buffers in the STP_DKG_STPState * struct. These buffers must be preallocated by the caller, typically on * the stack, based on the number of participants `n` and the threshold `t` * * A number of buffers are needed in the STP state that depend on the `n` * (number of participants) and `t` (threshold) parameters. * These can be allocated on the stack as follows: * @code * uint16_t stp_share_complaints[n * n]; * uint64_t last_ts[n]; * STP_DKG_Cheater stp_cheaters[t * t - 1]; * uint8_t tp_commitments_hashes[n][stp_dkg_commitment_HASHBYTES]; * uint8_t tp_share_macs[n * n][crypto_auth_hmacsha256_BYTES]; * uint8_t tp_commitments[n * n][crypto_core_ristretto255_BYTES]; * * stp_dkg_stp_set_bufs(&stp, * &tp_commitments_hashes, * &tp_share_macs, * &tp_commitments, * &stp_share_complaints, * &stp_cheaters, * sizeof(stp_cheaters) / sizeof(STP_DKG_Cheater), * last_ts); * @endcode * * @param[in] ctx Pointer to the STP_DKG_STPState structure being * initialized * @param[in] commitment_hashes Pointer to a list of DKG commitment hashes * @param[in] share_macs Pointer to a list of Hash-based Message * Authentication Codes (HMACs) for encrypted shares * @param[in] commitments Pointer to a list of curve points representing * commitments * @param[in] share_complaints Pointer to a list of share complaint flags * @param[in] cheaters List of detected cheaters and protocol violators at * the end of a failed protocol run * @param[in] cheater_max Maximum number of cheat attempts to be recorded. * Normally, the maximum number of cheaters is `t * t - 1`, where * `t` is the threshold parameter. It should be provided as * (sizeof(cheaters) / sizeof(TP_DKG_Cheater)) * @param[in] last_ts Pointer to a list of last timestamps */ void stp_dkg_stp_set_bufs(STP_DKG_STPState *ctx, uint8_t (*commitment_hashes)[][stp_dkg_commitment_HASHBYTES], uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint16_t (*share_complaints)[], STP_DKG_Cheater (*cheaters)[], const size_t cheater_max, uint64_t *last_ts); /** * @brief Enum representing the steps of the STP DKG peer state engine * * Each value corresponds to a stage in the peer-side execution of the * STP DKG protocol. The protocol transitions between these states as * messages are received and processed. */ typedef enum { STP_DKG_Peer_Broadcast_NPK_SIDNonce, STP_DKG_Peer_Rcv_NPK_SIDNonce, STP_DKG_Peer_Noise_Handshake, STP_DKG_Peer_Finish_Noise_Handshake, STP_DKG_Peer_Rcv_Commitments_Send_Commitments, STP_DKG_Peer_Rcv_Commitments_Send_Shares, STP_DKG_Peer_Verify_Commitments, STP_DKG_Peer_Handle_DKG_Complaints, STP_DKG_Peer_Defend_DKG_Accusations, STP_DKG_Peer_Check_Shares, STP_DKG_Peer_Finish_DKG, STP_DKG_Peer_Confirm_Transcripts, STP_DKG_Peer_Done } STP_DKG_Peer_Steps; /** * @brief Callback type for loading the corresponding long-term signing * public keys and Noise_XK public keys of a peer * * The user of this API may provide a callback function that gets called * when processing the first message by a peer to look up the long-term * signing key and Noise_XK key for a given peer ID. * * @param[in] id Peer ID * @param[in] arg A void pointer to some argument stored in the peers state * context. This can be set during `stp_dkg_peer_set_bufs()` * @param[out] sigpk Buffer to fill with the peer's long-term signing public * key * @param[out] noise_pk Buffer to fill with the peer's long-term noise * public key * * @return 0 on success, non-zero on error */ typedef int (*Keyloader_CB)(const uint8_t id[crypto_generichash_BYTES], void *arg, uint8_t sigpk[crypto_sign_PUBLICKEYBYTES], uint8_t noise_pk[crypto_scalarmult_BYTES]); /** * @struct STP_DKG_PeerState * @brief Struct representing the state of a peer during STP DKG execution * * This struct contains the state of a peer during the execution of the * protocol. * Some fields in this struct are internal variables and should not * be used. The following fields are useful and can be accessed by * users of the API: * * @var STP_DKG_PeerState::n Total number of peers participating in the * DKG session * * @var STP_DKG_PeerState::t The threshold, the minimum number of peers * required to use the shared secret generated by this DKG generated * by this DKG * * @var STP_DKG_PeerState::index Index of this peer (1-based). This value * is between 1 to `n` inclusive * * @var STP_DKG_PeerState::peerids Pointer to a list of `n` items, * containing the hashes of all peers long-term signing keys * * @var STP_DKG_PeerState::sig_pks Pointer to a list of `n` items, * containing the long-term signing public keys of all peers * * @var STP_DKG_PeerState::peer_noise_pks Pointer to a list of `n` items * containing the Noise_XK public keys of all peers * * @var STP_DKG_PeerState::share Resulting secret share output of the DKG for * a peer. This value should probably be persisted for later usage * * @var STP_DKG_PeerState::cheaters List of detected cheaters and protocol * violators at the end of a failed protocol run * * @var STP_DKG_PeerState::cheater_len Length of the `cheaters` list */ typedef struct { uint8_t (*peerids)[][crypto_generichash_BYTES]; STP_DKG_Peer_Steps step; STP_DKG_Peer_Steps prev; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t n; uint8_t t; uint8_t index; Keyloader_CB keyloader_cb; void *keyloader_cb_arg; uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t sig_sk[crypto_sign_SECRETKEYBYTES]; uint8_t stp_sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t noise_pk[crypto_scalarmult_BYTES]; uint8_t noise_sk[crypto_scalarmult_SCALARBYTES]; uint64_t stp_last_ts; uint64_t *last_ts; uint64_t ts_epsilon; uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*peer_noise_pks)[][crypto_scalarmult_BYTES]; Noise_XK_device_t *dev; Noise_XK_session_t *(*noise_outs)[]; Noise_XK_session_t *(*noise_ins)[]; TOPRF_Share (*k_shares)[][2]; uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE]; uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES]; uint8_t (*ki_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t (*k_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t (*commitments_hashes)[][stp_dkg_commitment_HASHBYTES]; uint16_t share_complaints_len; uint16_t *share_complaints; uint8_t my_share_complaints_len; uint8_t *my_share_complaints; uint8_t k_commitment[crypto_core_ristretto255_BYTES]; size_t cheater_len; STP_DKG_Cheater (*cheaters)[]; size_t cheater_max; crypto_generichash_state transcript; uint8_t final_transcript[crypto_generichash_BYTES]; TOPRF_Share share[2]; } STP_DKG_PeerState; /** * @brief Gets the size needed for allocation of a STP_DKG_PeerState struct * * WARNING: if you use this to allocate space for the state struct, it * is essential to have this aligned at 32 bytes. * * @return Size in bytes of STP_DKG_PeerState */ size_t stp_dkg_peerstate_size(void); /** * @brief Gets the total number of peers in the DKG * * @param[in] ctx Pointer to the peer state * * @return The total number of peers (`n`) */ uint8_t stp_dkg_peerstate_n(const STP_DKG_PeerState *ctx); /** * @brief Gets the threshold value used in the DKG * * @param[in] ctx Pointer to the peer state * * @return The threshold value (`t`) */ uint8_t stp_dkg_peerstate_t(const STP_DKG_PeerState *ctx); /** * @brief Gets the session ID for this DKG execution * * @param[in] ctx Pointer to the peer state * * @return Pointer to a buffer containing the session ID. */ const uint8_t *stp_dkg_peerstate_sessionid(const STP_DKG_PeerState *ctx); /** * @brief Gets the long-term signing secret key of the local peer * * @param[in] ctx Pointer to the peer state * * @return Pointer to the secret key buffer */ const uint8_t *stp_dkg_peerstate_lt_sk(const STP_DKG_PeerState *ctx); /** * @brief Gets the DKG output share of the local peer * * @param[in] ctx Pointer to the peer state * * @return Pointer to the share buffer */ const uint8_t *stp_dkg_peerstate_share(const STP_DKG_PeerState *ctx); /** * @brief Gets for the commitments of the generated shares result of this * protocol * * @param[in] ctx Pointer to the peer state * * @return Pointer to the commitments of the generated shares result */ const uint8_t *stp_dkg_peerstate_commitments(const STP_DKG_PeerState *ctx); /** * @brief Gets the current step of the peer in the DKG protocol * * @param[in] ctx Pointer to the peer state * * @return The current `STP_DKG_Peer_Steps` enum value */ int stp_dkg_peerstate_step(const STP_DKG_PeerState *ctx); /* * Peer functions */ /** * @brief Starts a new execution of a STP DKG protocol for a peer * * Initializes the internal state of a peer participating in the protocol, * using the message received from the STP initiator (`msg0`). It sets up * the protocol context, extracts relevant information from `msg0`, verifies * freshness and structure, and begins the transcript for future protocol * messages. * * @param[out] ctx Pointer to a STP_DKG_PeerState struct. This struct * will be initialized by this function * @param[in] ts_epsilon Maximum allowed message age in seconds before * it is considered stale and rejected. This value is used to * prevent replay attacks and enforce freshness. For small, * local setups (e.g., 2-out-of-3 participants), values as low * as 2–3 seconds may suffice. For large-scale deployments * (e.g., 126-out-of-127), this may need to be increased to * several hours * @param[in] lt_sk The long-term private signing secret key of the peer * @param[in] noise_sks The long-term Noise_XK protocol secret key of the * peer * @param[in] msg0 The initiating message received from the STP (created * after running `stp_dkg_start_stp()`) * @param[out] stp_ltpk Output buffer where the STP's long-term public * signing key is copied. It should be used to verify if this * key is actually authorized to initiate an STP DKG with the * peer * * @return 0 on success, non-zero on error */ STP_DKG_Err stp_dkg_start_peer(STP_DKG_PeerState *ctx, const uint64_t ts_epsilon, const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], const uint8_t noise_sks[crypto_scalarmult_SCALARBYTES], const STP_DKG_Message *msg0, uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]); /** * @brief Sets all variable-sized buffers in the STP_DKG_STPState structure * * The buffer sizes depend on the `n` and `t` parameters of the DKG * protocol, which could be known in advance. If not, these parameters * are announced by the TP in `msg0`, which is an input to the * `stp_dkg_start_peer()` function. After this `stp_dkg_start_peer()` call, * the peer state is initialized and can be used to find out the `n` and * `t` parameters. * * To allocate all the buffers on the stack: * @code * STP_DKG_PeerState ctx; * stp_dkg_start_peer(&ctx,....); * const uint8_t n = ctx->n; * const uint8_t t = ctx->t; * * uint8_t peerids[n][crypto_generichash_BYTES]; * Noise_XK_session_t *noise_outs[n]; * Noise_XK_session_t *noise_ins[n]; * TOPRF_Share dealer_shares[n][2]; * uint8_t encrypted_shares[n][noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE]; * uint8_t dealer_commitments[n*n][crypto_core_ristretto255_BYTES]; * uint8_t share_macs[n][n*n][crypto_auth_hmacsha256_BYTES]; * uint8_t peer_k_commitments[n][crypto_core_ristretto255_BYTES]; * uint8_t commitments_hashes[n][stp_dkg_commitment_HASHBYTES]; * uint16_t peer_dealer_share_complaints[n*n]; * uint8_t peer_my_dealer_share_complaints[n]; * uint64_t peer_last_ts[n]; * STP_DKG_Cheater peer_cheaters[t*t - 1]; * if(0!=stp_dkg_peer_set_bufs(&peer, &peerids, * &keyloader_cb, &cb_arg, * <_pks, * &peers_noise_pks, * &noise_outs, &noise_ins, * &dealer_shares, * &encrypted_shares, * &share_macs, * &dealer_commitments, * &peer_k_commitments, * &commitments_hashes, * &peer_cheaters, sizeof(peer_cheaters) / sizeof(STP_DKG_Cheater) / n, * peer_dealer_share_complaints, * peer_my_dealer_share_complaints, * peer_last_ts)) return 1; * @endcode * * * TODO document the missing parameters * * @param[in] ctx Pointer to the STP_DKG_STPState structure being * initialized * @param[in] commitment_hashes Pointer to a list of DKG commitment hashes * @param[in] share_macs Pointer to a list of Hash-based Message * Authentication Codes (HMACs) for encrypted shares * @param[in] commitments Pointer to a list of curve points representing * commitments * @param[in] share_complaints Pointer to a list of share complaint flags * @param[in] cheaters List of detected cheaters and protocol violators at * the end of a failed protocol run * @param[in] cheater_max Maximum number of cheat attempts to be recorded. * Normally, the maximum number of cheaters is `t * t - 1`, where * `t` is the threshold parameter. It should be provided as * (sizeof(cheaters) / sizeof(TP_DKG_Cheater)) * @param[in] last_ts Pointer to a list of last timestamps for each peer */ int stp_dkg_peer_set_bufs(STP_DKG_PeerState *ctx, uint8_t (*peerids)[][crypto_generichash_BYTES], Keyloader_CB keyloader_cb, void *keyloader_cb_arg, uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], Noise_XK_session_t *(*noise_outs)[], Noise_XK_session_t *(*noise_ins)[], TOPRF_Share (*k_shares)[][2], uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE], uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*ki_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*k_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*commitments_hashes)[][stp_dkg_commitment_HASHBYTES], STP_DKG_Cheater (*cheaters)[], const size_t cheater_max, uint16_t *share_complaints, uint8_t *my_share_complaints, uint64_t *last_ts); /** * @brief Calculates the size of the buffer needed to hold all peer * outputs for the next STP step * * This function determines the total size required to collect all * peer messages that will be used as input for the next step of the * STP in the DKG protocol. * * An implementer should allocate a buffer of this size and concatenate * all messages from all peers in the order of the peers' indices. * The allocated buffer is to be passed as an input `stp_dkg_stp_next()`. * After this, the buffer SHOULD be deallocated. * * @param[in] ctx An initialized STP_DKG_STPState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t stp_dkg_stp_input_size(const STP_DKG_STPState *ctx); /** * @brief Calculates the size of the message from each peer to be * received by the STP * * Fills a list with the expected message size from each peer for the * current step. If all peers send messages of equal size, returns 0 * and fills all entries with the same value. Otherwise, returns 1 and * fills each entry with the corresponding peer's message size. * * @param[in] ctx An initialized STP_DKG_STPState struct * @param[out] sizes Array of size_t with exactly `n` elements to be filled * with message sizes * * @return 0 on if the sizes differ from peer to peer, otherwise all peers * will be sending messages of equal size. In the latter case, all * items of the `sizes` array hold the same valid value. */ int stp_dkg_stp_input_sizes(const STP_DKG_STPState *ctx, size_t *sizes); /** * @brief Calculates the size of the buffer needed to hold the output from * the `stp_dkg_stp_next()` function * * Determines the buffer size required to hold the output of * `stp_dkg_stp_next()` for the current protocol step. * An implementer should allocate a buffer of this size and pass it as the * `output` parameter to `stp_dkg_stp_next()`. * * @param[in] ctx An initialized STP_DKG_STPState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t stp_dkg_stp_output_size(const STP_DKG_STPState *ctx); /** * @brief Executes the next step of the STP DKG protocol for the STP * * Processes the current protocol step using the provided input buffer * and writes the result to the output buffer. Then, it advances the * protocol state. * * This is an example of how to use this function in concert with * `stp_dkg_stp_input_size()` and `stp_dkg_stp_output_size()`: * @code * uint8_t stp_out[stp_dkg_stp_output_size(&tp)]; * uint8_t stp_in[stp_dkg_stp_input_size(&tp)]; * recv(socket, stp_in, sizeof(stp_in)); * int ret = stp_dkg_stp_next(&tp, stp_in, sizeof(stp_in), stp_out, sizeof(stp_out)); * @endcode * * @param[in] ctx Pointer to a valid STP_DKG_STPState * @param[in] input Buffer containing input data for the current step * @param[in] input_len Size of the input buffer * @param[out] output Buffer to receive the output of the current step * @param[in] output_len Size of the output buffer * * @return 0 on success, non-zero on error */ int stp_dkg_stp_next(STP_DKG_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); /** * @brief Extracts the message for a specific peer from the STP's * output buffer * * This function converts the output of `stp_dkg_stp_next()` into a message * to be sent to the i-th peer. * Depending on the current STP step, the * output may be a broadcast (same messages to all) or dedicated and * unique messages for each peer. * This function returns a pointer to a message and the size of the message * to be sent for a particular peer specified as a parameter. * * This is an example of how to use this function in concert with * `stp_dkg_stp_next()`: * @code * ret = stp_dkg_stp_next(&tp, tp_in, sizeof(tp_in), tp_out, sizeof(tp_out)); * for (int i = 0; i < tp.n; i++) { * const uint8_t *msg; * size_t len; * if (0 != stp_dkg_stp_peer_msg(&tp, tp_out, sizeof(tp_out), i, &msg, &len)) { * return 1; * } * send(i, msg, len); * } * @endcode * * @param[in] ctx Pointer to a valid STP_DKG_STPState * @param[in] base Pointer to the output buffer from `stp_dkg_stp_next()` * @param[in] base_size Size of the output buffer of `stp_dkg_stp_next()` * @param[in] peer Index of the peer (0-based) * @param[out] msg Pointer to the message to be sent to the i-th peer * @param[out] len Pointer to the length of the message to be sent to the * i-th peer * * @return 0 on success, non-zero on error */ int stp_dkg_stp_peer_msg(const STP_DKG_STPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); /** * @brief Checks if the STP protocol has more steps to execute or more * `stp_dkg_stp_next()` calls are necessary * * @param[in] stp Pointer to the STP_DKG_STPState * * @return 1 if more steps are outstanding */ int stp_dkg_stp_not_done(const STP_DKG_STPState *stp); /** * @brief Converts a cheater object to a human-readable string * * This function takes a STP_DKG_Cheater object (produced when cheating * behavior is detected) and formats a descriptive string explaining the * nature of the cheating incident. * * This variant is used for cheater objects created by STP * * @param[in] c Pointer to the cheater object * @param[out] out Pointer to the pre-allocated buffer to receive the * formatted string * @param[in] outlen Size of the output buffer * * @return The index of the cheating peer */ uint8_t stp_dkg_stp_cheater_msg(const STP_DKG_Cheater *c, char *out, const size_t outlen); /** * @brief Computes the size of the input buffer required for the next * call to `stp_dkg_peer_next()` * * This function calculates how much memory the caller needs to allocate * for the buffer that will be passed as input to `stp_dkg_peer_next()`. * An implementer should allocate a buffer of this size. * The allocated buffer is to be passed as an input to the * `stp_dkg_peer_next()` function. After this, the buffer SHOULD be * deallocated. * * @param[in] ctx An initialized STP_DKG_PeerState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t stp_dkg_peer_input_size(const STP_DKG_PeerState *ctx); /** * @brief Calculates the size of the buffer needed to hold the output * from the `stp_dkg_peer_next()` function * * An implementer should allocate a buffer of this size and pass it as * the `output` parameter to `stp_dkg_peer_next()`. * for the next protocol step. * * @param[in] ctx An initialized STP_DKG_PeerState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t stp_dkg_peer_output_size(const STP_DKG_PeerState *ctx); /** * @brief Executes the next step of the STP DKG protocol for a peer * * Processes the current protocol step for the peer using the provided * input buffer, writes the result to the output buffer, and advances the * protocol state. * * This is an example of how to use this function in concert with * `stp_dkg_peer_input_size()` and `stp_dkg_peer_output_size()` while * allocating the buffers on the stack: * @code * uint8_t peers_out[stp_dkg_peer_output_size(&peer)]; * * uint8_t peer_in[stp_dkg_peer_input_size(&peer)]; * recv(socket, peer_in, sizeof(peer_in)); * ret = stp_dkg_peer_next(&peer, * peer_in, sizeof(peer_in), * peers_out, sizeof(peers_out)); * @endcode * * @param[in] ctx Pointer to a valid STP_DKG_PeerState * @param[in] input Buffer containing input data for the current step * @param[in] input_len Size of the input buffer * @param[out] output Buffer to receive the output of the current step * @param[in] output_len Size of the output buffer * * @return 0 on success, non-zero on error */ int stp_dkg_peer_next(STP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); /** * @brief Checks if the peer protocol has more steps to execute * * This function checks if more steps are outstanding and further * calls to `stp_dkg_peer_next()` are necessary or if the protocol is * finished for this peer. * * @param[in] peer Pointer to the STP_DKG_PeerState * @return 1 if more steps are outstanding */ int stp_dkg_peer_not_done(const STP_DKG_PeerState *peer); /** * @brief Frees all resources allocated by the peer state * * This function MUST be called before a peer's state is deallocated. * The underlying Noise_XK implementation allocates a lot of internal * state on the heap, which must be freed manually to avoid memory leaks. * * @param[in] ctx Pointer to the STP_DKG_PeerState to be freed */ void stp_dkg_peer_free(STP_DKG_PeerState *ctx); /** * @brief Converts a cheater object to a human readable string * * Use this variant for cheater objects created by a peer. * * @param[in] c Pointer to the cheater object * @param[out] out Pointer to the pre-allocated buffer receiving the string * @param[in] outlen Size of the pre-allocated buffer * * @return The index of the cheating peer, or 0 if undetermined */ uint8_t stp_dkg_peer_cheater_msg(const STP_DKG_Cheater *c, char *out, const size_t outlen); extern FILE* log_file; #endif //STP_DKG_H liboprf-0.9.4/src/tests/000077500000000000000000000000001514673400200151145ustar00rootroot00000000000000liboprf-0.9.4/src/tests/.gitignore000066400000000000000000000002641514673400200171060ustar00rootroot00000000000000cfrg_oprf_test_vector_decl.h cfrg_oprf_test_vectors.h tv1 tv2 tp-dkg tp-dkg-corrupt stp-dkg stp-dkg-corrupt mpmult toprf-update toprf-update-corrupt ft-mult update-poc allocations liboprf-0.9.4/src/tests/README.md000066400000000000000000000002641514673400200163750ustar00rootroot00000000000000# IRTF/CFRG testvectors these tests verify the test vectors from the IRTF/CFRG as per: https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/main/draft-irtf-cfrg-voprf.md#oprf-mode liboprf-0.9.4/src/tests/allocations.c000066400000000000000000000141441514673400200175740ustar00rootroot00000000000000#include #include "stp-dkg.h" #include "toprf-update.h" #include "utils.h" static size_t stp_peer_ctx_size(const size_t n, const size_t t) { size_t ret = 0; uint8_t peerids[n][crypto_generichash_BYTES]; ret+=sizeof(peerids); Noise_XK_session_t *noise_outs[n]; ret+=sizeof(noise_outs); Noise_XK_session_t *noise_ins[n]; ret+=sizeof(noise_ins); TOPRF_Share dealer_shares[n][2]; ret+=sizeof(dealer_shares); uint8_t encrypted_shares[n][noise_xk_handshake3_SIZE + stp_dkg_encrypted_share_SIZE]; ret+=sizeof(encrypted_shares); uint8_t dealer_commitments[n*n][crypto_core_ristretto255_BYTES]; ret+=sizeof(dealer_commitments); uint8_t share_macs[n*n][crypto_auth_hmacsha256_BYTES]; ret+=sizeof(share_macs); uint8_t peer_k_commitments[n][crypto_core_ristretto255_BYTES]; ret+=sizeof(peer_k_commitments); uint8_t commitments_hashes[n][stp_dkg_commitment_HASHBYTES]; ret+=sizeof(commitments_hashes); uint16_t peer_dealer_share_complaints[n*n]; ret+=sizeof(peer_dealer_share_complaints); uint8_t peer_my_dealer_share_complaints[n]; ret+=sizeof(peer_my_dealer_share_complaints); uint64_t peer_last_ts[n]; ret+=sizeof(peer_last_ts); STP_DKG_Cheater peer_cheaters[t*t - 1]; ret+=sizeof(peer_cheaters); return ret; } static size_t toprf_update_ctx_size(const size_t n, const size_t t) { const uint8_t dealers = (t-1)*2 + 1; size_t ret = 0; TOPRF_Share k0_share[2] = {0}; ret+=sizeof k0_share; uint8_t k0_commitments[n][crypto_core_ristretto255_BYTES]; ret+=sizeof k0_commitments; ret+=sizeof k0_commitments; uint8_t kid[toprf_keyid_SIZE]; ret+=sizeof kid; uint8_t lt_pks[n+1][crypto_sign_PUBLICKEYBYTES]; ret+=sizeof lt_pks; uint8_t lt_sks[crypto_sign_SECRETKEYBYTES]; ret+=sizeof lt_sks; uint8_t peers_noise_pks[n][crypto_scalarmult_BYTES]; ret+=sizeof peers_noise_pks; uint8_t peers_noise_sks[crypto_scalarmult_SCALARBYTES]; ret+=sizeof peers_noise_sks; uint8_t pkid[toprf_keyid_SIZE]; ret+=sizeof pkid; uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]; ret+=sizeof stp_ltpk; Noise_XK_session_t *noise_outs[n]; ret+=sizeof noise_outs; Noise_XK_session_t *noise_ins[n]; ret+=sizeof noise_ins; TOPRF_Share pshares[n][2]; ret+=sizeof pshares; uint8_t p_commitments[n*n][crypto_core_ristretto255_BYTES]; ret+=sizeof p_commitments; uint8_t p_commitments_hashes[n][toprf_update_commitment_HASHBYTES]; ret+=sizeof p_commitments_hashes; uint8_t peers_p_share_macs[n*n][crypto_auth_hmacsha256_BYTES]; ret+=sizeof peers_p_share_macs; uint16_t peer_p_complaints[n*n]; ret+=sizeof peer_p_complaints; uint8_t peer_my_p_complaints[n]; ret+=sizeof peer_my_p_complaints; uint8_t encrypted_shares[n][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE]; ret+=sizeof encrypted_shares; uint64_t peer_last_ts[n]; ret+=sizeof peer_last_ts; uint8_t lambdas[dealers][crypto_core_ristretto255_SCALARBYTES]; ret+=sizeof lambdas; TOPRF_Share k0p_shares[dealers][2]; ret+=sizeof k0p_shares; uint8_t k0p_commitments[dealers*(n+1)][crypto_core_ristretto255_BYTES]; ret+=sizeof k0p_commitments; uint8_t zk_challenge_nonce_commitments[n][crypto_scalarmult_ristretto255_BYTES]; ret+=sizeof zk_challenge_nonce_commitments; uint8_t zk_challenge_nonces[n][2][crypto_scalarmult_ristretto255_SCALARBYTES]; ret+=sizeof zk_challenge_nonces; uint8_t zk_challenge_commitments[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES]; ret+=sizeof zk_challenge_commitments; uint8_t zk_challenge_e_i[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]; ret+=sizeof zk_challenge_e_i; TOPRF_Update_Cheater peer_cheaters[n*n - 1]; ret+=sizeof peer_cheaters; return ret; } static void stp_dkg(void) { STP_DKG_PeerState ctx; ctx.share_complaints_len=0; for(ctx.t=2;ctx.t<64;ctx.t++) { printf("t=%2d\n", ctx.t); for(ctx.n=ctx.t+1;ctx.n<129;ctx.n++) { ctx.step=0; //ctx.share_complaints_len=ctx.t; //ctx.my_share_complaints_len=ctx.t-1; size_t itot=0, otot=0; int i=0; while(stp_dkg_peer_not_done(&ctx)) { const size_t out_size = stp_dkg_peer_output_size(&ctx); const size_t in_size = stp_dkg_peer_input_size(&ctx); if(itot> 10; if(total<16) printf(GREEN); else if(total>64) printf(RED); printf("n=%3d total: %7ldKB ctx size: %7ldKB max: %6ldKB/%6ldKB %d"NORMAL"\n", ctx.n, total, ctx_size >> 10, itot >> 10, otot >> 10, i); } } } static void toprf_update(void) { TOPRF_Update_PeerState ctx; ctx.p_complaints_len = 0; ctx.my_p_complaints_len = 0; ctx.index = 1; for(ctx.n=5;ctx.n<129;ctx.n++) { printf("n=%2d\n", ctx.n); for(ctx.t=2;ctx.t<=((ctx.n-1)>>1)+1;ctx.t++) { ctx.step=0; //ctx.p_complaints_len=ctx.n; size_t itot=0, otot=0; while(toprf_update_peer_not_done(&ctx)) { const size_t out_size = toprf_update_peer_output_size(&ctx); const size_t in_size = toprf_update_peer_input_size(&ctx); if(in_size>itot) itot=in_size; if(out_size>otot) otot=out_size; ctx.step++; } const size_t ctx_size=toprf_update_ctx_size(ctx.n, ctx.t)+sizeof ctx; const size_t total = (ctx_size+itot+otot) >> 10; if(total<16) printf(GREEN); else if(total>64) printf(RED); printf("t=%3d total: %7ldKB ctx size: %7ldKB max: %6ldKB/%6ldKB"NORMAL"\n", ctx.t, total, ctx_size >> 10, itot >> 10, otot >> 10); } } } int main(void) { //printf("xxx: %ld\n", // (sizeof(STP_DKG_Message) /* header */ \ // + noise_xk_handshake3_SIZE /* 4th&final noise handshake */ \ // + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped k share */ \ // + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped k blind */ \ // + crypto_secretbox_xchacha20poly1305_MACBYTES /* mac of msg */ )); printf("toprf-update allocations\n"); toprf_update(); printf("stp-dkg allocations\n"); stp_dkg(); return 0; } liboprf-0.9.4/src/tests/dkg.c000066400000000000000000000107611514673400200160320ustar00rootroot00000000000000#include #include "dkg.h" #include "toprf.h" #include "utils.h" extern int liboprf_debug; typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_BYTES]; } __attribute((packed)) TOPRF_Part; static void topart(TOPRF_Part *r, const TOPRF_Share *s) { r->index=s->index; crypto_scalarmult_ristretto255_base(r->value, s->value); } static int test_dkg_start(const uint8_t n, const uint8_t a[crypto_core_ristretto255_SCALARBYTES], const TOPRF_Share shares[n]) { const size_t response_len = 3; uint8_t responses[response_len][TOPRF_Part_BYTES]; uint8_t result[crypto_scalarmult_ristretto255_BYTES]; uint8_t v[crypto_scalarmult_ristretto255_BYTES]; topart((TOPRF_Part *) responses[0], &shares[4]); topart((TOPRF_Part *) responses[1], &shares[2]); topart((TOPRF_Part *) responses[2], &shares[0]); if(toprf_thresholdmult(response_len, responses, result)) return 1; crypto_scalarmult_ristretto255_base(v, a); if(memcmp(v,result,sizeof v)!=0) { fprintf(stderr,"\e[0;31mmeh!\e[0m\n"); dump(v,sizeof v, "v"); dump(result,sizeof v, "r"); return 1; } return 0; } static int test_dkg_finish(const uint8_t n, const TOPRF_Share shares[n]) { const size_t response_len = 3; uint8_t responses[response_len][TOPRF_Part_BYTES]; uint8_t v0[crypto_scalarmult_ristretto255_BYTES]={0}; uint8_t v1[crypto_scalarmult_ristretto255_BYTES]={0}; //dump((uint8_t*) &shares[4], sizeof(TOPRF_Share), "&shares[4][0] "); topart((TOPRF_Part *) responses[0], &shares[4]); topart((TOPRF_Part *) responses[1], &shares[2]); topart((TOPRF_Part *) responses[2], &shares[0]); //topart((TOPRF_Part *) responses[3], &shares[1][0]); //topart((TOPRF_Part *) responses[4], &shares[3][0]); if(toprf_thresholdmult(response_len, responses, v0)) return 1; dump(v0,sizeof v0, "v0 "); topart((TOPRF_Part *) responses[0], &shares[3]); topart((TOPRF_Part *) responses[1], &shares[1]); topart((TOPRF_Part *) responses[2], &shares[0]); //topart((TOPRF_Part *) responses[3], &shares[2][0]); //topart((TOPRF_Part *) responses[4], &shares[4][0]); if(toprf_thresholdmult(response_len, responses, v1)) return 1; dump(v1,sizeof v1, "v1 "); if(memcmp(v0,v1,sizeof v1)!=0) { fprintf(stderr,"\e[0;31mfailed to verify shares from dkg_finish!\e[0m\n"); return 1; } return 0; } int main(void) { liboprf_debug = 1; uint8_t n=5, threshold=3; uint8_t commitments[n][threshold][crypto_core_ristretto255_BYTES]; TOPRF_Share shares[n][n]; for(int i=0;i #include #include #include "../dkg-vss.h" #include "../utils.h" #include "../mpmult.h" #include "../toprf.h" static void corrupt_ci_good_ci0(const uint8_t n, const uint8_t t, const uint8_t peer, TOPRF_Share shares[][2], uint8_t commitments[][crypto_core_ristretto255_BYTES], uint8_t blind[crypto_core_ristretto255_SCALARBYTES]) { // is not detected by anything, corrupts final result uint8_t secret[crypto_core_ristretto255_SCALARBYTES]={0}; secret[31]=0x10; //secret[0]=1; //crypto_core_ristretto255_scalar_random(secret); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting sharing of Ξ»_iΞ±_iΞ²_i %d, C_i0 is correct though\n"NORMAL, peer); (void)dkg_vss_share(n, t, secret, commitments, shares, blind); } static void corrupt_random_ci0_ci(const uint8_t n, const uint8_t t, const uint8_t peer, TOPRF_Share shares[][2], uint8_t commitments[][crypto_core_ristretto255_BYTES], uint8_t blind[crypto_core_ristretto255_SCALARBYTES]) { // is detected by zpk, but even if we reconstruct the secret // committed by C_i0 the end result will be corrupt. uint8_t secret[crypto_core_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_random(secret); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting with totally random sharing instead of Ξ»_iΞ±_iΞ²_i %d\n"NORMAL, peer); (void)dkg_vss_share(n, t, secret, &commitments[1], shares, blind); (void)dkg_vss_commit(secret, blind, commitments[0]); } static void corrupt_ci0_good_ci(const uint8_t peer, uint8_t commitments[][crypto_core_ristretto255_BYTES]) { // is detected by both zkp and vsps, but even if ignored does not // influence the correctness of the calculation. uint8_t secret[crypto_core_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_random(secret); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting C_i0 Ξ»_iΞ±_iΞ²_i %d\n"NORMAL, peer); dkg_vss_commit(secret,secret,commitments[0]); } static void corrupt_vsps_t1(const uint8_t n, const uint8_t t, const int8_t delta, const uint8_t peer, const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], TOPRF_Share shares[][2], uint8_t commitments[][crypto_core_ristretto255_BYTES], uint8_t blind[crypto_core_ristretto255_SCALARBYTES]) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting with wrong degree of the polynom peer %d\n"NORMAL, peer); (void)dkg_vss_share(n, t+delta, secret, &commitments[1], shares, blind); (void)dkg_vss_commit(secret, blind, commitments[0]); } static void corrupt_commitment(const uint8_t peer, uint8_t commitments[][crypto_core_ristretto255_BYTES]) { // corrupts the 1st commitment with the 2nd if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting commitment of peer %d\n"NORMAL, peer); memcpy(commitments[0], commitments[1], crypto_core_ristretto255_BYTES); } static void corrupt_share(const uint8_t peer, const uint8_t share_idx, const uint8_t share_type, TOPRF_Share shares[][2]) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting share of peer %d\n"NORMAL, peer); shares[share_idx][share_type].value[2]^=0xff; // flip some bits } static void corrupt_wrongshare_correct_commitment(const uint8_t peer, const uint8_t share_idx, TOPRF_Share shares[][2], uint8_t commitments[][crypto_core_ristretto255_BYTES]) { TOPRF_Share tmp; // swap shares memcpy(&tmp, &shares[share_idx][0], sizeof tmp); memcpy(&shares[share_idx][0], &shares[share_idx][1], sizeof tmp); memcpy(&shares[share_idx][1], &tmp, sizeof tmp); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting share (but correct commitment) of peer %d\n"NORMAL, peer); dkg_vss_commit(shares[share_idx][0].value,shares[share_idx][1].value,commitments[share_idx]); } static int vss_share(const uint8_t n, const uint8_t threshold, const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], const uint8_t blind[crypto_core_ristretto255_SCALARBYTES], uint8_t commitments[n][crypto_core_ristretto255_BYTES], TOPRF_Share shares[n][2]) { uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES]; uint8_t b[threshold][crypto_core_ristretto255_SCALARBYTES]; if(secret!=NULL) memcpy(a[0], secret, crypto_core_ristretto255_SCALARBYTES); if(blind !=NULL) memcpy(b[0], blind, crypto_core_ristretto255_SCALARBYTES); for(int k=0;k 0 && arr[d] < arr[d-1]) { t = arr[d]; t1 = indexes[d]; arr[d] = arr[d-1]; indexes[d] = indexes[d-1]; arr[d-1] = t; indexes[d-1] = t1; d--; } } } int test_sort_shares(void) { //uint8_t shares[8] = { 12, 1, 8, 7, 3, 11, 10, 5 }; uint8_t qual[4] = { 12, 3, 7, 5 }; uint8_t sorted_qual[4] = { 0, 4, 3, 7 }; sort_shares(4, qual, sorted_qual); const uint8_t vqual[4] = { 3, 5, 7, 12}; const uint8_t vsorted_qual[4] = { 4, 7, 3, 0}; if(memcmp(vqual, qual, 4)!=0) return 1; if(memcmp(vsorted_qual, sorted_qual, 4)!=0) return 1; return 0; } int main(void) { liboprf_log_file = stderr; liboprf_debug = 0; if(test_sort_shares()!=0) return 1; if(test_interpol()!=0) return 1; uint8_t n=13, t=6; TOPRF_Share a_shares[n][2]; uint8_t a_commitments[n][crypto_core_ristretto255_BYTES]; if(dkg_vss_share(n, t, NULL, a_commitments, a_shares, NULL)) return 1; uint8_t a[crypto_scalarmult_ristretto255_SCALARBYTES]; if(0!=dkg_vss_reconstruct(t, 0, n, a_shares, a_commitments, a, NULL)) return 1; liboprf_debug=1; dump(a, sizeof a, "a");liboprf_debug=0; // step 2. generate ρ TOPRF_Share b_shares[n][2]; uint8_t b_commitments[n][crypto_core_ristretto255_BYTES]; // generate kc, the original old key, we are gonna update if(dkg_vss_share(n, t, NULL, b_commitments, b_shares, NULL)) return 1; uint8_t b[crypto_scalarmult_ristretto255_SCALARBYTES]; if(0!=dkg_vss_reconstruct(t, 0, n, b_shares, b_commitments, b, NULL)) return 1; liboprf_debug=1; dump(b, sizeof b, "b");liboprf_debug=0; if(0!=toprf_mpc_vsps_check(t-1, a_commitments)) return 1; if(0!=toprf_mpc_vsps_check(t-1, b_commitments)) return 1; fprintf(stderr, "[0] vsps(A_i) & vsps(B_i) ok\n"); // 3. execute the FT-Mult protocol, to calculate FT-Mult(kc, ρ), generating sharings of r. TOPRF_Share r_shares[n][2]; uint8_t r_commitments[n][crypto_core_ristretto255_BYTES]; if(0!=ft_mult(n, t, a_shares, a_commitments, b_shares, b_commitments, r_shares, r_commitments)) return 1; uint8_t r[crypto_scalarmult_ristretto255_SCALARBYTES]; if(0!=dkg_vss_reconstruct(t, 0, n, r_shares, r_commitments, r, NULL)) return 1; liboprf_debug=1;dump(r, sizeof r, "r ");liboprf_debug=0; uint8_t tmp[crypto_scalarmult_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_mul(tmp, a, b); liboprf_debug=1;dump(tmp, sizeof tmp, "a*b");liboprf_debug=0; if(memcmp(tmp, r, sizeof tmp)!=0) { fprintf(liboprf_log_file,RED"fail a*b != ft-mul(a,b)\n"NORMAL); return 1; } fprintf(stderr, GREEN"everything correct!\n"NORMAL); return 0; } liboprf-0.9.4/src/tests/fuzz-toprf-update/000077500000000000000000000000001514673400200205225ustar00rootroot00000000000000liboprf-0.9.4/src/tests/fuzz-toprf-update/.gitignore000066400000000000000000000001671514673400200225160ustar00rootroot00000000000000fuzz-bin fuzz-dump in out fuzz-complog fuzz-bin-asan fuzz-bin-asan-peer fuzz-bin-peer fuzz-complog-peer fuzz-dump-peer liboprf-0.9.4/src/tests/fuzz-toprf-update/README.txt000066400000000000000000000005001514673400200222130ustar00rootroot00000000000000fuzz tp-dkg using AFL++ to fuzz step x (x:=1..9) of the TP run STEP=x make clean fuzz to fuzz using asan STEP=x make clean fuzz-asan to fuzz the peers step x: STEP=x make clean fuzz-peer to fuzz using asan: STEP=x make clean fuzz-asan-peer note: for some fuzz targets there cannot be any complaints liboprf-0.9.4/src/tests/fuzz-toprf-update/makefile000066400000000000000000000054231514673400200222260ustar00rootroot00000000000000STEP?=3 N?=9 T?=4 includes=-I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal defines=-D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -DUNIT_TEST -DUNITTEST_CORRUPT libs=-lsodium -loprf -loprf-noiseXK src=../../dkg-vss.c ../../dkg.c ../../toprf-update.c ../toprf-update.c ../../mpmult.c ../../utils.c ../../toprf.c SODIUM_NEWER_THAN_1_0_18 := $(shell pkgconf --atleast-version=1.0.19 libsodium; echo $$?) ifeq ($(SODIUM_NEWER_THAN_1_0_18),1) includes+= -I../../aux_ src+=../../aux_/kdf_hkdf_sha256.c else defines+= -DHAVE_SODIUM_HKDF=1 endif in/tc0: fuzz-dump ./fuzz-dump $(N) $(T) $(STEP) in/tc0 in/tc0p: fuzz-dump-peer ./fuzz-dump-peer $(N) $(T) $(STEP) in/tc0p fuzz-dump: $(src) gcc $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -DFUZZ_DUMP -DUNITTEST $(includes) $(defines) $(LDFLAGS) $(libs) -fno-omit-frame-pointer fuzz-dump-peer: $(src) gcc $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -DFUZZ_DUMP -DFUZZ_PEER -DUNITTEST $(includes) $(defines) $(LDFLAGS) $(libs) -fno-omit-frame-pointer fuzz-complog: $(src) AFL_USE_ASAN=1 AFL_LLVM_CMPLOG=1 afl-clang-lto -O2 -march=native -o $@ $^ -DUNITTEST $(includes) $(defines) $(libs) -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-complog-peer: $(src) AFL_USE_ASAN=1 AFL_LLVM_CMPLOG=1 afl-clang-lto -O2 -march=native -o $@ $^ -DFUZZ_PEER -DUNITTEST $(includes) $(defines) $(libs) -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin-asan: $(src) AFL_USE_ASAN=1 afl-clang-lto -O2 -march=native -o $@ $^ -DUNITTEST $(includes) $(defines) $(libs) -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin-asan-peer: $(src) AFL_USE_ASAN=1 afl-clang-lto -O2 -march=native -o $@ $^ -DFUZZ_PEER -DUNITTEST $(includes) $(defines) $(libs) -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin: $(src) afl-clang-lto -o $@ $^ -DUNITTEST $(includes) $(defines) $(libs) -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin-peer: $(src) afl-clang-lto -o $@ $^ -DFUZZ_PEER -DUNITTEST $(includes) $(defines) $(libs) -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-asan: in/tc0 fuzz-bin fuzz-bin-asan fuzz-complog afl-fuzz -c ./fuzz-complog -i in -o out -- ./fuzz-bin-asan $(N) $(T) $(STEP) fuzz-asan-peer: in/tc0p fuzz-bin-peer fuzz-bin-asan-peer fuzz-complog-peer afl-fuzz -c ./fuzz-complog-peer -i in -o out -- ./fuzz-bin-asan-peer $(N) $(T) $(STEP) fuzz: in/tc0 fuzz-bin fuzz-bin-asan fuzz-complog afl-fuzz -c ./fuzz-complog -i in -o out -- ./fuzz-bin $(N) $(T) $(STEP) fuzz-peer: in/tc0p fuzz-bin-peer fuzz-bin-asan-peer fuzz-complog-peer afl-fuzz -c ./fuzz-complog-peer -i in -o out -- ./fuzz-bin-peer $(N) $(T) $(STEP) clean: rm -rf fuzz-complog fuzz-bin fuzz-dump in/* out fuzz-bin-asan-peer fuzz-bin-peer fuzz-complog-peer fuzz-dump-peer liboprf-0.9.4/src/tests/fuzz-toprf-update/toprf_update_msg_parser.py000077500000000000000000000015401514673400200260150ustar00rootroot00000000000000#!/usr/bin/env python import sys from construct import * dkg_msg = Struct( "signature" / Array(64, Byte), "type" / Int8ub, "version" / Int8ub, "msgno" / Int8ub, "size" / Int32ub, "sender" / Int8ub, "to" / Int8ub, "ts" / Timestamp(Int64ub, 1., 1970), "sessionid" / Array(32, Byte), "data" / Array(this.size - 113, Byte), ) messages = GreedyRange(dkg_msg) with open(sys.argv[1], 'rb') as fd: raw = fd.read() while len(raw) > 0: print(raw[:113].hex()) try: msg = dkg_msg.parse(raw) print(f"{str(msg.ts)[:-6]} type: {msg.type}, version: {msg.version}, msgno: {msg.msgno}, len: {msg.size}, from: {msg.sender}, to: {msg.to:x}\nsessionid: {bytes(msg.sessionid).hex()}\ndata: {bytes(msg.data).hex()}") raw = raw[msg.size:] except: print(raw[67:71].hex()) raw = raw[113:] liboprf-0.9.4/src/tests/fuzz-tp-dkg/000077500000000000000000000000001514673400200172765ustar00rootroot00000000000000liboprf-0.9.4/src/tests/fuzz-tp-dkg/.gitignore000066400000000000000000000001671514673400200212720ustar00rootroot00000000000000fuzz-bin fuzz-dump in out fuzz-complog fuzz-bin-asan fuzz-bin-asan-peer fuzz-bin-peer fuzz-complog-peer fuzz-dump-peer liboprf-0.9.4/src/tests/fuzz-tp-dkg/README.txt000066400000000000000000000005001514673400200207670ustar00rootroot00000000000000fuzz tp-dkg using AFL++ to fuzz step x (x:=1..9) of the TP run STEP=x make clean fuzz to fuzz using asan STEP=x make clean fuzz-asan to fuzz the peers step x: STEP=x make clean fuzz-peer to fuzz using asan: STEP=x make clean fuzz-asan-peer note: for some fuzz targets there cannot be any complaints liboprf-0.9.4/src/tests/fuzz-tp-dkg/makefile000066400000000000000000000103671514673400200210050ustar00rootroot00000000000000STEP?=6 N?=3 T?=2 #in/msg0: msg0 # ./msg0 >in/msg0 # #msg0: msg0.c # gcc -o msg0 msg0.c -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf # #peer-start: peer-start.c ../../tp-dkg.c # afl-clang-lto -std=c11 -o peer-start peer-start.c ../../tp-dkg.c -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK # #fuzz-ps: peer-star # afl-fuzz -i in -o out ./peer-start in/tc0: fuzz-dump ./fuzz-dump $(N) $(T) $(STEP) in/tc0 in/tc0p: fuzz-dump-peer ./fuzz-dump-peer $(N) $(T) $(STEP) in/tc0p fuzz-dump: ../../tp-dkg.c ../tp-dkg.c gcc $(CPPFLAGS) $(CFLAGS) -g -o fuzz-dump ../../tp-dkg.c ../tp-dkg.c -DFUZZ_DUMP -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM $(LDFLAGS) -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer fuzz-dump-peer: ../../tp-dkg.c ../tp-dkg.c gcc $(CPPFLAGS) $(CFLAGS) -g -o fuzz-dump-peer ../../tp-dkg.c ../tp-dkg.c -DFUZZ_DUMP -DFUZZ_PEER -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM $(LDFLAGS) -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer fuzz-complog: ../../tp-dkg.c ../tp-dkg.c AFL_USE_ASAN=1 AFL_LLVM_CMPLOG=1 afl-clang-lto -o fuzz-complog ../../tp-dkg.c ../tp-dkg.c -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-complog-peer: ../../tp-dkg.c ../tp-dkg.c AFL_USE_ASAN=1 AFL_LLVM_CMPLOG=1 afl-clang-lto -o fuzz-complog-peer ../../tp-dkg.c ../tp-dkg.c -DFUZZ_PEER -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin-asan: ../../tp-dkg.c ../tp-dkg.c AFL_USE_ASAN=1 afl-clang-lto -o fuzz-bin-asan ../../tp-dkg.c ../tp-dkg.c -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin-asan-peer: ../../tp-dkg.c ../tp-dkg.c AFL_USE_ASAN=1 afl-clang-lto -o fuzz-bin-asan-peer ../../tp-dkg.c ../tp-dkg.c -DFUZZ_PEER -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin: ../../tp-dkg.c ../tp-dkg.c afl-clang-lto -o fuzz-bin ../../tp-dkg.c ../tp-dkg.c -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-bin-peer: ../../tp-dkg.c ../tp-dkg.c afl-clang-lto -o fuzz-bin-peer ../../tp-dkg.c ../tp-dkg.c -DFUZZ_PEER -DUNITTEST -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf -loprf-noiseXK -fno-omit-frame-pointer -fsanitize-memory-track-origins=2 fuzz-asan: in/tc0 fuzz-bin fuzz-bin-asan fuzz-complog afl-fuzz -c ./fuzz-complog -i in -o out -- ./fuzz-bin-asan $(N) $(T) $(STEP) fuzz-asan-peer: in/tc0 fuzz-bin-peer fuzz-bin-asan-peer fuzz-complog-peer afl-fuzz -c ./fuzz-complog-peer -i in -o out -- ./fuzz-bin-asan-peer $(N) $(T) $(STEP) fuzz: in/tc0 fuzz-bin fuzz-bin-asan fuzz-complog afl-fuzz -c ./fuzz-complog -i in -o out -- ./fuzz-bin $(N) $(T) $(STEP) fuzz-peer: in/tc0p fuzz-bin-peer fuzz-bin-asan-peer fuzz-complog-peer afl-fuzz -c ./fuzz-complog-peer -i in -o out -- ./fuzz-bin-peer $(N) $(T) $(STEP) clean: rm -rf fuzz-complog fuzz-bin fuzz-dump in/* out liboprf-0.9.4/src/tests/fuzz-tp-dkg/msg0.c000066400000000000000000000014341514673400200203120ustar00rootroot00000000000000#include "tp-dkg.h" #include #include #include #include // gcc -o tpdkg-msg0 msg0.c -I.. -I../noise_xk/include -I../noise_xk/include/karmel -I../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf #define tpdkg_freshness_TIMEOUT 10 int main(void) { uint8_t n=3, t=2; uint8_t peer_lt_pks[crypto_sign_PUBLICKEYBYTES]; // only known by corresponding peer uint8_t peer_lt_sks[crypto_sign_SECRETKEYBYTES]; crypto_sign_keypair(peer_lt_pks, peer_lt_sks); TP_DKG_TPState tp; uint8_t msg0[tpdkg_msg0_SIZE]; int ret = tpdkg_start_tp(&tp, tpdkg_freshness_TIMEOUT, n, t, "proto test", 10, sizeof msg0, (TP_DKG_Message*) &msg0); if(0!=ret) return ret; write(1, msg0, tpdkg_msg0_SIZE); return 0; } liboprf-0.9.4/src/tests/fuzz-tp-dkg/peer-start.c000066400000000000000000000022201514673400200215240ustar00rootroot00000000000000#include #include #include "tp-dkg.h" // afl-clang-fast -o peer-start peer-start.c -I../.. -I../../noise_xk/include -I../../noise_xk/include/karmel -I../../noise_xk/include/karmel/minimal -D_BSD_SOURCE -D_DEFAULT_SOURCE -DWITH_SODIUM -lsodium -loprf __AFL_FUZZ_INIT(); int main() { // anything else here, e.g. command line arguments, initialization, etc. uint8_t peer_lt_sks[32]={250}; TP_DKG_PeerState peer; #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT // and before __AFL_LOOP! while (__AFL_LOOP(10000)) { int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a // call! if (len < sizeof(TP_DKG_Message)) continue; // check for a required/useful minimum input length /* Setup function call, e.g. struct target *tmp = libtarget_init() */ /* Call function to be fuzzed, e.g.: */ tpdkg_start_peer(&peer, 10, peer_lt_sks, (TP_DKG_Message*) buf); /* Reset state. e.g. libtarget_free(tmp) */ } return 0; } liboprf-0.9.4/src/tests/fuzz-tp-dkg/tpdkg_msg_parser.py000077500000000000000000000012771514673400200232150ustar00rootroot00000000000000#!/usr/bin/env python import sys from construct import * tpdkg_msg = Struct( "signature" / Array(64, Byte), "msgno" / Int8ub, "size" / Int32ub, "sender" / Int8ub, "to" / Int8ub, "ts" / Timestamp(Int64ub, 1., 1970), "data" / Array(this.size - 79, Byte), ) messages = GreedyRange(tpdkg_msg) with open(sys.argv[1], 'rb') as fd: raw = fd.read() while len(raw) > 0: print(raw[:83].hex()) try: msg = tpdkg_msg.parse(raw) print(f"{str(msg.ts)[:-6]} msgno: {msg.msgno}, len: {msg.size}, from: {msg.sender}, to: {msg.to:x}, data {bytes(msg.data).hex()}") raw = raw[msg.size:] except: print(raw[65:69].hex()) raw = raw[83:] liboprf-0.9.4/src/tests/makefile000066400000000000000000000123631514673400200166210ustar00rootroot00000000000000CFLAGS?= -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \ -fstack-protector-strong -fasynchronous-unwind-tables -fpic \ -ftrapv -D_GLIBCXX_ASSERTIONS \ -Wl,-z,defs -Wl,-z,relro \ -Wl,-z,noexecstack -Wl,-z,now -fsanitize=signed-integer-overflow \ -fsanitize-undefined-trap-on-error CC?=gcc INCLUDES=-I.. -I../noise_xk/include -I../noise_xk/include/karmel/ -I../noise_xk/include/karmel/minimal/ ARCH := $(shell uname -m) ifeq ($(ARCH),x86_64) CFLAGS+=-fcf-protection=full endif ifeq ($(ARCH),parisc64) else ifeq ($(ARCH),parisc64) else CFLAGS+=-fstack-clash-protection endif SODIUM_NEWER_THAN_1_0_18 := $(shell pkgconf --atleast-version=1.0.19 libsodium; echo $$?) ifeq ($(SODIUM_NEWER_THAN_1_0_18),1) CFLAGS+= -I../aux_ EXTRA_SOURCES+= ../aux_/kdf_hkdf_sha256.c else CFLAGS+= -DHAVE_SODIUM_HKDF=1 endif all: tv1 tv2 dkg toprf tp-dkg tp-dkg-corrupt mpmult stp-dkg stp-dkg-corrupt toprf-update toprf-update-corrupt tv1: test.c cfrg_oprf_test_vectors.h cfrg_oprf_test_vector_decl.h ../oprf.c ../utils.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g -o tv1 -DCFRG_TEST_VEC=1 -DCFRG_OPRF_TEST_VEC=1 -DTC=0 test.c ../oprf.c ../utils.c -lsodium tv2: test.c cfrg_oprf_test_vectors.h cfrg_oprf_test_vector_decl.h ../oprf.c ../utils.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g -o tv2 -DCFRG_TEST_VEC=1 -DCFRG_OPRF_TEST_VEC=1 -DTC=1 test.c ../oprf.c ../utils.c -lsodium toprf: toprf.c $(CC) $(CPPFLAGS) $(CFLAGS) -g -o toprf toprf.c ../toprf.c ../oprf.c $(EXTRA_SOURCES) -lsodium dkg: ../dkg.c ../utils.c dkg.c ../dkg.c ../utils.c ../toprf.c ../oprf.c $(CC) $(CPPFLAGS) $(CFLAGS) -g $(INCLUDES) -DUNIT_TEST -o dkg dkg.c ../dkg.c ../utils.c ../toprf.c ../oprf.c ../noise_xk/liboprf-noiseXK.a $(LDFLAGS) -lsodium tp-dkg: ../tp-dkg.c tp-dkg.c ../tp-dkg.c ../utils.c ../toprf.c ../oprf.c ../dkg.c $(CC) $(CPPFLAGS) $(CFLAGS) -D_DEFAULT_SOURCE -g -std=c11 $(INCLUDES) -DWITH_SODIUM -DUNITTEST -o tp-dkg tp-dkg.c ../tp-dkg.c ../utils.c ../toprf.c ../oprf.c ../dkg.c ../noise_xk/liboprf-noiseXK.a $(LDFLAGS) -lsodium tp-dkg-corrupt: ../tp-dkg.c tp-dkg.c ../tp-dkg.c ../utils.c ../toprf.c ../oprf.c ../dkg.c $(CC) $(CPPFLAGS) $(CFLAGS) -D_DEFAULT_SOURCE -g -std=c11 $(INCLUDES) -DWITH_SODIUM -DUNITTEST -DUNITTEST_CORRUPT -o tp-dkg-corrupt tp-dkg.c ../tp-dkg.c ../utils.c ../toprf.c ../oprf.c ../dkg.c ../noise_xk/liboprf-noiseXK.a $(LDFLAGS) -lsodium stp-dkg: ../dkg-vss.c ../dkg.c ../stp-dkg.c stp-dkg.c ../stp-dkg.h ../mpmult.c ../utils.c ../toprf.c ../oprf.c ../dkg.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -o stp-dkg stp-dkg.c ../dkg-vss.c ../mpmult.c ../utils.c ../toprf.c ../stp-dkg.c ../oprf.c ../dkg.c ../noise_xk/liboprf-noiseXK.a $(LDFLAGS) -lsodium stp-dkg-corrupt: ../dkg-vss.c ../dkg.c ../stp-dkg.c stp-dkg.c ../stp-dkg.h ../mpmult.c ../utils.c ../toprf.c ../oprf.c ../dkg.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -DUNITTEST_CORRUPT -o stp-dkg-corrupt stp-dkg.c ../dkg-vss.c ../mpmult.c ../utils.c ../toprf.c ../stp-dkg.c ../oprf.c ../dkg.c ../noise_xk/liboprf-noiseXK.a $(LDFLAGS) -lsodium mpmult: ../mpmult.c mpmult.c ../utils.c ../toprf.c ../dkg-vss.c ../oprf.c ../dkg.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -o mpmult mpmult.c ../mpmult.c ../utils.c ../toprf.c ../dkg-vss.c ../oprf.c ../dkg.c ../noise_xk/liboprf-noiseXK.a -lsodium update-poc: ../dkg-vss.c ../dkg.c update-poc.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -o update-poc update-poc.c ../dkg-vss.c ../utils.c ../toprf.c ../noise_xk/liboprf-noiseXK.a -lsodium ft-mult: ../dkg-vss.c ../dkg.c ft-mult.c ../toprf.c $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -o ft-mult ft-mult.c ../dkg-vss.c ../utils.c ../toprf.c ../noise_xk/liboprf-noiseXK.a -lsodium toprf-update: ../dkg-vss.c ../dkg.c ../toprf-update.c toprf-update.c ../toprf-update.h ../mpmult.c ../oprf.c ../dkg.c $(EXTRA_SOURCES) $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -o toprf-update toprf-update.c ../dkg-vss.c ../mpmult.c ../utils.c ../toprf.c ../toprf-update.c ../oprf.c ../dkg.c $(EXTRA_SOURCES) ../noise_xk/liboprf-noiseXK.a -lsodium toprf-update-corrupt: ../dkg-vss.c ../dkg.c ../toprf-update.c toprf-update.c ../toprf-update.h ../mpmult.c ../utils.c ../oprf.c ../dkg.c $(EXTRA_SOURCES) $(CC) $(CPPFLAGS) $(CFLAGS) -Wall -g $(INCLUDES) -DUNIT_TEST -DUNITTEST_CORRUPT -o toprf-update-corrupt toprf-update.c ../dkg-vss.c ../mpmult.c ../utils.c ../toprf.c ../toprf-update.c ../oprf.c ../dkg.c $(EXTRA_SOURCES) ../noise_xk/liboprf-noiseXK.a -lsodium allocations: allocations.c $(CC) $(CPPFLAGS) $(CFLAGS) -g -o allocations allocations.c -I.. -I../noise_xk/include -I../noise_xk/include/karmel/ -I../noise_xk/include/karmel/minimal -loprf -loprf-noiseXK cfrg_oprf_test_vectors.h: testvecs2h.py ./testvecs2h.py $@ >$@ cfrg_oprf_test_vector_decl.h: testvecs2h.py ./testvecs2h.py $@ >$@ tests: all ./dkg ./tv1 ./tv2 ./toprf (ulimit -s 66000; ./tp-dkg 3 2) (ulimit -s 66000; ./tp-dkg-corrupt 3 2 || exit 0) (ulimit -s 1966000; ./stp-dkg 9 4) (ulimit -s 1966000; ./stp-dkg-corrupt 9 4 || exit 0) (ulimit -s 1966000; ./toprf-update 9 4) (ulimit -s 1966000; ./toprf-update-corrupt 9 4) ./mpmult clean: rm -f cfrg_oprf_test_vector_decl.h cfrg_oprf_test_vectors.h tv1 tv2 tp-dkg dkg toprf mpmult stp-dkg-corrupt stp-dkg update-poc toprf-update toprf-update-corrupt liboprf-0.9.4/src/tests/mpmult.c000066400000000000000000000157111514673400200166030ustar00rootroot00000000000000#include #include "../oprf.h" #include "../mpmult.h" #include "../dkg.h" #include "../utils.h" #include #include #include int test_mpmul(void) { const uint8_t threshold = 2, dealers = (threshold*2) + 1U, peers = dealers * 2; // share value k0 uint8_t k0[crypto_core_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_random(k0); //debian_rng(k0); dump(k0, sizeof k0, "k0"); // split k into shares uint8_t shares0[peers][TOPRF_Share_BYTES]; toprf_create_shares(k0, peers, threshold, shares0); if(liboprf_debug) { for(unsigned j=0;j #include #include #include "../utils.h" #include "../toprf.h" #include "../dkg-vss.h" #include "../mpmult.h" #include "../stp-dkg.h" // simulate network #define NETWORK_BUF_SIZE (1024*1024*16) //static size_t _send(uint8_t *net, size_t *pkt_len, const uint8_t *msg, const size_t msg_len) { static void _send(uint8_t *net, size_t *pkt_len, const uint8_t *msg, const size_t msg_len) { if(*pkt_len+msg_len >= NETWORK_BUF_SIZE || msg_len==0 || msg == NULL) { return;// 0; } memcpy(net+*pkt_len, msg, msg_len); *pkt_len+=msg_len; //return msg_len; } //static size_t _recv(const uint8_t *net, size_t *pkt_len, uint8_t *buf, const size_t msg_len) { static void _recv(const uint8_t *net, size_t *pkt_len, uint8_t *buf, const size_t msg_len) { if(*pkt_len < msg_len || msg_len == 0) { return; // 0; } memcpy(buf, net, msg_len); *pkt_len-=msg_len; //return msg_len; } typedef struct { size_t len; uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*noise_pks)[][crypto_scalarmult_BYTES]; } Keyloader_CB_Arg; int keyloader_cb(const uint8_t id[crypto_generichash_BYTES], void *arg, uint8_t sigpk[crypto_sign_PUBLICKEYBYTES], uint8_t noise_pk[crypto_scalarmult_BYTES]) { Keyloader_CB_Arg *args = (Keyloader_CB_Arg *) arg; uint8_t pkhash[crypto_generichash_BYTES]; dump(id, crypto_generichash_BYTES, "loading keys for keyid"); for(unsigned i=0;ilen;i++) { crypto_generichash(pkhash,sizeof pkhash,(*args->sig_pks)[i+1],crypto_sign_PUBLICKEYBYTES,NULL,0); if(memcmp(pkhash, id, sizeof pkhash) == 0) { memcpy(sigpk, (*args->sig_pks)[i+1], crypto_sign_PUBLICKEYBYTES); memcpy(noise_pk, (*args->noise_pks)[i], crypto_scalarmult_BYTES); return 0; } } return 1; } int main(const int argc, const char **argv) { int ret=0; // enable logging liboprf_log_file = stderr; liboprf_debug = 1; if(argc<3) { #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) fprintf(stderr, "error incorrect numbers of parameters, run as: %% %s [ []]\n", argv[0]); #else fprintf(stderr, "error incorrect numbers of parameters, run as: %% %s \n", argv[0]); #endif // defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) exit(1); } const uint8_t n=atoi(argv[1]); const uint8_t t=atoi(argv[2]); // mock long-term peer keys // all known by STP uint8_t lt_pks[n+1][crypto_sign_PUBLICKEYBYTES]; // only known by corresponding peer uint8_t lt_sks[n+1][crypto_sign_SECRETKEYBYTES]; for(uint8_t i=0;i 0) break; return ret; } for(uint8_t i=0;i0) { fprintf(stderr, "peer %d has detected some cheaters:\n", i+1); int total_cheaters=0; uint8_t tmp[n+1]; memset(tmp,0,n+1); for(int j=0;j n) return 1; if(tmp[p]==0) total_cheaters++; tmp[p]++; } fprintf(stderr, RED":/ dkg failed, total cheats detected %d, list of cheaters:", total_cheaters); for(int j=1;j<=n;j++) { if(tmp[j]==0) continue; fprintf(stderr," %d(%d)", j, tmp[j]); } fprintf(stderr, NORMAL"\n"); //return 1; } } fprintf(stderr, "----------------------------\nfinal results as seen by stp:\n"); if(stp.cheater_len>0) { int total_cheaters=0; uint8_t tmp[n+1]; memset(tmp,0,n+1); for(int i=0;i n) return 1; if(tmp[p]==0) total_cheaters++; tmp[p]++; } fprintf(stderr, RED":/ dkg failed, total cheats detected %d, list of cheaters:", total_cheaters); for(int i=1;i<=n;i++) { if(tmp[i]==0) continue; fprintf(stderr," %d(%d)", i, tmp[i]); } fprintf(stderr, NORMAL"\n"); return 1; } fprintf(stderr, "\x1b[0;32mewige blumenkraft!!5!\x1b[0m\n"); return ret; } liboprf-0.9.4/src/tests/test.c000066400000000000000000000031411514673400200162360ustar00rootroot00000000000000#include #include #include #include "cfrg_oprf_test_vectors.h" #include "../oprf.h" #include "../utils.h" extern int liboprf_debug; int main(void) { liboprf_debug = 1; int res; uint8_t r[crypto_core_ristretto255_SCALARBYTES]; uint8_t blinded[crypto_core_ristretto255_BYTES]; res = oprf_Blind(input, input_len, r, blinded); if(res) return 1; if(memcmp(blinded, blinded_element, blindedelement_len)!=0) { fail("calulated Blinded Element is not expected value:"); dump(blinded, sizeof(blinded), "calculated: "); dump(blinded_element, blindedelement_len, "expected: "); return 1; } uint8_t Z[crypto_core_ristretto255_BYTES]; res = oprf_Evaluate(sks, blinded, Z); if(res) { fprintf(stderr,"oprf_Evaluate returned error\n"); return 1; } if(memcmp(Z, evaluationelement, evaluationelement_len)!=0) { fail("calulated Evaluation Element is not expected value:"); dump(Z, sizeof(Z), "calculated: "); dump(evaluationelement, evaluationelement_len, "expected: "); return 1; } uint8_t N[crypto_core_ristretto255_BYTES]; res = oprf_Unblind(r, Z, N); if(res) { fprintf(stderr,"oprf_Unblind returned error\n"); return 1; } uint8_t rwd[OPRF_BYTES]; res = oprf_Finalize(input, input_len, N, rwd); if(res) { fprintf(stderr,"oprf_Finalize returned error\n"); return 1; } if(memcmp(rwd, output, output_len)!=0) { fail("calulated output is not expected value:"); dump(rwd, sizeof(rwd), "calculated: "); dump(output, output_len, "expected: "); return 1; } printf("all ok\n"); return 0; } liboprf-0.9.4/src/tests/testvecs2h.py000077500000000000000000000105171514673400200175670ustar00rootroot00000000000000#!/usr/bin/env python3 import json, sys from itertools import zip_longest # for Python 3.x def split_by_n(iterable, n): return zip_longest(*[iter(iterable)]*n, fillvalue='') # src: ht vectors = """ { "groupDST": "48617368546f47726f75702d4f50524656312d002d72697374726574746f3235352d534841353132", "hash": "SHA512", "identifier": "ristretto255-SHA512", "keyInfo": "74657374206b6579", "mode": 0, "seed": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3", "skSm": "5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063b0e", "vectors": [ { "Batch": 1, "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", "BlindedElement": "609a0ae68c15a3cf6903766461307e5c8bb2f95e7e6550e1ffa2dc99e412803c", "EvaluationElement": "7ec6578ae5120958eb2db1745758ff379e77cb64fe77b0b2d8cc917ea0869c7e", "Input": "00", "Output": "527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb7708264e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf6" }, { "Batch": 1, "Blind": "64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f6706", "BlindedElement": "da27ef466870f5f15296299850aa088629945a17d1f5b7f5ff043f76b3c06418", "EvaluationElement": "b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e17cecb5c90d02c25", "Input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", "Output": "f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e750cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c73" } ] } """ def toC(k, v): print(f"#define {k.lower()}_len {len(v)//2}") print( f"const uint8_t {k.lower()}[{k.lower()}_len] = {{\n %s}};\n" % ",\n ".join( (", ".join((c for c in line if c)) for line in split_by_n( (f"0x{x[0]}{x[1]}" for x in split_by_n(v,2)) ,8)) )) vex = json.loads(vectors) print("// this file has been automatically generated using testvecs2h.py") if sys.argv[1] == 'cfrg_oprf_test_vectors.h': # run this if there is a change in the values of the test vectors # ./testvecs2h.py >cfrg_oprf_test_vectors.h print("#ifndef cfrg_test_vectors_h\n#define cfrg_test_vectors_h\n") print("#include \n") toC("sks", vex['skSm']) for tc in range(2): for k, v in vex['vectors'][tc].items(): if k == "Batch": continue print(f"#define tc{tc}_{k.lower()}_len {len(v)//2}") print( f"const uint8_t tc{tc}_{k.lower()}[tc{tc}_{k.lower()}_len] = {{\n %s}};\n" % ",\n ".join( (", ".join((c for c in line if c)) for line in split_by_n( (f"0x{x[0]}{x[1]}" for x in split_by_n(v,2)) ,8)) )) elif sys.argv[1] == 'cfrg_oprf_test_vector_decl.h': # only run this code below if there is a change in the keys of the test vectors # ./testvecs2h.py >cfrg_oprf_test_vector_decl.h print("#ifndef cfrg_test_vector_decl_h\n#define cfrg_test_vector_decl_h\n") print("#include \n") for tc in range(2): for k, v in vex['vectors'][tc].items(): if k == "Batch": continue print(f"#define tc{tc}_{k.lower()}_len {len(v)//2}") print(f"extern const uint8_t tc{tc}_{k.lower()}[tc{tc}_{k.lower()}_len];\n") else: sys.exit(-1) print(""" #if(TC==0) #define input tc0_input #define input_len tc0_input_len #define blind_registration tc0_blind #define blind_login tc0_blind #define blind_len tc0_blind_len #define blinded_element tc0_blindedelement #define blindedelement_len tc0_blindedelement_len #define evaluationelement tc0_evaluationelement #define evaluationelement_len tc0_evaluationelement_len #define output tc0_output #define output_len tc0_output_len #else #define input tc1_input #define input_len tc1_input_len #define blind_registration tc1_blind #define blind_login tc1_blind #define blind_len tc1_blind_len #define blinded_element tc1_blindedelement #define blindedelement_len tc1_blindedelement_len #define evaluationelement tc1_evaluationelement #define evaluationelement_len tc1_evaluationelement_len #define output tc1_output #define output_len tc1_output_len #endif""") print("#endif") liboprf-0.9.4/src/tests/toprf-update.c000066400000000000000000000627011514673400200177000ustar00rootroot00000000000000#include #include "../utils.h" #include "../toprf.h" #include "../dkg-vss.h" #include "../mpmult.h" #include "../toprf-update.h" #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #include #endif #ifdef __AFL_FUZZ_INIT __AFL_FUZZ_INIT(); #endif #ifdef FUZZ_DUMP #if !defined(FUZZ_PEER) static void fuzz_dump(const uint8_t step, TOPRF_Update_STPState *ctx, const uint8_t *buf_in, const size_t buf_in_size, const char **argv, const int argc) { #else static void fuzz_dump(const uint8_t step, TOPRF_Update_PeerState *ctx, const uint8_t *buf_in, const size_t buf_in_size, const char **argv, const int argc) { #endif //!defined(FUZZ_PEER) if(argc<5) { fprintf(stderr, "error incorrect number of params, run as: %% %s \n", argv[0]); exit(1); } if(ctx->step==step) { FILE *tc = fopen(argv[4], "wb"); fwrite(buf_in, 1, buf_in_size, tc); fclose(tc); exit(0); } } #endif // simulate network #define NETWORK_BUF_SIZE (1024*1024*16) //static size_t _send(uint8_t *net, size_t *pkt_len, const uint8_t *msg, const size_t msg_len) { static void _send(uint8_t *net, size_t *pkt_len, const uint8_t *msg, const size_t msg_len) { if(*pkt_len+msg_len >= NETWORK_BUF_SIZE || msg_len==0 || msg == NULL) { return;// 0; } memcpy(net+*pkt_len, msg, msg_len); *pkt_len+=msg_len; //return msg_len; } //static size_t _recv(const uint8_t *net, size_t *pkt_len, uint8_t *buf, const size_t msg_len) { static void _recv(const uint8_t *net, size_t *pkt_len, uint8_t *buf, const size_t msg_len) { if(*pkt_len < msg_len || msg_len == 0) { return; // 0; } memcpy(buf, net, msg_len); *pkt_len-=msg_len; //return msg_len; } static uint8_t dkg_vss_verify_commitments(const uint8_t n, const uint8_t self, const uint8_t commitments[n][n][crypto_core_ristretto255_BYTES], const TOPRF_Share shares[n][2], uint8_t complaints[n]) { uint8_t complaints_len=0; for(uint8_t i=1;i<=n;i++) { if(i==self) continue; if(0!=dkg_vss_verify_commitment(commitments[i-1][self-1], shares[i-1])) { // complain about P_i fprintf(stderr, "\x1b[0;31mfailed to verify contribs of P_%d in stage 1\x1b[0m\n", i); complaints[complaints_len++]=i; //return 1; } else { #ifdef UNIT_TEST if(liboprf_debug) fprintf(stderr, "\x1b[0;32mP_%d stage 1 correct!\x1b[0m\n", i); #endif // UNIT_TEST } } return complaints_len; } static int dkg_vss(const uint8_t n, const uint8_t t, TOPRF_Share final_shares[n][2], uint8_t commitments[n][crypto_core_ristretto255_BYTES]) { uint8_t dealer_commitments[n][n][crypto_core_ristretto255_BYTES]; TOPRF_Share shares[n][n][2]; for(int i=0;i0) { // todo accused dealer P_i publishes Ξ±_i, ρ_i such that A_i = 𝓗(Ξ±_i,ρ_i) // if dealer P_i fails, disqualify them. // otherwise the accuser sets their shares to Ξ±_i, ρ_i return 1; } // todo handle complaints, build qual set uint8_t qual[n+1]; for(int i=0;istep!=step) return 0; TOPRF_Update_STPState checkpoint; memcpy(&checkpoint, stp, sizeof(checkpoint)); TOPRF_Update_PeerState pcheckpoints[stp->n]; memcpy(&pcheckpoints, peers, sizeof(pcheckpoints)); #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT // and before __AFL_LOOP! while (__AFL_LOOP(10000)) { int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a call! if (len < sizeof(DKG_Message)) continue; // check for a required/useful minimum input length // doing vla - but avoiding 0 sized ones is ugly const size_t stp_out_size = toprf_update_stp_output_size(stp); uint8_t stp_out_buf[stp_out_size==0?1:stp_out_size], *stp_out; if(stp_out_size==0) stp_out = NULL; else stp_out = stp_out_buf; /* Setup function call, e.g. struct target *tmp = libtarget_init() */ /* Call function to be fuzzed, e.g.: */ int ret = toprf_update_stp_next(stp, buf, len, stp_out, stp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); if(stp->cheater_len > 0) return 125; return ret; } while(toprf_update_stp_not_done(stp)) { for(uint8_t i=0;in;i++) { const uint8_t *msg; size_t len; if(0!=toprf_update_stp_peer_msg(stp, stp_out, stp_out_size, i, &msg, &len)) { return 1; } _send(network_buf[i+1], &pkt_len[i+1], msg, len); } while(pkt_len[0]==0 && toprf_update_peer_not_done(&peers[1])) { for(uint8_t i=0;in;i++) { // 0sized vla meh const size_t peer_out_size = toprf_update_peer_output_size(&peers[i]); uint8_t peers_out_buf[peer_out_size==0?1:peer_out_size], *peers_out; if(peer_out_size==0) peers_out = NULL; else peers_out = peers_out_buf; // 0sized vla meh for the last time.. const size_t peer_in_size = toprf_update_peer_input_size(&peers[i]); uint8_t peer_in_buf[peer_in_size==0?1:peer_in_size], *peer_in; if(peer_in_size==0) peer_in = NULL; else peer_in = peer_in_buf; _recv(network_buf[i+1], &pkt_len[i+1], peer_in, peer_in_size); ret = toprf_update_peer_next(&peers[i], peer_in, peer_in_size, peers_out, peer_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peers_out, peer_out_size); } } // doing vla - but avoiding 0 sized ones is ugly const size_t stp_out_size = toprf_update_stp_output_size(stp); uint8_t stp_out_buf[stp_out_size==0?1:stp_out_size], *stp_out; if(stp_out_size==0) stp_out = NULL; else stp_out = stp_out_buf; // avoiding zero-sized vla is still ugly const size_t stp_in_size = toprf_update_stp_input_size(stp); uint8_t stp_in_buf[stp_in_size==0?1:stp_in_size], *stp_in; if(stp_in_size==0) stp_in = NULL; else stp_in = stp_in_buf; _recv(network_buf[0], &pkt_len[0], stp_in, stp_in_size); ret = toprf_update_stp_next(stp, stp_in, stp_in_size, stp_out, stp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); if(stp->cheater_len > 0) return 55; return ret; } } /* Reset state. e.g. libtarget_free(tmp) */ memcpy(stp, &checkpoint, sizeof(TOPRF_Update_STPState)); memcpy(peers, &pcheckpoints, sizeof(pcheckpoints)); } return 0; } #else // !defined(FUZZ_PEER) static int fuzz_loop(const uint8_t step, TOPRF_Update_STPState *stp, TOPRF_Update_PeerState *peers, uint8_t network_buf[][NETWORK_BUF_SIZE],size_t pkt_len[]) { if(peers[0].step!=step) return 0; TOPRF_Update_STPState checkpoint; memcpy(&checkpoint, stp, sizeof(checkpoint)); TOPRF_Update_PeerState pcheckpoints[stp->n]; memcpy(&pcheckpoints, peers, sizeof(pcheckpoints)); #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT // and before __AFL_LOOP! int ret; while (__AFL_LOOP(10000)) { int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a call! if (len < sizeof(DKG_Message)) continue; // check for a required/useful minimum input length // doing vla - but avoiding 0 sized ones is ugly const size_t peer_out_size = toprf_update_peer_output_size(&peers[0]); uint8_t peer_out_buf[peer_out_size==0?1:peer_out_size], *peer_out; if(peer_out_size==0) peer_out = NULL; else peer_out = peer_out_buf; ret = toprf_update_peer_next(&peers[0], buf, len, peer_out, peer_out_size); if(ret!=0) { //for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peer_out, peer_out_size); for(uint8_t i=1;in;i++) { // 0sized vla meh const size_t peer_out_size = toprf_update_peer_output_size(&peers[i]); uint8_t peers_out_buf[peer_out_size==0?1:peer_out_size], *peers_out; if(peer_out_size==0) peers_out = NULL; else peers_out = peers_out_buf; // 0sized vla meh for the last time.. const size_t peer_in_size = toprf_update_peer_input_size(&peers[i]); uint8_t peer_in_buf[peer_in_size==0?1:peer_in_size], *peer_in; if(peer_in_size==0) peer_in = NULL; else peer_in = peer_in_buf; _recv(network_buf[i+1], &pkt_len[i+1], peer_in, peer_in_size); ret = toprf_update_peer_next(&peers[i], peer_in, peer_in_size, peers_out, peer_out_size); if(0!=ret) { // clean up peers //for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peers_out, peer_out_size); } while(toprf_update_stp_not_done(stp)) { while(pkt_len[0]==0 && toprf_update_peer_not_done(&peers[1])) { for(uint8_t i=0;in;i++) { // 0sized vla meh const size_t peer_out_size = toprf_update_peer_output_size(&peers[i]); uint8_t peers_out_buf[peer_out_size==0?1:peer_out_size], *peers_out; if(peer_out_size==0) peers_out = NULL; else peers_out = peers_out_buf; // 0sized vla meh for the last time.. const size_t peer_in_size = toprf_update_peer_input_size(&peers[i]); uint8_t peer_in_buf[peer_in_size==0?1:peer_in_size], *peer_in; if(peer_in_size==0) peer_in = NULL; else peer_in = peer_in_buf; _recv(network_buf[i+1], &pkt_len[i+1], peer_in, peer_in_size); ret = toprf_update_peer_next(&peers[i], peer_in, peer_in_size, peers_out, peer_out_size); if(0!=ret) { // clean up peers //for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peers_out, peer_out_size); } } // doing vla - but avoiding 0 sized ones is ugly const size_t stp_out_size = toprf_update_stp_output_size(stp); uint8_t stp_out_buf[stp_out_size==0?1:stp_out_size], *stp_out; if(stp_out_size==0) stp_out = NULL; else stp_out = stp_out_buf; // avoiding zero-sized vla is still ugly const size_t stp_in_size = toprf_update_stp_input_size(stp); uint8_t stp_in_buf[stp_in_size==0?1:stp_in_size], *stp_in; if(stp_in_size==0) stp_in = NULL; else stp_in = stp_in_buf; _recv(network_buf[0], &pkt_len[0], stp_in, stp_in_size); ret = toprf_update_stp_next(stp, stp_in, stp_in_size, stp_out, stp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) toprf_update_peer_free(&peers[i]); if(stp->cheater_len > 0) return 55; return ret; } for(uint8_t i=0;in;i++) { const uint8_t *msg; size_t len; if(0!=toprf_update_stp_peer_msg(stp, stp_out, stp_out_size, i, &msg, &len)) { return 1; } _send(network_buf[i+1], &pkt_len[i+1], msg, len); } } /* Reset state. e.g. libtarget_free(tmp) */ memcpy(stp, &checkpoint, sizeof(TOPRF_Update_STPState)); memcpy(peers, &pcheckpoints, sizeof(pcheckpoints)); } return 0; } #endif #endif int main(const int argc, const char **argv) { int ret=0; // enable logging liboprf_log_file = stderr; liboprf_debug = 0; if(argc<3) { #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) fprintf(stderr, "error incorrect numbers of parameters, run as: %% %s [ []]\n", argv[0]); #else fprintf(stderr, "error incorrect numbers of parameters, run as: %% %s \n", argv[0]); #endif // defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) exit(1); } #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) uint8_t step=atoi(argv[3]); #ifdef FUZZ_PEER if(step<1 || step > 33 ) { #else if(step<1 || step > 23) { #endif fprintf(stderr, "error incorrect value for step must be 1-23 or 33 for peers, run as: %% %s <1..{23|33}> \n", argv[0]); exit(1); } #endif const uint8_t n=atoi(argv[1]); const uint8_t t=atoi(argv[2]); const uint8_t dealers = (t-1)*2 + 1; // share value k0 TOPRF_Share k0_shares[n][2]; uint8_t k0_commitments[n][crypto_core_ristretto255_BYTES]; if(0!=dkg_vss(n,t, k0_shares, k0_commitments)) return 1; if(0!=toprf_mpc_vsps_check(t-1, k0_commitments)) return 1; liboprf_debug = 1; for(int i=0;i0) fprintf(stderr, RED"[!] pkt_len[0] > 0 -> %ld unconsumed\n"NORMAL, pkt_len[0]); #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(FUZZ_PEER) ret = fuzz_loop(step, &stp, peers, network_buf, pkt_len); if(0!=ret) return ret; #endif #if defined(FUZZ_DUMP) && !defined(FUZZ_PEER) fuzz_dump(step, &stp, stp_in, stp_in_size, argv, argc); #endif ret = toprf_update_stp_next(&stp, stp_in, stp_in_size, stp_out, stp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;i 0) break; return ret; } for(uint8_t i=0;i #include #include "../oprf.h" #include "../toprf.h" int main(void) { // setup // todo use dkg const unsigned peers = 3, threshold = 2; uint8_t k[crypto_core_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_random(k); // split k into shares uint8_t shares[peers][TOPRF_Share_BYTES]; toprf_create_shares(k, peers, threshold, shares); // start the OPRF const uint8_t password[8]="password"; uint8_t r[crypto_core_ristretto255_SCALARBYTES]; uint8_t alpha[crypto_core_ristretto255_BYTES]; // we blind once if(oprf_Blind(password, sizeof password, r, alpha)) return 1; // until here all is like with the non-threshold version // calculate points of shares // this really happens at each peer separately uint8_t xresps[peers][TOPRF_Part_BYTES]; for(size_t i=0;i #include #include #include "utils.h" #include "toprf.h" #include "tp-dkg.h" #include "dkg.h" #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #include #endif #ifdef __AFL_FUZZ_INIT __AFL_FUZZ_INIT(); #endif typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_BYTES]; } __attribute((packed)) TOPRF_Part; static void topart(TOPRF_Part *r, const TOPRF_Share *s) { r->index=s->index; crypto_scalarmult_ristretto255_base(r->value, s->value); } static void shuffle(uint8_t *array, const size_t n) { if (n < 2) return; srand((unsigned) time(NULL)); for(unsigned i=0; i= NETWORK_BUF_SIZE || msg_len==0 || msg == NULL) { return;// 0; } memcpy(net+*pkt_len, msg, msg_len); *pkt_len+=msg_len; //return msg_len; } //static size_t _recv(const uint8_t *net, size_t *pkt_len, uint8_t *buf, const size_t msg_len) { static void _recv(const uint8_t *net, size_t *pkt_len, uint8_t *buf, const size_t msg_len) { if(*pkt_len < msg_len || msg_len == 0) { return; // 0; } memcpy(buf, net, msg_len); *pkt_len-=msg_len; //return msg_len; } #ifdef FUZZ_DUMP #if !defined(FUZZ_PEER) static void fuzz_dump(const uint8_t step, TP_DKG_TPState *ctx, const uint8_t *buf_in, const size_t buf_in_size, const char **argv, const int argc) { #else static void fuzz_dump(const uint8_t step, TP_DKG_PeerState *ctx, const uint8_t *buf_in, const size_t buf_in_size, const char **argv, const int argc) { #endif //!defined(FUZZ_PEER) if(argc<5) { fprintf(stderr, "error incorrect number of params, run as: %% %s \n", argv[0]); exit(1); } if(ctx->step==step) { FILE *tc = fopen(argv[4], "wb"); fwrite(buf_in, 1, buf_in_size, tc); fclose(tc); exit(0); } } #endif #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) #if !defined(FUZZ_PEER) static int fuzz_loop(const uint8_t step, TP_DKG_TPState *tp, TP_DKG_PeerState *peers, uint8_t network_buf[][NETWORK_BUF_SIZE],size_t pkt_len[]) { if(tp->step!=step) return 0; TP_DKG_TPState checkpoint; memcpy(&checkpoint, tp, sizeof(checkpoint)); TP_DKG_PeerState pcheckpoints[tp->n]; memcpy(&pcheckpoints, peers, sizeof(pcheckpoints)); #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT // and before __AFL_LOOP! while (__AFL_LOOP(10000)) { int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a call! if (len < sizeof(DKG_Message)) continue; // check for a required/useful minimum input length // doing vla - but avoiding 0 sized ones is ugly const size_t tp_out_size = tpdkg_tp_output_size(tp); uint8_t tp_out_buf[tp_out_size==0?1:tp_out_size], *tp_out; if(tp_out_size==0) tp_out = NULL; else tp_out = tp_out_buf; /* Setup function call, e.g. struct target *tmp = libtarget_init() */ /* Call function to be fuzzed, e.g.: */ int ret = tpdkg_tp_next(tp, buf, len, tp_out, tp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); if(tp->cheater_len > 0) return 125; return ret; } while(tpdkg_tp_not_done(tp)) { for(uint8_t i=0;in;i++) { const uint8_t *msg; size_t len; if(0!=tpdkg_tp_peer_msg(tp, tp_out, tp_out_size, i, &msg, &len)) { return 1; } _send(network_buf[i+1], &pkt_len[i+1], msg, len); } while(pkt_len[0]==0 && tpdkg_peer_not_done(&peers[1])) { for(uint8_t i=0;in;i++) { // 0sized vla meh const size_t peer_out_size = tpdkg_peer_output_size(&peers[i]); uint8_t peers_out_buf[peer_out_size==0?1:peer_out_size], *peers_out; if(peer_out_size==0) peers_out = NULL; else peers_out = peers_out_buf; // 0sized vla meh for the last time.. const size_t peer_in_size = tpdkg_peer_input_size(&peers[i]); uint8_t peer_in_buf[peer_in_size==0?1:peer_in_size], *peer_in; if(peer_in_size==0) peer_in = NULL; else peer_in = peer_in_buf; _recv(network_buf[i+1], &pkt_len[i+1], peer_in, peer_in_size); ret = tpdkg_peer_next(&peers[i], peer_in, peer_in_size, peers_out, peer_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peers_out, peer_out_size); } } // doing vla - but avoiding 0 sized ones is ugly const size_t tp_out_size = tpdkg_tp_output_size(tp); uint8_t tp_out_buf[tp_out_size==0?1:tp_out_size], *tp_out; if(tp_out_size==0) tp_out = NULL; else tp_out = tp_out_buf; // avoiding zero-sized vla is still ugly const size_t tp_in_size = tpdkg_tp_input_size(tp); uint8_t tp_in_buf[tp_in_size==0?1:tp_in_size], *tp_in; if(tp_in_size==0) tp_in = NULL; else tp_in = tp_in_buf; _recv(network_buf[0], &pkt_len[0], tp_in, tp_in_size); ret = tpdkg_tp_next(tp, tp_in, tp_in_size, tp_out, tp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); if(tp->cheater_len > 0) return 55; return ret; } } /* Reset state. e.g. libtarget_free(tmp) */ memcpy(tp, &checkpoint, sizeof(TP_DKG_TPState)); memcpy(peers, &pcheckpoints, sizeof(pcheckpoints)); } return 0; } #else // !defined(FUZZ_PEER) static int fuzz_loop(const uint8_t step, TP_DKG_TPState *tp, TP_DKG_PeerState *peers, uint8_t network_buf[][NETWORK_BUF_SIZE],size_t pkt_len[]) { if(peers[0].step!=step) return 0; TP_DKG_TPState checkpoint; memcpy(&checkpoint, tp, sizeof(checkpoint)); TP_DKG_PeerState pcheckpoints[tp->n]; memcpy(&pcheckpoints, peers, sizeof(pcheckpoints)); #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT // and before __AFL_LOOP! int ret; while (__AFL_LOOP(10000)) { int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a call! if (len < sizeof(DKG_Message)) continue; // check for a required/useful minimum input length // doing vla - but avoiding 0 sized ones is ugly const size_t peer_out_size = tpdkg_peer_output_size(&peers[0]); uint8_t peer_out_buf[peer_out_size==0?1:peer_out_size], *peer_out; if(peer_out_size==0) peer_out = NULL; else peer_out = peer_out_buf; ret = tpdkg_peer_next(&peers[0], buf, len, peer_out, peer_out_size); if(ret!=0) { //for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peer_out, peer_out_size); for(uint8_t i=1;in;i++) { // 0sized vla meh const size_t peer_out_size = tpdkg_peer_output_size(&peers[i]); uint8_t peers_out_buf[peer_out_size==0?1:peer_out_size], *peers_out; if(peer_out_size==0) peers_out = NULL; else peers_out = peers_out_buf; // 0sized vla meh for the last time.. const size_t peer_in_size = tpdkg_peer_input_size(&peers[i]); uint8_t peer_in_buf[peer_in_size==0?1:peer_in_size], *peer_in; if(peer_in_size==0) peer_in = NULL; else peer_in = peer_in_buf; _recv(network_buf[i+1], &pkt_len[i+1], peer_in, peer_in_size); ret = tpdkg_peer_next(&peers[i], peer_in, peer_in_size, peers_out, peer_out_size); if(0!=ret) { // clean up peers //for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peers_out, peer_out_size); } while(tpdkg_tp_not_done(tp)) { while(pkt_len[0]==0 && tpdkg_peer_not_done(&peers[1])) { for(uint8_t i=0;in;i++) { // 0sized vla meh const size_t peer_out_size = tpdkg_peer_output_size(&peers[i]); uint8_t peers_out_buf[peer_out_size==0?1:peer_out_size], *peers_out; if(peer_out_size==0) peers_out = NULL; else peers_out = peers_out_buf; // 0sized vla meh for the last time.. const size_t peer_in_size = tpdkg_peer_input_size(&peers[i]); uint8_t peer_in_buf[peer_in_size==0?1:peer_in_size], *peer_in; if(peer_in_size==0) peer_in = NULL; else peer_in = peer_in_buf; _recv(network_buf[i+1], &pkt_len[i+1], peer_in, peer_in_size); ret = tpdkg_peer_next(&peers[i], peer_in, peer_in_size, peers_out, peer_out_size); if(0!=ret) { // clean up peers //for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); return ret; } _send(network_buf[0], &pkt_len[0], peers_out, peer_out_size); } } // doing vla - but avoiding 0 sized ones is ugly const size_t tp_out_size = tpdkg_tp_output_size(tp); uint8_t tp_out_buf[tp_out_size==0?1:tp_out_size], *tp_out; if(tp_out_size==0) tp_out = NULL; else tp_out = tp_out_buf; // avoiding zero-sized vla is still ugly const size_t tp_in_size = tpdkg_tp_input_size(tp); uint8_t tp_in_buf[tp_in_size==0?1:tp_in_size], *tp_in; if(tp_in_size==0) tp_in = NULL; else tp_in = tp_in_buf; _recv(network_buf[0], &pkt_len[0], tp_in, tp_in_size); ret = tpdkg_tp_next(tp, tp_in, tp_in_size, tp_out, tp_out_size); if(0!=ret) { // clean up peers for(uint8_t i=0;in;i++) tpdkg_peer_free(&peers[i]); if(tp->cheater_len > 0) return 55; return ret; } for(uint8_t i=0;in;i++) { const uint8_t *msg; size_t len; if(0!=tpdkg_tp_peer_msg(tp, tp_out, tp_out_size, i, &msg, &len)) { return 1; } _send(network_buf[i+1], &pkt_len[i+1], msg, len); } } /* Reset state. e.g. libtarget_free(tmp) */ memcpy(tp, &checkpoint, sizeof(TP_DKG_TPState)); memcpy(peers, &pcheckpoints, sizeof(pcheckpoints)); } return 0; } #endif #endif int main(const int argc, const char **argv) { int ret; // enable logging liboprf_log_file = stderr; liboprf_debug = 1; if(argc<3) { #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) fprintf(stderr, "error incorrect numbers of parameters, run as: %% %s [ []]\n", argv[0]); #else fprintf(stderr, "error incorrect numbers of parameters, run as: %% %s \n", argv[0]); #endif // defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) exit(1); } uint8_t n=(uint8_t)atoi(argv[1]),t=(uint8_t)atoi(argv[2]); #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) || defined(FUZZ_DUMP) uint8_t step=atoi(argv[3]); #ifdef FUZZ_PEER if(step<1 || step > 9 || step==7 || step==8) { #else if(step<1 || step > 9) { #endif fprintf(stderr, "error incorrect value for step must be 1-9 (but not 7 or 8), run as: %% %s <1-7> \n", argv[0]); exit(1); } #endif // mock long-term peer keys // all known by TP uint8_t peer_lt_pks[n][crypto_sign_PUBLICKEYBYTES]; // only known by corresponding peer uint8_t peer_lt_sks[n][crypto_sign_SECRETKEYBYTES]; for(uint8_t i=0;i 0) break; return ret; } for(uint8_t i=0;i n) return 1; if(tmp[p]==0) total_cheaters++; tmp[p]++; } fprintf(stderr, "\e[0;31m:/ dkg failed, total cheaters %d, list of cheaters:", total_cheaters); for(int i=1;i<=n;i++) { if(tmp[i]==0) continue; fprintf(stderr," %d(%d)", i, tmp[i]); } fprintf(stderr, "\e[0m\n"); return 1; } // clean up peers for(uint8_t i=0;i #include #include #include "../dkg-vss.h" #include "../utils.h" #include "../mpmult.h" #include "../toprf.h" static uint8_t dkg_vss_verify_commitments(const uint8_t n, const uint8_t self, const uint8_t commitments[n][n][crypto_core_ristretto255_BYTES], const TOPRF_Share shares[n][2], uint8_t complaints[n]) { uint8_t complaints_len=0; for(uint8_t i=1;i<=n;i++) { if(i==self) continue; if(0!=dkg_vss_verify_commitment(commitments[i-1][self-1], shares[i-1])) { // complain about P_i fprintf(stderr, "\x1b[0;31mfailed to verify contribs of P_%d in stage 1\x1b[0m\n", i); complaints[complaints_len++]=i; //return 1; } else { #ifdef UNIT_TEST if(liboprf_debug) fprintf(stderr, "\x1b[0;32mP_%d stage 1 correct!\x1b[0m\n", i); #endif // UNIT_TEST } } return complaints_len; } int dkg_vss(const uint8_t n, const uint8_t t, TOPRF_Share final_shares[n][2], uint8_t commitments[n][crypto_core_ristretto255_BYTES]) { uint8_t dealer_commitments[n][n][crypto_core_ristretto255_BYTES]; TOPRF_Share shares[n][n][2]; for(int i=0;i0) { // todo accused dealer P_i publishes Ξ±_i, ρ_i such that A_i = 𝓗(Ξ±_i,ρ_i) // if dealer P_i fails, disqualify them. // otherwise the accuser sets their shares to Ξ±_i, ρ_i return 1; } // todo handle complaints, build qual set uint8_t qual[n+1]; for(int i=0;i //htons #include "utils.h" #include "toprf-update.h" #include "dkg-vss.h" #include "mpmult.h" #ifndef HAVE_SODIUM_HKDF #include "aux_/crypto_kdf_hkdf_sha256.h" #endif #ifdef __ZEPHYR__ #include #endif // todo handle adding new peers who don't have a share of kc // todo handle random order of peers - related to prev todo // todo revert to non-fast-track mult to catch the case when dealer // deals something else than Ξ»_iΞ±_iΞ²_i but 𝓒_i0 is based on the // correct value, so the ZK proof does not fail. // todo add toprf_update_(stp|peer)_cheater_msg() #ifdef UNITTEST_CORRUPT static void corrupt_ci0_good_ci(const uint8_t peer, uint8_t commitments[][crypto_core_ristretto255_BYTES]) { // this corruption does not influence the outcome of the protocol // it merely fails the zkp *and* the vsps, but the end result is correct! uint8_t secret[crypto_core_ristretto255_SCALARBYTES]; crypto_core_ristretto255_scalar_random(secret); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting C_i0 Ξ»_iΞ±_iΞ²_i %d\n"NORMAL, peer); dkg_vss_commit(secret,secret,commitments[0]); } /// deals shares with polynomial t+1 instead of 1 static void corrupt_vsps_t1(const TOPRF_Update_PeerState *ctx, const uint8_t peer, TOPRF_Share (*shares)[][2], uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]) { if(ctx->index!=peer) return; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting with wrong degree of the polynom peer %d\n"NORMAL, peer); (void)dkg_vss_share(ctx->n, ctx->t+1, NULL, (*commitments), (*shares), NULL); } static void corrupt_mult_vsps_t1(TOPRF_Update_PeerState *ctx, const uint8_t peer) { if(ctx->index!=peer) return; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] !!! Corrupting mult sharing with degree t+1 polynomial\n"NORMAL, peer); const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); (void)toprf_mpc_ftmult_step1(dealers, ctx->n, ctx->t+1, ctx->index-1, ctx->kc0_share, ctx->p_share, (*ctx->lambdas), // we reuse p_shares as we need to store n shares, and k0p_shares has only dealer entries (*ctx->p_shares), (*ctx->k0p_commitments), ctx->k0p_tau); } static void corrupt_commitment(TOPRF_Update_PeerState *ctx, const uint8_t peer, uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]) { // corrupts the 1st commitment with the 2nd if(ctx->index!=peer) return; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting commitment of peer %d\n"NORMAL, peer); memcpy((*commitments)[2], (*commitments)[1], crypto_core_ristretto255_BYTES); } static void corrupt_wrongshare_correct_commitment(TOPRF_Update_PeerState *ctx, // swaps the share and it's blinder, const uint8_t peer, // recalculates commitment const uint8_t share_idx, TOPRF_Share (*shares)[][2], uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]) { if(ctx->index!=peer) return; TOPRF_Share tmp; // swap shares memcpy(&tmp, &(*shares)[share_idx][0], sizeof tmp); memcpy(&(*shares)[share_idx][0], &(*shares)[share_idx][1], sizeof tmp); memcpy(&(*shares)[share_idx][1], &tmp, sizeof tmp); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting share (but correct commitment) of peer %d\n"NORMAL, peer); dkg_vss_commit((*shares)[share_idx][0].value,(*shares)[share_idx][1].value,(*commitments)[share_idx]); } static void corrupt_share(TOPRF_Update_PeerState *ctx, const uint8_t peer, const uint8_t share_idx, const uint8_t share_type, TOPRF_Share (*shares)[][2]) { if(ctx->index!=peer) return; if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, RED"!!! Corrupting share of peer %d\n"NORMAL, peer); dump((uint8_t*) (*shares)[share_idx], TOPRF_Share_BYTES * 2, "correct share"); } (*shares)[share_idx][share_type].value[2]^=0xff; // flip some bits if(liboprf_log_file!=NULL) { dump((uint8_t*) (*shares)[share_idx], TOPRF_Share_BYTES * 2, "corrupt share"); } } static void corrupt_false_accuse(TOPRF_Update_PeerState *ctx, const uint8_t peer, const uint8_t p2, uint8_t *fails_len, uint8_t *fails) { if(ctx->index!=peer) return; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"!!! Corrupting falsely accusing peer %d by peer %d\n"NORMAL, p2, peer); fails[(*fails_len)++]=p2; } #endif // UNITTEST_CORRUPT size_t toprf_update_peerstate_size(void) { return sizeof(TOPRF_Update_PeerState); } uint8_t toprf_update_peerstate_n(const TOPRF_Update_PeerState *ctx) { return ctx->n; } uint8_t toprf_update_peerstate_t(const TOPRF_Update_PeerState *ctx) { return ctx->t; } const uint8_t* toprf_update_peerstate_sessionid(const TOPRF_Update_PeerState *ctx) { return ctx->sessionid; } const uint8_t* toprf_update_peerstate_share(const TOPRF_Update_PeerState *ctx) { if(toprf_update_peer_not_done(ctx)) return NULL; return (const uint8_t*) &ctx->k0p_share; } const uint8_t* toprf_update_peerstate_commitments(const TOPRF_Update_PeerState *ctx) { if(toprf_update_peer_not_done(ctx)) return NULL; return (const uint8_t*) (*ctx->p_commitments); } const uint8_t* toprf_update_peerstate_commitment(const TOPRF_Update_PeerState *ctx) { if(toprf_update_peer_not_done(ctx)) return NULL; return (const uint8_t*) ctx->k0p_commitment; } int toprf_update_peerstate_step(const TOPRF_Update_PeerState *ctx) { return ctx->step; } size_t toprf_update_stpstate_size(void) { return sizeof(TOPRF_Update_STPState); } uint8_t toprf_update_stpstate_n(const TOPRF_Update_STPState *ctx) { return ctx->n; } uint8_t toprf_update_stpstate_t(const TOPRF_Update_STPState *ctx) { return ctx->t; } size_t toprf_update_stpstate_cheater_len(const TOPRF_Update_STPState *ctx) { return ctx->cheater_len; } const uint8_t* toprf_update_stpstate_sessionid(const TOPRF_Update_STPState *ctx) { return ctx->sessionid; } const uint8_t* toprf_update_stpstate_delta(const TOPRF_Update_STPState *ctx) { if(toprf_update_stp_not_done(ctx)) return NULL; return ctx->delta; } const uint8_t* toprf_update_stpstate_commitments(const TOPRF_Update_STPState *ctx) { if(toprf_update_stp_not_done(ctx)) return NULL; return (const uint8_t*) (*ctx->k0p_final_commitments); } int toprf_update_stpstate_step(const TOPRF_Update_STPState *ctx) { return ctx->step; } static int toprf_send_msg(uint8_t* msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_sk, const uint8_t sessionid[dkg_sessionid_SIZE]) { int ret = send_msg(msg_buf, msg_buf_len, MSG_TYPE_SEMI_TRUSTED | MSG_TYPE_UPDATE, 0, msgno, from, to, sig_sk, sessionid); //dkg_dump_msg(msg_buf, msg_buf_len, from); return ret; } static int toprf_recv_msg(const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_pk, const uint8_t sessionid[dkg_sessionid_SIZE], const uint64_t ts_epsilon, uint64_t *last_ts) { return recv_msg(msg_buf, msg_buf_len, MSG_TYPE_SEMI_TRUSTED | MSG_TYPE_UPDATE, 0, msgno, from, to, sig_pk, sessionid, ts_epsilon, last_ts); } static void set_cheater(TOPRF_Update_Cheater *cheater, const int step, const int error, const uint8_t peer, const uint8_t other_peer) { cheater->step = step; cheater->error = error; cheater->peer = peer; cheater->other_peer=other_peer; } static TOPRF_Update_Cheater* stp_add_cheater(TOPRF_Update_STPState *ctx, const int error, const uint8_t peer, const uint8_t other_peer) { if(ctx->cheater_len >= ctx->cheater_max) return NULL; TOPRF_Update_Cheater *cheater = &(*ctx->cheaters)[ctx->cheater_len++]; set_cheater(cheater, ctx->step, error, peer, other_peer); return cheater; } static TOPRF_Update_Cheater* peer_add_cheater(TOPRF_Update_PeerState *ctx,const int error, const uint8_t peer, const uint8_t other_peer) { if(ctx->cheater_len >= ctx->cheater_max) return NULL; TOPRF_Update_Cheater *cheater = &(*ctx->cheaters)[ctx->cheater_len++]; set_cheater(cheater, ctx->step, error, peer, other_peer); return cheater; } static unsigned isdealer(const uint8_t i, const uint8_t t) { return i <= ((t-1)*2 + 1); } static int stp_recv_msg(TOPRF_Update_STPState *ctx, const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to) { //dkg_dump_msg(msg_buf, msg_buf_len, 0); int ret = toprf_recv_msg(msg_buf, msg_buf_len, msgno, from, to, (*ctx->sig_pks)[from], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[from-1]); if(0!=ret) { if(stp_add_cheater(ctx, 64+ret, from, to) == NULL) return TOPRF_Update_Err_CheatersFull; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"failed to validate msg %d from %d, err: %d\n"NORMAL, msgno, from, ret); return 1; } return 0; } static int peer_recv_msg(TOPRF_Update_PeerState *ctx, const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to) { //dkg_dump_msg(msg_buf, msg_buf_len, ctx->index); int ret = toprf_recv_msg(msg_buf, msg_buf_len, msgno, from, to, (*ctx->sig_pks)[from], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[from-1]); if(0!=ret) { if(peer_add_cheater(ctx, 64+ret, from, to) == NULL) return TOPRF_Update_Err_CheatersFull; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed to validate msg %d from %d, err: %d\n"NORMAL, ctx->index, msgno, from, ret); return 1; } return 0; } static TOPRF_Update_Err stp_broadcast(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, const char *step_title, const uint8_t msg_count, // usually n, sometimes dealers const size_t msg_size, const uint8_t msgno, const TOPRF_Update_STP_Steps next_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] %s\x1b[0m\n", step_title); if(msg_count * msg_size != input_len) return TOPRF_Update_Err_ISize; const size_t cheaters = ctx->cheater_len; if(sizeof(TOPRF_Update_Message) + input_len != output_len) return TOPRF_Update_Err_OSize; const uint8_t *ptr = input; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; for(uint8_t i=0;icheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(0!=toprf_send_msg(output, output_len, msgno+1, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; // add broadcast msg to transcript update_transcript(&ctx->transcript_state, output, output_len); ctx->step = next_step; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_route(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, const char *step_title, const uint8_t send_count, const uint8_t recv_count, const uint8_t msgno, const size_t msg_size, const TOPRF_Update_STP_Steps next_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] %s\x1b[0m\n", step_title); if(input_len != msg_size * send_count * recv_count) return TOPRF_Update_Err_ISize; if(input_len != output_len) return TOPRF_Update_Err_OSize; //const size_t cheaters = ctx->cheater_len; const uint8_t (*inputs)[send_count][recv_count][msg_size] = (const uint8_t (*)[send_count][recv_count][msg_size]) input; uint8_t *wptr = output; for(uint8_t i=0;isig_pks)[j+1], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[j]); if(0!=ret) { if(stp_add_cheater(ctx, 64+ret, j+1, i+1) == NULL) return TOPRF_Update_Err_CheatersFull; const TOPRF_Update_Message *msg = (const TOPRF_Update_Message*) (*inputs)[j][i]; fprintf(liboprf_log_file,"[x] msgno: %d, from: %d to: %d ", msg->msgno, msg->from, msg->to); dump((*inputs)[j][i], msg_size, "msg"); continue; } memcpy(wptr, (*inputs)[j][i], msg_size); wptr+=msg_size; } } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; ctx->step = next_step; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err unwrap_envelope(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, const uint8_t msgno, const uint8_t **contents) { // verify STP message envelope const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) input; //dkg_dump_msg(input, input_len, ctx->index); int ret = toprf_recv_msg(input, input_len, msgno, 0, 0xff, (*ctx->sig_pks)[0], ctx->sessionid, ctx->ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return TOPRF_Update_Err_BroadcastEnv+ret; // add broadcast msg to transcript update_transcript(&ctx->transcript_state, input, input_len); *contents = msg->data; return TOPRF_Update_Err_OK; } static void handle_complaints(const uint8_t n, const uint8_t accuser, const uint8_t fails_len, const uint8_t fails[], uint16_t *ctx_complaints_len, uint16_t *ctx_complaints, const uint8_t self, uint8_t *ctx_my_complaints_len, uint8_t *ctx_my_complaints) { // keep a copy all complaint pairs (complainer, complained) for(unsigned k=0;k n || fails[k] < 1) { //fails[k] has an invalid peer idx value. // todo cheater handling //if(stp_add_cheater(ctx, 7, i+1, msg->data[k+1]) == NULL) return 6; continue; } uint16_t pair=(uint16_t) ((accuser<<8) | fails[k]); int j=0; for(j=0;j<*ctx_complaints_len;j++) if(ctx_complaints[j]==pair) break; if(j<*ctx_complaints_len) { //already seen this accuser/accused pair. // todo cheater handling //if(stp_add_cheater(ctx, 18, 8, i+1, msg->data[k+1]) == NULL) return 6; continue; } ctx_complaints[(*ctx_complaints_len)++] = pair; if(self!=0 && fails[k] == self && ctx_my_complaints_len != NULL && ctx_my_complaints != NULL) { ctx_my_complaints[(*ctx_my_complaints_len)++] = accuser; } if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[%d] peer %d failed to verify commitments from peer %d!\x1b[0m\n", self, accuser, fails[k]); } } } static TOPRF_Update_Err stp_complaint_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len, const char* step_title, const uint8_t msg_count, const size_t msg_size, const uint8_t msgno, const uint8_t dealers, const TOPRF_Update_STP_Steps pass_step, const TOPRF_Update_STP_Steps fail_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] %s\x1b[0m\n", step_title); if(input_len != msg_size * msg_count) return TOPRF_Update_Err_ISize; if(sizeof(TOPRF_Update_Message) + input_len != output_len) return TOPRF_Update_Err_OSize; //const size_t cheaters = ctx->cheater_len; ctx->p_complaints_len = 0; const uint8_t *ptr = input; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; for(uint8_t i=0;ilen) - sizeof(TOPRF_Update_Message) < msg->data[0]) return TOPRF_Update_Err_OOB; const uint8_t *fails_len = msg->data; const uint8_t *fails = msg->data+1; handle_complaints(msg_count, i+1, *fails_len, fails, &ctx->p_complaints_len, ctx->p_complaints, 0, 0, 0); memcpy(wptr, ptr, msg_size); wptr+=msg_size; } // if more than t^2 complaints are received the protocol also fails if(ctx->p_complaints_len >= ctx->t * ctx->t) { if(stp_add_cheater(ctx, 6, 0xfe, 0xfe) == NULL) return TOPRF_Update_Err_CheatersFull; return TOPRF_Update_Err_TooManyCheaters; } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(0!=toprf_send_msg(output, output_len, msgno+1, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; // add broadcast msg to transcript update_transcript(&ctx->transcript_state, output, output_len); ctx->prev = ctx->step; if(ctx->p_complaints_len == 0) { ctx->step = pass_step; } else { dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*sizeof(uint16_t), "[!] complaints_2"); ctx->step = fail_step; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_complaint_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, const char *step_title, const size_t msg_size, const uint8_t msgno, const uint8_t dealers, const TOPRF_Update_Peer_Steps pass_step, const TOPRF_Update_Peer_Steps fail_step) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] %s\x1b[0m\n", ctx->index, step_title); if(input_len != sizeof(TOPRF_Update_Message) + msg_size * ctx->n) return TOPRF_Update_Err_ISize; //const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,msgno+1,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; for(uint8_t i=0;in;i++, ptr+=msg_size) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,msg_size,msgno,i+1,0xff)) continue; if(ntohl(msg->len) - sizeof(TOPRF_Update_Message) < msg->data[0]) return TOPRF_Update_Err_OOB; const uint8_t *fails_len = msg->data; const uint8_t *fails = msg->data+1; handle_complaints(ctx->n, i+1, *fails_len, fails, &ctx->p_complaints_len, ctx->p_complaints, ctx->index, &ctx->my_p_complaints_len, ctx->my_p_complaints); } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; ctx->prev = ctx->step; if(ctx->p_complaints_len == 0) { ctx->step = pass_step; } else { dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*sizeof(uint16_t), "[!] complaints_2"); ctx->step = fail_step; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err ft_or_full_vsps(const uint8_t n, const uint8_t t, const uint8_t dealers, const uint8_t self, const uint8_t C_i[n][crypto_core_ristretto255_BYTES], const uint8_t (*C_ij)[dealers][n][crypto_core_ristretto255_BYTES], const char *ft_msg, const char *sub_msg, const char *no_sub_msg, uint8_t *fails_len, uint8_t fails[dealers]) { //fprintf(stderr,"asdf %d %d %d %d\n", n, t, dealers, self); //for(unsigned i=0;in || t>=n || n>128 || n<2*t+1) return 1; if(proto_name_len<1) return 2; if(proto_name_len>1024) return 3; if(msg0_len != toprfupdate_stp_start_msg_SIZE) return 4; ctx->ts_epsilon = ts_epsilon; ctx->step = TOPRF_Update_STP_Broadcast_NPKs; ctx->n = n; ctx->t = t; ctx->p_complaints_len = 0; ctx->y2_complaints_len = 0; ctx->cheater_len = 0; // dst hash(len(protoname) | "TOPRF Update for protocol " | protoname | n | t) crypto_generichash_state dst_state; crypto_generichash_init(&dst_state, NULL, 0, crypto_generichash_BYTES); uint16_t len=htons((uint16_t) proto_name_len+20); // we have a guard above restricting to 1KB the proto_name_len crypto_generichash_update(&dst_state, (uint8_t*) &len, 2); crypto_generichash_update(&dst_state, (const uint8_t*) "TOPRF Update for protocol ", 26); crypto_generichash_update(&dst_state, (const uint8_t*) proto_name, proto_name_len); crypto_generichash_update(&dst_state, &n, 1); crypto_generichash_update(&dst_state, &t, 1); uint8_t dst[crypto_generichash_BYTES]; crypto_generichash_final(&dst_state,dst,sizeof dst); // set sessionid nonce, we abuse this session_id field in the state // to temporarily store the session id nonce; which will later // become the real session_id after the other peers also contributed // their nonces randombytes_buf(&ctx->sessionid, sizeof ctx->sessionid); // a list of all long-term pubkeys ctx->sig_pks = sig_pks; // keep a copy of our long-term signing key memcpy(ctx->sig_sk, ltssk, crypto_sign_SECRETKEYBYTES); // data = {stp_lt_pks, dst, keyid} uint8_t *ptr = msg0->data; memcpy(ptr, (*sig_pks)[0], crypto_sign_PUBLICKEYBYTES); ptr+=crypto_sign_PUBLICKEYBYTES; memcpy(ptr, dst, sizeof dst); ptr+=sizeof dst; memcpy(ptr, keyid, toprf_keyid_SIZE); if(0!=toprf_send_msg((uint8_t*) msg0, toprfupdate_stp_start_msg_SIZE, toprfupdate_stp_start_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 5; // init transcript crypto_generichash_init(&ctx->transcript_state, NULL, 0, crypto_generichash_BYTES); crypto_generichash_update(&ctx->transcript_state, (const uint8_t*) "toprf update session transcript", 31); // feed msg0 into transcript update_transcript(&ctx->transcript_state, (uint8_t*) msg0, msg0_len); return 0; } void toprf_update_stp_set_bufs(TOPRF_Update_STPState *ctx, uint16_t p_complaints[], uint16_t y2_complaints[], TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES], uint64_t *last_ts) { ctx->p_complaints = p_complaints; memset(ctx->p_complaints, 0, sizeof(uint16_t) * ctx->n*ctx->n); ctx->y2_complaints = y2_complaints; memset(ctx->y2_complaints, 0, sizeof(uint16_t) * ctx->n*ctx->n); ctx->cheaters = cheaters; memset(*cheaters, 0, cheater_max*sizeof(TOPRF_Update_Cheater)); ctx->cheater_max = cheater_max; ctx->last_ts = last_ts; ctx->p_commitments_hashes = p_commitments_hashes; ctx->p_share_macs = p_share_macs; ctx->p_commitments = p_commitments; ctx->kc0_commitments = kc0_commitments; ctx->k0p_commitments = k0p_commitments; ctx->zk_challenge_commitments = zk_challenge_commitments; ctx->zk_challenge_e_i = zk_challenge_e_i; ctx->k0p_final_commitments = k0p_final_commitments; #ifdef __ZEPHYR__ uint64_t now = (uint64_t) k_uptime_get(); #else uint64_t now = (uint64_t)time(NULL); #endif for(uint8_t i=0;in;i++) ctx->last_ts[i]=now; } TOPRF_Update_Err toprf_update_start_peer(TOPRF_Update_PeerState *ctx, const uint64_t ts_epsilon, const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], const uint8_t noise_sk[crypto_scalarmult_SCALARBYTES], const TOPRF_Update_Message *msg0, uint8_t keyid[toprf_keyid_SIZE], uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[?] init1 start peer\x1b[0m\n"); //dkg_dump_msg((const uint8_t*) msg0, toprfupdate_stp_start_msg_SIZE, msg0->from); ctx->ts_epsilon = ts_epsilon; ctx->stp_last_ts = 0; int ret = toprf_recv_msg((const uint8_t*) msg0, toprfupdate_stp_start_msg_SIZE, toprfupdate_stp_start_msg, 0, 0xff, msg0->data, msg0->sessionid, ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return TOPRF_Update_Err_Env+ ret; // extract data from message // we abuse sessionid as a temporary storage for the nonce_stp value, until we have the final sessionid memcpy(ctx->sessionid, msg0->sessionid, sizeof ctx->sessionid); const uint8_t *ptr=msg0->data; memcpy(stp_ltpk,ptr,crypto_sign_PUBLICKEYBYTES); ptr+=crypto_sign_PUBLICKEYBYTES + crypto_generichash_BYTES; // also skip DST memcpy(keyid,ptr,toprf_keyid_SIZE); ctx->p_complaints_len = 0; ctx->my_p_complaints_len = 0; ctx->cheater_len = 0; memcpy(ctx->sig_sk, lt_sk, crypto_sign_SECRETKEYBYTES); memcpy(ctx->noise_sk, noise_sk, crypto_scalarmult_SCALARBYTES); crypto_generichash_init(&ctx->transcript_state, NULL, 0, crypto_generichash_BYTES); crypto_generichash_update(&ctx->transcript_state, (const uint8_t*) "toprf update session transcript", 31); // feed msg0 into transcript update_transcript(&ctx->transcript_state, (const uint8_t*) msg0, toprfupdate_stp_start_msg_SIZE); ctx->dev = NULL; ctx->step = TOPRF_Update_Peer_Broadcast_NPK_SIDNonce; return TOPRF_Update_Err_OK; } int toprf_update_peer_set_bufs(TOPRF_Update_PeerState *ctx, const uint8_t self, const uint8_t n, const uint8_t t, const TOPRF_Share k0[2], uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peer_noise_pks)[][crypto_scalarmult_BYTES], Noise_XK_session_t *(*noise_outs)[], Noise_XK_session_t *(*noise_ins)[], TOPRF_Share (*p_shares)[][2], uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE], TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES], TOPRF_Share (*k0p_shares)[][2], uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES], uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], uint16_t *p_complaints, uint8_t *my_p_complaints, uint64_t *last_ts) { if(2>n || t>=n || n>128 || n<2*t+1) return 1; ctx->index = self; ctx->n = n; ctx->t = t; memcpy((uint8_t*) ctx->kc0_share, (const uint8_t*) k0, sizeof(TOPRF_Share)*2); ctx->kc0_commitments = kc0_commitments; ctx->sig_pks = sig_pks; ctx->peer_noise_pks = peer_noise_pks; ctx->noise_outs = noise_outs; ctx->noise_ins = noise_ins; ctx->p_shares = p_shares; ctx->p_commitments = p_commitments; ctx->p_commitments_hashes = p_commitments_hashes; ctx->p_share_macs = p_share_macs; ctx->encrypted_shares = encrypted_shares; ctx->lambdas = lambdas; ctx->k0p_shares = k0p_shares; ctx->k0p_commitments = k0p_commitments; ctx->zk_challenge_nonce_commitments = zk_challenge_nonce_commitments; ctx->zk_challenge_nonces = zk_challenge_nonces; ctx->zk_challenge_commitments = zk_challenge_commitments; ctx->zk_challenge_e_i = zk_challenge_e_i; ctx->p_complaints = p_complaints; memset(ctx->p_complaints, 0, sizeof(uint16_t) * n); ctx->my_p_complaints = my_p_complaints; memset(ctx->my_p_complaints, 0, n); ctx->cheaters = cheaters; memset(cheaters,0,sizeof(TOPRF_Update_Cheater)*cheater_max); ctx->cheater_max = cheater_max; ctx->last_ts = last_ts; for(uint8_t i=0;in;i++) ctx->last_ts[i]=0; return 0; } #define toprfupdate_peer_init_msg_SIZE (sizeof(TOPRF_Update_Message) + dkg_sessionid_SIZE + crypto_core_ristretto255_BYTES) static TOPRF_Update_Err peer_step1_handler(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] init2 send msg1 containing session id nonce\x1b[0m\n", ctx->index); if(output_len != toprfupdate_peer_init_msg_SIZE) return TOPRF_Update_Err_OSize; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; randombytes_buf(wptr, dkg_sessionid_SIZE); wptr+=dkg_sessionid_SIZE; if(0!=dkg_vss_commit(ctx->kc0_share[0].value, ctx->kc0_share[1].value,wptr)) return TOPRF_Update_Err_VSSCommit; if(0!=toprf_send_msg(output, toprfupdate_peer_init_msg_SIZE, toprfupdate_peer_init_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Rcv_NPK_SIDNonce; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step2_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] init3 broadcast msg1 containing session id nonces of peers\x1b[0m\n"); if(input_len != toprfupdate_peer_init_msg_SIZE * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_init_msg_SIZE * ctx->n + sizeof(TOPRF_Update_Message)) return TOPRF_Update_Err_OSize; crypto_generichash_state sid_state; crypto_generichash_init(&sid_state, NULL, 0, dkg_sessionid_SIZE); crypto_generichash_update(&sid_state, ctx->sessionid, dkg_sessionid_SIZE); const uint8_t *ptr = input; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_init_msg_SIZE) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; if(stp_recv_msg(ctx,ptr,toprfupdate_peer_init_msg_SIZE,toprfupdate_peer_init_msg,i+1,0xff)) continue; const uint8_t *dptr = msg->data; crypto_generichash_update(&sid_state, dptr, dkg_sessionid_SIZE); dptr+=dkg_sessionid_SIZE; memcpy((*ctx->kc0_commitments)[i], dptr, crypto_core_ristretto255_BYTES); memcpy(wptr, ptr, toprfupdate_peer_init_msg_SIZE); wptr+=toprfupdate_peer_init_msg_SIZE; } if(ctx->cheater_len>0) return TOPRF_Update_Err_CheatersFound; crypto_generichash_final(&sid_state,ctx->sessionid,sizeof ctx->sessionid); if(0!=toprf_send_msg(output, output_len, toprfupdate_stp_bc_init_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; update_transcript(&ctx->transcript_state, output, output_len); ctx->step = TOPRF_Update_STP_Route_Noise_Handshakes1; return TOPRF_Update_Err_OK; } #define toprfupdate_peer_ake1_msg_SIZE (sizeof(TOPRF_Update_Message) + noise_xk_handshake1_SIZE) static TOPRF_Update_Err peer_step3_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] noise1 receive peers session nonces, finalize sessionid, start noise sessions\x1b[0m\n", ctx->index); if(input_len != toprfupdate_peer_init_msg_SIZE * ctx->n + sizeof(TOPRF_Update_Message)) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_ake1_msg_SIZE * ctx->n) return TOPRF_Update_Err_OSize; const TOPRF_Update_Message* msg2 = (const TOPRF_Update_Message*) input; int ret = toprf_recv_msg(input, input_len, toprfupdate_stp_bc_init_msg, 0, 0xff, (*ctx->sig_pks)[0], msg2->sessionid, ctx->ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return TOPRF_Update_Err_BroadcastEnv+ret; update_transcript(&ctx->transcript_state, input, input_len); // create noise device uint8_t iname[15]; snprintf((char*) iname, sizeof iname, "toprf peer %02x", ctx->index); uint8_t dummy[32]={0}; // the following function needs a deserialization key, which we never use. ctx->dev = Noise_XK_device_create(13, (uint8_t*) "toprf p2p v0.1", iname, dummy, ctx->noise_sk); crypto_generichash_state sid_state; crypto_generichash_init(&sid_state, NULL, 0, dkg_sessionid_SIZE); crypto_generichash_update(&sid_state, ctx->sessionid, dkg_sessionid_SIZE); const uint8_t *ptr = msg2->data; for(uint8_t i=0;in;i++, ptr+=toprfupdate_peer_init_msg_SIZE) { const TOPRF_Update_Message* msg1 = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_init_msg_SIZE,toprfupdate_peer_init_msg,i+1,0xff)) continue; const uint8_t *dptr = msg1->data; // extract peer sig and noise pk crypto_generichash_update(&sid_state, dptr, dkg_sessionid_SIZE); dptr+=dkg_sessionid_SIZE; if(memcmp(dptr, (*ctx->kc0_commitments)[i], crypto_core_ristretto255_BYTES)!=0) { return TOPRF_Update_Err_CommmitmentsMismatch; } } if(ctx->cheater_len>0) return TOPRF_Update_Err_CheatersFound; crypto_generichash_final(&sid_state,ctx->sessionid,sizeof ctx->sessionid); if(memcmp(ctx->sessionid, msg2->sessionid, dkg_sessionid_SIZE)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "invalid sessionid generated\n"); return TOPRF_Update_Err_InvSessionID; } uint8_t *wptr = output; for(uint8_t i=0;in;i++, wptr+=toprfupdate_peer_ake1_msg_SIZE) { TOPRF_Update_Message *msg3 = (TOPRF_Update_Message *) wptr; uint8_t rname[15]; snprintf((char*) rname, sizeof rname, "toprf peer %02x", i+1); if(0!=dkg_init_noise_handshake(ctx->index, ctx->dev, (*ctx->peer_noise_pks)[i], rname, &(*ctx->noise_outs)[i], msg3->data)) return TOPRF_Update_Err_Noise; if(0!=toprf_send_msg(wptr, toprfupdate_peer_ake1_msg_SIZE, toprfupdate_peer_ake1_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; } ctx->step = TOPRF_Update_Peer_Noise_Handshake; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step4_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_route(ctx, input, input_len, output, output_len, "noise2 route p2p noise handshakes to peers", ctx->n, ctx->n, toprfupdate_peer_ake1_msg, toprfupdate_peer_ake1_msg_SIZE, TOPRF_Update_STP_Route_Noise_Handshakes2); } #define toprfupdate_peer_ake2_msg_SIZE (sizeof(TOPRF_Update_Message) + noise_xk_handshake2_SIZE) static TOPRF_Update_Err peer_step5_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] noise3 receive session requests\x1b[0m\n", ctx->index); if(input_len != toprfupdate_peer_ake1_msg_SIZE * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_ake2_msg_SIZE * ctx->n) return TOPRF_Update_Err_OSize; const uint8_t *ptr = input; uint8_t *wptr = output; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_ake1_msg_SIZE,wptr+=toprfupdate_peer_ake2_msg_SIZE) { TOPRF_Update_Message* msg3 = (TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_ake1_msg_SIZE,toprfupdate_peer_ake1_msg,i+1,ctx->index)) continue; // respond to noise handshake request TOPRF_Update_Message *msg4 = (TOPRF_Update_Message *) wptr; uint8_t rname[15]; snprintf((char*) rname, sizeof rname, "toprf peer %02x", i+1); if(0!=dkg_respond_noise_handshake(ctx->index, ctx->dev, rname, &(*ctx->noise_ins)[i], msg3->data, msg4->data)) return TOPRF_Update_Err_Noise; if(0!=toprf_send_msg(wptr, toprfupdate_peer_ake2_msg_SIZE, toprfupdate_peer_ake2_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; } if(ctx->cheater_len>0) return TOPRF_Update_Err_CheatersFound; ctx->step=TOPRF_Update_Peer_Finish_Noise_Handshake; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step6_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_route(ctx, input, input_len, output, output_len, "noise4 route p2p noise handshakes to peers", ctx->n, ctx->n, toprfupdate_peer_ake2_msg, toprfupdate_peer_ake2_msg_SIZE, TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments); } static void hash_commitments(const TOPRF_Update_PeerState *ctx, const uint8_t c_len, const uint8_t commitments[c_len][crypto_core_ristretto255_BYTES], uint8_t **wptr) { crypto_generichash(*wptr, toprf_update_commitment_HASHBYTES, (uint8_t*) commitments, crypto_core_ristretto255_BYTES*c_len, NULL, 0); if(liboprf_log_file!=NULL) { dump(*wptr, toprf_update_commitment_HASHBYTES, "[%d] commitment hash", ctx->index); dump((uint8_t*) commitments, crypto_core_ristretto255_BYTES*c_len, "[%d] committed", ctx->index); } *wptr+=toprf_update_commitment_HASHBYTES; } static TOPRF_Update_Err dkg1(TOPRF_Update_PeerState *ctx, const uint8_t n, const char *type, const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], TOPRF_Share shares[n][2], uint8_t commitments[n][crypto_core_ristretto255_BYTES], uint8_t **wptr) { // start DKG if(dkg_vss_share(ctx->n, ctx->t, secret, commitments, shares, NULL)) { return TOPRF_Update_Err_VSSShare; } #ifdef UNITTEST_CORRUPT corrupt_vsps_t1(ctx,1, ctx->p_shares, ctx->p_commitments); //corrupt_commitment(ctx,2,ctx->p_commitments); //corrupt_commitment(ctx,3,ctx->p_commitments); //corrupt_wrongshare_correct_commitment(ctx,4,2,ctx->p_shares,ctx->p_commitments); //corrupt_share(ctx,5,3,1,ctx->p_shares); //corrupt_share(ctx,5,2,0,ctx->p_shares); #endif // UNITTEST_CORRUPT if(liboprf_log_file!=NULL) { dump((const uint8_t*) commitments, crypto_core_ristretto255_BYTES*ctx->n, "[%d] dealer %s commitments", ctx->index, type); } hash_commitments(ctx,ctx->n,commitments,wptr); return TOPRF_Update_Err_OK; } static void derive_key(const Noise_XK_session_t *noise_session, const uint8_t i, const char *type, uint8_t key[crypto_auth_KEYBYTES]) { const uint8_t *mk = Noise_XK_session_get_key(noise_session); char kdf_context[64]; size_t context_len = (size_t) snprintf(kdf_context, sizeof(kdf_context), "key for encryption of %s share for %d", type, i); crypto_kdf_hkdf_sha256_expand(key, crypto_auth_KEYBYTES, kdf_context, context_len, mk); } static void encrypt_shares(const TOPRF_Update_PeerState *ctx, const uint8_t i, const char *type, const TOPRF_Share share[2], const uint8_t nonce_ctr, uint8_t hmac[crypto_auth_hmacsha256_BYTES], uint8_t ct[toprf_update_encrypted_shares_SIZE]) { uint8_t key[crypto_auth_KEYBYTES]; derive_key((*ctx->noise_outs)[i],i+1,type,key); uint8_t nonce[crypto_stream_NONCEBYTES]={0}; nonce[0]=nonce_ctr; crypto_stream_xor(ct, (const uint8_t*) share, TOPRF_Share_BYTES*2, nonce, key); crypto_auth(hmac, ct, toprf_update_encrypted_shares_SIZE, key); if(liboprf_log_file!=NULL) { //dump(key, sizeof key, "[%d] key for %s share of p_%d", ctx->index, type, i+1); //dump(ct, toprf_update_encrypted_shares_SIZE, "[%d] encrypted %s share of p_%d", ctx->index, type, i+1); //dump(hmac, crypto_auth_hmacsha256_BYTES, "[%d] hmac for %s share of p_%d", ctx->index, type, i+1); } } #define toprfupdate_peer_dkg1_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (toprf_update_commitment_HASHBYTES + ctx->n * crypto_auth_hmacsha256_BYTES)) static TOPRF_Update_Err peer_dkg1_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] noise5 finish session handshake, start core update with dkg for p\x1b[0m\n", ctx->index); if(input_len != toprfupdate_peer_ake2_msg_SIZE * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_dkg1_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; const uint8_t *ptr = input; for(uint8_t i=0;in;i++, ptr+=toprfupdate_peer_ake2_msg_SIZE) { TOPRF_Update_Message* msg4 = (TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_ake2_msg_SIZE,toprfupdate_peer_ake2_msg,i+1,ctx->index)) continue; // process final step of noise handshake if(0!=dkg_finish_noise_handshake(ctx->index, ctx->dev, &(*ctx->noise_outs)[i], msg4->data)) return TOPRF_Update_Err_Noise; } if(ctx->cheater_len>0) return TOPRF_Update_Err_CheatersFound; TOPRF_Update_Message* msg5 = (TOPRF_Update_Message*) output; uint8_t *wptr = msg5->data; uint8_t *dptr = (uint8_t*) (*ctx->encrypted_shares); TOPRF_Update_Err ret; ret = dkg1(ctx, ctx->n, "p", NULL, (*ctx->p_shares), (*ctx->p_commitments), &wptr); if(ret != TOPRF_Update_Err_OK) return ret; for(uint8_t i=0;in;i++) { // we need to send an empty packet, so that the handshake completes // and we have a final symetric key, the key during the handshake changes, only // when the handshake completes does the key become static. // this is important, so that when there are complaints, we can disclose the key. uint8_t empty[1]={0}; // would love to do [0] but that is undefined c if(0!=dkg_noise_encrypt(empty, 0, dptr, noise_xk_handshake3_SIZE, &(*ctx->noise_outs)[i])) return TOPRF_Update_Err_NoiseEncrypt; dptr+=noise_xk_handshake3_SIZE; // we might need to disclose the encryption key for the p shares, // but we don't want even the STP to learn more than necessary for // proving the correct encryption of the shares, hence the // following: we extract the current noise key, hkdf() it into two // dedicated subkeys, encrypt the shares using a stream cipher, // and calculate an hmac over these with the subkeys. encrypt_shares(ctx,i,"p",(*ctx->p_shares)[i],0,wptr,dptr); dptr+=toprf_update_encrypted_shares_SIZE; wptr+=crypto_auth_hmacsha256_BYTES; } //broadcast dealer_commitments if(0!=toprf_send_msg(output, toprfupdate_peer_dkg1_msg_SIZE(ctx), toprfupdate_peer_dkg1_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Rcv_CHashes_Send_Commitments; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_dkg1_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "dkg1 broadcast commitment hashes and share hmacs for p dkg step 1", ctx->n, toprfupdate_peer_dkg1_msg_SIZE(ctx), toprfupdate_peer_dkg1_msg, TOPRF_Update_STP_Broadcast_DKG_Commitments); if(ret != TOPRF_Update_Err_OK) return ret; const uint8_t *ptr = input; for(unsigned i=0;in;i++,ptr+=toprfupdate_peer_dkg1_msg_SIZE(ctx)) { const DKG_Message* msg = (const DKG_Message*) ptr; const uint8_t *dptr=msg->data; memcpy((*ctx->p_commitments_hashes)[i], dptr, toprf_update_commitment_HASHBYTES); dptr+=toprf_update_commitment_HASHBYTES; for(uint8_t j=0;jn;j++) { memcpy((*ctx->p_share_macs)[i*ctx->n+j], dptr, crypto_auth_hmacsha256_BYTES); dptr+=crypto_auth_hmacsha256_BYTES; } } return TOPRF_Update_Err_OK; } #define toprfupdate_peer_dkg2_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + crypto_core_ristretto255_BYTES * ctx->n) static TOPRF_Update_Err peer_dkg2_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg2 receive commitment hashes, broadcast commitments\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_dkg1_msg_SIZE(ctx) * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_dkg2_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_dkg1_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; for(uint8_t i=0;in;i++, ptr+=toprfupdate_peer_dkg1_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg5 = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_dkg1_msg_SIZE(ctx),toprfupdate_peer_dkg1_msg,i+1,0xff)) continue; const uint8_t *dptr=msg5->data; // extract peer p commitment hash memcpy((*ctx->p_commitments_hashes)[i], dptr, toprf_update_commitment_HASHBYTES); dptr+=toprf_update_commitment_HASHBYTES; for(uint8_t j=0;jn;j++) { // extract and store encrypted p share mac memcpy((*ctx->p_share_macs)[j*ctx->n + i], dptr, crypto_auth_hmacsha256_BYTES); //dump(dptr, crypto_auth_hmacsha256_BYTES, "[%d] p share macs [%d,%d]", ctx->index, j+1, i+1); dptr+=crypto_auth_hmacsha256_BYTES; } if(liboprf_log_file!=NULL) { dump((*ctx->p_commitments_hashes)[i], toprf_update_commitment_HASHBYTES, "[%d] p commitment hash [%d]", ctx->index, i+1); } } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; // we stashed our commitments temporarily in k_commitments memcpy(wptr, (*ctx->p_commitments), ctx->n * crypto_core_ristretto255_BYTES); //broadcast dealer_commitments if(0!=toprf_send_msg(output, toprfupdate_peer_dkg2_msg_SIZE(ctx), toprfupdate_peer_dkg2_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Rcv_Commitments_Send_Shares; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_vsps_check(TOPRF_Update_STPState *ctx, const char *type, const uint8_t dealers, const uint8_t clen, const uint8_t (*ctx_commitments)[][crypto_core_ristretto255_BYTES]) { TOPRF_Update_Err ret; const uint8_t (*c)[dealers][clen][crypto_core_ristretto255_BYTES] = (const uint8_t (*)[dealers][clen][crypto_core_ristretto255_BYTES]) ctx_commitments; // calculate preliminary final commitments uint8_t kcom[clen][crypto_core_ristretto255_BYTES]; for(unsigned i=0;it, dealers, 0, kcom, c, "VSPS failed k during DKG, doing full VSPS check on all peers", "VSPS failed k", "ERROR, could not find any dealer commitments that fail the VSPS check", &fails_len, fails); if(ret!=TOPRF_Update_Err_OK) { return ret; } for(unsigned i=0;in - fails_len < 2) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] less than 2 honest %s dealers: %d \n"NORMAL, type, ctx->n - fails_len); if(stp_add_cheater(ctx,2,0,0) == NULL) { return TOPRF_Update_Err_CheatersFull; } } if(fails_len >= ctx->t) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] more than t %s cheaters (t=%d, cheaters=%d)\n"NORMAL, type, ctx->t, fails_len); if(stp_add_cheater(ctx,3,fails_len,0) == NULL) { return TOPRF_Update_Err_CheatersFull; } } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_check_chash(TOPRF_Update_STPState *ctx, const uint8_t i, const char *type, const uint8_t dealers, const uint8_t clen, const uint8_t *commitments, const uint8_t (*commitments_hashes)[][toprf_update_commitment_HASHBYTES], uint8_t (*ctx_commitments)[][crypto_core_ristretto255_BYTES]) { uint8_t (*c)[dealers][clen][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][clen][crypto_core_ristretto255_BYTES]) ctx_commitments; uint8_t chash[toprf_update_commitment_HASHBYTES]; crypto_generichash(chash, toprf_update_commitment_HASHBYTES, commitments, crypto_core_ristretto255_BYTES*clen, NULL, 0); if(memcmp(chash, (*commitments_hashes)[i], toprf_update_commitment_HASHBYTES)!=0) { dump((*commitments_hashes)[i], toprf_update_commitment_HASHBYTES, "[%d] commitment hash", i+1); dump(commitments, crypto_core_ristretto255_BYTES*clen, "[%d] committed", i+1); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] failed to verify hash for %s commitments of dealer %d\n"NORMAL, type, i+1); if(stp_add_cheater(ctx, 4, i+1, 0) == NULL) { return TOPRF_Update_Err_CheatersFull; } } memcpy((*c)[i], commitments, crypto_core_ristretto255_BYTES * clen); return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_dkg2_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { TOPRF_Update_Err ret = stp_broadcast(ctx, input, input_len, output, output_len, "dkg3 broadcast commitments dkg step 1", ctx->n, toprfupdate_peer_dkg2_msg_SIZE(ctx), toprfupdate_peer_dkg2_msg, TOPRF_Update_STP_Route_Encrypted_Shares); if(ret!=TOPRF_Update_Err_OK) return ret; const uint8_t *ptr = input; // fixup step, that has already been advanced in the call to stp_broadcast above. uint8_t step = ctx->step; ctx->step = TOPRF_Update_STP_Broadcast_DKG_Commitments; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_dkg2_msg_SIZE(ctx)) { const DKG_Message* msg = (const DKG_Message*) ptr; const uint8_t *dptr = msg->data; ret = stp_check_chash(ctx,i,"p",ctx->n, ctx->n, dptr,ctx->p_commitments_hashes,ctx->p_commitments); if(TOPRF_Update_Err_OK!=ret) { ctx->step=step; return ret; } } ret = stp_vsps_check(ctx, "p", ctx->n, ctx->n, ctx->p_commitments); ctx->step=step; return ret; } #define toprfupdate_peer_dkg3_msg_SIZE (sizeof(TOPRF_Update_Message) /* header */ \ + noise_xk_handshake3_SIZE /* 4th&final noise handshake */ \ + toprf_update_encrypted_shares_SIZE ) static TOPRF_Update_Err peer_dkg3_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg4 receive commitments & distribute encrypted shares\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_dkg2_msg_SIZE(ctx) * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != ctx->n * toprfupdate_peer_dkg3_msg_SIZE) return TOPRF_Update_Err_OSize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_dkg2_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; for(uint8_t i=0;in;i++, ptr+=toprfupdate_peer_dkg2_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg5 = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_dkg2_msg_SIZE(ctx),toprfupdate_peer_dkg2_msg,i+1,0xff)) continue; // extract peer commitments const uint8_t *dptr = msg5->data; memcpy((*ctx->p_commitments)[i*ctx->n], dptr, crypto_core_ristretto255_BYTES * ctx->n); if(liboprf_log_file!=NULL) { dump((*ctx->p_commitments)[i*ctx->n], crypto_core_ristretto255_BYTES*ctx->n, "[%d] p commitments [%d]", ctx->index, i+1); } // verify against commitment hashes uint8_t chash[toprf_update_commitment_HASHBYTES]; crypto_generichash(chash, toprf_update_commitment_HASHBYTES, (*ctx->p_commitments)[i*ctx->n], crypto_core_ristretto255_BYTES*ctx->n, NULL, 0); if(memcmp(chash, (*ctx->p_commitments_hashes)[i], toprf_update_commitment_HASHBYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed to verify hash for p commitments of dealer %d\n"NORMAL, ctx->index, i+1); if(peer_add_cheater(ctx, 2, i+1, 0) == NULL) return TOPRF_Update_Err_CheatersFull; } } // yes we abort here if the hash commitment fails. if(ctx->cheater_len>0) return TOPRF_Update_Err_CheatersFound; // we could check VSPS here, but that would complicate msg size // calculation taking into account demoted dealers, so we do it // after the shares have been dealt. uint8_t *wptr = output; for(uint8_t i=0;in;i++, wptr+=toprfupdate_peer_dkg3_msg_SIZE) { TOPRF_Update_Message *msg7 = (TOPRF_Update_Message *) wptr; memcpy(msg7->data, (*ctx->encrypted_shares)[i], noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE); if(0!=toprf_send_msg(wptr, toprfupdate_peer_dkg3_msg_SIZE, toprfupdate_peer_dkg3_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; } ctx->step = TOPRF_Update_Peer_Verify_Commitments; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_dkg3_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_route(ctx, input, input_len, output, output_len, "dkg4 route shares to peers", ctx->n, ctx->n, toprfupdate_peer_dkg3_msg, toprfupdate_peer_dkg3_msg_SIZE, TOPRF_Update_STP_Broadcast_Complaints); } static TOPRF_Update_Err decrypt_shares(const TOPRF_Update_PeerState *ctx, const uint8_t i, const char *type, const uint8_t hmac[crypto_auth_hmacsha256_BYTES], const uint8_t ct[toprf_update_encrypted_shares_SIZE], const uint8_t nonce_ctr, TOPRF_Share share[2]) { uint8_t key[crypto_auth_KEYBYTES]; derive_key((*ctx->noise_ins)[i],ctx->index,type,key); uint8_t nonce[crypto_stream_NONCEBYTES]={0}; nonce[0]=nonce_ctr; #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if(0!=crypto_auth_verify(hmac, ct, toprf_update_encrypted_shares_SIZE, key)) { //dump(key, sizeof key, "[%d] key for %s share of p_%d", ctx->index, type, i+1); dump(ct, toprf_update_encrypted_shares_SIZE, "[%d] encrypted %s share of p_%d", ctx->index, type, i+1); return TOPRF_Update_Err_HMac; } #endif crypto_stream_xor((uint8_t*) share, ct, TOPRF_Share_BYTES*2, nonce, key); return TOPRF_Update_Err_OK; } static void verify_commitments(const TOPRF_Update_PeerState *ctx, const char *type, const uint8_t dealers, const uint8_t clen, const uint8_t cidx, const uint8_t commitments[][crypto_core_ristretto255_BYTES], const TOPRF_Share (*shares)[][2], uint8_t *fails_len, uint8_t *fails) { *fails_len=0; memset(fails, 0, dealers); const uint8_t (*c)[clen][crypto_core_ristretto255_BYTES] = (const uint8_t (*)[clen][crypto_core_ristretto255_BYTES]) commitments; // verify that the shares match the commitment for(uint8_t i=0;iindex, type, i+1); fails[(*fails_len)++]=i+1; } } if(liboprf_log_file!=NULL) { if(*fails_len>0) { fprintf(liboprf_log_file, RED"[%d] %s commitment fails#: %d -> ", ctx->index, type, *fails_len); for(unsigned i=0;i<*fails_len;i++) fprintf(liboprf_log_file, "%s%d", (i>0)?", ":"", fails[i]); fprintf(liboprf_log_file, NORMAL"\n"); } else { fprintf(liboprf_log_file, GREEN"[%d] no %s commitment fails\n"NORMAL, ctx->index, type); } } } #define toprfupdate_peer_verify_shares_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (size_t)(ctx->n + 1)) static TOPRF_Update_Err peer_verify_shares_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg-verify1 DKG step 2 - receive shares, verify commitments\x1b[0m\n", ctx->index); if(input_len != toprfupdate_peer_dkg3_msg_SIZE * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_verify_shares_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; const uint8_t *ptr = input; for(uint8_t i=0;in;i++) { if(peer_recv_msg(ctx,ptr,toprfupdate_peer_dkg3_msg_SIZE,toprfupdate_peer_dkg3_msg,i+1,ctx->index)) continue; const uint8_t *dptr = ((const TOPRF_Update_Message*) ptr)->data; // decrypt final empty handshake packet if(0!=dkg_noise_decrypt(dptr, noise_xk_handshake3_SIZE, NULL, 0, &(*ctx->noise_ins)[i])) return TOPRF_Update_Err_NoiseDecrypt; dptr += noise_xk_handshake3_SIZE; TOPRF_Update_Err ret; ret = decrypt_shares(ctx, i, "p", (*ctx->p_share_macs)[(ctx->index-1)*ctx->n + i], dptr, 0, (*ctx->p_shares)[i]); if(TOPRF_Update_Err_OK!=ret) { dump((*ctx->p_share_macs)[(ctx->index-1)*ctx->n + i], crypto_auth_hmacsha256_BYTES, "[%d] p hmac_%d", ctx->index, i+1); return ret; } ptr+=toprfupdate_peer_dkg3_msg_SIZE; } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *fails_len = msg->data; uint8_t *fails = fails_len+1; verify_commitments(ctx, "p", ctx->n, ctx->n, ctx->index-1, (*ctx->p_commitments), ctx->p_shares, fails_len, fails); #ifdef UNITTEST_CORRUPT corrupt_false_accuse(ctx, 2, 3, fails_len, fails); #endif //UNITTEST_CORRUPT if(0!=toprf_send_msg(output, toprfupdate_peer_verify_shares_msg_SIZE(ctx), toprfupdate_peer_verify_shares_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Handle_DKG_Complaints; return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_verify_shares_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (toprfupdate_peer_verify_shares_msg_SIZE(ctx) * ctx->n)) static TOPRF_Update_Err stp_verify_shares_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_complaint_handler(ctx, input, input_len, output, output_len, "dkg-verify2 broadcast complaints of peers", ctx->n, toprfupdate_peer_verify_shares_msg_SIZE(ctx), toprfupdate_peer_verify_shares_msg, ctx->n, TOPRF_Update_STP_Broadcast_DKG_Transcripts, TOPRF_Update_STP_Broadcast_DKG_Defenses); } static TOPRF_Update_Err peer_dkg_fork(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len) { return peer_complaint_handler(ctx, input, input_len, "dkg-verify3 receive complaints broadcast", toprfupdate_peer_verify_shares_msg_SIZE(ctx), toprfupdate_peer_verify_shares_msg, ctx->n, TOPRF_Update_Peer_Finish_DKG, TOPRF_Update_Peer_Defend_DKG_Accusations); } static TOPRF_Update_Err peer_defend(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg-defend1 disclose share encryption key\x1b[0m\n", ctx->index); if(output_len != toprf_update_peer_output_size(ctx)) return TOPRF_Update_Err_OSize; if(output_len == 0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] nothing to defend against, no message to send\n", ctx->index); } ctx->step = TOPRF_Update_Peer_Check_Shares; return 0; } // send out all shares that belong to peers that complained. TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; for(int i=0;imy_p_complaints_len;i++) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;36m[%d] defending against p complaint from %d\x1b[0m\n", ctx->index, ctx->my_p_complaints[i]); *wptr++ = ctx->my_p_complaints[i]; // reveal key for noise wrapped share sent previously derive_key((*ctx->noise_outs)[ctx->my_p_complaints[i]-1],ctx->my_p_complaints[i],"p",wptr); wptr+=dkg_noise_key_SIZE; memcpy(wptr, (*ctx->encrypted_shares)[ctx->my_p_complaints[i]-1] + noise_xk_handshake3_SIZE, toprf_update_encrypted_shares_SIZE); wptr+=toprf_update_encrypted_shares_SIZE; } if(0!=toprf_send_msg(output, output_len, toprfupdate_peer_share_key_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Check_Shares; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_check_defenses(TOPRF_Update_STPState *ctx, const uint8_t dealers, const uint8_t clen, const uint8_t coffset, const unsigned int ctr, const unsigned i, const uint8_t nonce_ctr, const uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], const uint8_t commitments[][crypto_core_ristretto255_BYTES], uint16_t *complaints_len, uint16_t *complaints, const uint8_t **dptr) { if(ctr>=ctx->n) return TOPRF_Update_Err_OOB; const uint8_t (*c)[clen][crypto_core_ristretto255_BYTES] = (const uint8_t (*)[clen][crypto_core_ristretto255_BYTES]) commitments; for(unsigned j=0;jctx->n) return TOPRF_Update_Err_OOB; const uint8_t *key=(*dptr)+1; const uint8_t *shares=key+dkg_noise_key_SIZE; *dptr += 1U + dkg_noise_key_SIZE + toprf_update_encrypted_shares_SIZE; if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[!] accused: %d, by %d\n", accused, accuser); dump(key,dkg_noise_key_SIZE,"key"); dump(shares,toprf_update_encrypted_shares_SIZE,"encrypted shares"); } #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if(0!=crypto_auth_verify((*share_macs)[(accused-1)*ctx->n+(accuser-1)], shares, toprf_update_encrypted_shares_SIZE, key)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,RED"[!] invalid HMAC on shares of accused: %d, by %d\n"NORMAL, accused, accuser); if(stp_add_cheater(ctx, 1, accused, accuser) == NULL) return TOPRF_Update_Err_CheatersFull; complaints[(*complaints_len)++]=accuser << 8 | accused; continue; } #endif TOPRF_Share share[2]; uint8_t nonce[crypto_stream_NONCEBYTES]={0}; nonce[0]=nonce_ctr; crypto_stream_xor((uint8_t*) share, shares, TOPRF_Share_BYTES*2, nonce, key); if(share[0].index != accuser) { // invalid share index TOPRF_Update_Cheater* cheater = stp_add_cheater(ctx, 3, accused, accuser); if(cheater == NULL) return TOPRF_Update_Err_CheatersFull; cheater->invalid_index = share[0].index; complaints[(*complaints_len)++]=accuser << 8 | accused; continue; } if(0!=dkg_vss_verify_commitment(c[accused-1][accuser-coffset],share)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify commitment of accused %d by accuser %d!\x1b[0m\n", accused, accuser); TOPRF_Update_Cheater* cheater = stp_add_cheater(ctx, 4, accused, accuser); if(cheater == NULL) return TOPRF_Update_Err_CheatersFull; cheater->invalid_index = share[0].index; complaints[(*complaints_len)++]=accuser << 8 | accused; continue; } else { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,GREEN"[!] succeeded to verify commitment of accused %d by accuser %d!\x1b[0m\n", accused, accuser); dump((uint8_t*) share, sizeof share, "share"); dump(c[accused-1][accuser-coffset], crypto_core_ristretto255_BYTES, "commitment"); } if(stp_add_cheater(ctx, 5, accuser, accused) == NULL) return TOPRF_Update_Err_CheatersFull; } } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_broadcast_defenses(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] dkg-defend2 broadcast defenses\x1b[0m\n"); if(input_len != toprf_update_stp_input_size(ctx)) return TOPRF_Update_Err_ISize; if(output_len != toprf_update_stp_output_size(ctx)) return TOPRF_Update_Err_OSize; unsigned int ctr1[ctx->n]; memset(ctr1,0,sizeof(ctr1)); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff)-1U); if(peer>=ctx->n) return TOPRF_Update_Err_OOB; ctr1[peer]++; } const uint8_t *ptr = input; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; size_t msg_size; for(uint8_t i=0;in;i++,ptr += msg_size) { if(ctr1[i]==0) { msg_size = 0; continue; // no complaints against this peer } msg_size = sizeof(TOPRF_Update_Message) \ + (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; if(stp_recv_msg(ctx,ptr,msg_size,toprfupdate_peer_share_key_msg,i+1,0xff)) continue; const TOPRF_Update_Message *msg = (const TOPRF_Update_Message *) ptr; const uint8_t *dptr = msg->data; TOPRF_Update_Err ret; ret = stp_check_defenses(ctx, ctx->n, ctx->n, 1, ctr1[i], i, 0, ctx->p_share_macs, *ctx->p_commitments, &ctx->p_complaints_len, ctx->p_complaints, &dptr); if(TOPRF_Update_Err_OK != ret) { return ret; } memcpy(wptr, ptr, msg_size); wptr+=msg_size; } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(0!=toprf_send_msg(output, output_len, toprfupdate_stp_bc_key_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; // add broadcast msg to transcript update_transcript(&ctx->transcript_state, output, output_len); ctx->step = TOPRF_Update_STP_Broadcast_DKG_Transcripts; return TOPRF_Update_Err_OK; } #define toprfupdate_peer_bc_transcript_msg_SIZE (sizeof(TOPRF_Update_Message) + crypto_generichash_BYTES + crypto_core_ristretto255_BYTES*2) static TOPRF_Update_Err peer_verify_vsps(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len); static TOPRF_Update_Err check_defenses(TOPRF_Update_PeerState *ctx, const uint8_t dealers, const uint8_t clen, const uint8_t coffset, const unsigned int ctr, const uint8_t i, const uint8_t nonce_ctr, const uint8_t (*share_macs)[][crypto_auth_hmacsha256_BYTES], const uint8_t commitments[][crypto_core_ristretto255_BYTES], uint16_t *complaints_len, uint16_t *complaints, const uint8_t **dptr) { if(ctr>=ctx->n) return TOPRF_Update_Err_OOB; const uint8_t (*c)[clen][crypto_core_ristretto255_BYTES] = (const uint8_t (*)[clen][crypto_core_ristretto255_BYTES]) commitments; for(unsigned j=0;jctx->n) return TOPRF_Update_Err_OOB; const uint8_t *key=(*dptr)+1; const uint8_t *shares=key+dkg_noise_key_SIZE; *dptr += 1U + dkg_noise_key_SIZE + toprf_update_encrypted_shares_SIZE; if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] accused: %d, by %d\n", ctx->index, accused, accuser); dump(key,dkg_noise_key_SIZE,"key"); dump(shares,toprf_update_encrypted_shares_SIZE,"encrypted shares"); } #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if(0!=crypto_auth_verify((*share_macs)[(accuser-1)*ctx->n+(accused-1)], shares, toprf_update_encrypted_shares_SIZE, key)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,RED"[%d] invalid HMAC on shares of accused: %d, by %d\n"NORMAL, ctx->index, accused, accuser); if(peer_add_cheater(ctx, 1, accused, accuser) == NULL) return TOPRF_Update_Err_CheatersFull; complaints[(*complaints_len)++]=accuser << 8 | accused; continue; } #endif TOPRF_Share share[2]; uint8_t nonce[crypto_stream_NONCEBYTES]={0}; nonce[0]=nonce_ctr; crypto_stream_xor((uint8_t*) share, shares, TOPRF_Share_BYTES*2, nonce, key); if(share[0].index != accuser) { // invalid share index TOPRF_Update_Cheater* cheater = peer_add_cheater(ctx, 3, accused, accuser); if(cheater == NULL) return TOPRF_Update_Err_CheatersFull; cheater->invalid_index = share[0].index; complaints[(*complaints_len)++]=accuser << 8 | accused; continue; } if(0!=dkg_vss_verify_commitment(c[accused-1][accuser-coffset],share)) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[%d] failed to verify commitment of accused %d by accuser %d!\x1b[0m\n", ctx->index, accused, accuser); dump((uint8_t*) share, sizeof share, "share"); dump(c[accused-1][accuser-1], crypto_core_ristretto255_BYTES, "commitment"); } TOPRF_Update_Cheater* cheater = peer_add_cheater(ctx, 4, accused, accuser); if(cheater == NULL) return TOPRF_Update_Err_CheatersFull; cheater->invalid_index = share[0].index; complaints[(*complaints_len)++]=accuser << 8 | accused; continue; } else { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,GREEN"[%d] succeeded to verify commitment of accused %d by accuser %d!\x1b[0m\n", ctx->index, accused, accuser); if(peer_add_cheater(ctx, 5, accuser, accused) == NULL) return TOPRF_Update_Err_CheatersFull; //ctx->share_complaints[ctx->share_complaints_len++]=accused; } } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err aggregate_complaints(const uint8_t n, unsigned *ctr, uint16_t *complaints_len, uint16_t *complaints) { memset(ctr,0,n*sizeof(unsigned)); for(int i=0;i<*complaints_len;i++) { const uint8_t peer = (uint8_t) (complaints[i] & 0xff)-1; if(peer>=n) return TOPRF_Update_Err_OOB; ctr[peer]++; complaints[i]=0; } *complaints_len=0; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_check_shares(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg-defend3 verify disclosed shares\x1b[0m\n", ctx->index); if(input_len != toprf_update_peer_input_size(ctx)) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_bc_transcript_msg_SIZE) return TOPRF_Update_Err_OSize; // verify STP message envelope const uint8_t *ptr=NULL; TOPRF_Update_Err ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_key_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; unsigned int ctr1[ctx->n]; aggregate_complaints(ctx->n,ctr1,&ctx->p_complaints_len,ctx->p_complaints); size_t msg_size; for(uint8_t i=0;in;i++,ptr += msg_size) { if(ctr1[i]==0) { msg_size = 0; continue; // no complaints against this peer } msg_size = sizeof(TOPRF_Update_Message) \ + (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; if(peer_recv_msg(ctx,ptr,msg_size,toprfupdate_peer_share_key_msg,i+1,0xff)) continue; const TOPRF_Update_Message *msg = (const TOPRF_Update_Message *) ptr; const uint8_t *dptr = msg->data; ret = check_defenses(ctx, ctx->n, ctx->n, 1, ctr1[i], i, 0, ctx->p_share_macs, (*ctx->p_commitments), &ctx->p_complaints_len, ctx->p_complaints, &dptr); if(TOPRF_Update_Err_OK != ret) return ret; } return peer_verify_vsps(ctx, output, output_len); } static TOPRF_Update_Err finalize_dkg(TOPRF_Update_PeerState *ctx, const char *type, const uint16_t complaints_len, const uint16_t *complaints, const TOPRF_Share (*dealer_shares)[][2], uint8_t (*dealer_commitments)[][crypto_core_ristretto255_BYTES], TOPRF_Share my_share[2], uint8_t my_commitment[crypto_core_ristretto255_BYTES]) { // 2. Players verify the VSPS property of the sum of the shared secrets by running // VSPS-Check on 𝓐_i,..,𝓐_n where // // 𝓐_j = Ξ  𝓐_i,j // i // // If this check fails the players run VSPS-Check on each individual // sharing from step 1. Any player that fails this check is disqualified. uint8_t (*c)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][ctx->n][crypto_core_ristretto255_BYTES]) dealer_commitments; uint8_t kcom[ctx->n][crypto_core_ristretto255_BYTES]; for(unsigned i=0;in;i++) { memcpy(kcom[i], (*c)[0][i], crypto_scalarmult_ristretto255_BYTES); for(unsigned j=1;jn;j++) { crypto_core_ristretto255_add(kcom[i], kcom[i], (*c)[j][i]); } } uint8_t fails_len=0; uint8_t fails[ctx->n]; memset(fails,0,ctx->n); TOPRF_Update_Err ret = ft_or_full_vsps(ctx->n, ctx->t, ctx->n, ctx->index, kcom, c, "VSPS failed k during DKG, doing full VSPS check on all peers", "VSPS failed k", "ERROR, could not find any dealer commitments that fail the VSPS check", &fails_len, fails); if(ret!=TOPRF_Update_Err_OK) return ret; if(ctx->n - fails_len < 2) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, RED"[%d] less than 2 honest dealers for %s: %d \n"NORMAL, ctx->index, type, ctx->n - fails_len); if(peer_add_cheater(ctx, 6, 0, 0) == NULL) return TOPRF_Update_Err_CheatersFull; } return TOPRF_Update_Err_NotEnoughDealers; } if(fails_len >= ctx->t) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, RED"[%d] more than t cheaters for %s (t=%d, cheaters=%d)\n"NORMAL, ctx->index, type, ctx->t, fails_len); if(peer_add_cheater(ctx, 7, fails_len, 0) == NULL) return TOPRF_Update_Err_CheatersFull; } return TOPRF_Update_Err_TooManyCheaters; } // todo persist qual so we can consider who is a dealer for the ft-mult proto uint8_t qual[ctx->n+1]; uint8_t qual_len=0; for(uint8_t i=0;in;i++) { unsigned j,k; for(j=0;j=fails_len) { if(k>=complaints_len) qual[qual_len++]=i+1; } else if(peer_add_cheater(ctx, 8, ctx->index, i+1) == NULL) return TOPRF_Update_Err_CheatersFull; } qual[qual_len]=0; if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] %s qual is: ", ctx->index, type); for(unsigned i=0;iindex; my_share[1].index=ctx->index; // finalize dkg if(0!=dkg_vss_finish(ctx->n,qual,(*dealer_shares),ctx->index,my_share, my_commitment)) return TOPRF_Update_Err_DKGFinish; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_verify_vsps(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg-verify4 VSPS check commitments, calculate share and broadcast transcript and final commitment\x1b[0m\n", ctx->index); if(output_len != toprfupdate_peer_bc_transcript_msg_SIZE) return TOPRF_Update_Err_OSize; TOPRF_Update_Message* msg20 = (TOPRF_Update_Message*) output; uint8_t *wptr = msg20->data; crypto_generichash_state transcript_state; memcpy((uint8_t*) &transcript_state, (const uint8_t*) &ctx->transcript_state, sizeof transcript_state); crypto_generichash_final(&transcript_state, wptr, crypto_generichash_BYTES); memcpy(ctx->transcript, wptr, crypto_generichash_BYTES); wptr+=crypto_generichash_BYTES; TOPRF_Update_Err ret; ret=finalize_dkg(ctx, "p", ctx->p_complaints_len, ctx->p_complaints, ctx->p_shares, ctx->p_commitments, ctx->p_share, wptr); if(TOPRF_Update_Err_OK != ret) return ret; if(0!=toprf_send_msg(output, toprfupdate_peer_bc_transcript_msg_SIZE, toprfupdate_peer_bc_transcript_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Confirm_Transcripts; return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_transcript_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + toprfupdate_peer_bc_transcript_msg_SIZE*ctx->n) static TOPRF_Update_Err stp_bc_transcript_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] dkg-final1 broadcast DKG transcripts\x1b[0m\n"); if((toprfupdate_peer_bc_transcript_msg_SIZE * ctx->n) != input_len) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_stp_bc_transcript_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; uint8_t transcript_hash[crypto_generichash_BYTES]; crypto_generichash_state transcript_state; memcpy((uint8_t*) &transcript_state, (const uint8_t*) &ctx->transcript_state, sizeof transcript_state); crypto_generichash_final(&transcript_state, transcript_hash, crypto_generichash_BYTES); size_t cheaters = ctx->cheater_len; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; const uint8_t *ptr = input; for(uint8_t i=0;in;i++, ptr+=toprfupdate_peer_bc_transcript_msg_SIZE) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; if(stp_recv_msg(ctx,ptr,toprfupdate_peer_bc_transcript_msg_SIZE,toprfupdate_peer_bc_transcript_msg,i+1,0xff)) continue; const uint8_t *dptr=msg->data; if(sodium_memcmp(transcript_hash, dptr, sizeof(transcript_hash))!=0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify transcript from %d!\x1b[0m\n", i); } if(stp_add_cheater(ctx, 1, i+1, 0) == NULL) return TOPRF_Update_Err_CheatersFull; continue; } dptr+=crypto_generichash_BYTES; memcpy((*ctx->p_commitments)[i], dptr, crypto_core_ristretto255_BYTES); memcpy(wptr, ptr, toprfupdate_peer_bc_transcript_msg_SIZE); wptr+=toprfupdate_peer_bc_transcript_msg_SIZE; } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; int _debug=liboprf_debug; liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, (*ctx->p_commitments))) { liboprf_debug=_debug; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] result of DKG final commitments fail VSPS\n"NORMAL); if(stp_add_cheater(ctx, 2, 0, 0) == NULL) return TOPRF_Update_Err_CheatersFull; } liboprf_debug=_debug; if(0!=toprf_send_msg(output, output_len, toprfupdate_stp_bc_transcript_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_STP_Route_Mult_Step1; return TOPRF_Update_Err_OK; } #define toprfupdate_peer_mult1_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (toprf_update_commitment_HASHBYTES + ctx->n * crypto_auth_hmacsha256_BYTES)) static TOPRF_Update_Err peer_final_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] dkg-final2 receive&check final transcript, receive & VSPS check p commitments\n"NORMAL, ctx->index); if(input_len != toprfupdate_stp_bc_transcript_msg_SIZE(ctx)) return TOPRF_Update_Err_ISize; if(output_len != isdealer(ctx->index, ctx->t) * toprfupdate_peer_mult1_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_transcript_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; size_t cheaters = ctx->cheater_len; uint8_t (*pcom)[ctx->n][crypto_core_ristretto255_BYTES] = (uint8_t (*)[ctx->n][crypto_core_ristretto255_BYTES]) ctx->p_commitments; for(uint8_t i=0;in;i++, ptr+=toprfupdate_peer_bc_transcript_msg_SIZE) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; const uint8_t *dptr = msg->data; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_bc_transcript_msg_SIZE,toprfupdate_peer_bc_transcript_msg,i+1,0xff)) continue; if(sodium_memcmp(ctx->transcript, dptr, crypto_generichash_BYTES)!=0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify transcript from %d!\x1b[0m\n", i); } if(peer_add_cheater(ctx, 1, i+1, 0) == NULL) return TOPRF_Update_Err_CheatersFull; continue; } dptr+=crypto_generichash_BYTES; memcpy((*pcom)[i], dptr, crypto_core_ristretto255_BYTES); } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; // in theory this should not be needed, and not fail. except for the // case when the dealer shares were corrupted after calculating a // correct commitment for them. but that should also be previously detected. int _debug=liboprf_debug; liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, (*pcom))) { liboprf_debug=_debug; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] result of p DKG commitments fail VSPS\n"NORMAL, ctx->index); if(peer_add_cheater(ctx, 2, 0, 0) == NULL) return TOPRF_Update_Err_CheatersFull; } liboprf_debug=_debug; if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; // reset complaints ctx->p_complaints_len = 0; ctx->my_p_complaints_len = 0; memset(ctx->p_complaints, 0, ctx->n*2); memset(ctx->my_p_complaints, 0, ctx->n); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult1 dealers calculate and share Ξ»_iΞ±_iΞ²_i\x1b[0m\n", ctx->index); // todo dealers based on cheaters and knowledge of kc0 const uint8_t dealers = (uint8_t) ((ctx->t-1)*2 + 1); // precompute lambdas // Ξ»_i is row 1 of inv VDM matrix uint8_t indexes[dealers]; for(uint8_t i=0;ilambdas), lambdas[0], dealers*crypto_core_ristretto255_SCALARBYTES); //dump((uint8_t*) lambdas[0], dealers*crypto_core_ristretto255_SCALARBYTES, "vdm[0] "); //dump((uint8_t*) (*ctx->lambdas), dealers*crypto_core_ristretto255_SCALARBYTES, "lambdas"); if(ctx->index>dealers) { // non-dealers are done ctx->step = TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments; return TOPRF_Update_Err_OK; } // dealers only // step 1. Each player P_i shares Ξ»_iΞ±_iΞ²_i, using VSS if(0!=toprf_mpc_ftmult_step1(dealers, ctx->n, ctx->t, ctx->index-1, ctx->kc0_share, ctx->p_share, (*ctx->lambdas), // we reuse p_shares as we need to store n shares, and k0p_shares has only dealer entries (*ctx->p_shares), (*ctx->k0p_commitments), ctx->k0p_tau)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] failed toprf_mpc_ftmult_step1\n", ctx->index); return TOPRF_Update_Err_FTMULTStep1; } #ifdef UNITTEST_CORRUPT corrupt_mult_vsps_t1(ctx, 1); corrupt_wrongshare_correct_commitment(ctx,2,4,ctx->p_shares,ctx->k0p_commitments); if(ctx->index==3) corrupt_ci0_good_ci(3, (*ctx->k0p_commitments)); corrupt_commitment(ctx,4,ctx->k0p_commitments); corrupt_share(ctx,6,3,1,ctx->p_shares); #endif // UNITTEST_CORRUPT // similar to dkg1 encrypt shares, broadcast commitment hash and hmacs. TOPRF_Update_Message* msg24 = (TOPRF_Update_Message*) output; uint8_t *wptr = msg24->data; // hash of k0p commitments hash_commitments(ctx,ctx->n+1,(*ctx->k0p_commitments),&wptr); for(uint8_t i=0;in;i++) { // we might need to disclose the encryption key for the p shares, // but we don't want even the STP to learn more than necessary for // proving the correct encryption of the shares, hence the // following: we extract the current noise key, hkdf() it into two // dedicated subkeys, encrypt the shares using a stream cipher, // and calculate an hmac over these with the subkeys. uint8_t *dptr = (uint8_t*) (*ctx->encrypted_shares)[i]; encrypt_shares(ctx,i,"k0p",(*ctx->p_shares)[i],1,wptr,dptr); dptr+=toprf_update_encrypted_shares_SIZE; wptr+=crypto_auth_hmacsha256_BYTES; } if(0!=toprf_send_msg(output, toprfupdate_peer_mult1_msg_SIZE(ctx), toprfupdate_peer_mult1_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step25_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1)*2 + 1); TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "mult2 broadcast commitment hashes and share hmacs for k0p ft-mult step 1", dealers, toprfupdate_peer_mult1_msg_SIZE(ctx), toprfupdate_peer_mult1_msg, TOPRF_Update_STP_Broadcast_Mult_Commitments); if(ret != TOPRF_Update_Err_OK) return ret; const uint8_t *ptr = input; for(unsigned i=0;idata; memcpy((*ctx->p_commitments_hashes)[i], dptr, toprf_update_commitment_HASHBYTES); dptr+=toprf_update_commitment_HASHBYTES; for(uint8_t j=0;jn;j++) { memcpy((*ctx->p_share_macs)[i*ctx->n+j], dptr, crypto_auth_hmacsha256_BYTES); dptr+=crypto_auth_hmacsha256_BYTES; } } return TOPRF_Update_Err_OK; } #define toprfupdate_peer_mult_coms_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + crypto_core_ristretto255_BYTES * (ctx->n+1U) * 2) static TOPRF_Update_Err peer_mult2_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1)*2 + 1); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult3 receive commitment hashes, broadcast commitments\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult1_msg_SIZE(ctx) * dealers) return TOPRF_Update_Err_ISize; if(output_len != isdealer(ctx->index, ctx->t) * toprfupdate_peer_mult_coms_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_mult1_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; for(uint8_t i=0;idata; // extract peer p commitment hash memcpy((*ctx->p_commitments_hashes)[i], dptr, toprf_update_commitment_HASHBYTES); dptr+=toprf_update_commitment_HASHBYTES; // todo rename {kc1|p}_{commitment_hashes|share_macs} into more // generic names so that they better fit dkg and mult usage for(uint8_t j=0;jn;j++) { // extract and store encrypted p share mac memcpy((*ctx->p_share_macs)[j*ctx->n + i], dptr, crypto_auth_hmacsha256_BYTES); //dump(dptr, crypto_auth_hmacsha256_BYTES, "[%d] p share macs [%d,%d]", ctx->index, j+1, i+1); dptr+=crypto_auth_hmacsha256_BYTES; } if(liboprf_log_file!=NULL) { dump((*ctx->p_commitments_hashes)[i], toprf_update_commitment_HASHBYTES, "[%d] p commitment hash [%d]", ctx->index, i+1); } } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(ctx->index>dealers) { // non-dealers are done ctx->step = TOPRF_Update_Peer_Send_K0P_Shares; return TOPRF_Update_Err_OK; } TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; // we stashed our commitments temporarily in k_commitments memcpy(wptr, (*ctx->k0p_commitments), (ctx->n+1U) * crypto_core_ristretto255_BYTES); if(liboprf_log_file!=NULL) dump((uint8_t*)(*ctx->k0p_commitments), (ctx->n+1U) * crypto_core_ristretto255_BYTES, "[%d] commitments", ctx->index); //broadcast dealer_commitments if(0!=toprf_send_msg(output, toprfupdate_peer_mult_coms_msg_SIZE(ctx), toprfupdate_peer_mult_coms_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Send_K0P_Shares; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_mult_com_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1)*2 + 1); TOPRF_Update_Err ret = stp_broadcast(ctx, input, input_len, output, output_len, "mult4 broadcast commitments mult step 1", dealers, toprfupdate_peer_mult_coms_msg_SIZE(ctx), toprfupdate_peer_mult_coms_msg, TOPRF_Update_STP_Route_Encrypted_Mult_Shares); if(ret!=TOPRF_Update_Err_OK) return ret; const uint8_t *ptr = input; // fixup step, that has already been advanced in the call to stp_broadcast above. uint8_t step = ctx->step; ctx->step = TOPRF_Update_STP_Broadcast_Mult_Commitments; for(uint8_t i=0;idata; ret = stp_check_chash(ctx,i,"k0p", dealers, ctx->n+1, dptr, ctx->p_commitments_hashes, ctx->k0p_commitments); if(TOPRF_Update_Err_OK!=ret) { ctx->step=step; return ret; } } ret = stp_vsps_check(ctx, "k0p", dealers, ctx->n+1, ctx->k0p_commitments); ctx->step=step; return ret; } #define toprfupdate_peer_mult2_msg_SIZE (sizeof(TOPRF_Update_Message) + sizeof(TOPRF_Share) * 4) static TOPRF_Update_Err peer_step26_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1)*2 + 1); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult5 receive Mul commitments & distribute encrypted Mult shares\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult_coms_msg_SIZE(ctx) * dealers) return TOPRF_Update_Err_ISize; if(output_len != isdealer(ctx->index, ctx->t) * ctx->n * toprfupdate_peer_mult2_msg_SIZE) return TOPRF_Update_Err_OSize; const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_mult_coms_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; for(uint8_t i=0;idata; // k0*p commitments memcpy((*ctx->k0p_commitments)[i*(ctx->n+1U)], dptr, (ctx->n+1U) * crypto_core_ristretto255_BYTES); // verify against commitment hashes uint8_t chash[toprf_update_commitment_HASHBYTES]; crypto_generichash(chash, toprf_update_commitment_HASHBYTES, (*ctx->k0p_commitments)[i*(ctx->n+1U)], crypto_core_ristretto255_BYTES*(ctx->n+1U), NULL, 0); if(memcmp(chash, (*ctx->p_commitments_hashes)[i], toprf_update_commitment_HASHBYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed to verify hash for k0p commitments of dealer %d\n"NORMAL, ctx->index, i+1); if(peer_add_cheater(ctx, 1, i+1, 0) == NULL) return TOPRF_Update_Err_CheatersFull; } } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(ctx->index>dealers) { // non-dealers are done ctx->step = TOPRF_Update_Peer_Recv_K0P_Shares; return TOPRF_Update_Err_OK; } // dealers only // also distribute k0*p shares to all uint8_t *wptr = output; for(uint8_t i=0;in;i++,wptr+=toprfupdate_peer_mult2_msg_SIZE) { TOPRF_Update_Message* msg26 = (TOPRF_Update_Message*) wptr; memcpy(msg26->data, (*ctx->encrypted_shares)[i], toprf_update_encrypted_shares_SIZE*2); if(0!=toprf_send_msg(wptr, toprfupdate_peer_mult2_msg_SIZE, toprfupdate_peer_mult2_msg, ctx->index, i+1, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; } ctx->step = TOPRF_Update_Peer_Recv_K0P_Shares; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step27_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); return stp_route(ctx, input, input_len, output, output_len, "mult6 route k0*p shares from all dealers to all peers", dealers, ctx->n, toprfupdate_peer_mult2_msg, toprfupdate_peer_mult2_msg_SIZE, TOPRF_Update_STP_Broadcast_Mult_Complaints); } #define toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + 2U + (size_t)((ctx->t-1U)*2 + 1U) * 2) static TOPRF_Update_Err peer_step28_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult7 receive k0*p shares, starts checking of commitments\x1b[0m\n", ctx->index); if(input_len != dealers * toprfupdate_peer_mult2_msg_SIZE) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; //uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) ctx->k0p_commitments; //for(unsigned i=0;in+1;j++) dump((*c)[i][j], crypto_core_ristretto255_BYTES, "c_%d%d", i+1,j); //} const size_t cheaters = ctx->cheater_len; const uint8_t *ptr = input; for(uint8_t i=0;iindex)) continue; const uint8_t *dptr = msg->data; TOPRF_Update_Err ret = decrypt_shares(ctx, i, "k0p", (*ctx->p_share_macs)[(ctx->index-1)*ctx->n + i], dptr, 1, (*ctx->k0p_shares)[i]); if(TOPRF_Update_Err_OK!=ret) { dump((*ctx->p_share_macs)[(ctx->index-1)*ctx->n + i], crypto_auth_hmacsha256_BYTES, "[%d] k0p hmac_%d", ctx->index, i+1); return ret; } ptr+=toprfupdate_peer_mult2_msg_SIZE; } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *fails_len = msg->data; uint8_t *fails = fails_len+1; verify_commitments(ctx, "k0p", dealers, ctx->n+1, ctx->index, (*ctx->k0p_commitments), ctx->k0p_shares, fails_len, fails); #ifdef UNITTEST_CORRUPT corrupt_false_accuse(ctx, 2, 3, fails_len, fails); #endif //UNITTEST_CORRUPT if(0!=toprf_send_msg(output, toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx), toprfupdate_peer_verify_mult_shares_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Handle_Mult_Share_Complaints; return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_verify_mult_shares_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx) * ctx->n)) static TOPRF_Update_Err stp_verify_mult_shares_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); return stp_complaint_handler(ctx, input, input_len, output, output_len, "mult-verify1 broadcast mult complaints of peers", ctx->n, toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx), toprfupdate_peer_verify_mult_shares_msg, dealers, TOPRF_Update_STP_Route_ZK_Challenge_Commitments, TOPRF_Update_STP_Broadcast_Mult_Defenses); } static TOPRF_Update_Err peer_mult_fork(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); return peer_complaint_handler(ctx, input, input_len, "mult-verify2 receive mult complaints broadcast", toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx), toprfupdate_peer_verify_mult_shares_msg, dealers, TOPRF_Update_Peer_Send_ZK_Challenge_Commitments, TOPRF_Update_Peer_Defend_Mult_Accusations); } static TOPRF_Update_Err peer_mult_defend(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult-defend1 disclose share encryption key for contested Mult shares\x1b[0m\n", ctx->index); if(output_len != toprf_update_peer_output_size(ctx)) return TOPRF_Update_Err_OSize; if(output_len == 0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] nothing to defend against, no message to send\n", ctx->index); } ctx->step = TOPRF_Update_Peer_Check_Mult_Shares; return 0; } // send out all shares that belong to peers that complained. TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; for(int i=0;imy_p_complaints_len;i++) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;36m[%d] defending against k0p complaint from %d\x1b[0m\n", ctx->index, ctx->my_p_complaints[i]); *wptr++ = ctx->my_p_complaints[i]; // reveal key for noise wrapped share sent previously derive_key((*ctx->noise_outs)[ctx->my_p_complaints[i]-1],ctx->my_p_complaints[i],"k0p",wptr); wptr+=dkg_noise_key_SIZE; memcpy(wptr, (*ctx->encrypted_shares)[ctx->my_p_complaints[i]-1], toprf_update_encrypted_shares_SIZE); wptr+=toprf_update_encrypted_shares_SIZE; } if(0!=toprf_send_msg(output, output_len, toprfupdate_peer_share_mult_key_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Check_Mult_Shares; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_broadcast_mult_defenses(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] mult-defend2 broadcast mult defenses\x1b[0m\n"); if(input_len != toprf_update_stp_input_size(ctx)) return TOPRF_Update_Err_ISize; if(output_len != toprf_update_stp_output_size(ctx)) return TOPRF_Update_Err_OSize; unsigned int ctr1[dealers]; memset(ctr1,0,sizeof(ctr1)); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff)-1U); if(peer>=dealers) return TOPRF_Update_Err_OOB; ctr1[peer]++; } ctx->y2_complaints_len = 0; memset(ctx->y2_complaints, 0, ctx->n*2); const uint8_t *ptr = input; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; size_t msg_size; for(uint8_t i=0;idata; TOPRF_Update_Err ret = stp_check_defenses(ctx, dealers, ctx->n + 1, 0, ctr1[i], i, 1, ctx->p_share_macs, *ctx->k0p_commitments, &ctx->y2_complaints_len, ctx->y2_complaints, &dptr); if(TOPRF_Update_Err_OK != ret) { return ret; } memcpy(wptr, ptr, msg_size); wptr+=msg_size; } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; dump((uint8_t*)ctx->y2_complaints, ctx->y2_complaints_len*2, "k0p recover dealers:"); if(0!=toprf_send_msg(output, output_len, toprfupdate_stp_bc_mult_key_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; // add broadcast msg to transcript update_transcript(&ctx->transcript_state, output, output_len); if(ctx->y2_complaints_len==0) { ctx->step = TOPRF_Update_STP_Route_ZK_Challenge_Commitments; } else { ctx->step = TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_check_mult_shares(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult-defend3 verify disclosed mult shares\x1b[0m\n", ctx->index); if(input_len != toprf_update_peer_input_size(ctx)) return TOPRF_Update_Err_ISize; // verify STP message envelope const uint8_t *ptr=NULL; TOPRF_Update_Err ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_mult_key_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; unsigned int ctr1[dealers]; aggregate_complaints(dealers,ctr1,&ctx->p_complaints_len,ctx->p_complaints); size_t msg_size; for(uint8_t i=0;idata; ret = check_defenses(ctx, dealers, ctx->n+1, 0, ctr1[i], i, 1, ctx->p_share_macs, *ctx->k0p_commitments, &ctx->p_complaints_len, ctx->p_complaints, &dptr); if(TOPRF_Update_Err_OK != ret) return ret; } dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*2, "k0p recover dealers:"); if(ctx->p_complaints_len > 0) { ctx->step = TOPRF_Update_Peer_Disclose_Mult_Shares; } else { ctx->step = TOPRF_Update_Peer_Send_ZK_Challenge_Commitments; } return TOPRF_Update_Err_OK; } static uint8_t unique_complaints(const uint8_t n, const uint16_t complaints_len, const uint16_t complaints[complaints_len]) { if(n==0) return 0xff; uint8_t total=0; uint8_t peer[n]; memset(peer, 0, sizeof peer); for(unsigned i=0;in || accused == 0) return 0xff; // we set them ourselves. this should not happen peer[accused-1]=1; } for(unsigned i=0;in, ctx->p_complaints_len, ctx->p_complaints) #define unique_y2_complaints(ctx) unique_complaints(ctx->n, ctx->y2_complaints_len, ctx->y2_complaints) static TOPRF_Update_Err disclose_shares(const uint8_t n, const uint8_t self, const char *type, const uint16_t complaints_len, const uint16_t complaints[complaints_len], TOPRF_Share shares[][2], uint8_t **wptr) { if(n==0) return 0xff; int sent[n]; memset(sent,0,sizeof sent); for(unsigned i=0;i=n) return TOPRF_Update_Err_OOB; if(sent[peer-1]!=0) continue; sent[peer-1]=1; memcpy(*wptr, shares[peer-1], TOPRF_Share_BYTES*2); dump(*wptr, TOPRF_Share_BYTES*2, "[%d] disclosing %s share of %d", self, type, peer); *wptr+=TOPRF_Share_BYTES*2; } return TOPRF_Update_Err_OK; } #define toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) \ + unique_p_complaints(ctx) * toprf_update_encrypted_shares_SIZE) static TOPRF_Update_Err peer_disclose_mult_shares(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult-reconst1 disclose shares to reconstruct Mult secrets of cheaters\x1b[0m\n", ctx->index); if(output_len != toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; TOPRF_Update_Err ret; ret = disclose_shares(ctx->n, ctx->index, "k0p", ctx->p_complaints_len, ctx->p_complaints, (*ctx->k0p_shares), &wptr); if(ret != TOPRF_Update_Err_OK) return ret; if(0!=toprf_send_msg(output, output_len, toprfupdate_peer_reconst_mult_shares_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Reconstruct_Mult_Shares; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err reconstruct(const uint8_t n, const uint8_t t, const char *type, const uint16_t complaints_len, const uint16_t complaints[complaints_len], const TOPRF_Share shares[unique_complaints(n, complaints_len, complaints)][n][2], const uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint8_t secrets[unique_complaints(n, complaints_len, complaints)][2][crypto_core_ristretto255_SCALARBYTES]) { const uint8_t dealers = (uint8_t) ((t-1U)*2 + 1U); uint8_t (*c)[dealers][n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][n+1][crypto_core_ristretto255_BYTES]) commitments; uint8_t seen[n]; memset(seen, 0, sizeof seen); for(unsigned i=0, share_idx=0;i> 8); if(accused == 0 || accused>=n) return TOPRF_Update_Err_OOB; if(seen[accused-1]) continue; seen[accused-1]=1; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] reconstructing %s share/commitment of dealer %d accused by %d\n", type, accused, accuser); uint8_t t1=t; for(t1=t;t1=n) return TOPRF_Update_Err_Reconstruct; if(secrets!=NULL) { if(0!=dkg_vss_reconstruct(t1, 0, n, shares[share_idx], &(*commitments)[(n+1) * (accused - 1) + 1], secrets[i][0], secrets[i][1])) return TOPRF_Update_Err_Reconstruct; dump((uint8_t*) secrets[i], 2*crypto_core_ristretto255_SCALARBYTES, "[!] reconstructed secret of %d", accused); } if(0!=dkg_vss_reconstruct(t1, accuser, n, shares[share_idx], &(*commitments)[(n+1) * (accused - 1) + 1], r[0].value, r[1].value)) return TOPRF_Update_Err_Reconstruct; dump((uint8_t*) &r, sizeof r, "[!] reconstructed share of %d - accused by %d", accused, accuser); if(0!=dkg_vss_verify_commitment((*c)[accused-1][accuser],r)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] failed to validate commitment for reconstructed %s share from %d\n"NORMAL, type, accused); dump((*c)[accused-1][accuser], 32, "[!] commitment", accuser); if(0!=dkg_vss_commit(r[0].value, r[1].value,(*c)[accused-1][accuser])) return TOPRF_Update_Err_VSSCommit; dump((*c)[accused-1][accuser], 32, "[!] corrected ", accuser); // todo check vsps on these commitments and if that fails return TOPRF_Update_Err_BadReconstruct; // better to do this only after all reconstructions have been // done in case multiple shares from the same dealer have // adjusted commitments. } share_idx++; } return TOPRF_Update_Err_OK; } #define toprfupdate_stp_reconst_mult_shares_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) \ + unique_y2_complaints(ctx) * toprf_update_encrypted_shares_SIZE) static TOPRF_Update_Err stp_broadcast_reconst_mult_shares(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "mult-reconst2 broadcast shares to reconstruct mult secrets of cheating dealers", ctx->n, toprfupdate_stp_reconst_mult_shares_msg_SIZE(ctx), toprfupdate_peer_reconst_mult_shares_msg, TOPRF_Update_STP_Route_ZK_Challenge_Commitments); if(ret != TOPRF_Update_Err_OK) return ret; TOPRF_Share k0p_shares[unique_y2_complaints(ctx)][ctx->n][2]; const uint8_t *ptr = input; for(uint8_t i=0;in;i++,ptr+=toprfupdate_stp_reconst_mult_shares_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; const uint8_t *dptr = msg->data; for(unsigned j=0;jfrom-1], dptr, TOPRF_Share_BYTES*2); dptr+=TOPRF_Share_BYTES*2; } } ret = reconstruct(ctx->n, ctx->t,"k0p", ctx->y2_complaints_len,ctx->y2_complaints,k0p_shares,ctx->k0p_commitments, NULL); return ret; } static TOPRF_Update_Err peer_reconstruct(TOPRF_Update_PeerState *ctx, const char *type, uint16_t *complaints_len, uint16_t complaints[*complaints_len], const TOPRF_Share shares[unique_complaints(ctx->n, *complaints_len, complaints)][ctx->n][2], const uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], TOPRF_Share (*my_shares)[2]) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) commitments; uint8_t seen[ctx->n]; memset(seen, 0, sizeof seen); for(unsigned i=0, share_idx=0;i<*complaints_len;i++) { TOPRF_Share r[2]; const uint8_t accused = (uint8_t) (complaints[i] & 0xff); const uint8_t accuser = (uint8_t) (complaints[i] >> 8); if(accused == 0 || accused>=ctx->n) return TOPRF_Update_Err_OOB; if(seen[accused-1]) continue; seen[accused-1]=1; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] reconstructing %s share/commitment of dealer %d accused by %d\n", ctx->index, type, accused, accuser); uint8_t t1=ctx->t; for(t1=ctx->t;t1n;t1++) { fprintf(liboprf_log_file, "trying degree t+%d\n", t1-ctx->t); if(0!=dkg_vss_reconstruct(t1, 0, ctx->n, shares[share_idx], &(*commitments)[(ctx->n+1) * (accused - 1) + 1], r[0].value, r[1].value)) continue; if(0!=dkg_vss_verify_commitment((*c)[accused-1][0],r)) continue; break; } if(t1>=ctx->n) return TOPRF_Update_Err_Reconstruct; if(0!=dkg_vss_reconstruct(t1, accuser, ctx->n, shares[share_idx], &(*commitments)[(ctx->n+1) * (accused - 1) + 1], r[0].value, r[1].value)) return TOPRF_Update_Err_Reconstruct; dump((uint8_t*) &r, sizeof r, "[%d] reconstructed share of %d - accused by %d", ctx->index, accused, accuser); if(0!=dkg_vss_verify_commitment((*c)[accused-1][accuser],r)) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed to validate commitment for reconstructed %s share from %d\n"NORMAL, ctx->index, type, accused); dump((*c)[accused-1][accuser], 32, "[%d] commitment", ctx->index); if(0!=dkg_vss_commit(r[0].value, r[1].value, (*c)[accused-1][accuser])) return TOPRF_Update_Err_VSSCommit; dump((*c)[accused-1][accuser], 32, "[%d] corrected ", ctx->index); // todo check vsps on these commitments and if that fails return TOPRF_Update_Err_BadReconstruct; // better to do this only after all reconstructions have been // done in case multiple shares from the same dealer have // adjusted commitments. } int incorrect = 0; for(unsigned j=0;j<*complaints_len;j++) { if((accused == (complaints[j] & 0xff)) && (ctx->index == (complaints[j] >> 8))) { incorrect = 1; break; } } if(accuser != ctx->index) { if(0!=dkg_vss_reconstruct(t1, ctx->index, ctx->n, shares[share_idx], &(*commitments)[(ctx->n+1) * (accused - 1) + 1], r[0].value, r[1].value)) return TOPRF_Update_Err_Reconstruct; } const int diff = (memcmp(r[0].value, my_shares[accused - 1][0].value, 32)!=0) | (memcmp(r[1].value, my_shares[accused - 1][1].value, 32)!=0) << 1; if(diff!=0) { if(!incorrect) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] reconstructed a different %s share from %d than " \ "was previously validated using its commitment, was accused by %d\n"NORMAL, ctx->index, type, accused, accuser); return TOPRF_Update_Err_BadReconstruct; } if(diff & 1) { dump(r[0].value, 32, "[%d] reconstructed s share %d", ctx->index, accused); memcpy(my_shares[accused - 1][0].value, r[0].value, 32); } if(diff & 2) { dump(r[1].value, 32, "[%d] reconstructed r share %d", ctx->index, accused); memcpy(my_shares[accused - 1][1].value, r[1].value, 32); } } share_idx++; } *complaints_len = 0; memset(complaints, 0, ctx->n*2); return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_send_zk_chalcoms(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len); static TOPRF_Update_Err peer_reconst_mult_shares(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] mult-reconst3 reconstruct secrets of cheating mult dealers\x1b[0m\n", ctx->index); if(input_len!= sizeof(TOPRF_Update_Message) + toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx) * ctx->n) return TOPRF_Update_Err_ISize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_reconst_mult_shares_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; TOPRF_Share k0p_shares[unique_p_complaints(ctx)][ctx->n][2]; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx),toprfupdate_peer_reconst_mult_shares_msg,i+1,0xff)) continue; const uint8_t *dptr = msg->data; for(unsigned j=0;jfrom-1], dptr, TOPRF_Share_BYTES*2); dptr+=TOPRF_Share_BYTES*2; } } ret = peer_reconstruct(ctx,"k0p", &ctx->p_complaints_len,ctx->p_complaints,k0p_shares,ctx->k0p_commitments,(*ctx->k0p_shares)); if(ret != TOPRF_Update_Err_OK) return ret; // reset my_complaints ctx->my_p_complaints_len = 0; memset(ctx->my_p_complaints, 0, ctx->n); return peer_send_zk_chalcoms(ctx, output, output_len); } //ctx->step = TOPRF_Update_Peer_Send_ZK_Challenge_Commitments; #define toprfupdate_peer_zkp1_msg_SIZE (sizeof(TOPRF_Update_Message) + crypto_scalarmult_ristretto255_BYTES) static TOPRF_Update_Err peer_send_zk_chalcoms(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk1 send ZK challenge commitments\x1b[0m\n", ctx->index); if(output_len != toprfupdate_peer_zkp1_msg_SIZE) return TOPRF_Update_Err_OSize; // generate 2x nonces for ZK proof challenge, broadcast a commitment to it. TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; crypto_core_ristretto255_scalar_random(ctx->zk_chal_nonce[0]); crypto_core_ristretto255_scalar_random(ctx->zk_chal_nonce[1]); if(0!=dkg_vss_commit(ctx->zk_chal_nonce[0], ctx->zk_chal_nonce[1], msg->data)) return TOPRF_Update_Err_VSSCommit; //dump(msg->data + i*crypto_scalarmult_ristretto255_BYTES, crypto_scalarmult_ristretto255_BYTES, "index, i); if(0!=toprf_send_msg(output, toprfupdate_peer_zkp1_msg_SIZE, toprfupdate_peer_zkp1_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Send_ZK_Commitments; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step29_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { return stp_broadcast(ctx, input, input_len, output, output_len, "step 29. broadcast zk challenge commitments", ctx->n, toprfupdate_peer_zkp1_msg_SIZE, toprfupdate_peer_zkp1_msg, TOPRF_Update_STP_Route_ZK_commitments); } #define toprfupdate_peer_zkp2_msg_SIZE (sizeof(TOPRF_Update_Message) + 2 * 3 * crypto_scalarmult_ristretto255_SCALARBYTES) static TOPRF_Update_Err peer_step30_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk2 everyone receives all e_j nonces, dealers broadcast ZK commitments\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp1_msg_SIZE * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != isdealer(ctx->index, ctx->t) * toprfupdate_peer_zkp2_msg_SIZE) return TOPRF_Update_Err_OSize; const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_zkp1_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; uint8_t (*zk_challenge_nonce_commitments)[ctx->n][crypto_scalarmult_ristretto255_BYTES] = (uint8_t (*)[ctx->n][crypto_scalarmult_ristretto255_BYTES]) (ctx->zk_challenge_nonce_commitments); for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_zkp1_msg_SIZE) { const TOPRF_Update_Message* msg27 = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_zkp1_msg_SIZE,toprfupdate_peer_zkp1_msg,i+1,0xff)) continue; //dump(msg27->data, crypto_scalarmult_ristretto255_BYTES, "zk_e_nonce_%d commitment", i); memcpy((*zk_challenge_nonce_commitments)[i], msg27->data, crypto_scalarmult_ristretto255_BYTES); //dump((*ctx->zk_challenge_nonce_commitments)[i], crypto_scalarmult_ristretto255_BYTES, ">zk_challenge_commitment[%d]", i+1); } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(ctx->index>dealers) { // non-dealers are done ctx->step = TOPRF_Update_Peer_Send_ZK_nonces; return TOPRF_Update_Err_OK; } // dealers only // also distribute k0*p shares to all uint8_t *wptr = output; TOPRF_Update_Message* msg29 = (TOPRF_Update_Message*) wptr; //dump((*ctx->p_commitments)[ctx->index-1], crypto_core_ristretto255_BYTES, "B[%d]", ctx->index); uint8_t (*msgs)[crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[crypto_scalarmult_ristretto255_SCALARBYTES]) msg29->data; if(0!=toprf_mpc_ftmult_zk_commitments((*ctx->p_commitments)[ctx->index-1], ctx->zk_params.d, // uint8_t d[crypto_scalarmult_ristretto255_SCALARBYTES], ctx->zk_params.s, // uint8_t s[crypto_scalarmult_ristretto255_SCALARBYTES], ctx->zk_params.x, // uint8_t x[crypto_scalarmult_ristretto255_SCALARBYTES], ctx->zk_params.s_1, // uint8_t s_1[crypto_scalarmult_ristretto255_SCALARBYTES], ctx->zk_params.s_2, // uint8_t s_2[crypto_scalarmult_ristretto255_SCALARBYTES], msgs)) { return TOPRF_Update_Err_FTMULTZKCommitments; } //dump(ctx->zk_params.d, crypto_core_ristretto255_SCALARBYTES, "[%d] d[%d]", ctx->index, ctx->index); //dump(ctx->zk_params.s, crypto_core_ristretto255_SCALARBYTES, "[%d] s[%d]", ctx->index, ctx->index); //dump(ctx->zk_params.x, crypto_core_ristretto255_SCALARBYTES, "[%d] x[%d]", ctx->index, ctx->index); //dump(ctx->zk_params.s_1, crypto_core_ristretto255_SCALARBYTES, "[%d] s_1[%d]", ctx->index, ctx->index); //dump(ctx->zk_params.s_2, crypto_core_ristretto255_SCALARBYTES, "[%d] s_2[%d]", ctx->index, ctx->index); //dump(msgs[0], crypto_scalarmult_ristretto255_SCALARBYTES, "[%d] M[%d]", ctx->index, ctx->index); //dump(msgs[1], crypto_scalarmult_ristretto255_SCALARBYTES, "[%d] M1[%d]", ctx->index, ctx->index); //dump(msgs[2], crypto_scalarmult_ristretto255_SCALARBYTES, "[%d] M2[%d]", ctx->index, ctx->index); if(0!=toprf_send_msg(wptr, toprfupdate_peer_zkp2_msg_SIZE, toprfupdate_peer_zkp2_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Send_ZK_nonces; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step31_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "zk3 broadcast ZK commitments", dealers, toprfupdate_peer_zkp2_msg_SIZE, toprfupdate_peer_zkp2_msg, TOPRF_Update_STP_Broadcast_ZK_nonces); if(ret != TOPRF_Update_Err_OK) return ret; uint8_t (*zk_challenge_commitments)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_commitments; const uint8_t *ptr = input; for(uint8_t i=0;idata, crypto_scalarmult_ristretto255_BYTES, "zk_e_nonce_%d commitment", i); memcpy((*zk_challenge_commitments)[i], msg->data, 3*crypto_scalarmult_ristretto255_SCALARBYTES); //dump((uint8_t*) (*zk_challenge_commitments)[i], 3*crypto_scalarmult_ristretto255_SCALARBYTES, "zk_chal_com[%d]",i); } return TOPRF_Update_Err_OK; } #define toprfupdate_peer_zkp3_msg_SIZE (sizeof(TOPRF_Update_Message) + 4*crypto_scalarmult_ristretto255_SCALARBYTES) static TOPRF_Update_Err peer_step32_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk4 receive dealers ZK commitments, broadcast zk nonce\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp2_msg_SIZE * dealers) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_peer_zkp3_msg_SIZE) return TOPRF_Update_Err_OSize; const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_zkp2_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; uint8_t (*zk_challenge_commitments)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_commitments; for(uint8_t i=0;idata, crypto_scalarmult_ristretto255_BYTES, "zk_e_nonce_%d commitment", i); memcpy((*zk_challenge_commitments)[i], msg29->data, 3*crypto_scalarmult_ristretto255_SCALARBYTES); //uint8_t (*msgs)[3][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[3][crypto_scalarmult_ristretto255_SCALARBYTES]) (*ctx->zk_challenge_commitments); //dump(msgs[i][0], crypto_scalarmult_ristretto255_SCALARBYTES, "[%d] M[%d]", ctx->index, i+1); //dump(msgs[i][1], crypto_scalarmult_ristretto255_SCALARBYTES, "[%d] M1[%d]", ctx->index, i+1); //dump(msgs[i][2], crypto_scalarmult_ristretto255_SCALARBYTES, "[%d] M2[%d]", ctx->index, i+1); // } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; TOPRF_Update_Message* msg31 = (TOPRF_Update_Message*) output; uint8_t *dptr = msg31->data; memcpy(dptr, ctx->zk_chal_nonce[0], 2*crypto_core_ristretto255_SCALARBYTES); //dump(dptr, 2*crypto_core_ristretto255_SCALARBYTES, "index); if(0!=toprf_send_msg(output, toprfupdate_peer_zkp3_msg_SIZE, toprfupdate_peer_zkp3_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Send_ZK_proofs; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step33_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "zk5 broadcast ZK nonces", ctx->n, toprfupdate_peer_zkp3_msg_SIZE, toprfupdate_peer_zkp3_msg, TOPRF_Update_STP_Broadcast_ZK_Proofs); if(ret!=TOPRF_Update_Err_OK) return ret; const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); const uint8_t *ptr = input; uint8_t zk_challenge_nonces[ctx->n][2][crypto_scalarmult_ristretto255_SCALARBYTES]; // todo? we skip verifying the challenge_nonce commitments, but we also don't base any decision on this // we do this merely to anticipate how many dealers will be exposed/reconstructed for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_zkp3_msg_SIZE) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; const uint8_t *dptr=msg->data; memcpy(zk_challenge_nonces[i], dptr, 2*crypto_scalarmult_ristretto255_SCALARBYTES); //dump(dptr, 2*crypto_core_ristretto255_SCALARBYTES, ">zk_nonce[%d][0]", i+1); } uint8_t (*zk_challenge_e_i)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_e_i; for(unsigned dealer=0;dealern;i++) { if(dealer==i) continue; crypto_core_ristretto255_scalar_add((*zk_challenge_e_i)[dealer], (*zk_challenge_e_i)[dealer], zk_challenge_nonces[i][0]); } //dump((*zk_challenge_e_i)[p][dealer], crypto_scalarmult_ristretto255_SCALARBYTES, "zk%d_e_%d", p+1, dealer+1); } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err aggregate_zk_challenges(TOPRF_Update_PeerState *ctx, const uint8_t dealers, const uint8_t n, const uint8_t zk_challenge_nonces[n][2][crypto_scalarmult_ristretto255_SCALARBYTES], const uint8_t zk_challenge_nonce_commitments[n][crypto_scalarmult_ristretto255_BYTES], uint8_t zk_challenge_e_i[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]) { // P_i verifies commitments for e_j,r_j // P_i computes e'_i: // e'_i = Ξ£ e_j // j!=i for(unsigned dealer=0;dealert-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk6 receive ZK nonces, dealers broadcast zk proof\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp3_msg_SIZE * ctx->n) return TOPRF_Update_Err_ISize; if(output_len != isdealer(ctx->index, ctx->t) * toprfupdate_peer_zkp4_msg_SIZE) return TOPRF_Update_Err_OSize; const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx, input, input_len, toprfupdate_stp_bc_zkp3_msg, &ptr); if(ret!=TOPRF_Update_Err_OK) return ret; uint8_t (*zk_challenge_nonces)[ctx->n][2][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[ctx->n][2][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_nonces; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_zkp3_msg_SIZE) { const TOPRF_Update_Message* msg31 = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_zkp3_msg_SIZE,toprfupdate_peer_zkp3_msg,i+1,0xff)) continue; const uint8_t *dptr=msg31->data; memcpy((*zk_challenge_nonces)[i], dptr, 2*crypto_scalarmult_ristretto255_SCALARBYTES); //dump(dptr, 2*crypto_core_ristretto255_SCALARBYTES, ">zk_nonce[%d][0]", i+1); } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; uint8_t (*zk_challenge_e_i)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_e_i; uint8_t (*zk_challenge_nonce_commitments)[ctx->n][crypto_scalarmult_ristretto255_BYTES] = (uint8_t (*)[ctx->n][crypto_scalarmult_ristretto255_BYTES]) ctx->zk_challenge_nonce_commitments; ret = aggregate_zk_challenges(ctx, dealers, ctx->n, (*zk_challenge_nonces), (*zk_challenge_nonce_commitments), (*zk_challenge_e_i)); if(ret!=TOPRF_Update_Err_OK) return ret; if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; if(ctx->index>dealers) { // non-dealers are done ctx->step = TOPRF_Update_Peer_Verify_ZK_proofs; return TOPRF_Update_Err_OK; } // dealers only TOPRF_Update_Message* msg31 = (TOPRF_Update_Message*) output; uint8_t *wptr=msg31->data; wptr=gen_zk_witnesses(ctx->index, dealers, ctx->kc0_share, ctx->p_share, ctx->k0p_tau, (*zk_challenge_e_i), ctx->zk_params, (*ctx->lambdas), wptr); if(0!=toprf_send_msg(output, toprfupdate_peer_zkp4_msg_SIZE, toprfupdate_peer_zkp4_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Verify_ZK_proofs; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err zk_verify_proof(TOPRF_Update_PeerState *ctx, const uint8_t self, const uint8_t prover, const uint8_t A_i[crypto_core_ristretto255_BYTES], const uint8_t B_i[crypto_core_ristretto255_BYTES], const uint8_t C_i0[crypto_core_ristretto255_BYTES], const uint8_t e_i[crypto_scalarmult_ristretto255_SCALARBYTES], const uint8_t zk_challenge_commitments[3][crypto_scalarmult_ristretto255_SCALARBYTES], const uint8_t lambda[crypto_core_ristretto255_SCALARBYTES], const TOPRF_Update_ZK_proof proof, uint8_t *fails) { uint8_t v0[crypto_scalarmult_ristretto255_BYTES]; uint8_t v1[crypto_scalarmult_ristretto255_BYTES]; const uint8_t *M = zk_challenge_commitments[0]; const uint8_t *M1 = zk_challenge_commitments[1]; const uint8_t *M2 = zk_challenge_commitments[2]; // g^y * h^w == M * B^e'_i if(0!=dkg_vss_commit(proof.y, proof.w, v0)) return TOPRF_Update_Err_VSSCommit; if(crypto_scalarmult_ristretto255(v1, e_i, B_i)) return TOPRF_Update_Err_InvPoint; crypto_core_ristretto255_add(v1, M, v1); if(memcmp(v1, v0, crypto_scalarmult_ristretto255_BYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed g^y * h^w == M * B^e'_i for dealer %d\n"NORMAL, self, prover+1); dump(v1, crypto_scalarmult_ristretto255_BYTES, "lhs"); dump(v0, crypto_scalarmult_ristretto255_BYTES, "rhs"); fails[1+fails[0]++]=prover+1; if(self!=0 && peer_add_cheater(ctx, 1, prover+1, 0xff) == NULL) return TOPRF_Update_Err_CheatersFull; return TOPRF_Update_Err_OK; } // g^z * h^w_1 == M_1 * A^e'_i if(0!=dkg_vss_commit(proof.z, proof.w_1, v0)) return TOPRF_Update_Err_VSSCommit; if(crypto_scalarmult_ristretto255(v1, e_i, A_i)) return TOPRF_Update_Err_InvPoint; if(crypto_scalarmult_ristretto255(v1, lambda, v1)) return TOPRF_Update_Err_InvPoint; crypto_core_ristretto255_add(v1, M1, v1); if(memcmp(v1, v0, crypto_scalarmult_ristretto255_BYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed g^z * h^w_1 == M_1 * A^e'_i for dealer %d\n"NORMAL, self, prover+1); dump(v1, crypto_scalarmult_ristretto255_BYTES, "lhs"); dump(v0, crypto_scalarmult_ristretto255_BYTES, "rhs"); fails[1+fails[0]++]=prover+1; if(self!=0 && peer_add_cheater(ctx, 3, prover+1, 0xff) == NULL) return TOPRF_Update_Err_CheatersFull; return TOPRF_Update_Err_OK; } // B^z * h^w_2 == M_2 * C^e'_i if(crypto_scalarmult_ristretto255(v0, proof.z, B_i)) return TOPRF_Update_Err_InvPoint; // we abuse v1 as a temp storage, v1 = h^w_2 if(crypto_scalarmult_ristretto255(v1, proof.w_2, H)) return TOPRF_Update_Err_InvPoint; crypto_core_ristretto255_add(v0, v0, v1); if(crypto_scalarmult_ristretto255(v1, e_i, C_i0)) return TOPRF_Update_Err_InvPoint; crypto_core_ristretto255_add(v1, M2, v1); if(memcmp(v1, v0, crypto_scalarmult_ristretto255_BYTES)!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed B^z * h^w_2 == M_2 * C^e'_i for dealer %d\n"NORMAL, self, prover+1); dump(v1, crypto_scalarmult_ristretto255_BYTES, "lhs"); dump(v0, crypto_scalarmult_ristretto255_BYTES, "rhs"); fails[1+fails[0]++]=prover+1; if(self!=0 && peer_add_cheater(ctx, 5, prover+1, 0xff) == NULL) return TOPRF_Update_Err_CheatersFull; return TOPRF_Update_Err_OK; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err stp_step35_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "zk7 broadcast ZK proofs", dealers, toprfupdate_peer_zkp4_msg_SIZE, toprfupdate_peer_zkp4_msg, TOPRF_Update_STP_Broadcast_Mult_Ci); if(ret!=TOPRF_Update_Err_OK) return ret; uint8_t fails[dealers+1]; memset(fails, 0, sizeof fails); const uint8_t (*zk_challenge_commitments)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES] = (const uint8_t (*)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_commitments; const uint8_t (*zk_challenge_e_i)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_e_i; const uint8_t *ptr = input; uint8_t indexes[dealers]; for(uint8_t i=0;idata; ret = zk_verify_proof(NULL, 0, i, (*ctx->kc0_commitments)[i], (*ctx->p_commitments)[i], (*ctx->k0p_commitments)[i*(ctx->n+1)], (*zk_challenge_e_i)[i], (*zk_challenge_commitments)[i], lambdas[0][i], (*proof), fails); if(ret != TOPRF_Update_Err_OK) return ret; } ctx->p_complaints_len = 0; const uint8_t *fails_len = fails; const uint8_t *xfails = fails_len+1; handle_complaints(dealers, 0, *fails_len, xfails, &ctx->p_complaints_len, ctx->p_complaints, 0, 0, 0); if(ctx->p_complaints_len != 0) { dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*sizeof(uint16_t), "[!] complaints"); ctx->step = TOPRF_Update_STP_Broadcast_ZK_Disclosures; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_step36_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk8 verify ZK proofs, accuse cheaters\x1b[0m\n", ctx->index); if(input_len != sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp4_msg_SIZE * dealers) return TOPRF_Update_Err_ISize; //const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx, input, input_len, toprfupdate_stp_bc_zkp4_msg, &ptr); if(ret!=TOPRF_Update_Err_OK) return ret; uint8_t fails[dealers+1]; memset(fails, 0, sizeof fails); const uint8_t (*zk_challenge_commitments)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES] = (const uint8_t (*)[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_commitments; const uint8_t (*zk_challenge_e_i)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES] = (uint8_t (*)[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]) ctx->zk_challenge_e_i; for(uint8_t i=0;idata; ret = zk_verify_proof(ctx, ctx->index, i, (*ctx->kc0_commitments)[i], (*ctx->p_commitments)[i], (*ctx->k0p_commitments)[i*(ctx->n+1)], (*zk_challenge_e_i)[i], (*zk_challenge_commitments)[i], (*ctx->lambdas)[i], (*proof), fails); if(ret != TOPRF_Update_Err_OK) return ret; } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; ctx->p_complaints_len = 0; const uint8_t *fails_len = &fails[0]; const uint8_t *xfails = fails_len+1; handle_complaints(dealers, ctx->index, *fails_len, xfails, &ctx->p_complaints_len, ctx->p_complaints, 0, 0, 0); ctx->prev = ctx->step; if(ctx->p_complaints_len == 0) { ctx->step = TOPRF_Update_Peer_Send_Mult_Ci; } else { dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*sizeof(uint16_t), "[%d] complaints", ctx->index); ctx->step = TOPRF_Update_Peer_Disclose_ZK_Cheaters; } return TOPRF_Update_Err_OK; } #define toprfupdate_peer_zk_disclose_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) \ + ctx->p_complaints_len * TOPRF_Share_BYTES * 2 ) static TOPRF_Update_Err peer_zkproof_disclose(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk-reconst1 disclose shares of dealers unable to prove c=a*b\n"NORMAL, ctx->index); if(output_len != toprf_update_peer_output_size(ctx)) return TOPRF_Update_Err_OSize; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; TOPRF_Update_Err ret; ret = disclose_shares(ctx->n, ctx->index, "k0p", ctx->p_complaints_len, ctx->p_complaints, (*ctx->k0p_shares), &wptr); if(ret != TOPRF_Update_Err_OK) return ret; if(0!=toprf_send_msg(output, output_len, toprfupdate_peer_zk_disclose_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Reconstruct_ZK_Shares; return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_zkp_disclose_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (toprfupdate_peer_zk_disclose_msg_SIZE(ctx) * ctx->n)) static TOPRF_Update_Err stp_bc_zk_disclosures(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "zk-reconst2 broadcast shares of dealers failing ZK proofs", ctx->n, toprfupdate_peer_zk_disclose_msg_SIZE(ctx), toprfupdate_peer_zk_disclose_msg, TOPRF_Update_STP_Broadcast_Mult_Ci); if(ret != TOPRF_Update_Err_OK) return ret; TOPRF_Share k0p_shares[ctx->p_complaints_len][ctx->n][2]; const uint8_t *ptr = input; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_zk_disclose_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; const uint8_t *dptr = msg->data; for(unsigned j=0;jp_complaints_len;j++) { memcpy(k0p_shares[j][msg->from-1], dptr, TOPRF_Share_BYTES*2); dptr+=TOPRF_Share_BYTES*2; } } TOPRF_Share secret[2]; const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) (*ctx->k0p_commitments); for(unsigned i=0;ip_complaints_len;i++) { const uint8_t accused = (uint8_t) (ctx->p_complaints[i] & 0xff); if(0!=dkg_vss_reconstruct(ctx->t, 0, ctx->n, k0p_shares[i], &(*c)[accused-1][1], secret[0].value, secret[1].value)) return TOPRF_Update_Err_Reconstruct; dump(secret[0].value, sizeof secret[0].value, "reconstructed lab"); if(0!=dkg_vss_commit(secret[0].value, secret[1].value, (*c)[accused-1][0])) return TOPRF_Update_Err_VSSCommit; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_step39_handler(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len); static TOPRF_Update_Err peer_reconst_zk_shares(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] zk-reconst3 reconstruct secrets of dealers failing ZK proof\x1b[0m\n", ctx->index); if(input_len!= sizeof(TOPRF_Update_Message) + toprfupdate_peer_zk_disclose_msg_SIZE(ctx) * ctx->n) return TOPRF_Update_Err_ISize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_zk_disclose_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; TOPRF_Share k0p_shares[ctx->p_complaints_len][ctx->n][2]; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_zk_disclose_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_zk_disclose_msg_SIZE(ctx),toprfupdate_peer_zk_disclose_msg,i+1,0xff)) continue; const uint8_t *dptr = msg->data; for(unsigned j=0;jp_complaints_len;j++) { memcpy(k0p_shares[j][msg->from-1], dptr, TOPRF_Share_BYTES*2); dptr+=TOPRF_Share_BYTES*2; } } TOPRF_Share secret[2]; const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) (*ctx->k0p_commitments); for(unsigned i=0;ip_complaints_len;i++) { const uint8_t accused = (uint8_t) (ctx->p_complaints[i] & 0xff); if(0!=dkg_vss_reconstruct(ctx->t, 0, ctx->n, k0p_shares[i], &(*c)[accused-1][1], secret[0].value, secret[1].value)) return TOPRF_Update_Err_Reconstruct; dump(secret[0].value, sizeof secret[0].value, "reconstructed lab"); if(0!=dkg_vss_commit(secret[0].value, secret[1].value, (*c)[accused-1][0])) return TOPRF_Update_Err_VSSCommit; } // reset my_complaints ctx->my_p_complaints_len = 0; memset(ctx->my_p_complaints, 0, ctx->n); return peer_step39_handler(ctx, output, output_len); } static TOPRF_Update_Err compute_mul_share(const uint8_t dealers, const TOPRF_Share shares_i[][2], TOPRF_Share rshare[2], uint8_t commitment[crypto_scalarmult_ristretto255_BYTES]) { // step 3. P_i computes: // 2t+1 // Ξ³_i = Ξ£ c_ji // j=1 // which is a share of Ξ³ = Ξ±Ξ², via random polynomial of degree t and // 2t+1 // Ο„_i = Ξ£ Ο„_ji // j=1 memcpy((uint8_t*) &rshare[0], (const uint8_t*) &shares_i[0][0], TOPRF_Share_BYTES); memcpy((uint8_t*) &rshare[1], (const uint8_t*) &shares_i[0][1], TOPRF_Share_BYTES); for(unsigned i=1;it-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] final1 aggregate shares into final results and broadcast their commitment\x1b[0m\n", ctx->index); if(output_len != toprfupdate_peer_mult3_msg_SIZE) return TOPRF_Update_Err_OSize; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; int ret = compute_mul_share(dealers ,(*ctx->k0p_shares), ctx->k0p_share, ctx->k0p_commitment); memcpy(msg->data, ctx->k0p_commitment, crypto_scalarmult_ristretto255_BYTES); if(ret!=TOPRF_Update_Err_OK) return ret; // use this below to calculate all commitments for the other peers uint8_t Cx_i[crypto_scalarmult_ristretto255_BYTES]; uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) ctx->k0p_commitments; memcpy(Cx_i, (*c)[0][ctx->index], crypto_scalarmult_ristretto255_BYTES); for(unsigned j=1;jindex]); } // todo this check might not be needed if(memcmp(Cx_i, ctx->k0p_commitment, sizeof Cx_i) != 0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] failed to verify commitment for k0p share"NORMAL, ctx->index); // todo cheater handling? who would be the cheater here? return TOPRF_Update_Err_CommmitmentsMismatch; // probably cannot happen? } if(0!=toprf_send_msg(output, toprfupdate_peer_mult3_msg_SIZE, toprfupdate_peer_mult3_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Final_VSPS_Checks; return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_mult3_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult3_msg_SIZE * ctx->n) static TOPRF_Update_Err stp_step40_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] final2 broadcast final mult commitments\x1b[0m\n"); if(input_len != ctx->n * toprfupdate_peer_mult3_msg_SIZE) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_stp_bc_mult3_msg_SIZE(ctx)) return TOPRF_Update_Err_OSize; //const size_t cheaters = ctx->cheater_len; const uint8_t *ptr = input; uint8_t *wptr = ((TOPRF_Update_Message *) output)->data; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_mult3_msg_SIZE) { if(stp_recv_msg(ctx,ptr,toprfupdate_peer_mult3_msg_SIZE, toprfupdate_peer_mult3_msg,i+1,0xff)) continue; const TOPRF_Update_Message *msg = (const TOPRF_Update_Message *) ptr; // keep a copy of all commitments for final verification and for check before reconstructing r and r' memcpy((*ctx->k0p_final_commitments)[i], msg->data, crypto_scalarmult_ristretto255_BYTES); memcpy(wptr, ptr, toprfupdate_peer_mult3_msg_SIZE); wptr+=toprfupdate_peer_mult3_msg_SIZE; } //if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; uint8_t fails[dealers+1]; memset(fails, 0, sizeof fails); uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) ctx->k0p_commitments; TOPRF_Update_Err ret; ret = ft_or_full_vsps(ctx->n+1, ctx->t, dealers, 0, (*ctx->k0p_final_commitments), c, "VSPS failed k0p, doing full VSPS check on all dealers", "VSPS failed k0p", "ERROR, could not find and dealer commitments that fail the VSPS check", fails, &fails[1]); if(ret!=TOPRF_Update_Err_OK) return ret; ctx->p_complaints_len = 0; const uint8_t *fails_len = fails; const uint8_t *xfails = fails_len+1; handle_complaints(dealers, 0, *fails_len, xfails, &ctx->p_complaints_len, ctx->p_complaints, 0, 0, 0); if(0!=toprf_send_msg(output, output_len, toprfupdate_stp_bc_mult3_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; // add broadcast msg to transcript update_transcript(&ctx->transcript_state, output, output_len); if(ctx->p_complaints_len != 0) { dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*sizeof(uint16_t), "[!] complaints"); ctx->step = TOPRF_Update_STP_Broadcast_VSPS_Disclosures; } else { ctx->step = TOPRF_Update_STP_Reconstruct_Delta; } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_step41_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] final3 receive final mult commitments, fast-track VSPS final results\x1b[0m\n", ctx->index); if(input_len != toprfupdate_stp_bc_mult3_msg_SIZE(ctx)) return TOPRF_Update_Err_ISize; const size_t cheaters = ctx->cheater_len; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx, input, input_len, toprfupdate_stp_bc_mult3_msg, &ptr); if(ret!=TOPRF_Update_Err_OK) return ret; uint8_t (*C_i)[crypto_scalarmult_ristretto255_BYTES] = (*ctx->p_commitments); for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_mult3_msg_SIZE) { const TOPRF_Update_Message* msg37 = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_mult3_msg_SIZE,toprfupdate_peer_mult3_msg,i+1,0xff)) continue; memcpy(C_i[i], msg37->data, crypto_scalarmult_ristretto255_BYTES); } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; uint8_t fails[dealers+1]; memset(fails, 0, sizeof fails); //liboprf_debug=0; //for(unsigned i=0;it-1, (const uint8_t (*)[crypto_core_ristretto255_BYTES]) (*ctx->k0p_commitments)[i*(ctx->n+1)])) { // if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] k0p vsps fails [%d]\n"NORMAL, ctx->index, i+1); // } //} //liboprf_debug=1; uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) ctx->k0p_commitments; ret = ft_or_full_vsps(ctx->n+1, ctx->t, dealers, ctx->index, C_i, c, "VSPS failed k0p, doing full VSPS check on all dealers", "VSPS failed k0p", "ERROR, could not find and dealer commitments that fail the VSPS check", fails, &fails[1]); if(ret!=TOPRF_Update_Err_OK) return ret; ctx->p_complaints_len = 0; const uint8_t *fails_len = fails; const uint8_t *xfails = fails_len+1; handle_complaints(dealers, ctx->index, *fails_len, xfails, &ctx->p_complaints_len, ctx->p_complaints, 0, 0, 0); ctx->prev = ctx->step; if(ctx->p_complaints_len == 0) { ctx->step = TOPRF_Update_Peer_Send_k0p_Share; } else { dump((uint8_t*) ctx->p_complaints, ctx->p_complaints_len*sizeof(uint16_t), "[%d] complaints", ctx->index); ctx->step = TOPRF_Update_Peer_Disclose_VSPS_Cheaters; } return TOPRF_Update_Err_OK; } #define toprfupdate_peer_vsps_disclose_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) \ + ctx->p_complaints_len * TOPRF_Share_BYTES * 2 ) static TOPRF_Update_Err peer_vsps_disclose(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] final-reconst1 disclose shares of dealers failing vsps\n"NORMAL, ctx->index); if(output_len != toprf_update_peer_output_size(ctx)) return TOPRF_Update_Err_OSize; TOPRF_Update_Message* msg = (TOPRF_Update_Message*) output; uint8_t *wptr = msg->data; TOPRF_Update_Err ret; ret = disclose_shares(ctx->n, ctx->index, "k0p", ctx->p_complaints_len, ctx->p_complaints, (*ctx->k0p_shares), &wptr); if(ret != TOPRF_Update_Err_OK) return ret; if(0!=toprf_send_msg(output, output_len, toprfupdate_peer_vsps_disclose_msg, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Reconstruct_VSPS_Shares; return TOPRF_Update_Err_OK; } static int vss_reshare(const uint8_t n, const uint8_t threshold, const uint8_t *prk, const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], TOPRF_Share shares[n][2], uint8_t commitments[n][crypto_core_ristretto255_BYTES], uint8_t blind[crypto_core_ristretto255_SCALARBYTES]) { if(threshold==0) return 1; if(secret==NULL) return 1; uint8_t a[threshold][crypto_core_ristretto255_SCALARBYTES]; uint8_t b[threshold][crypto_core_ristretto255_SCALARBYTES]; memcpy(a[0], secret, crypto_core_ristretto255_SCALARBYTES); // todo inlude also the idx of the dealer in the ctx. char share_ctx[] = "k0p lambda * a * b re-sharing"; char blind_ctx[] = "k0p blind re-sharing"; for(int k=0;kn][2] ,uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]) { if(complaints_len==0) return TOPRF_Update_Err_OK; uint8_t secrets[complaints_len][2][crypto_core_ristretto255_SCALARBYTES]; TOPRF_Update_Err ret; ret = reconstruct(ctx->n, ctx->t, "k0p", complaints_len,complaints, shares, commitments, secrets); if(ret != TOPRF_Update_Err_OK) return ret; TOPRF_Share reshares[ctx->n][2]; for(unsigned i=0;in, ctx->t, ctx->sessionid, secrets[i][0], reshares, &(*commitments)[1], secrets[i][1])) return TOPRF_Update_Err_VSSShare; if(0!=dkg_vss_commit(secrets[i][0], secrets[i][1], (*commitments)[0])) return 1; int _debug=liboprf_debug; liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, (*commitments))) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] VSPS asdfasdf failed k0p\n"NORMAL); } liboprf_debug=_debug; dump((uint8_t*) (*commitments), (ctx->n+1U)*crypto_core_ristretto255_BYTES, "reshared k0p commitments"); } return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_vsps_disclose_msg_SIZE(ctx) (sizeof(TOPRF_Update_Message) + (toprfupdate_peer_vsps_disclose_msg_SIZE(ctx) * ctx->n)) static TOPRF_Update_Err stp_bc_vsps_disclosures(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { TOPRF_Update_Err ret; ret = stp_broadcast(ctx, input, input_len, output, output_len, "final-reconst2 broadcast shares of dealers failing vsps check", ctx->n, toprfupdate_peer_vsps_disclose_msg_SIZE(ctx), toprfupdate_peer_vsps_disclose_msg, TOPRF_Update_STP_Reconstruct_Delta); if(ret != TOPRF_Update_Err_OK) return ret; TOPRF_Share k0p_shares[ctx->p_complaints_len][ctx->n][2]; const uint8_t *ptr = input; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_vsps_disclose_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; const uint8_t *dptr = msg->data; for(unsigned j=0;jp_complaints_len;j++) { memcpy(k0p_shares[j][msg->from-1], dptr, TOPRF_Share_BYTES*2); dptr+=TOPRF_Share_BYTES*2; } } ret = stp_reshare(ctx, ctx->p_complaints_len, ctx->p_complaints, k0p_shares, ctx->k0p_commitments); if(ret!=TOPRF_Update_Err_OK) return ret; const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); uint8_t (*c)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES] = (uint8_t (*)[dealers][ctx->n+1][crypto_core_ristretto255_BYTES]) ctx->k0p_commitments; for(unsigned i=0;in;i++) { memcpy((*ctx->k0p_final_commitments)[i], (*c)[0][i+1], crypto_scalarmult_ristretto255_BYTES); for(unsigned j=1;jk0p_final_commitments)[i], (*ctx->k0p_final_commitments)[i], (*c)[j][i+1]); } } return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_reshare(TOPRF_Update_PeerState *ctx ,uint16_t *complaints_len ,uint16_t complaints[*complaints_len] ,const TOPRF_Share shares[*complaints_len][ctx->n][2] ,TOPRF_Share my_shares[(ctx->t-1)*2+1][2] ,TOPRF_Share my_share[2] ,uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]) { if(*complaints_len==0) return TOPRF_Update_Err_OK; uint8_t secrets[*complaints_len][2][crypto_core_ristretto255_SCALARBYTES]; TOPRF_Update_Err ret; ret = reconstruct(ctx->n, ctx->t, "k0p", *complaints_len, complaints, shares, commitments, secrets); if(ret != TOPRF_Update_Err_OK) return ret; TOPRF_Share reshares[ctx->n][2]; for(unsigned i=0;i<*complaints_len;i++) { if(vss_reshare(ctx->n, ctx->t, ctx->sessionid, secrets[i][0], reshares, &(*commitments)[1], secrets[i][1])) return TOPRF_Update_Err_VSSShare; if(0!=dkg_vss_commit(secrets[i][0], secrets[i][1], (*commitments)[0])) return 1; int _debug=liboprf_debug; liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, (*commitments))) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] VSPS asdfasdf failed k0p\n"NORMAL); } liboprf_debug=_debug; dump((uint8_t*) (*commitments), (ctx->n+1U)*crypto_core_ristretto255_BYTES, "reshared k0p commitments"); const uint8_t accused = (uint8_t) (complaints[i] & 0xff); memcpy(my_shares[accused-1], reshares[ctx->index-1], sizeof(TOPRF_Share)*2); } const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); uint8_t commitment[crypto_scalarmult_ristretto255_BYTES]; if(*complaints_len>0) { ret = compute_mul_share(dealers, my_shares, my_share, commitment); if(ret!=TOPRF_Update_Err_OK) return ret; } *complaints_len = 0; memset(complaints, 0, ctx->n*2); return TOPRF_Update_Err_OK; } static int peer_step44_handler(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len); static TOPRF_Update_Err peer_reconst_vsps_shares(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] final-reconst3 reconstruct secrets of dealers failing VSPS check\x1b[0m\n", ctx->index); if(input_len!= sizeof(TOPRF_Update_Message) + toprfupdate_peer_zk_disclose_msg_SIZE(ctx) * ctx->n) return TOPRF_Update_Err_ISize; // verify STP message envelope const uint8_t *ptr=NULL; int ret = unwrap_envelope(ctx,input,input_len,toprfupdate_stp_bc_vsps_disclose_msg,&ptr); if(ret!=TOPRF_Update_Err_OK) return ret; TOPRF_Share k0p_shares[ctx->p_complaints_len][ctx->n][2]; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_vsps_disclose_msg_SIZE(ctx)) { const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) ptr; if(peer_recv_msg(ctx,ptr,toprfupdate_peer_vsps_disclose_msg_SIZE(ctx),toprfupdate_peer_vsps_disclose_msg,i+1,0xff)) continue; const uint8_t *dptr = msg->data; for(unsigned j=0;jp_complaints_len;j++) { memcpy(k0p_shares[j][msg->from-1], dptr, TOPRF_Share_BYTES*2); dptr+=TOPRF_Share_BYTES*2; } } ret = peer_reshare(ctx, &ctx->p_complaints_len, ctx->p_complaints, k0p_shares, (*ctx->k0p_shares), ctx->k0p_share, ctx->k0p_commitments); if(ret!=TOPRF_Update_Err_OK) return ret; // reset my_complaints ctx->my_p_complaints_len = 0; memset(ctx->my_p_complaints, 0, ctx->n); return peer_step44_handler(ctx, output, output_len); } #define toprfupdate_peer_end2_msg_SIZE (sizeof(TOPRF_Update_Message) + 2 * TOPRF_Share_BYTES) static int peer_step44_handler(TOPRF_Update_PeerState *ctx, uint8_t *output, const size_t output_len) { // todo maybe check the global transcript before sending the r & r' shares to stp? if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] final4 send final shared p to STP\x1b[0m\n", ctx->index); if(output_len != toprfupdate_peer_end2_msg_SIZE) return TOPRF_Update_Err_OSize; TOPRF_Update_Message* msg41 = (TOPRF_Update_Message*) output; memcpy(msg41->data, (uint8_t*) ctx->p_share, 2*TOPRF_Share_BYTES); if(0!=toprf_send_msg(output, toprfupdate_peer_end2_msg_SIZE, toprfupdate_peer_end2_msg, ctx->index, 0, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_Peer_Final_OK; return TOPRF_Update_Err_OK; } #define toprfupdate_stp_bc_end3_msg_SIZE (sizeof(TOPRF_Update_Message) + 1) static int stp_step45_handler(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] final5. reconstruct delta\x1b[0m\n"); if(input_len != ctx->n * toprfupdate_peer_end2_msg_SIZE) return TOPRF_Update_Err_ISize; if(output_len != toprfupdate_stp_bc_end3_msg_SIZE) return TOPRF_Update_Err_OSize; const size_t cheaters = ctx->cheater_len; const uint8_t *ptr = input; TOPRF_Share p_shares[ctx->n][2]; for(uint8_t i=0;in;i++,ptr+=toprfupdate_peer_end2_msg_SIZE) { const TOPRF_Update_Message *msg = (const TOPRF_Update_Message *) ptr; if(stp_recv_msg(ctx,ptr,toprfupdate_peer_end2_msg_SIZE, toprfupdate_peer_end2_msg,i+1,0)) continue; memcpy(p_shares[i],msg->data,2*TOPRF_Share_BYTES); } if(ctx->cheater_len>cheaters) return TOPRF_Update_Err_CheatersFound; TOPRF_Update_Message *outmsg = (TOPRF_Update_Message *) output; uint8_t *fail=outmsg->data; *fail = 0; int _debug=liboprf_debug; liboprf_debug=0; if(0!=toprf_mpc_vsps_check(ctx->t-1, (*ctx->p_commitments))) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] VSPS failed k0p\n"NORMAL); *fail=1; } liboprf_debug=_debug; for(unsigned i=0;in;i++) { if(0!=dkg_vss_verify_commitment((*ctx->p_commitments)[i], p_shares[i])) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[!] failed to verify commitment for p share %d\n"NORMAL, i+1); dump((*ctx->p_commitments)[i], crypto_scalarmult_ristretto255_BYTES, "[!] C[%d]", i+1); dump((uint8_t*) p_shares[i], 2*TOPRF_Share_BYTES, "[!] s[%d]", i+1); *fail=1; } } if(*fail == 0) { // reconstruct delta dkg_vss_reconstruct(ctx->t, 0, ctx->n, p_shares, (*ctx->p_commitments), ctx->delta, NULL); dump(ctx->delta, crypto_scalarmult_ristretto255_SCALARBYTES, "[!] βˆ†"); } if(0!=toprf_send_msg(output, output_len, toprfupdate_stp_end3_msg, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return TOPRF_Update_Err_Send; ctx->step = TOPRF_Update_STP_Done; return TOPRF_Update_Err_OK; } static TOPRF_Update_Err peer_step46_handler(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] final6. receive final confirmation from STP\x1b[0m\n", ctx->index); if(input_len != toprfupdate_stp_bc_end3_msg_SIZE) return TOPRF_Update_Err_ISize; // verify STP message envelope const TOPRF_Update_Message* msg = (const TOPRF_Update_Message*) input; int ret = toprf_recv_msg(input, input_len, toprfupdate_stp_end3_msg, 0, 0xff, (*ctx->sig_pks)[0], ctx->sessionid, ctx->ts_epsilon, &ctx->stp_last_ts); if(0!=ret) return TOPRF_Update_Err_BroadcastEnv+ret; if(msg->data[0]!=0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, RED"[%d] STP indicated failure at final step discarding all results, keeping old key\n"NORMAL, ctx->index); return TOPRF_Update_Err_Proto; } else { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;32m[%d] STP indicated full success updating old key to new key\n"NORMAL, ctx->index); } ctx->step = TOPRF_Update_Peer_Done; return TOPRF_Update_Err_OK; } int toprf_update_stp_not_done(const TOPRF_Update_STPState *stp) { return stp->stepstepn;i++) { if((*ctx->noise_ins)[i]!=NULL) Noise_XK_session_free((*ctx->noise_ins)[i]); if((*ctx->noise_outs)[i]!=NULL) Noise_XK_session_free((*ctx->noise_outs)[i]); } if(ctx->dev!=NULL) Noise_XK_device_free(ctx->dev); } size_t toprf_update_stp_input_size(const TOPRF_Update_STPState *ctx) { size_t sizes[ctx->n]; memset(sizes,0,sizeof sizes); if(toprf_update_stp_input_sizes(ctx, sizes) == 1) { return sizes[0] * ctx->n; } else { size_t result=0; for(int i=0;in;i++) result+=sizes[i]; return result; } } int toprf_update_stp_input_sizes(const TOPRF_Update_STPState *ctx, size_t *sizes) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); size_t item=0; switch(ctx->step) { case TOPRF_Update_STP_Broadcast_NPKs: { item=toprfupdate_peer_init_msg_SIZE; break; } case TOPRF_Update_STP_Route_Noise_Handshakes1: { item=toprfupdate_peer_ake1_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Route_Noise_Handshakes2: { item=toprfupdate_peer_ake2_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments: { item = toprfupdate_peer_dkg1_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_DKG_Commitments: { item = toprfupdate_peer_dkg2_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Route_Encrypted_Shares: { item = toprfupdate_peer_dkg3_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Broadcast_Complaints: { item = toprfupdate_peer_verify_shares_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_DKG_Defenses: { uint8_t ctr1[ctx->n]; memset(ctr1,0,ctx->n); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff) - 1U); if(peer>=ctx->n) return TOPRF_Update_Err_OOB; ctr1[peer]++; } for(int i=0;in;i++) { if(ctr1[i]>0) { sizes[i]=sizeof(TOPRF_Update_Message) \ + (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; } else { sizes[i]=0; } } return 0; } case TOPRF_Update_STP_Broadcast_DKG_Transcripts: { item = toprfupdate_peer_bc_transcript_msg_SIZE; break; } case TOPRF_Update_STP_Route_Mult_Step1: { for(uint8_t i=0;in;i++) { sizes[i] = isdealer(i+1, ctx->t) * toprfupdate_peer_mult1_msg_SIZE(ctx); } return 0; } case TOPRF_Update_STP_Broadcast_Mult_Commitments: { for(uint8_t i=0;in;i++) { sizes[i] = isdealer(i+1, ctx->t) * toprfupdate_peer_mult_coms_msg_SIZE(ctx); } return 0; } case TOPRF_Update_STP_Route_Encrypted_Mult_Shares: { for(uint8_t i=0;in;i++) { sizes[i] = isdealer(i+1, ctx->t) * (toprfupdate_peer_mult2_msg_SIZE * ctx->n); } return 0; } case TOPRF_Update_STP_Broadcast_Mult_Complaints: { item = toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_Mult_Defenses: { uint8_t ctr1[dealers]; memset(ctr1,0,sizeof ctr1); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff) - 1U); if(peer>=dealers) return TOPRF_Update_Err_OOB; ctr1[peer]++; } for(int i=0;in;i++) { if(i0)) { sizes[i]=sizeof(TOPRF_Update_Message) \ + (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; } else { sizes[i]=0; } } return 0; } case TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares: { item = toprfupdate_stp_reconst_mult_shares_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Route_ZK_Challenge_Commitments: { item = toprfupdate_peer_zkp1_msg_SIZE; break; } case TOPRF_Update_STP_Route_ZK_commitments: { for(uint8_t i=0;in;i++) { sizes[i] = isdealer(i+1, ctx->t) * toprfupdate_peer_zkp2_msg_SIZE; } return 0; } case TOPRF_Update_STP_Broadcast_ZK_nonces: { item = toprfupdate_peer_zkp3_msg_SIZE; break; } case TOPRF_Update_STP_Broadcast_ZK_Proofs: { for(uint8_t i=0;in;i++) { sizes[i] = isdealer(i+1, ctx->t) * toprfupdate_peer_zkp4_msg_SIZE; } return 0; } case TOPRF_Update_STP_Broadcast_ZK_Disclosures: { item = toprfupdate_peer_zk_disclose_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_Mult_Ci: { item = toprfupdate_peer_mult3_msg_SIZE; break; } case TOPRF_Update_STP_Broadcast_VSPS_Disclosures: { item = toprfupdate_peer_vsps_disclose_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Reconstruct_Delta: { item = toprfupdate_peer_end2_msg_SIZE; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] isize invalid stp step: %d\n", ctx->step); } } for(uint8_t i=0;in;i++) { sizes[i] = item; } return 1; } size_t toprf_update_stp_output_size(const TOPRF_Update_STPState *ctx) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); switch(ctx->step) { case TOPRF_Update_STP_Broadcast_NPKs: return toprfupdate_peer_init_msg_SIZE * ctx->n + sizeof(TOPRF_Update_Message); case TOPRF_Update_STP_Route_Noise_Handshakes1: return toprfupdate_peer_ake1_msg_SIZE * ctx->n * ctx->n; case TOPRF_Update_STP_Route_Noise_Handshakes2: return toprfupdate_peer_ake2_msg_SIZE * ctx->n * ctx->n; case TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments: return sizeof(TOPRF_Update_Message) + (toprfupdate_peer_dkg1_msg_SIZE(ctx) * ctx->n); case TOPRF_Update_STP_Broadcast_DKG_Commitments: return sizeof(TOPRF_Update_Message) + (toprfupdate_peer_dkg2_msg_SIZE(ctx) * ctx->n); case TOPRF_Update_STP_Route_Encrypted_Shares: return toprfupdate_peer_dkg3_msg_SIZE * ctx->n * ctx->n; case TOPRF_Update_STP_Broadcast_Complaints: return toprfupdate_stp_bc_verify_shares_msg_SIZE(ctx); case TOPRF_Update_STP_Broadcast_DKG_Defenses: return sizeof(TOPRF_Update_Message) + toprf_update_stp_input_size(ctx); case TOPRF_Update_STP_Broadcast_DKG_Transcripts: return toprfupdate_stp_bc_transcript_msg_SIZE(ctx); case TOPRF_Update_STP_Route_Mult_Step1: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult1_msg_SIZE(ctx) * dealers; case TOPRF_Update_STP_Broadcast_Mult_Commitments: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult_coms_msg_SIZE(ctx) * dealers; case TOPRF_Update_STP_Broadcast_Mult_Complaints: return toprfupdate_stp_bc_verify_mult_shares_msg_SIZE(ctx); case TOPRF_Update_STP_Broadcast_Mult_Defenses: return sizeof(TOPRF_Update_Message) + toprf_update_stp_input_size(ctx); case TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares: return sizeof(TOPRF_Update_Message) + toprfupdate_stp_reconst_mult_shares_msg_SIZE(ctx) * ctx->n; case TOPRF_Update_STP_Route_Encrypted_Mult_Shares: return (toprfupdate_peer_mult2_msg_SIZE * ctx->n) * dealers; case TOPRF_Update_STP_Route_ZK_Challenge_Commitments: return sizeof(TOPRF_Update_Message) + (toprfupdate_peer_zkp1_msg_SIZE * ctx->n); case TOPRF_Update_STP_Route_ZK_commitments: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp2_msg_SIZE * dealers; case TOPRF_Update_STP_Broadcast_ZK_nonces: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp3_msg_SIZE * ctx->n; case TOPRF_Update_STP_Broadcast_ZK_Proofs: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp4_msg_SIZE * dealers; case TOPRF_Update_STP_Broadcast_ZK_Disclosures: return toprfupdate_stp_bc_zkp_disclose_msg_SIZE(ctx); case TOPRF_Update_STP_Broadcast_Mult_Ci: return toprfupdate_stp_bc_mult3_msg_SIZE(ctx); case TOPRF_Update_STP_Broadcast_VSPS_Disclosures: return toprfupdate_stp_bc_vsps_disclose_msg_SIZE(ctx); case TOPRF_Update_STP_Reconstruct_Delta: return toprfupdate_stp_bc_end3_msg_SIZE; default: if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] osize invalid stp step: %d\n", ctx->step); } return 0; } int toprf_update_stp_peer_msg(const TOPRF_Update_STPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len) { if(peer>=ctx->n) return -1; const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); switch(ctx->prev) { case TOPRF_Update_STP_Broadcast_NPKs: { *msg = base; *len = toprfupdate_peer_init_msg_SIZE * ctx->n + sizeof(TOPRF_Update_Message); break; } case TOPRF_Update_STP_Route_Noise_Handshakes1: { *msg = base + peer * toprfupdate_peer_ake1_msg_SIZE * ctx->n; *len = toprfupdate_peer_ake1_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Route_Noise_Handshakes2: { *msg = base + peer * toprfupdate_peer_ake2_msg_SIZE * ctx->n; *len = toprfupdate_peer_ake1_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments: { *msg = base; *len = sizeof(TOPRF_Update_Message) + (toprfupdate_peer_dkg1_msg_SIZE(ctx) * ctx->n); break; } case TOPRF_Update_STP_Broadcast_DKG_Commitments: { *msg = base; *len = sizeof(TOPRF_Update_Message) + (toprfupdate_peer_dkg2_msg_SIZE(ctx) * ctx->n); break; } case TOPRF_Update_STP_Route_Encrypted_Shares: { *msg = base + peer * toprfupdate_peer_dkg3_msg_SIZE * ctx->n; *len = toprfupdate_peer_dkg3_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Broadcast_Complaints: { *msg = base; *len = toprfupdate_stp_bc_verify_shares_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_DKG_Defenses: { *msg = base; *len = sizeof(TOPRF_Update_Message); uint8_t ctr1[ctx->n]; memset(ctr1,0,sizeof ctr1); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff) - 1U); if(peer>=ctx->n) return TOPRF_Update_Err_OOB; ctr1[peer]++; } for(int i=0;in;i++) { if(ctr1[i]>0) { *len+=sizeof(TOPRF_Update_Message) \ + (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; } } break; } case TOPRF_Update_STP_Broadcast_DKG_Transcripts: { *msg = base; *len = toprfupdate_stp_bc_transcript_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Route_Mult_Step1: { *msg = base; *len = sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult1_msg_SIZE(ctx) * dealers; break; } case TOPRF_Update_STP_Broadcast_Mult_Commitments: { *msg = base; *len = sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult_coms_msg_SIZE(ctx) * dealers; break; } case TOPRF_Update_STP_Route_Encrypted_Mult_Shares: { *msg = base + peer * toprfupdate_peer_mult2_msg_SIZE * ((ctx->t-1U)*2 + 1U); *len = toprfupdate_peer_mult2_msg_SIZE * ((ctx->t-1U)*2 + 1U); break; } case TOPRF_Update_STP_Broadcast_Mult_Complaints: { *msg = base; *len = toprfupdate_stp_bc_verify_mult_shares_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_Mult_Defenses: { *msg = base; *len = sizeof(TOPRF_Update_Message); uint8_t ctr1[dealers]; memset(ctr1,0,sizeof ctr1); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff) - 1U); if(peer>=dealers) return TOPRF_Update_Err_OOB; ctr1[peer]++; } for(int i=0;i0) { *len+=sizeof(TOPRF_Update_Message) \ + (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; } } break; } case TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares: { *msg = base; *len = sizeof(TOPRF_Update_Message) + toprfupdate_stp_reconst_mult_shares_msg_SIZE(ctx) * ctx->n; break; } case TOPRF_Update_STP_Route_ZK_Challenge_Commitments: { *msg = base; *len = sizeof(TOPRF_Update_Message) + (toprfupdate_peer_zkp1_msg_SIZE * ctx->n); break; } case TOPRF_Update_STP_Route_ZK_commitments: { *msg = base; *len = sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp2_msg_SIZE * dealers; break; } case TOPRF_Update_STP_Broadcast_ZK_nonces: { *msg = base; *len = sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp3_msg_SIZE * ctx->n; break; } case TOPRF_Update_STP_Broadcast_ZK_Proofs: { *msg = base; *len = sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp4_msg_SIZE * dealers; break; } case TOPRF_Update_STP_Broadcast_ZK_Disclosures: { *msg = base; *len = toprfupdate_stp_bc_zkp_disclose_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_Mult_Ci: { *msg = base; *len = toprfupdate_stp_bc_mult3_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Broadcast_VSPS_Disclosures: { *msg = base; *len = toprfupdate_stp_bc_vsps_disclose_msg_SIZE(ctx); break; } case TOPRF_Update_STP_Reconstruct_Delta: { *msg = base; *len = toprfupdate_stp_bc_end3_msg_SIZE; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid stp step in toprf_update_stp_peer_msg\n"); return 1; } } if(base+base_size < *msg + *len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "buffer overread detected in toprf_update_stp_peer_msg %ld\n", (base+base_size) - (*msg + *len)); return 2; } return 0; } size_t toprf_update_peer_input_size(const TOPRF_Update_PeerState *ctx) { const uint8_t dealers = (uint8_t) ((ctx->t-1U)*2 + 1U); switch(ctx->step) { case TOPRF_Update_Peer_Broadcast_NPK_SIDNonce: return 0; case TOPRF_Update_Peer_Rcv_NPK_SIDNonce: return toprfupdate_peer_init_msg_SIZE * ctx->n + sizeof(TOPRF_Update_Message); case TOPRF_Update_Peer_Noise_Handshake: return toprfupdate_peer_ake1_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Finish_Noise_Handshake: return toprfupdate_peer_ake2_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Rcv_CHashes_Send_Commitments: return sizeof(TOPRF_Update_Message) + (toprfupdate_peer_dkg1_msg_SIZE(ctx) * ctx->n); case TOPRF_Update_Peer_Rcv_Commitments_Send_Shares: return sizeof(TOPRF_Update_Message) + (toprfupdate_peer_dkg2_msg_SIZE(ctx) * ctx->n); case TOPRF_Update_Peer_Verify_Commitments: return ctx->n * toprfupdate_peer_dkg3_msg_SIZE; case TOPRF_Update_Peer_Finish_DKG: return 0; case TOPRF_Update_Peer_Handle_DKG_Complaints: return toprfupdate_stp_bc_verify_shares_msg_SIZE(ctx); case TOPRF_Update_Peer_Defend_DKG_Accusations: return 0; case TOPRF_Update_Peer_Check_Shares: { uint8_t ctr1[ctx->n]; memset(ctr1,0,ctx->n); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff) - 1U); if(peer>=ctx->n) return TOPRF_Update_Err_OOB; ctr1[peer]++; } size_t ret = sizeof(TOPRF_Update_Message); for(int i=0;in;i++) { if(ctr1[i]>0) { ret+=sizeof(TOPRF_Update_Message) \ + (1U+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; } } return ret; } case TOPRF_Update_Peer_Confirm_Transcripts: return toprfupdate_stp_bc_transcript_msg_SIZE(ctx); case TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult1_msg_SIZE(ctx) * dealers; case TOPRF_Update_Peer_Send_K0P_Shares: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_mult_coms_msg_SIZE(ctx) * dealers; case TOPRF_Update_Peer_Recv_K0P_Shares: return toprfupdate_peer_mult2_msg_SIZE * dealers; case TOPRF_Update_Peer_Handle_Mult_Share_Complaints: return toprfupdate_stp_bc_verify_mult_shares_msg_SIZE(ctx); case TOPRF_Update_Peer_Defend_Mult_Accusations: return 0; case TOPRF_Update_Peer_Check_Mult_Shares: { uint8_t ctr1[dealers]; memset(ctr1,0,sizeof ctr1); for(int i=0;ip_complaints_len;i++) { const uint8_t peer = (uint8_t) ((ctx->p_complaints[i] & 0xff) - 1U); if(peer>=dealers) return TOPRF_Update_Err_OOB; ctr1[peer]++; } size_t ret = sizeof(TOPRF_Update_Message); for(int i=0;i0) { ret+=sizeof(TOPRF_Update_Message) \ + (1U+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE) * ctr1[i]; } } return ret; } case TOPRF_Update_Peer_Disclose_Mult_Shares: return 0; case TOPRF_Update_Peer_Reconstruct_Mult_Shares: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx) * ctx->n; case TOPRF_Update_Peer_Send_ZK_Challenge_Commitments: return 0; case TOPRF_Update_Peer_Send_ZK_Commitments: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp1_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Send_ZK_nonces: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp2_msg_SIZE * dealers; case TOPRF_Update_Peer_Send_ZK_proofs: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp3_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Verify_ZK_proofs: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zkp4_msg_SIZE * dealers; case TOPRF_Update_Peer_Disclose_ZK_Cheaters: return 0; case TOPRF_Update_Peer_Reconstruct_ZK_Shares: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_zk_disclose_msg_SIZE(ctx) * ctx->n; case TOPRF_Update_Peer_Send_Mult_Ci: return 0; case TOPRF_Update_Peer_Final_VSPS_Checks: return toprfupdate_stp_bc_mult3_msg_SIZE(ctx); case TOPRF_Update_Peer_Disclose_VSPS_Cheaters: return 0; case TOPRF_Update_Peer_Reconstruct_VSPS_Shares: return sizeof(TOPRF_Update_Message) + toprfupdate_peer_vsps_disclose_msg_SIZE(ctx) * ctx->n; case TOPRF_Update_Peer_Send_k0p_Share: return 0; case TOPRF_Update_Peer_Final_OK: return toprfupdate_stp_bc_end3_msg_SIZE; default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] peer input size invalid step\n", ctx->index); } } return 1; } size_t toprf_update_peer_output_size(const TOPRF_Update_PeerState *ctx) { switch(ctx->step) { case TOPRF_Update_Peer_Broadcast_NPK_SIDNonce: return toprfupdate_peer_init_msg_SIZE; case TOPRF_Update_Peer_Rcv_NPK_SIDNonce: return toprfupdate_peer_ake1_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Noise_Handshake: return toprfupdate_peer_ake2_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Finish_Noise_Handshake: return toprfupdate_peer_dkg1_msg_SIZE(ctx); case TOPRF_Update_Peer_Rcv_CHashes_Send_Commitments: return toprfupdate_peer_dkg2_msg_SIZE(ctx); case TOPRF_Update_Peer_Rcv_Commitments_Send_Shares: return ctx->n * toprfupdate_peer_dkg3_msg_SIZE; case TOPRF_Update_Peer_Verify_Commitments: return toprfupdate_peer_verify_shares_msg_SIZE(ctx); case TOPRF_Update_Peer_Handle_DKG_Complaints: return 0; case TOPRF_Update_Peer_Defend_DKG_Accusations: { if(ctx->my_p_complaints_len == 0) return 0; size_t res = sizeof(TOPRF_Update_Message) \ + ctx->my_p_complaints_len * (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE); return res; } case TOPRF_Update_Peer_Check_Shares: return toprfupdate_peer_bc_transcript_msg_SIZE; case TOPRF_Update_Peer_Finish_DKG: return toprfupdate_peer_bc_transcript_msg_SIZE; case TOPRF_Update_Peer_Confirm_Transcripts: return isdealer(ctx->index, ctx->t) * toprfupdate_peer_mult1_msg_SIZE(ctx); case TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments: return isdealer(ctx->index, ctx->t) * toprfupdate_peer_mult_coms_msg_SIZE(ctx); case TOPRF_Update_Peer_Send_K0P_Shares: return isdealer(ctx->index, ctx->t) * toprfupdate_peer_mult2_msg_SIZE * ctx->n; case TOPRF_Update_Peer_Recv_K0P_Shares: return toprfupdate_peer_verify_mult_shares_msg_SIZE(ctx); case TOPRF_Update_Peer_Handle_Mult_Share_Complaints: return 0; case TOPRF_Update_Peer_Defend_Mult_Accusations: { if(ctx->my_p_complaints_len == 0) return 0; size_t res = sizeof(TOPRF_Update_Message) \ + ctx->my_p_complaints_len * (1+dkg_noise_key_SIZE+toprf_update_encrypted_shares_SIZE); return res; } case TOPRF_Update_Peer_Check_Mult_Shares: return 0; case TOPRF_Update_Peer_Disclose_Mult_Shares: { if(ctx->p_complaints_len == 0) return 0; return toprfupdate_peer_reconst_mult_shares_msg_SIZE(ctx); } case TOPRF_Update_Peer_Reconstruct_Mult_Shares: case TOPRF_Update_Peer_Send_ZK_Challenge_Commitments: return toprfupdate_peer_zkp1_msg_SIZE; case TOPRF_Update_Peer_Send_ZK_Commitments: return isdealer(ctx->index, ctx->t) * toprfupdate_peer_zkp2_msg_SIZE; case TOPRF_Update_Peer_Send_ZK_nonces: return toprfupdate_peer_zkp3_msg_SIZE; case TOPRF_Update_Peer_Send_ZK_proofs: return isdealer(ctx->index, ctx->t) * toprfupdate_peer_zkp4_msg_SIZE; case TOPRF_Update_Peer_Verify_ZK_proofs: return 0; case TOPRF_Update_Peer_Disclose_ZK_Cheaters: return toprfupdate_peer_zk_disclose_msg_SIZE(ctx); case TOPRF_Update_Peer_Reconstruct_ZK_Shares: case TOPRF_Update_Peer_Send_Mult_Ci: return toprfupdate_peer_mult3_msg_SIZE; case TOPRF_Update_Peer_Final_VSPS_Checks: return 0; case TOPRF_Update_Peer_Disclose_VSPS_Cheaters: return toprfupdate_peer_vsps_disclose_msg_SIZE(ctx); case TOPRF_Update_Peer_Reconstruct_VSPS_Shares: case TOPRF_Update_Peer_Send_k0p_Share: return toprfupdate_peer_end2_msg_SIZE; case TOPRF_Update_Peer_Final_OK: return 0; default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] peer output size invalid step\n", ctx->index); } } return 1; } int toprf_update_stp_next(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { int ret = 0; if(ctx->cheater_max <= ctx->cheater_len) return TOPRF_Update_Err_CheatersFull; ctx->prev=ctx->step; switch(ctx->step) { case TOPRF_Update_STP_Broadcast_NPKs: { ret = stp_step2_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_Noise_Handshakes1: { ret = stp_step4_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_Noise_Handshakes2: { ret = stp_step6_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments: { ret = stp_dkg1_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_DKG_Commitments: { ret = stp_dkg2_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_Encrypted_Shares: { ret = stp_dkg3_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_Complaints: { ret = stp_verify_shares_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_DKG_Defenses: { ret = stp_broadcast_defenses(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_DKG_Transcripts: { ret = stp_bc_transcript_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_Mult_Step1: { ret = stp_step25_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_Mult_Commitments: { ret = stp_mult_com_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_Encrypted_Mult_Shares: { ret = stp_step27_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_Mult_Complaints: { ret = stp_verify_mult_shares_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_Mult_Defenses: { ret = stp_broadcast_mult_defenses(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares: { ret = stp_broadcast_reconst_mult_shares(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_ZK_Challenge_Commitments: { ret = stp_step29_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Route_ZK_commitments: { ret = stp_step31_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_ZK_nonces: { ret = stp_step33_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_ZK_Proofs: { ret = stp_step35_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_ZK_Disclosures: { ret = stp_bc_zk_disclosures(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_Mult_Ci: { ret = stp_step40_handler(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Broadcast_VSPS_Disclosures: { ret = stp_bc_vsps_disclosures(ctx, input, input_len, output, output_len); break;} case TOPRF_Update_STP_Reconstruct_Delta: { ret = stp_step45_handler(ctx, input, input_len, output, output_len); break;} default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] stp next invalid step\n"); return 99; } } if(ret!=0) ctx->step=99; // so that not_done reports done return ret; } int toprf_update_peer_next(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { int ret=0; if(ctx->cheater_max <= ctx->cheater_len) return TOPRF_Update_Err_CheatersFull; ctx->prev=ctx->step; switch(ctx->step) { case TOPRF_Update_Peer_Broadcast_NPK_SIDNonce: { ret = peer_step1_handler(ctx, output, output_len) ; break; } case TOPRF_Update_Peer_Rcv_NPK_SIDNonce: { ret = peer_step3_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Noise_Handshake: { ret = peer_step5_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Finish_Noise_Handshake: { ret = peer_dkg1_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Rcv_CHashes_Send_Commitments: { ret = peer_dkg2_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Rcv_Commitments_Send_Shares: { ret = peer_dkg3_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Verify_Commitments: { ret = peer_verify_shares_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Handle_DKG_Complaints: { ret = peer_dkg_fork(ctx, input, input_len); break; } case TOPRF_Update_Peer_Defend_DKG_Accusations: { ret = peer_defend(ctx, output, output_len); break; } case TOPRF_Update_Peer_Check_Shares: { ret = peer_check_shares(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Finish_DKG: { ret = peer_verify_vsps(ctx, output, output_len); break; } case TOPRF_Update_Peer_Confirm_Transcripts: { ret = peer_final_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments: { ret = peer_mult2_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Send_K0P_Shares: { ret = peer_step26_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Recv_K0P_Shares: { ret = peer_step28_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Handle_Mult_Share_Complaints: { ret = peer_mult_fork(ctx, input, input_len); break; } case TOPRF_Update_Peer_Defend_Mult_Accusations: { ret = peer_mult_defend(ctx, output, output_len); break; } case TOPRF_Update_Peer_Check_Mult_Shares: { ret = peer_check_mult_shares(ctx,input,input_len); break; } case TOPRF_Update_Peer_Disclose_Mult_Shares: { ret = peer_disclose_mult_shares(ctx, output, output_len); break; } case TOPRF_Update_Peer_Reconstruct_Mult_Shares: { ret = peer_reconst_mult_shares(ctx,input,input_len,output,output_len); break;} case TOPRF_Update_Peer_Send_ZK_Challenge_Commitments: { ret = peer_send_zk_chalcoms(ctx, output, output_len); break; } case TOPRF_Update_Peer_Send_ZK_Commitments: { ret = peer_step30_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Send_ZK_nonces: { ret = peer_step32_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Send_ZK_proofs: { ret = peer_step34_handler(ctx, input, input_len, output, output_len); break; } case TOPRF_Update_Peer_Verify_ZK_proofs: { ret = peer_step36_handler(ctx, input, input_len); break; } case TOPRF_Update_Peer_Disclose_ZK_Cheaters: { ret = peer_zkproof_disclose(ctx, output, output_len); break; } case TOPRF_Update_Peer_Reconstruct_ZK_Shares: { ret = peer_reconst_zk_shares(ctx,input,input_len,output,output_len); break;} case TOPRF_Update_Peer_Send_Mult_Ci: { ret = peer_step39_handler(ctx, output, output_len); break; } case TOPRF_Update_Peer_Final_VSPS_Checks: { ret = peer_step41_handler(ctx, input, input_len); break; } case TOPRF_Update_Peer_Disclose_VSPS_Cheaters: { ret = peer_vsps_disclose(ctx, output, output_len); break; } case TOPRF_Update_Peer_Reconstruct_VSPS_Shares: { ret = peer_reconst_vsps_shares(ctx,input,input_len,output,output_len); break;} case TOPRF_Update_Peer_Send_k0p_Share: { ret = peer_step44_handler(ctx, output, output_len); break; } case TOPRF_Update_Peer_Final_OK: { ret = peer_step46_handler(ctx, input, input_len); break; } case TOPRF_Update_Peer_Done: { // we are done ret = 0; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] peer next invalid step\n", ctx->index); ret = 99; } } if(ret!=0) ctx->step=99; // so that not_done reports done return ret; } liboprf-0.9.4/src/toprf-update.h000066400000000000000000001237321514673400200165450ustar00rootroot00000000000000#ifndef TOPRF_UPDATE_H #define TOPRF_UPDATE_H /** * @file toprf-update.h * @brief TOPRF Update * * SPDX-FileCopyrightText: 2025, Marsiske Stefan * SPDX-License-Identifier: LGPL-3.0-or-later * * This file defines the structures, types, and functions for updating a threshold OPRF * instantiation. */ #include #include #include #include "dkg.h" #include "toprf.h" #define noise_xk_handshake1_SIZE 48UL #define noise_xk_handshake2_SIZE 48UL #define noise_xk_handshake3_SIZE 64UL #define toprf_update_noise_key_SIZE (32UL) #define toprf_update_sessionid_SIZE 32U #define toprf_update_commitment_HASHBYTES 32U #define toprf_update_encrypted_shares_SIZE (TOPRF_Share_BYTES * 2) #define toprf_keyid_SIZE 32U typedef DKG_Message TOPRF_Update_Message; // TODO: refactor this, as it's the same in tp-dkg and stp-dkg /** * @struct TOPRF_Update_Cheater * @brief Records information about protocol violations * * This struct is used to communicate and track detailed information about * detected violations of the protocol rules by participants. * * @var TOPRF_Update_Cheater::step The protocol step in which the violation occurred * @var TOPRF_Update_Cheater::error Error code specifying the type of violation * @var TOPRF_Update_Cheater::peer Index of the peer that caused the violation * @var TOPRF_Update_Cheater::other_peer Optional index of the peer that reported the * violation (set to 0xfe if unused) * @var TOPRF_Update_Cheater::invalid_index */ typedef struct { int step; int error; uint8_t peer; uint8_t other_peer; int invalid_index; } TOPRF_Update_Cheater; /** * @enum TOPRF_Update_Err * @brief Error codes for the TOPRF-Update protocol * * These error codes provide detailed information about various failure * conditions that can occur during the TOPRF update protocol execution. */ typedef enum { TOPRF_Update_Err_OK = 0, TOPRF_Update_Err_ISize, TOPRF_Update_Err_OSize, TOPRF_Update_Err_OOB, TOPRF_Update_Err_Send, TOPRF_Update_Err_CheatersFound, TOPRF_Update_Err_CheatersFull, TOPRF_Update_Err_InvSessionID, TOPRF_Update_Err_VSSShare, TOPRF_Update_Err_VSSCommit, TOPRF_Update_Err_Noise, TOPRF_Update_Err_NoiseEncrypt, TOPRF_Update_Err_NoiseDecrypt, TOPRF_Update_Err_HMac, TOPRF_Update_Err_NoSubVSPSFail, TOPRF_Update_Err_NotEnoughDealers, TOPRF_Update_Err_TooManyCheaters, TOPRF_Update_Err_DKGFinish, TOPRF_Update_Err_FTMULTStep1, TOPRF_Update_Err_FTMULTZKCommitments, TOPRF_Update_Err_InvPoint, TOPRF_Update_Err_CommmitmentsMismatch, TOPRF_Update_Err_Proto, TOPRF_Update_Err_BadReconstruct, TOPRF_Update_Err_Reconstruct, TOPRF_Update_Err_BroadcastEnv = 32, TOPRF_Update_Err_Env = 64 } TOPRF_Update_Err; /** * @enum TOPRF_Update_STP_Steps * @brief TOPRF-Update protocol steps for the STP * * These values define the sequential steps that the STP * follows during the execution of the TOPRF update protocol. */ typedef enum { TOPRF_Update_STP_Broadcast_NPKs, TOPRF_Update_STP_Route_Noise_Handshakes1, TOPRF_Update_STP_Route_Noise_Handshakes2, TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments, TOPRF_Update_STP_Broadcast_DKG_Commitments, TOPRF_Update_STP_Route_Encrypted_Shares, TOPRF_Update_STP_Broadcast_Complaints, TOPRF_Update_STP_Broadcast_DKG_Defenses, TOPRF_Update_STP_Broadcast_DKG_Transcripts, TOPRF_Update_STP_Route_Mult_Step1, TOPRF_Update_STP_Broadcast_Mult_Commitments, TOPRF_Update_STP_Route_Encrypted_Mult_Shares, TOPRF_Update_STP_Broadcast_Mult_Complaints, TOPRF_Update_STP_Broadcast_Mult_Defenses, TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares, TOPRF_Update_STP_Route_ZK_Challenge_Commitments, TOPRF_Update_STP_Route_ZK_commitments, TOPRF_Update_STP_Broadcast_ZK_nonces, TOPRF_Update_STP_Broadcast_ZK_Proofs, TOPRF_Update_STP_Broadcast_ZK_Disclosures, TOPRF_Update_STP_Broadcast_Mult_Ci, TOPRF_Update_STP_Broadcast_VSPS_Disclosures, TOPRF_Update_STP_Reconstruct_Delta, TOPRF_Update_STP_Done } TOPRF_Update_STP_Steps; /** * @struct TOPRF_Update_STPState * @brief State of the STP during protocol execution * * This struct maintains the state of the STP during the execution of * the TOPRF Update protocol. * Some fields in this struct are internal variables and should not * be used. The following fields are useful and can be accessed by * users of the API: * * @var TOPRF_Update_STPState::n The total number of peers participating * in this protocol * * @var TOPRF_Update_STPState::t The threshold necessary to use shared * secret generated by this DKG * * @var TOPRF_Update_STPState::step the current step in the protocol * * @var TOPRF_Update_STPState::prev the previous step in the protocol * * @var TOPRF_Update_STPState::cheaters List of detected cheaters and * protocol violators at the end of a failed protocol run * * @var TOPRF_Update_STPState::delta The final public delta value, which * can be applied to previous OPRF results with the old key to update * those results to the new key * * @var TOPRF_Update_STPState::k0p_final_commitments The final * commitments to the shares of the updated key. */ // TODO: consider making this struct private typedef struct { TOPRF_Update_STP_Steps step; TOPRF_Update_STP_Steps prev; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t n; uint8_t t; uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t sig_sk[crypto_sign_SECRETKEYBYTES]; uint64_t *last_ts; uint64_t ts_epsilon; const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES]; uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES]; uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES]; uint16_t p_complaints_len; uint16_t *p_complaints; uint16_t y2_complaints_len; uint16_t *y2_complaints; uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES]; size_t cheater_len; TOPRF_Update_Cheater (*cheaters)[]; size_t cheater_max; uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES]; uint8_t delta[crypto_scalarmult_ristretto255_BYTES]; crypto_generichash_state transcript_state; uint8_t transcript[crypto_generichash_BYTES]; } TOPRF_Update_STPState; /** * @brief Gets the size of a TOPRF_Update_STPState struct * * Returns the memory size required for the STPState structure. * This is useful when dynamically allocating memory for this * structure. If you do, note that the struct must be aligned at * a 32-byte boundary for proper operation. * The python wrapper, pyoprf, handles this like this: * @code * import ctypes * ctypes.create_string_buffer(liboprf.toprf_update_stpstate_size()+32) * @endcode * * @return Size of the TOPRF_Update_STPState structure in bytes */ size_t toprf_update_stpstate_size(void); /** * @brief Gets the value of `n` (number of participants) from the STP state * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return The number of participants in the protocol */ uint8_t toprf_update_stpstate_n(const TOPRF_Update_STPState *ctx); /** * @brief Gets the value of `t` (threshold) from the STP state * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return The threshold value used in the protocol */ uint8_t toprf_update_stpstate_t(const TOPRF_Update_STPState *ctx); /** * @brief Gets the number of cheaters discovered so far from the STP state * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return The count of detected cheaters and protocol violators */ size_t toprf_update_stpstate_cheater_len(const TOPRF_Update_STPState *ctx); /** * @brief Gets the session ID from the STP state * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return Pointer to the session ID byte array */ const uint8_t *toprf_update_stpstate_sessionid(const TOPRF_Update_STPState *ctx); /** * @brief Gets the final result, the public delta value from the STP state * * This function returns the public delta value which can be applied to * previous OPRF results to update them to use the new key. It should only * be used after the protocol has finished successfully. * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return Pointer to the delta value byte array, or NULL if protocol * not complete */ const uint8_t *toprf_update_stpstate_delta(const TOPRF_Update_STPState *ctx); /** * @brief Gets the commitments for the shares of the new key from the STP * state * * This function returns the public commitments to the shares of the new key. * It should only be used after the protocol has finished successfully. * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return Pointer to the commitments byte array, or NULL if protocol * not complete */ const uint8_t *toprf_update_stpstate_commitments(const TOPRF_Update_STPState *ctx); /** * @brief Gets the current step of the STP in the protocol from the STP state * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState structure * * @return The current protocol step as an integer */ int toprf_update_stpstate_step(const TOPRF_Update_STPState *ctx); #define toprfupdate_stp_start_msg_SIZE (sizeof(TOPRF_Update_Message) + crypto_generichash_BYTES /*dst*/ \ + toprf_keyid_SIZE + crypto_sign_PUBLICKEYBYTES) /** * @brief Starts a new execution of the STP TOPRF update protocol * * This function initializes the state of the STP and creates an initial * message containing the parameters for the peers to join the protocol. * * @param[in] ctx Pointer to a STP_DKG_STPState struct. This struct * will be initialized by this function * @param[in] ts_epsilon Maximum allowed message age in seconds before * it is considered stale and rejected. This value is used to * prevent replay attacks and enforce freshness. For small, * local setups (e.g., 2-out-of-3 participants), values as low * as 2–3 seconds may suffice. For large-scale deployments * (e.g., 126-out-of-127), this may need to be increased to * several hours * @param[in] n Number of peers participating in this execution * @param[in] t Threshold necessary to use the results of this update. * This value should be the same as it was for the previous * key being shared * @param[in] proto_name An array of bytes used as a domain seperation tag * (DST). Set it to the name of your application * @param[in] proto_name_len The size of the array `proto_name`, to allow * non-zero terminated DSTs * @param[in] keyid The ID of the key to be updated. Must be exactly * toprf_keyid_SIZE bytes long * @param[in] sig_pks Pointer to a (n+1)-element array of signing public * keys. The STP's public key must be at index 0. The rest of the * items must be in order * @param[in] ltssk Long-term signing private key of the STP * @param[in] msg0_len Size of memory allocated for the `msg0` parameter. It * should be tpdkg_msg0_SIZE * @param[out] msg0 Message to be sent to all peers to initialize them * * @return 0 on success, non-zero on error */ int toprf_update_start_stp(TOPRF_Update_STPState *ctx, const uint64_t ts_epsilon, const uint8_t n, const uint8_t t, const char *proto_name, const size_t proto_name_len, const uint8_t keyid[toprf_keyid_SIZE], const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES], const uint8_t ltssk[crypto_sign_SECRETKEYBYTES], const size_t msg0_len, TOPRF_Update_Message *msg0); /** * @brief Sets variable-sized buffers in the TOPRF_Update_STPState structure * * A number of buffers are needed in the STP state that depend on the `n` * (number of participants) and `t` (threshold) parameters. * * These can be allocated on the stack as follows: * @code * uint16_t stp_p_complaints[n*n]; * uint16_t stp_y2_complaints[n*n]; * uint64_t last_ts[n]; * * uint8_t stp_p_commitments_hashes[n][toprf_update_commitment_HASHBYTES]; * uint8_t stp_p_share_macs[n*n][crypto_auth_hmacsha256_BYTES]; * uint8_t stp_p_commitments[n*n][crypto_core_ristretto255_BYTES]; * * uint8_t stp_k0p_commitments[dealers*(n+1)][crypto_core_ristretto255_BYTES]; * uint8_t stp_zk_challenge_commitments[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES]; * uint8_t stp_zk_challenge_e_i[dealers][crypto_scalarmult_ristretto255_SCALARBYTES]; * * uint8_t k0p_final_commitments[n][crypto_scalarmult_ristretto255_BYTES]; * TOPRF_Update_Cheater stp_cheaters[t*t - 1]; * toprf_update_stp_set_bufs(&stp, * stp_p_complaints, * stp_y2_complaints, * &stp_cheaters, * sizeof(stp_cheaters) / sizeof(TOPRF_Update_Cheater), * &stp_p_commitments_hashes, * &stp_p_commitments, * &k0_commitments, * &stp_k0p_commitments, * &stp_zk_challenge_commitments, * &stp_zk_challenge_e_i, * &k0p_final_commitments, * last_ts); * @endcode * * @param[in] ctx Pointer to initialized TOPRF_Update_STPState structure * @param[in] p_complaints Buffer for polynomial complaints * @param[in] cheaters List of detected cheaters and protocol violators at * the end of a failed protocol run * @param[in] cheater_max Maximum number of cheat attempts to be recorded. * Normally, the maximum number of cheaters is `t * t - 1`, where * `t` is the threshold parameter. It should be provided as * (sizeof(cheaters) / sizeof(TP_DKG_Cheater)) * @param[in] p_commitments_hashes Buffer for polynomial commitment hashes * @param[in] p_share_macs Buffer for share Message Authentication Codes * (MACs) * @param[in] p_commitments Buffer for polynomial commitments * @param[in] last_ts Pointer to a list of last timestamps */ void toprf_update_stp_set_bufs(TOPRF_Update_STPState *ctx, uint16_t p_complaints[], uint16_t y2_complaint[], TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES], uint64_t *last_ts); /** * @enum TOPRF_Update_Peer_Steps * @brief Protocol steps for the peers * * These values define the sequential steps that each peer follows during * the execution of the TOPRF update protocol. */ typedef enum { TOPRF_Update_Peer_Broadcast_NPK_SIDNonce, TOPRF_Update_Peer_Rcv_NPK_SIDNonce, TOPRF_Update_Peer_Noise_Handshake, TOPRF_Update_Peer_Finish_Noise_Handshake, TOPRF_Update_Peer_Rcv_CHashes_Send_Commitments, TOPRF_Update_Peer_Rcv_Commitments_Send_Shares, TOPRF_Update_Peer_Verify_Commitments, TOPRF_Update_Peer_Handle_DKG_Complaints, TOPRF_Update_Peer_Defend_DKG_Accusations, TOPRF_Update_Peer_Check_Shares, TOPRF_Update_Peer_Finish_DKG, TOPRF_Update_Peer_Confirm_Transcripts, TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments, TOPRF_Update_Peer_Send_K0P_Shares, TOPRF_Update_Peer_Recv_K0P_Shares, TOPRF_Update_Peer_Handle_Mult_Share_Complaints, TOPRF_Update_Peer_Defend_Mult_Accusations, TOPRF_Update_Peer_Check_Mult_Shares, TOPRF_Update_Peer_Disclose_Mult_Shares, TOPRF_Update_Peer_Reconstruct_Mult_Shares, TOPRF_Update_Peer_Send_ZK_Challenge_Commitments, TOPRF_Update_Peer_Send_ZK_Commitments, TOPRF_Update_Peer_Send_ZK_nonces, TOPRF_Update_Peer_Send_ZK_proofs, TOPRF_Update_Peer_Verify_ZK_proofs, TOPRF_Update_Peer_Disclose_ZK_Cheaters, TOPRF_Update_Peer_Reconstruct_ZK_Shares, TOPRF_Update_Peer_Send_Mult_Ci, TOPRF_Update_Peer_Final_VSPS_Checks, TOPRF_Update_Peer_Disclose_VSPS_Cheaters, TOPRF_Update_Peer_Reconstruct_VSPS_Shares, TOPRF_Update_Peer_Send_k0p_Share, TOPRF_Update_Peer_Final_OK, TOPRF_Update_Peer_Done } TOPRF_Update_Peer_Steps; /** * @enum TOPRF_Update_Message_Type * @brief Message type identifiers used in the TOPRF-Update protocol * * These identifiers are used to tag messages exchanged during the protocol, * allowing recipients to properly interpret and process each message. */ typedef enum { toprfupdate_stp_start_msg, toprfupdate_peer_init_msg, toprfupdate_stp_bc_init_msg, toprfupdate_peer_ake1_msg, toprfupdate_peer_ake2_msg, toprfupdate_peer_dkg1_msg, toprfupdate_stp_bc_dkg1_msg, toprfupdate_peer_dkg2_msg, toprfupdate_stp_bc_dkg2_msg, toprfupdate_peer_dkg3_msg, toprfupdate_peer_verify_shares_msg, toprfupdate_stp_bc_verify_shares_msg, toprfupdate_peer_share_key_msg, toprfupdate_stp_bc_key_msg, toprfupdate_peer_bc_transcript_msg, toprfupdate_stp_bc_transcript_msg, toprfupdate_peer_mult1_msg, toprfupdate_stp_bc_mult1_msg, toprfupdate_peer_mult_coms_msg, toprfupdate_stp_bc_mult_coms_msg, toprfupdate_peer_mult2_msg, toprfupdate_peer_verify_mult_shares_msg, toprfupdate_peer_share_mult_key_msg, toprfupdate_stp_bc_mult_key_msg, toprfupdate_peer_reconst_mult_shares_msg, toprfupdate_stp_bc_reconst_mult_shares_msg, toprfupdate_peer_zkp1_msg, toprfupdate_stp_bc_zkp1_msg, toprfupdate_peer_zkp2_msg, toprfupdate_stp_bc_zkp2_msg, toprfupdate_peer_zkp3_msg, toprfupdate_stp_bc_zkp3_msg, toprfupdate_peer_zkp4_msg, toprfupdate_stp_bc_zkp4_msg, toprfupdate_peer_zk_disclose_msg, toprfupdate_stp_bc_zk_disclose_msg, toprfupdate_peer_mult3_msg, toprfupdate_stp_bc_mult3_msg, toprfupdate_peer_vsps_disclose_msg, toprfupdate_stp_bc_vsps_disclose_msg, toprfupdate_peer_end2_msg, toprfupdate_stp_end3_msg, } TOPRF_Update_Message_Type; /** * @struct TOPRF_Update_ZK_params * @brief Parameters for zero-knowledge proofs * * This structure contains the parameters used in zero-knowledge proofs * within the TOPRF update protocol. */ typedef struct { uint8_t d[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t s[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t x[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t s_1[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t s_2[crypto_scalarmult_ristretto255_SCALARBYTES]; } TOPRF_Update_ZK_params; /** * @struct TOPRF_Update_ZK_proof * @brief Values in a zero-knowledge proof * * This structure contains the public values used in a zero-knowledge * proof within the TOPRF update protocol. */ typedef struct { uint8_t y[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t w[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t z[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t w_1[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t w_2[crypto_scalarmult_ristretto255_SCALARBYTES]; } TOPRF_Update_ZK_proof; /** * @struct TOPRF_Update_PeerState * @brief State of a peer during the TOPRF update protocol * * This contains the state of a peer during the execution of the TOPRF * Update protocol. * * Some fields in this struct are internal variables and should not * be used. The following fields are useful and can be accessed by * users of the API: * * @var TOPRF_Update_PeerState::n Total number of peers participating in * this protocol * * @var TOPRF_Update_PeerState::t Threshold necessary to use the shared * secret generated by this DKG * * @var TOPRF_Update_PeerState::index The index of the peer * * @var TOPRF_Update_PeerState::cheaters List of detected cheaters and * protocol violators at the end of the failed protocol run */ typedef struct { TOPRF_Update_Peer_Steps step; TOPRF_Update_Peer_Steps prev; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t n; uint8_t t; uint8_t index; TOPRF_Share kc0_share[2]; uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t sig_sk[crypto_sign_SECRETKEYBYTES]; uint8_t noise_pk[crypto_scalarmult_BYTES]; uint8_t noise_sk[crypto_scalarmult_SCALARBYTES]; uint64_t stp_last_ts; uint64_t *last_ts; uint64_t ts_epsilon; const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*peer_noise_pks)[][crypto_scalarmult_BYTES]; Noise_XK_device_t *dev; Noise_XK_session_t *(*noise_outs)[]; Noise_XK_session_t *(*noise_ins)[]; uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE]; TOPRF_Share (*p_shares)[][2]; uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES]; uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES]; uint16_t p_complaints_len; uint16_t *p_complaints; uint8_t my_p_complaints_len; uint8_t *my_p_complaints; TOPRF_Share p_share[2]; uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES]; TOPRF_Share (*k0p_shares)[][2]; uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES]; uint8_t k0p_tau[crypto_core_ristretto255_SCALARBYTES]; uint8_t zk_chal_nonce[2][crypto_core_ristretto255_SCALARBYTES]; uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES]; uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES]; TOPRF_Update_ZK_params zk_params; TOPRF_Share k0p_share[2]; uint8_t k0p_commitment[crypto_core_ristretto255_BYTES]; size_t cheater_len; TOPRF_Update_Cheater (*cheaters)[]; size_t cheater_max; crypto_generichash_state transcript_state; uint8_t transcript[crypto_generichash_BYTES]; } TOPRF_Update_PeerState; /** * @brief Gets the size of a TOPRF_Update_PeerState struct * * Returns the memory size required for the PeerState structure. * Useful when dynamically allocating memory for this structure. Note that * the struct must be aligned at a 32-byte boundary for proper operation. * * @return Size of the TOPRF_Update_PeerState structure in bytes */ size_t toprf_update_peerstate_size(void); /** * @brief Gets the value of `n` (number of participants) from the peer state * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * * @return The number of participants in the protocol */ uint8_t toprf_update_peerstate_n(const TOPRF_Update_PeerState *ctx); /** * @brief Gets the value of T (threshold) from the peer state * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * * @return The threshold value used in the protocol */ uint8_t toprf_update_peerstate_t(const TOPRF_Update_PeerState *ctx); /** * @brief Gets the session ID from the peer state * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * * @return Pointer to the session ID byte array */ const uint8_t *toprf_update_peerstate_sessionid(const TOPRF_Update_PeerState *ctx); /** * @brief Gets the private share of the updated OPRF key * * Returns the final result, the peer's private share of the updated * OPRF key from a TOPRF_Update_PeerState struct. This should only be * called after the protocol has finished successfully. * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * @return Pointer to the share byte array, or NULL if protocol not complete */ const uint8_t *toprf_update_peerstate_share(const TOPRF_Update_PeerState *ctx); /** * @brief Gets the commitment to the peer's share of the updated OPRF key * * Returns the public commitment to the peer's share of the updated * OPRF key from a TOPRF_Update_PeerState struct. This should only be * called after the protocol has finished successfully. * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * * @return Pointer to the commitment byte array, or NULL if protocol * not complete */ const uint8_t *toprf_update_peerstate_commitment(const TOPRF_Update_PeerState *ctx); /** * @brief Gets all the public commitments to the shares of the updated * OPRF key * * Returns the public commitments to all shares of the updated OPRF key * from a TOPRF_Update_PeerState struct. This should only be used after * the protocol has finished successfully. * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * @return Pointer to the commitments byte array, or NULL if protocol not complete */ const uint8_t *toprf_update_peerstate_commitments(const TOPRF_Update_PeerState *ctx); /** * @brief Gets the current step of the protocol from the peer state * * @param ctx Pointer to an initialized TOPRF_Update_PeerState structure * * @return The current protocol step as an integer */ int toprf_update_peerstate_step(const TOPRF_Update_PeerState *ctx); /** * @brief Starts a new execution of the TOPRF-Update protocol for a peer * * This function initializes the state of the peer. * * @param[in] ctx Pointer to a TOPRF_Update_PeerState struct. This struct will be initialized by this function * @param[in] ts_epsilon Maximum allowed message age in seconds before * it is considered stale and rejected. This value is used to * prevent replay attacks and enforce freshness. For small, * local setups (e.g., 2-out-of-3 participants), values as low * as 2–3 seconds may suffice. For large-scale deployments * (e.g., 126-out-of-127), this may need to be increased to * several hours * @param[in] lt_sk Long-term private signing key of the peer * @param[in] noise_sk The Noise_XK private key of the peer * @param[in] msg0 The initiating message received from the TP (created * after running `tpdkg_tp_start()`) * @param[out] keyid The ID of the key to be updated. Use this to look up * the `n-t` parameters, the key to be updated, and the various * long-term public keys of the other participants * @param[out] stp_ltpk The STP's long-term public signing key. This should * be used to verify if this key is actually authorized to * initiate an STP DKG with the peer * * @return 0 on success, non-zero on error */ TOPRF_Update_Err toprf_update_start_peer(TOPRF_Update_PeerState *ctx, const uint64_t ts_epsilon, const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES], const uint8_t noise_sk[crypto_scalarmult_SCALARBYTES], const TOPRF_Update_Message *msg0, uint8_t keyid[toprf_keyid_SIZE], uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]); /** * @brief Sets all the variable sized buffers in the TOPRF_Update_PeerState * structure * * This function sets up pointers to all variable-sized buffers required by * a peer during the TOPRF key update protocol. * * The size of most buffers depends on the `n` (number of participants) and * `t` (threshold) values, which are derived from the DKG parameters and * communicated in `msg0` from the STP. These are inputs to * `toprf_update_start_peer()`, and must be passed to this function as * parameters. * After calling this function, the `TOPRF_Update_PeerState` struct is ready * to be used, and the caller can query the parameters `n`, `t` and the * `keyid` to load the corresponding data. * * To allocate all the buffers on the stack: * @code * Noise_XK_session_t *noise_outs[n]; * Noise_XK_session_t *noise_ins[n]; * * TOPRF_Share pshares[n][2]; * uint8_t p_commitments[n*n][crypto_core_ristretto255_BYTES]; * uint8_t p_commitments_hashes[n][toprf_update_commitment_HASHBYTES]; * uint8_t peers_p_share_macs[n*n][crypto_auth_hmacsha256_BYTES]; * uint16_t peer_p_complaints[n*n]; * uint8_t peer_my_p_complaints[n]; * * uint8_t encrypted_shares[n][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE]; * * uint64_t peer_last_ts[n]; * uint8_t lambdas[dealers][crypto_core_ristretto255_SCALARBYTES]; * TOPRF_Share k0p_shares[dealers][2]; * uint8_t k0p_commitments[dealers*(n+1)][crypto_core_ristretto255_BYTES]; * uint8_t zk_challenge_nonce_commitments[n*2][crypto_scalarmult_ristretto255_BYTES]; * uint8_t zk_challenge_nonces[n*2][2][crypto_scalarmult_ristretto255_SCALARBYTES]; * uint8_t zk_challenge_commitments[dealers*2][3][crypto_scalarmult_ristretto255_SCALARBYTES]; * uint8_t zk_challenge_e_i[2*dealers][crypto_scalarmult_ristretto255_SCALARBYTES]; * TOPRF_Update_Cheater peer_cheaters[n*n - 1]; * * if(0!=toprf_update_peer_set_bufs(&peer, index, n, t, k0_share, * &k0_commitments, * <_pks, &peers_noise_pks, noise_sk, * &noise_outs, &noise_ins, * &pshares, * &p_commitments, * &p_commitments_hashes, * &peers_p_share_macs, * &encrypted_shares, * &peer_cheaters, sizeof(peer_cheaters) / sizeof(TOPRF_Update_Cheater) / n, * &lambdas, * &k0p_shares, * &k0p_commitments, * &zk_challenge_nonce_commitments, * &zk_challenge_nonces, * &zk_challenge_commitments, * &zk_challenge_e_i, * peer_p_complaints, * peer_my_p_complaints, * peer_last_ts)) return 1; * @endcode * * @param[in] index Must be initialized based on the index of the `k0` * share index. If no share is held by the peer, the index * must be established out of band. TODO: fix this * @param[in] n The total number of participants in the protocol. It must * be initialized based on the info associated with the `keyid` * returned by `toprf_update_start_peer()` * @param[in] t Threshold of participants required to reconstruct the * secret. It must be initialized based on the info associated * with the `keyid` returned by `toprf_update_start_peer()` * @param[in] k0 Pointer to the old key shares held by this peer). Pass * NULL if the peer holds no shares. * @param[in] kc0_commitments The commitments to the shares of the old key. * Even if peer has no share, they must have these commitments. * TODO: check if this is actually true. * @param[in] sig_pks Array of long-term signing public keys for all * participants. The STP's public key must be at index 0. All * other public keys must be in order of the shares of `k0` they * hold. Peers that hold no `k0` shares can be listed in * arbitrary order, following those with shares * @param[in] peer_noise_pks Array of all Noise_XK public keys of peers, * in the order of the indices of the shares of `k0`. Peers that * hold no `k0` shares can be listed in arbitrary order, following * those with shares. * * @return 0 on success, non-zero on error * */ int toprf_update_peer_set_bufs(TOPRF_Update_PeerState *ctx, const uint8_t self, const uint8_t n, const uint8_t t, const TOPRF_Share k0[2], uint8_t (*k0_commitments)[][crypto_core_ristretto255_BYTES], const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*noise_pks)[][crypto_scalarmult_BYTES], Noise_XK_session_t *(*noise_outs)[], Noise_XK_session_t *(*noise_ins)[], TOPRF_Share (*p_shares)[][2], uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES], uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES], uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE], TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max, uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES], TOPRF_Share (*k0p_shares)[][2], uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES], uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES], uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES], uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES], uint16_t *p_complaints, uint8_t *my_p_complaints, uint64_t *last_ts); /** * @brief Calculates the size of the buffer needed to hold all outputs * from peers, which serve as input to the next step of the STP * * An implementer should allocate a buffer of this size and concatenate * all messages from all peers in the order of the peers. The allocated * buffer is to be passed as an input to the `toprf_update_stp_next()` * function. After this, the buffer SHOULD be deallocated. * * @param[in] ctx An initialized TOPRF_Update_STPState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t toprf_update_stp_input_size(const TOPRF_Update_STPState *ctx); /** * @brief Calculates the size of the message from each peer to be received * by the STP * * This function fills the `sizes` array (which must have exactly `n` * elements, one per peer) with the expected message size from each * peer based on the current STP step. * * @param[in] ctx An initialized TOPRF_Update_STPState struct * @param[out] sizes An array of type `size_t` with exactly `n` elements * * @return 0 on if the sizes differ from peer to peer, otherwise all * peers will be sending messages of equal size. In the latter * case, all items of the sizes array hold the same valid value */ int toprf_update_stp_input_sizes(const TOPRF_Update_STPState *ctx, size_t *sizes); /** * @brief Calculates the size of the buffer needed to hold the output * from the `toprf_update_stp_next()` function * * An implementer should allocate a buffer of this size and pass if as * parameter to `toprf_update_stp_next()`. * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t toprf_update_stp_output_size(const TOPRF_Update_STPState *ctx); /** * @brief Executes the next step of the TOPRF update protocol for the STP * * This function handles one round of the protocol on the STP side by * reading peer input, processing it, and writing output that may need * to be sent to peers. * * @param[in] ctx Pointer to an initialized TOPRF_Update_STPState struct * @param[in] input Pointer to input buffer for this step * @param[in] input_len Size of the input buffer * @param[out] output Pointer to the output buffer for this step * @param[in] output_len Size of the output buffer * * @return 0 on success, non-zero on error * * An example of how to use this function in concert with * `toprf_update_stp_input_size()` and `toprf_update_stp_output_size()`: * @code * uint8_t stp_out[toprf_update_stp_output_size(&tp)]; * uint8_t stp_in[toprf_update_stp_input_size(&tp)]; * recv(socket, stp_in, sizeof(stp_in)); * ret = toprf_update_stp_next(&stp, stp_in, sizeof(stp_in), * stp_out, sizeof(stp_out)); * @endcode */ int toprf_update_stp_next(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); /** * @brief Extracts the per-peer message from the output of * `toprf_update_stp_next()` for the specified peer * * This function converts the output of `toprf_update_stp_next()` into a * message to be sent to the i-th peer. Some outputs of steps produce * broadcast messages (same messages for all peers), while others generate * decicated and unique messages for each peer. * This function returns a pointer to a message and the size of the message * to be sent for a particular peer specified as a parameter. * * Example of how to use this function in concert with `toprf_update_stp_next()`: * @code * ret = toprf_update_stp_next(&stp, stp_in, sizeof(stp_in), stp_out, sizeof stp_out); * * for(int i=0;i #include "oprf.h" #include "toprf.h" #include #ifdef UNIT_TEST #include "utils.h" #endif /* @copyright 2023, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ // implements TOPRF from https://eprint.iacr.org/2017/363 // quote from page 9 (first line is last on page 8) // The underlying PRF, fk(x) = H2(x, (H1(x))k), remains unchanged, but the // key k is shared using Shamir secret-sharing across n servers, where server Si // stores the key share ki. The initialization of such secret-sharing can be done via // a Distributed Key Generation (DKG) for discrete-log-based systems, e.g. [16], // and in Figure 2 we assume it is done with a UC functionality FDKG which we // discuss further below. For evaluation, given any subset SE of t + 1 servers, the // user U sends to each of them the same message a = (Hβ€²(x))r for random r, // exactly as in the single-server OPRF protocol 2HashDH. If each server Si in SE // returned bi = aki then U could reconstruct the value ak using standard Lagrange // interpolation in the exponent, i.e. ak = οΏ½ i∈SE bΞ»i i with the Lagrange coefficients // Ξ»i computed using the indexes of servers in SE. After computing ak, the value // of fk(x) is computed by U by deblinding ak exactly as in the case of protocol // 2HashDH. Note that this takes a single exponentiation for each server and two // exponentiations for the user (to compute a and to deblind ak) plus one multi- // exponentiation by U to compute the Lagrange interpolation on the bi values. // run with // gcc -o toprf -g -Wall toprf.c -lsodium liboprf.a typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_BYTES]; } __attribute((packed)) TOPRF_Part; void __attribute__((visibility("hidden"))) lcoeff(const uint8_t index, const uint8_t x, const size_t degree, const uint8_t peers[degree], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES]) { uint8_t xscalar[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; xscalar[0]=x; uint8_t iscalar[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; iscalar[0]=index; uint8_t divident[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; divident[0]=1; uint8_t divisor[crypto_scalarmult_ristretto255_SCALARBYTES]={0}; divisor[0]=1; for(size_t j=0;j 0 && arr[d] < arr[d-1]) { t = arr[d]; t1 = indexes[d]; arr[d] = arr[d-1]; indexes[d] = indexes[d-1]; arr[d-1] = t; indexes[d-1] = t1; d--; } } } int toprf_thresholdmult(const size_t response_len, const uint8_t _responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]) { const TOPRF_Part *responses=(TOPRF_Part*) _responses; uint8_t lpoly[crypto_scalarmult_ristretto255_SCALARBYTES]; uint8_t gki[crypto_scalarmult_ristretto255_BYTES]; memset(result,0,crypto_scalarmult_ristretto255_BYTES); // sort the responses by their indexes uint8_t indexed_indexes[response_len]; if(response_len>255) return 1; sort_parts((uint8_t) response_len, (TOPRF_Part*) responses, indexed_indexes); uint8_t indexes[response_len]; for(size_t i=0;ivalue, lpoly); TOPRF_Part *Z=(TOPRF_Part*) _Z; if(oprf_Evaluate(kl,blinded, Z->value)) return 1; return 0; } int toprf_thresholdcombine(const size_t response_len, const uint8_t _responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]) { if(response_len>255) return 1; const TOPRF_Part *responses=(TOPRF_Part*) _responses; memset(result,0,crypto_scalarmult_ristretto255_BYTES); uint8_t indexed_indexes[response_len]; sort_parts((uint8_t) response_len, (TOPRF_Part*) responses, indexed_indexes); for(size_t i=0;iindex=k->index; if(oprf_Evaluate(k->value, alpha, beta->value)) return 1; // hash (ssid_S + alpha, outlen=64) crypto_generichash_state h_state; crypto_generichash_init(&h_state, NULL, 0, crypto_core_ristretto255_HASHBYTES); uint16_t len=htons((uint16_t) ssid_S_len); // we have a guard above restricting to 1KB the proto_name_len crypto_generichash_update(&h_state, (uint8_t*) &len, 2); crypto_generichash_update(&h_state, ssid_S, ssid_S_len); crypto_generichash_update(&h_state, alpha, crypto_core_ristretto255_BYTES); uint8_t hash[crypto_core_ristretto255_HASHBYTES]; crypto_generichash_final(&h_state,hash,sizeof hash); // hash-to-curve uint8_t point[crypto_scalarmult_ristretto255_BYTES]; if(0!=voprf_hash_to_group(hash, sizeof hash, point)) return -1; TOPRF_Part h2; const TOPRF_Share *z=(TOPRF_Share*) _z; if(oprf_Evaluate(z->value, point, h2.value)) return 1; crypto_core_ristretto255_add(beta->value, beta->value, h2.value); return 0; } liboprf-0.9.4/src/toprf.h000066400000000000000000000207341514673400200152630ustar00rootroot00000000000000#ifndef TOPRF_H #define TOPRF_H /** * @file toprf.h * @brief API for the Threshold Oblivious Pseudorandom Function (TOPRF) * implementation * * SPDX-FileCopyrightText: 2023, Marsiske Stefan * SPDX-License-Identifier: LGPL-3.0-or-later * * This file defines the structures, types, and functions for implementing * a Threshold Oblivious Pseudorandom Function (TOPRF) based on the * paper section 3 of the paper: * "TOPPSS: Cost-minimal Password-Protected Secret Sharing based on * Threshold OPRF" by Stanislaw Jarecki, Aggelos Kiayias, Hugo Krawczyk, * and Jiayu Xu, 2017 (https://eprint.iacr.org/2017/363) */ #include #include /** * @struct TOPRF_Share * @brief Share structure for TOPRF */ typedef struct { uint8_t index; uint8_t value[crypto_core_ristretto255_SCALARBYTES]; } __attribute((packed)) TOPRF_Share; #define TOPRF_Share_BYTES (sizeof(TOPRF_Share)) #define TOPRF_Part_BYTES (crypto_core_ristretto255_BYTES + 1UL) /** * @brief Interpolates a polynomial of degree `t` at an arbitrary point * `x: y = f(x)` * * Uses Lagrange interpolation to reconstruct the polynomial value at `x`, * given `t` shares (evaluations) of the polynomial. * * @param[in] x The value at which the polynomial is evaluated * @param[in] t The degree of the polynomial * @param[in] shares Evaluated points on the polynomial. * @param[out] y Output buffer to store the computed result, `f(x)` */ void interpolate(const uint8_t x, const uint8_t t, const TOPRF_Share shares[t], uint8_t y[crypto_scalarmult_ristretto255_SCALARBYTES]); /** * @brief Computes the Lagrange coefficient for `f(x)` * * This function calculates a Lagrange coefficient for `f(x)` * based on the index and the indices of the other contributing peers * * @param[in] index The index of the peer for which the Lagrange coefficient * is being calculated * @param[in] x The evaluation point for the polynomial * @param[in] degree Total number of shares participating (number of peers) * @param[in] peers Array of indices of all participating peers that * contribute to the reconstruction * @param[out] result Output buffer to store the computed Lagrange * coefficient */ void lcoeff(const uint8_t index, const uint8_t x, const size_t degree, const uint8_t peers[degree], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES]); /** * @brief Computes the Lagrange coefficient for `f(0) * * This function calculates a lagrange coefficient for `f(0)` based on * the index and the indices of the other contributing peers * * @param[in] index The index of the peer for which the Lagrange coefficient * is being calculated * @param[in] peers_len Total number of shares in the peers * @param[in] peers Shares that contribute to the reconstruction * @param[out] result Output buffer to store the computed Lagrange * coefficient */ void coeff(const uint8_t index, const size_t peers_len, const uint8_t peers[peers_len], uint8_t result[crypto_scalarmult_ristretto255_SCALARBYTES]); /** * @brief Splits a secret into `n` shares using Shamir's secret sharing over * the curve Ristretto255 * * The secret is shared in a (threshold, n) scheme: any threshold number * of shares can reconstruct the secret, but fewer reveal nothing. * This function wraps `lcoeff()`, allowing to recover the shared * secret without providing `x=0` as a parameter. This is mostly for * backward compatibility. * * @param[in] secret The scalar value to be secretly shared * @param[in] n The number of shares created * @param[in] threshold Minimum number of shares required to reconstruct * the secret * @param[out] shares Output buffer receiving `n` generated shares * * @return 0 on success, non-zero on failure */ void toprf_create_shares(const uint8_t secret[crypto_core_ristretto255_SCALARBYTES], const uint8_t n, const uint8_t threshold, uint8_t shares[n][TOPRF_Share_BYTES]); /** * @brief Combines shares in the exponent using Lagrange interpolation over * the curve Ristretto255 * * This function combines a threshold number of shares to recover the secret * in the exponent. It uses Lagrange interpolation over the curve * Ristretto255. * The peers are unaware of whether they participate in threshold or * standalone mode. Their computation remains the same in both cases. * * @param[in] response_len Number of elements in the `responses` array * @param[in] responses Array of shares to be combined * @param[out] result Output buffer receiving the reconstructed secret * * @return 0 on success, non-zero on error */ int toprf_thresholdmult(const size_t response_len, const uint8_t responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]); /** * @brief Efficiently evaluates a blinded input using the private key * in a threshold setting * * This function is the efficient threshold version of `oprf_Evaluate()` * defined in oprf.h. * It needs to know in advance the indices of all shares that will be * combined later in the `toprf_thresholdcombine()` function. This * precomputation reduces the total costs and distributes them to the peers * * @param[in] k The server's secret key share. For OPAQUE, this is kU, the * user's OPRF private key * @param[in] blinded Serialized OPRF group element, an output of * `oprf_Blind()`. For OPAQUE, this is the blinded user's * password, pwdU * @param[in] self The index of the current peer * @param[in] indexes Array of indices of all peers contributing to this * OPRF evaluation * @param[in] index_len Number of participating peers (Length of `indexes`) * @param[out] Z Serialized OPRF group element, used as input to * `oprf_Unblind()` * * @return 0 on success, non-zero on error */ int toprf_Evaluate(const uint8_t k[TOPRF_Share_BYTES], const uint8_t blinded[crypto_core_ristretto255_BYTES], const uint8_t self, const uint8_t *indexes, const uint16_t index_len, uint8_t Z[TOPRF_Part_BYTES]); /** * @brief Combines the partial results to reconstruct the final OPRF output * * This function is combines the results of the `toprf_Evaluate()` to recover * the shared secret in the exponent. * @param[in] response_len Number of elements in the `responses` array * @param[in] responses Array of shares to be combined * @param[out] result Output buffer receiving the reconstructed secret * * @return 0 on success, non-zero on error */ int toprf_thresholdcombine(const size_t response_len, const uint8_t _responses[response_len][TOPRF_Part_BYTES], uint8_t result[crypto_scalarmult_ristretto255_BYTES]); typedef int (*toprf_evalcb)(void *ctx, const uint8_t k[crypto_core_ristretto255_SCALARBYTES], const uint8_t alpha[crypto_core_ristretto255_BYTES], uint8_t beta[crypto_core_ristretto255_BYTES]); typedef int (*toprf_keygencb)(void *ctx, uint8_t k[crypto_core_ristretto255_SCALARBYTES]); /** * @brief Implements the 3HashTDH protocol * * This function implements the 3HashTDH protocol from the paper: * "Threshold PAKE with Security against Compromise of All Servers" * (https://eprint.iacr.org/2024/1455) by Gu, Jarecki, Kedzior, * Nazarian, Xu. * * Use this function to implement a threshold OPRF. * * @param[in] k A share of the secret key * @param[in] z A random zero-sharing of the secret key. This is a share of * a random `t`-degree polynomial that evaluates to zero, where t * is the threshold * @param[in] alpha The blinded element from the client * @param[in] ssid_S A session-specific identifier that all participants in * the threshold evaluation must agree on (it must be the same * for all participants) * @param[in] ssid_S_len Length of the `ssid_S` identifier * @param[out] beta Output buffer containing the result of evaluation, to * be returned to the client * * @return 0 on success, non-zero on error */ int toprf_3hashtdh(const uint8_t k[TOPRF_Share_BYTES], const uint8_t z[TOPRF_Share_BYTES], const uint8_t alpha[crypto_core_ristretto255_BYTES], const uint8_t *ssid_S, const uint16_t ssid_S_len, uint8_t beta[TOPRF_Part_BYTES]); #endif // TOPRF_H liboprf-0.9.4/src/tp-dkg.c000066400000000000000000001547011514673400200153140ustar00rootroot00000000000000#include #include #include #include //htons #include // __BYTE_ORDER __BIG_ENDIAN #include // memcpy #include // va_{start|end} #include // free, rand #include "dkg.h" #include "tp-dkg.h" #include "utils.h" #ifdef __ZEPHYR__ #include #endif /* @copyright 2024, Stefan Marsiske toprf@ctrlc.hu This file is part of liboprf. SPDX-FileCopyrightText: 2024, Marsiske Stefan SPDX-License-Identifier: LGPL-3.0-or-later liboprf is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. liboprf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the License along with liboprf. If not, see . */ /* This file implements a high-level DKG protocol facilitated by a trusted orchestrator which connects to all participating peers in a star topology. The underlying algorithm is based on the JF-DKG (fig 1.) a variant on Pedersens DKG from the paper "Secure Distributed Key Generation for Discrete-Log Based Cryptosystems" by R. Gennaro, S. Jarecki, H. Krawczyk, and T. Rabin. */ #define tpdkg_msg1_SIZE (sizeof(DKG_Message)) #define tpdkg_msg2_SIZE (sizeof(DKG_Message) + crypto_sign_PUBLICKEYBYTES + crypto_scalarmult_BYTES) #define tpdkg_msg4_SIZE (sizeof(DKG_Message) + noise_xk_handshake1_SIZE) #define tpdkg_msg5_SIZE (sizeof(DKG_Message) + noise_xk_handshake2_SIZE) #define tpdkg_msg6_SIZE(ctx) (sizeof(DKG_Message) + (size_t)(crypto_core_ristretto255_BYTES * ctx->t) ) #define tpdkg_msg9_SIZE(ctx) (sizeof(DKG_Message) + (size_t)(ctx->n + 1) ) #define tpdkg_msg10_SIZE(ctx) (sizeof(DKG_Message) + (size_t)(ctx->n * tpdkg_msg9_SIZE(ctx)) ) #define tpdkg_msg19_SIZE (sizeof(DKG_Message) + crypto_generichash_BYTES) #define tpdkg_msg20_SIZE (sizeof(DKG_Message) + 2) #define tpdkg_msg21_SIZE (sizeof(DKG_Message) + 2) size_t tpdkg_peerstate_size(void) { return sizeof(TP_DKG_PeerState); } uint8_t tpdkg_peerstate_n(const TP_DKG_PeerState *ctx) { return ctx->n; } uint8_t tpdkg_peerstate_t(const TP_DKG_PeerState *ctx) { return ctx->t; } const uint8_t* tpdkg_peerstate_sessionid(const TP_DKG_PeerState *ctx) { return ctx->sessionid; } const uint8_t* tpdkg_peerstate_lt_sk(const TP_DKG_PeerState *ctx) { return ctx->lt_sk; } const uint8_t* tpdkg_peerstate_share(const TP_DKG_PeerState *ctx) { return (const uint8_t*) &ctx->share; } int tpdkg_peerstate_step(const TP_DKG_PeerState *ctx) { return ctx->step; } size_t tpdkg_tpstate_size(void) { return sizeof(TP_DKG_TPState); } uint8_t tpdkg_tpstate_n(const TP_DKG_TPState *ctx) { return ctx->n; } uint8_t tpdkg_tpstate_t(const TP_DKG_TPState *ctx) { return ctx->t; } size_t tpdkg_tpstate_cheater_len(const TP_DKG_TPState *ctx) { return ctx->cheater_len; } const uint8_t* tpdkg_tpstate_sessionid(const TP_DKG_TPState *ctx) { return ctx->sessionid; } int tpdkg_tpstate_step(const TP_DKG_TPState *ctx) { return ctx->step; } static int tp_send_msg(uint8_t* msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_sk, const uint8_t sessionid[dkg_sessionid_SIZE]) { return send_msg(msg_buf, msg_buf_len, MSG_TYPE_TRUSTED | MSG_TYPE_DKG, 0, msgno, from, to, sig_sk, sessionid); } static int tp_recv_msg(const uint8_t *msg_buf, const size_t msg_buf_len, const uint8_t msgno, const uint8_t from, const uint8_t to, const uint8_t *sig_pk, const uint8_t sessionid[dkg_sessionid_SIZE], const uint64_t ts_epsilon, uint64_t *last_ts) { return recv_msg(msg_buf, msg_buf_len, MSG_TYPE_TRUSTED | MSG_TYPE_DKG, 0, msgno, from, to, sig_pk, sessionid, ts_epsilon, last_ts); } static TP_DKG_Cheater* add_cheater(TP_DKG_TPState *ctx, const int step, const int error, const uint8_t peer, const uint8_t other_peer) { if(ctx->cheater_len >= ctx->cheater_max) return NULL; TP_DKG_Cheater *cheater = &(*ctx->cheaters)[ctx->cheater_len++]; cheater->step = step; cheater->error = error; cheater->peer = peer; cheater->other_peer=other_peer; return cheater; } size_t tpdkg_tp_input_size(const TP_DKG_TPState *ctx) { size_t sizes[ctx->n]; //memset(sizes,0,sizeof sizes); if(tpdkg_tp_input_sizes(ctx, sizes) == 1) { return sizes[0] * ctx->n; } else { size_t result=0; for(int i=0;in;i++) result+=sizes[i]; return result; } } int tpdkg_tp_input_sizes(const TP_DKG_TPState *ctx, size_t *sizes) { size_t item=0; switch(ctx->step) { case 0: { item=0; break; } case 1: { item=(tpdkg_msg2_SIZE + crypto_sign_BYTES); break; } case 2: { item=tpdkg_msg4_SIZE * ctx->n; break; } case 3: { item=tpdkg_msg4_SIZE * ctx->n; break; } case 4: { item=tpdkg_msg6_SIZE(ctx); break; } case 5: { item=ctx->n * tpdkg_msg8_SIZE; break; } case 6: { item=tpdkg_msg9_SIZE(ctx); break; } case 7: { uint8_t ctr[ctx->n]; memset(ctr,0,ctx->n); for(int i=0;icomplaints_len;i++) ctr[((*ctx->complaints)[i] & 0xff) - 1]++; for(int i=0;in;i++) { if(ctr[i]>0) { sizes[i]=sizeof(DKG_Message) + (1+dkg_noise_key_SIZE) * ctr[i]; } else { sizes[i]=0; } } return 0; } case 8: { item=tpdkg_msg19_SIZE; break; } case 9: { item=tpdkg_msg21_SIZE; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid tp step\n"); } } for(uint8_t i=0;in;i++) { sizes[i] = item; } return 1; } size_t tpdkg_tp_output_size(const TP_DKG_TPState *ctx) { switch(ctx->step) { case 0: return ctx->n*tpdkg_msg1_SIZE; case 1: return tpdkg_msg2_SIZE * ctx->n + sizeof(DKG_Message); case 2: return tpdkg_msg4_SIZE * ctx->n * ctx->n; case 3: return tpdkg_msg5_SIZE * ctx->n * ctx->n; case 4: return sizeof(DKG_Message) + (tpdkg_msg6_SIZE(ctx) * ctx->n); case 5: return ctx->n * ctx->n * tpdkg_msg8_SIZE; case 6: return tpdkg_msg10_SIZE(ctx); case 7: return 0; case 8: return tpdkg_msg20_SIZE; case 9: return 0; default: if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid tp step\n"); } return 0; } int tpdkg_tp_peer_msg(const TP_DKG_TPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len) { if(peer>=ctx->n) return -1; switch(ctx->prev) { case 0: { *msg = base + peer*tpdkg_msg1_SIZE; *len = tpdkg_msg1_SIZE; break; } case 1: { *msg = base; *len = tpdkg_msg2_SIZE * ctx->n + sizeof(DKG_Message); break; } case 2: { *msg = base + peer * tpdkg_msg4_SIZE * ctx->n; *len = tpdkg_msg4_SIZE * ctx->n; break; } case 3: { *msg = base + peer * tpdkg_msg5_SIZE * ctx->n; *len = tpdkg_msg5_SIZE * ctx->n; break; } case 4: { *msg = base; *len = sizeof(DKG_Message) + (tpdkg_msg6_SIZE(ctx) * ctx->n); break; } case 5: { *msg = base + peer * ctx->n * tpdkg_msg8_SIZE; *len = ctx->n * tpdkg_msg8_SIZE; break; } case 6: { *msg = base; *len = tpdkg_msg10_SIZE(ctx); break; } case 7: { *len = 0; *msg = NULL; break; } case 8: { *msg = base; *len = tpdkg_msg20_SIZE; break; } case 9: { *len = 0; *msg = NULL; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid tp step in tpdkg_tp_peer_msg\n"); return 1; } } if(base+base_size < *msg + *len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "buffer overread detected in tpdkg_tp_peer_msg %ld\n", (base+base_size) - (*msg + *len)); return 2; } return 0; } size_t tpdkg_peer_input_size(const TP_DKG_PeerState *ctx) { switch(ctx->step) { case 0: return tpdkg_msg1_SIZE; case 1: return tpdkg_msg2_SIZE * ctx->n + sizeof(DKG_Message); case 2: return tpdkg_msg4_SIZE * ctx->n; case 3: return tpdkg_msg5_SIZE * ctx->n; case 4: return sizeof(DKG_Message) + (tpdkg_msg6_SIZE(ctx) * ctx->n); case 5: return ctx->n * tpdkg_msg8_SIZE; case 6: return tpdkg_msg10_SIZE(ctx); case 7: return 0; case 8: return 0; case 9: return tpdkg_msg20_SIZE; case 10: return 0; default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] invalid step\n", ctx->index); } } return 1; } size_t tpdkg_peer_output_size(const TP_DKG_PeerState *ctx) { switch(ctx->step) { case 0: return tpdkg_msg2_SIZE+crypto_sign_BYTES; case 1: return tpdkg_msg4_SIZE * ctx->n; case 2: return tpdkg_msg5_SIZE * ctx->n; case 3: return tpdkg_msg6_SIZE(ctx); case 4: return ctx->n * tpdkg_msg8_SIZE; case 5: return tpdkg_msg9_SIZE(ctx); case 6: return 0; case 7: { if(ctx->complaints_len > 0) { if(ctx->my_complaints_len > 0) { return sizeof(DKG_Message) + ctx->my_complaints_len * (1+dkg_noise_key_SIZE); } return 0; } return tpdkg_msg19_SIZE; } case 8: return tpdkg_msg19_SIZE; case 9: return tpdkg_msg21_SIZE; case 10: return 0; default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] invalid step\n", ctx->index); } } return 1; } void tpdkg_peer_set_bufs(TP_DKG_PeerState *ctx, uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], Noise_XK_session_t *(*noise_outs)[], Noise_XK_session_t *(*noise_ins)[], TOPRF_Share (*shares)[], TOPRF_Share (*xshares)[], uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint16_t *complaints, uint8_t *my_complaints, uint64_t *last_ts) { ctx->peer_sig_pks = peers_sig_pks; ctx->peer_noise_pks = peers_noise_pks; ctx->noise_outs = noise_outs; ctx->noise_ins = noise_ins; ctx->shares = shares; ctx->xshares = xshares; ctx->commitments = commitments; ctx->complaints = complaints; ctx->my_complaints = my_complaints; ctx->last_ts = last_ts; for(uint8_t i=0;in;i++) ctx->last_ts[i]=0; } int tpdkg_tp_not_done(const TP_DKG_TPState *tp) { return tp->step<10; } int tpdkg_peer_not_done(const TP_DKG_PeerState *peer) { return peer->step<11; } void tpdkg_peer_free(TP_DKG_PeerState *ctx) { for(int i=0;in;i++) { if((*ctx->noise_ins)[i]!=NULL) Noise_XK_session_free((*ctx->noise_ins)[i]); if((*ctx->noise_outs)[i]!=NULL) Noise_XK_session_free((*ctx->noise_outs)[i]); } if(ctx->dev!=NULL) Noise_XK_device_free(ctx->dev); } void tpdkg_tp_set_bufs(TP_DKG_TPState *ctx, uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint16_t (*complaints)[], uint8_t (*encrypted_shares)[][tpdkg_msg8_SIZE], TP_DKG_Cheater (*cheaters)[], const size_t cheater_max, uint8_t (*tp_peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peer_lt_pks)[][crypto_sign_PUBLICKEYBYTES], uint64_t *last_ts) { ctx->commitments = commitments; ctx->complaints = complaints; ctx->encrypted_shares = encrypted_shares; ctx->cheaters = cheaters; memset(*cheaters, 0, cheater_max*sizeof(TP_DKG_Cheater)); ctx->cheater_max = cheater_max; ctx->peer_sig_pks = tp_peers_sig_pks; ctx->peer_lt_pks = peer_lt_pks; ctx->last_ts = last_ts; #ifdef __ZEPHYR__ uint64_t now = (uint64_t) k_uptime_get(); #else uint64_t now = (uint64_t)time(NULL); #endif for(uint8_t i=0;in;i++) ctx->last_ts[i]=now; } int tpdkg_start_tp(TP_DKG_TPState *ctx, const uint64_t ts_epsilon, const uint8_t n, const uint8_t t, const char *proto_name, const size_t proto_name_len, const size_t msg0_len, DKG_Message *msg0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 0. start protocol\x1b[0m\n"); if(2>n || t>=n || n>128) return 1; if(proto_name_len<1) return 2; if(proto_name_len>1024) return 3; if(msg0_len != tpdkg_msg0_SIZE) return 4; ctx->ts_epsilon = ts_epsilon; ctx->step = 0; ctx->n = n; ctx->t = t; ctx->complaints_len = 0; ctx->cheater_len = 0; // dst hash(len(protoname) | "DKG for protocol " | protoname) crypto_generichash_state dst_state; crypto_generichash_init(&dst_state, NULL, 0, crypto_generichash_BYTES); uint16_t len=htons((uint16_t) proto_name_len+20U); // we have a guard above restricting to 1KB the proto_name_len crypto_generichash_update(&dst_state, (uint8_t*) &len, 2); crypto_generichash_update(&dst_state, (const uint8_t*) "TP DKG for protocol ", 20); crypto_generichash_update(&dst_state, (const uint8_t*) proto_name, proto_name_len); uint8_t dst[crypto_generichash_BYTES]; crypto_generichash_final(&dst_state,dst,sizeof dst); // set session id randombytes_buf(&ctx->sessionid, sizeof ctx->sessionid); // generate signing key for this session crypto_sign_keypair(ctx->sig_pk, ctx->sig_sk); // data = {tp_sign_pk, dst, sessionid, n, t} uint8_t *ptr = msg0->data; memcpy(ptr, ctx->sig_pk, sizeof ctx->sig_pk); ptr+=sizeof ctx->sig_pk; memcpy(ptr, dst, sizeof dst); ptr+=sizeof dst; *ptr++ = n; *ptr++ = t; if(0!=tp_send_msg((uint8_t*) msg0, tpdkg_msg0_SIZE, 0, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 5; // init transcript crypto_generichash_init(&ctx->transcript, NULL, 0, crypto_generichash_BYTES); crypto_generichash_update(&ctx->transcript, (const uint8_t*) "tp dkg session transcript", 25); // feed msg0 into transcript update_transcript(&ctx->transcript, (uint8_t*) msg0, msg0_len); if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[!] msgno: %d, from: %d to: 0x%x ", msg0->msgno, msg0->from, msg0->to); dump((uint8_t*) msg0, tpdkg_msg0_SIZE, "msg"); } return 0; } int tpdkg_start_peer(TP_DKG_PeerState *ctx, const uint64_t ts_epsilon, const uint8_t peer_lt_sk[crypto_sign_SECRETKEYBYTES], const DKG_Message *msg0) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[?] step 0.5 start peer\x1b[0m\n"); if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[?] msgno: %d, from: %d to: 0x%x ", msg0->msgno, msg0->from, msg0->to); dump((const uint8_t*) msg0, tpdkg_msg0_SIZE, "msg"); } ctx->ts_epsilon = ts_epsilon; ctx->tp_last_ts = 0; int ret = tp_recv_msg((const uint8_t*) msg0, tpdkg_msg0_SIZE, 0, 0, 0xff, msg0->data, msg0->sessionid, ts_epsilon, &ctx->tp_last_ts); if(0!=ret) return 64 + ret; // extract data from message memcpy(ctx->sessionid, msg0->sessionid, sizeof ctx->sessionid); const uint8_t *ptr=msg0->data; memcpy(ctx->tp_sig_pk,ptr,sizeof ctx->tp_sig_pk); ptr+=sizeof ctx->tp_sig_pk + crypto_generichash_BYTES; // also skip DST ctx->n = *ptr++; ctx->t = *ptr++; if(ctx->t < 2) return 1; if(ctx->t >= ctx->n) return 2; if(ctx->n > 128) return 3; ctx->complaints_len = 0; ctx->my_complaints_len = 0; memcpy(ctx->lt_sk, peer_lt_sk, crypto_sign_SECRETKEYBYTES); crypto_generichash_init(&ctx->transcript, NULL, 0, crypto_generichash_BYTES); crypto_generichash_update(&ctx->transcript, (const uint8_t*) "tp dkg session transcript", 25); // feed msg0 into transcript update_transcript(&ctx->transcript, (const uint8_t*) msg0, tpdkg_msg0_SIZE); ctx->dev = NULL; ctx->step = 0; return 0; } static int tp_step1_handler(const TP_DKG_TPState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 1. assign peer indices\x1b[0m\n"); if(output_len!=ctx->n * tpdkg_msg1_SIZE) return 2; uint8_t* ptr = output; for(uint8_t i=1;i<=ctx->n;i++,ptr+=tpdkg_msg1_SIZE) { if(0!=tp_send_msg(ptr, sizeof(DKG_Message), 1, 0, i, ctx->sig_sk, ctx->sessionid)) return 3; dkg_dump_msg(ptr, tpdkg_msg1_SIZE, 0); } return 0; } static int peer_step23_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[?] step 2. receive peers index\x1b[0m\n"); if(input_len != tpdkg_msg1_SIZE) return 1; if(output_len != tpdkg_msg2_SIZE+crypto_sign_BYTES) return 2; dkg_dump_msg(input, input_len, ctx->index); const DKG_Message *msg1=(const DKG_Message*) input; int ret = tp_recv_msg(input, tpdkg_msg1_SIZE, 1, 0, msg1->to, ctx->tp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->tp_last_ts); if(0!=ret) return 4 + ret; if(msg1->to > 128 || msg1->to < 1) return 3; ctx->index=msg1->to; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 3. send msg2 containing ephemeral pubkey\x1b[0m\n", ctx->index); crypto_sign_keypair(ctx->sig_pk, ctx->sig_sk); randombytes_buf(ctx->noise_sk, sizeof ctx->noise_sk); crypto_scalarmult_base(ctx->noise_pk, ctx->noise_sk); uint8_t *wptr = ((DKG_Message *) output)->data; memcpy(wptr, ctx->sig_pk, sizeof ctx->sig_pk); wptr+=sizeof ctx->sig_pk; memcpy(wptr, ctx->noise_pk, sizeof ctx->noise_pk); if(0!=tp_send_msg(output, tpdkg_msg2_SIZE, 2, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return 4; // sign message with long-term key crypto_sign_detached(output+tpdkg_msg2_SIZE,NULL,output,tpdkg_msg2_SIZE,ctx->lt_sk); sodium_memzero(ctx->lt_sk,crypto_sign_SECRETKEYBYTES); dkg_dump_msg(output, output_len, ctx->index); return 0; } static int tp_step4_handler(TP_DKG_TPState *ctx, const uint8_t *msg2s, const size_t msg2s_len, uint8_t *msg3_buf, const size_t msg3_buf_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 4. broadcast msg2 containing ephemeral pubkeys of peers\x1b[0m\n"); if(((tpdkg_msg2_SIZE + crypto_sign_BYTES) * ctx->n) != msg2s_len) return 1; if(msg3_buf_len != (tpdkg_msg2_SIZE * ctx->n) + sizeof(DKG_Message)) return 2; const uint8_t *ptr = msg2s; uint8_t *wptr = ((DKG_Message *) msg3_buf)->data; for(uint8_t i=0;in;i++,ptr+=tpdkg_msg2_SIZE+crypto_sign_BYTES) { const DKG_Message* msg = (const DKG_Message*) ptr; // verify long-term pk sig on initial message dkg_dump_msg(ptr, tpdkg_msg2_SIZE, 0); #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if(0!=crypto_sign_verify_detached(ptr+tpdkg_msg2_SIZE,ptr,tpdkg_msg2_SIZE,(*ctx->peer_lt_pks)[i])) return 3; #endif int ret = tp_recv_msg(ptr, tpdkg_msg2_SIZE, 2, i+1U, 0xff, msg->data, ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) { if(add_cheater(ctx, 4, 64+ret, i+1U,0xff) == NULL) return 7; continue; } // keep copy of ephemeral signing key memcpy((*ctx->peer_sig_pks)[i], msg->data, crypto_sign_PUBLICKEYBYTES); // strip away long-term signature memcpy(wptr, ptr, tpdkg_msg2_SIZE); wptr+=tpdkg_msg2_SIZE; } if(ctx->cheater_len>0) return 6; if(0!=tp_send_msg(msg3_buf, msg3_buf_len, 3, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 5; update_transcript(&ctx->transcript, msg3_buf, msg3_buf_len); return 0; } static int peer_step5_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 5. receive peers ephemeral pubkeys, start noise sessions\x1b[0m\n", ctx->index); if(input_len != tpdkg_msg2_SIZE * ctx->n + sizeof(DKG_Message)) return 1; if(output_len != tpdkg_msg4_SIZE * ctx->n) return 2; int ret = tp_recv_msg(input, input_len, 3, 0, 0xff, ctx->tp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->tp_last_ts); if(0!=ret) return 32+ret; update_transcript(&ctx->transcript, input, input_len); // create noise device uint8_t iname[13]; snprintf((char*) iname, sizeof iname, "dkg peer %02x", ctx->index); uint8_t dummy[32]={0}; // the following function needs a deserialization key, which we never use. ctx->dev = Noise_XK_device_create(13, (uint8_t*) "dpkg p2p v0.1", iname, dummy, ctx->noise_sk); const DKG_Message* msg3 = (const DKG_Message*) input; const uint8_t *ptr = msg3->data; uint8_t *wptr = output; for(uint8_t i=0;in;i++) { const DKG_Message* msg2 = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg2_SIZE, ctx->index); ret = tp_recv_msg(ptr, tpdkg_msg2_SIZE, 2, i+1U, 0xff, msg2->data, ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) return 64+ret; // extract peer sig and noise pk memcpy((*ctx->peer_sig_pks)[i], msg2->data, crypto_sign_PUBLICKEYBYTES); memcpy((*ctx->peer_noise_pks)[i], msg2->data + crypto_sign_PUBLICKEYBYTES, crypto_scalarmult_BYTES); ptr+=tpdkg_msg2_SIZE; DKG_Message *msg4 = (DKG_Message *) wptr; uint8_t rname[13]; snprintf((char*) rname, sizeof rname, "dkg peer %02x", i+1U); dkg_init_noise_handshake(ctx->index, ctx->dev, (*ctx->peer_noise_pks)[i], rname, &(*ctx->noise_outs)[i], msg4->data); if(0!=tp_send_msg(wptr, tpdkg_msg4_SIZE, 4, ctx->index, i+1U, ctx->sig_sk, ctx->sessionid)) return 5; dkg_dump_msg(wptr, tpdkg_msg4_SIZE, ctx->index); wptr+=tpdkg_msg4_SIZE; } return 0; } static int tp_step68_handler(TP_DKG_TPState *ctx, const uint8_t *msg4s, const size_t msg4s_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step %d. route p2p noise handshakes to peers\x1b[0m\n", 6 + (ctx->step - 1) * 2); if(msg4s_len != tpdkg_msg4_SIZE * ctx->n * ctx->n) return 1; if(msg4s_len != output_len) return 2; const uint8_t (*inputs)[ctx->n][ctx->n][tpdkg_msg4_SIZE] = (const uint8_t (*)[ctx->n][ctx->n][tpdkg_msg4_SIZE]) msg4s; uint8_t *wptr = output; for(uint8_t i=0;in;i++) { for(uint8_t j=0;jn;j++) { if(tpdkg_msg4_SIZE != tpdkg_msg5_SIZE) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "tpdkg_msg4_SIZE must be equal tpdkg_msg5_SIZE for the check to be correct in tp_step68_handler\n"); return 3; } int ret = tp_recv_msg((*inputs)[j][i], tpdkg_msg4_SIZE, (uint8_t) (2+ctx->step), j+1, i+1U, (*ctx->peer_sig_pks)[j], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[j]); if(0!=ret) { if(add_cheater(ctx, 6 + (ctx->step - 1) * 2, 64+ret, j+1U, i+1U) == NULL) return 7; const DKG_Message *msg = (const DKG_Message*) (*inputs)[j][i]; fprintf(liboprf_log_file,"[x] msgno: %d, from: %d to: %d ", msg->msgno, msg->from, msg->to); dump((*inputs)[j][i], tpdkg_msg4_SIZE, "msg"); continue; } memcpy(wptr, (*inputs)[j][i], tpdkg_msg4_SIZE); wptr+=tpdkg_msg4_SIZE; } } if(ctx->cheater_len>0) return 6; return 0; } static int peer_step7_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 7. receive session requests\x1b[0m\n", ctx->index); if(input_len != tpdkg_msg4_SIZE * ctx->n) return 1; if(output_len != tpdkg_msg5_SIZE * ctx->n) return 2; const uint8_t *ptr = input; uint8_t *wptr = output; for(uint8_t i=0;in;i++) { DKG_Message* msg4 = (DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg4_SIZE, ctx->index); int ret = tp_recv_msg(ptr, tpdkg_msg4_SIZE, 4, i+1U, ctx->index, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) return 64+ret; ptr+=tpdkg_msg4_SIZE; // respond to noise handshake request DKG_Message *msg5 = (DKG_Message *) wptr; uint8_t rname[13]; snprintf((char*) rname, sizeof rname, "dkg peer %02x", i+1U); dkg_respond_noise_handshake(ctx->index, ctx->dev, rname, &(*ctx->noise_ins)[i], msg4->data, msg5->data); if(0!=tp_send_msg(wptr, tpdkg_msg5_SIZE, 5, ctx->index, i+1U, ctx->sig_sk, ctx->sessionid)) return 4; dkg_dump_msg(wptr, tpdkg_msg5_SIZE, ctx->index); wptr+=tpdkg_msg5_SIZE; } return 0; } static int peer_step911_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 9-11 finish session handshake, broadcast commitments\x1b[0m\n", ctx->index); if(input_len != tpdkg_msg5_SIZE * ctx->n) return 1; if(output_len != tpdkg_msg6_SIZE(ctx)) return 2; const uint8_t *ptr = input; for(uint8_t i=0;in;i++) { DKG_Message* msg5 = (DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg5_SIZE, ctx->index); int ret = tp_recv_msg(ptr, tpdkg_msg5_SIZE, 5, i+1U, ctx->index, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) return 64+ret; ptr+=tpdkg_msg5_SIZE; // process final step of noise handshake dkg_finish_noise_handshake(ctx->index, ctx->dev, &(*ctx->noise_outs)[i], msg5->data); } DKG_Message* msg6 = (DKG_Message*) output; if(0!=dkg_start(ctx->n, ctx->t, (uint8_t (*)[32]) msg6->data, *ctx->shares)) return 4; if(0!=tp_send_msg(output, tpdkg_msg6_SIZE(ctx), 6, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return 4; dkg_dump_msg(output, tpdkg_msg6_SIZE(ctx), ctx->index); if(liboprf_log_file!=NULL) { dump(msg6->data, ctx->t*crypto_core_ristretto255_BYTES, "[%d] commitments", ctx->index); } return 0; } static int tp_step12_handler(TP_DKG_TPState *ctx, const uint8_t *msg6s, const size_t msg6s_len, uint8_t *msg7_buf, const size_t msg7_buf_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 12. broadcast commitments of peers\x1b[0m\n"); if((tpdkg_msg6_SIZE(ctx) * ctx->n) != msg6s_len) return 1; if(msg7_buf_len != sizeof(DKG_Message) + msg6s_len) return 2; const uint8_t *ptr = msg6s; uint8_t *wptr = ((DKG_Message *) msg7_buf)->data; for(uint8_t i=0;in;i++,ptr+=tpdkg_msg6_SIZE(ctx)) { const DKG_Message* msg = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg6_SIZE(ctx), 0); int ret = tp_recv_msg(ptr, tpdkg_msg6_SIZE(ctx), 6, i+1U, 0xff, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) { if(add_cheater(ctx, 12, 64+ret, i+1U,0xff) == NULL) return 7; continue; } memcpy((*ctx->commitments)[i*ctx->t], msg->data, crypto_core_ristretto255_BYTES * ctx->t); if(liboprf_log_file!=NULL) { dump((*ctx->commitments)[i*ctx->t], crypto_core_ristretto255_BYTES * ctx->t, "[!] commitments[%d]", i+1U); } memcpy(wptr, ptr, tpdkg_msg6_SIZE(ctx)); wptr+=tpdkg_msg6_SIZE(ctx); } if(ctx->cheater_len>0) return 6; if(0!=tp_send_msg(msg7_buf, msg7_buf_len, 7, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 4; dkg_dump_msg(msg7_buf, msg7_buf_len, 0); // add broadcast msg to transcript update_transcript(&ctx->transcript, msg7_buf, msg7_buf_len); return 0; } static int peer_step13_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 13. receive commitments, distribute shares via noise chans\x1b[0m\n", ctx->index); if(input_len != sizeof(DKG_Message) + (tpdkg_msg6_SIZE(ctx) * ctx->n)) return 1; if(output_len != ctx->n * tpdkg_msg8_SIZE) return 2; // verify TP message envelope const DKG_Message* msg7 = (const DKG_Message*) input; dkg_dump_msg(input, input_len, ctx->index); int ret = tp_recv_msg(input, input_len, 7, 0, 0xff, ctx->tp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->tp_last_ts); if(0!=ret) return 32+ret; // add broadcast msg to transcript update_transcript(&ctx->transcript, input, input_len); const uint8_t *ptr = msg7->data; uint8_t *wptr = output; for(uint8_t i=0;in;i++, wptr+=tpdkg_msg8_SIZE,ptr+=tpdkg_msg6_SIZE(ctx)) { const DKG_Message* msg6 = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg6_SIZE(ctx), ctx->index); if(0!=tp_recv_msg(ptr, tpdkg_msg6_SIZE(ctx), 6, i+1U, 0xff, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i])) return 64+ret; // extract peer commitments memcpy((*ctx->commitments)[i*ctx->t], msg6->data, crypto_core_ristretto255_BYTES * ctx->t); DKG_Message *msg8 = (DKG_Message *) wptr; // we need to send an empty packet, so that the handshake completes // and we have a final symetric key, the key during the handshake changes, only // when the handshake completes does the key become static. // this is important, so that when there are complaints, we can disclose the key. uint8_t empty[1]={0}; if(0!=dkg_noise_encrypt(empty, 0, msg8->data, noise_xk_handshake3_SIZE, &(*ctx->noise_outs)[i])) return 5; #ifdef UNITTEST_CORRUPT // corrupt all shares static int corrupted_shares = 0; uint8_t corrupted_share[sizeof(TOPRF_Share)]; memcpy(corrupted_share, &(*ctx->shares)[i], sizeof(TOPRF_Share)); if(i+1U != ctx->index && corrupted_shares++ < ctx->t-1) { dump(corrupted_share, sizeof(TOPRF_Share), "[%d] corrupting share_%d", ctx->index, i+1U); corrupted_share[2]^=0xff; // flip some bits dump(corrupted_share, sizeof(TOPRF_Share), "[%d] corrupted share_%d ", ctx->index, i+1U); } if(0!=dkg_noise_encrypt((uint8_t*) corrupted_share, sizeof(TOPRF_Share), #else if(0!=dkg_noise_encrypt((uint8_t*) &(*ctx->shares)[i], sizeof(TOPRF_Share), #endif // UNITTEST_CORRUPT msg8->data + noise_xk_handshake3_SIZE, sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, &(*ctx->noise_outs)[i])) return 6; // we also need to use a key-commiting mac over the encrypted share, since poly1305 is not... crypto_auth(msg8->data + noise_xk_handshake3_SIZE + sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, msg8->data + noise_xk_handshake3_SIZE, sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, Noise_XK_session_get_key((*ctx->noise_outs)[i])); if(0!=tp_send_msg(wptr, tpdkg_msg8_SIZE, 8, ctx->index, i+1U, ctx->sig_sk, ctx->sessionid)) return 7; dkg_dump_msg(wptr, tpdkg_msg8_SIZE, ctx->index); } return 0; } static int tp_step14_handler(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 14. route shares from all peers to all peers\x1b[0m\n"); if(input_len != tpdkg_msg8_SIZE * ctx->n * ctx->n) return 1; if(input_len != output_len) return 2; const uint8_t (*inputs)[ctx->n][ctx->n][tpdkg_msg8_SIZE] = (const uint8_t (*)[ctx->n][ctx->n][tpdkg_msg8_SIZE]) input; uint8_t *wptr = output; for(uint8_t i=0;in;i++) { for(uint8_t j=0;jn;j++) { dkg_dump_msg((*inputs)[j][i], tpdkg_msg8_SIZE, 0); int ret = tp_recv_msg((*inputs)[j][i], tpdkg_msg8_SIZE, 8, j+1U, i+1U, (*ctx->peer_sig_pks)[j], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[j]); if(0!=ret) { if(add_cheater(ctx, 14, 64+ret, j+1U, i+1U) == NULL) return 7; continue; } memcpy(wptr, (*inputs)[j][i], tpdkg_msg8_SIZE); wptr+=tpdkg_msg8_SIZE; } } if(ctx->cheater_len>0) return 6; // keep a copy for complaint resolution. memcpy((*ctx->encrypted_shares), input, input_len); return 0; } static int peer_step15_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 15. DKG step 2 - receive shares, verify commitments\x1b[0m\n", ctx->index); if(input_len != ctx->n * tpdkg_msg8_SIZE) return 1; if(output_len != tpdkg_msg9_SIZE(ctx)) return 2; const uint8_t *ptr = input; for(uint8_t i=0;in;i++) { const DKG_Message* msg8 = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg8_SIZE, ctx->index); int ret = tp_recv_msg(ptr, tpdkg_msg8_SIZE, 8, i+1U, ctx->index, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) return 64+ret; // decrypt final empty handshake packet if(0!=dkg_noise_decrypt(msg8->data, noise_xk_handshake3_SIZE, NULL, 0, &(*ctx->noise_ins)[i])) return 4; if(0!=crypto_auth_verify(msg8->data + noise_xk_handshake3_SIZE + sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, msg8->data + noise_xk_handshake3_SIZE, sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, Noise_XK_session_get_key((*ctx->noise_ins)[i]))) { return 5; } if(0!=dkg_noise_decrypt(msg8->data + noise_xk_handshake3_SIZE, sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, (uint8_t*) &(*ctx->xshares)[i], sizeof(TOPRF_Share), &(*ctx->noise_ins)[i])) return 6; ptr+=tpdkg_msg8_SIZE; } DKG_Message* msg9 = (DKG_Message*) output; uint8_t *fails_len = msg9->data; uint8_t *fails = msg9->data+1; memset(fails, 0, ctx->n); dkg_verify_commitments(ctx->n, ctx->t, ctx->index, ctx->commitments, *ctx->xshares, fails, fails_len); #ifdef UNITTEST_CORRUPT static int totalfails = 0; for(uint8_t i=1;i<=ctx->n;i++) { if(totalfails < ctx->t - ctx->index && *fails_len < ctx->t-1 && i != ctx->index) { // avoid duplicates int j; for(j=1;j<=msg9->data[0];j++) if(msg9->data[j]==i) break; if(j<=msg9->data[0]) continue; fails[msg9->data[0]++]=i; totalfails++; } } #endif //UNITTEST_CORRUPT if(liboprf_log_file!=NULL) { for(int j=0;j<*fails_len;j++) { fprintf(liboprf_log_file,"\x1b[0;31m[%d] failed to verify commitments from %d!\x1b[0m\n", ctx->index, fails[j]); } } if(0!=tp_send_msg(output, tpdkg_msg9_SIZE(ctx), 9, ctx->index, 0xff, ctx->sig_sk, ctx->sessionid)) return 7; dkg_dump_msg(output, tpdkg_msg9_SIZE(ctx), ctx->index); return 0; } static int tp_step16_handler(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 16. broadcast complaints of peers\x1b[0m\n"); if((tpdkg_msg9_SIZE(ctx) * ctx->n) != input_len) return 1; if(output_len != tpdkg_msg10_SIZE(ctx)) return 2; ctx->complaints_len = 0; const uint8_t *ptr = input; uint8_t *wptr = ((DKG_Message *) output)->data; for(uint8_t i=0;in;i++, ptr+=tpdkg_msg9_SIZE(ctx)) { const DKG_Message* msg = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg9_SIZE(ctx), 0); int ret = tp_recv_msg(ptr, tpdkg_msg9_SIZE(ctx), 9, i+1U, 0xff, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) { if(add_cheater(ctx, 16, 64+ret, i+1U, 0xff) == NULL) return 6; continue; } if(msg->len - sizeof(DKG_Message) < msg->data[0]) return 4; // keep a copy all complaint pairs (complainer, complained) for(int k=0;kdata[0] && (k+1)len-sizeof(DKG_Message);k++) { if(msg->data[k+1] > ctx->n || msg->data[k+1] < 1) { if(add_cheater(ctx, 16, 7, i+1U, msg->data[k+1]) == NULL) return 6; continue; } uint16_t pair=(uint16_t) (((i+1U)<<8) | msg->data[k+1]); int j=0; for(j=0;jcomplaints_len;j++) if((*ctx->complaints)[j]==pair) break; if(jcomplaints_len) { if(add_cheater(ctx, 16, 8, i+1U, msg->data[k+1]) == NULL) return 6; continue; } (*ctx->complaints)[ctx->complaints_len++] = pair; if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] peer %d failed to verify commitments from peer %d!\x1b[0m\n", i+1U, msg->data[1+k]); } } memcpy(wptr, ptr, tpdkg_msg9_SIZE(ctx)); wptr+=tpdkg_msg9_SIZE(ctx); } dump((uint8_t*) (*ctx->complaints), ctx->complaints_len*sizeof(uint16_t), "[!] complaints"); // if more than t^2 complaints are received the protocol also fails if(ctx->complaints_len >= ctx->t * ctx->t) { if(add_cheater(ctx, 16, 6, 0xfe, 0xfe) == NULL) return 6; return 5; } if(ctx->cheater_len>0) return 5; if(0!=tp_send_msg(output, output_len, 10, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 7; dkg_dump_msg(output, output_len, 0); // add broadcast msg to transcript update_transcript(&ctx->transcript, output, output_len); return 0; } static int peer_step17_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 17. receive complaints broadcast\x1b[0m\n", ctx->index); if(input_len != tpdkg_msg10_SIZE(ctx)) return 1; // verify TP message envelope const DKG_Message* msg10 = (const DKG_Message*) input; dkg_dump_msg(input, input_len, ctx->index); int ret = tp_recv_msg(input, input_len, 10, 0, 0xff, ctx->tp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->tp_last_ts); if(0!=ret) return 16+ret; // add broadcast msg to transcript update_transcript(&ctx->transcript, input, input_len); const uint8_t *ptr = msg10->data; for(uint8_t i=0;in;i++) { const DKG_Message* msg9 = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg9_SIZE(ctx), ctx->index); ret = tp_recv_msg(ptr, tpdkg_msg9_SIZE(ctx), 9, i+1U, 0xff, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) return 32+ret; if(msg9->len - sizeof(DKG_Message) < msg9->data[0]) return 5; // keep a copy all complaint pairs (complainer, complained) for(int k=0;kdata[0] && (k+1)len-sizeof(DKG_Message);k++) { uint16_t pair=(uint16_t) (((i+1U)<<8) | msg9->data[k+1]); int j=0; for(j=0;jcomplaints_len;j++) if(ctx->complaints[j]==pair) break; if(jcomplaints_len) continue; ctx->complaints[ctx->complaints_len++] = pair; if(msg9->data[k+1] == ctx->index) { ctx->my_complaints[ctx->my_complaints_len++] = i+1U; if(liboprf_log_file!=NULL) fprintf(liboprf_log_file,"\x1b[0;31m[%d] peer %d failed to verify commitments from peer %d!\x1b[0m\n", ctx->index, i+1U, msg9->data[1+k]); } } ptr+=tpdkg_msg9_SIZE(ctx); } if(ctx->complaints_len == 0) { ctx->prev = ctx->step; ctx->step+=1; // skip to step 19 } return 0; } static int peer_step17a_handler(TP_DKG_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 17a. potentially broadcast contested shares\x1b[0m\n", ctx->index); if(output_len != tpdkg_peer_output_size(ctx)) return 2; if(output_len == 0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"[%d] nothing to defend against, no message to send\n", ctx->index); } return 0; } // send out all shares that belong to peers that complained. DKG_Message* msg11 = (DKG_Message*) output; uint8_t *wptr = msg11->data; for(int i=0;imy_complaints_len;i++) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;36m[%d] defending against complaint from %d\x1b[0m\n", ctx->index, ctx->my_complaints[i]); *wptr++ = ctx->my_complaints[i]; // reveal key for noise wrapped share sent previously memcpy(wptr, Noise_XK_session_get_key((*ctx->noise_outs)[ctx->my_complaints[i]-1]), dkg_noise_key_SIZE); wptr+=dkg_noise_key_SIZE; } if(0!=tp_send_msg(output, tpdkg_peer_output_size(ctx), 11, ctx->index, 0x0, ctx->sig_sk, ctx->sessionid)) return 3; dkg_dump_msg(output, tpdkg_peer_output_size(ctx), ctx->index); // we skip to the end... ctx->step=99; return 0; } static int tp_step18_handler(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 18. collect keys of contested shares and verify the commitments\x1b[0m\n"); if(input_len != tpdkg_tp_input_size(ctx)) return 1; unsigned int ctr[ctx->n]; uint16_t complaints[ctx->complaints_len]; memset(ctr,0,sizeof(ctr)); for(int i=0;icomplaints_len;i++) { ctr[((*ctx->complaints)[i] & 0xff)-1]++; complaints[i] = (*ctx->complaints)[i]; } uint8_t (*noisy_shares)[ctx->n][ctx->n][tpdkg_msg8_SIZE] = (uint8_t (*)[ctx->n][ctx->n][tpdkg_msg8_SIZE]) ctx->encrypted_shares; const uint8_t *ptr = input; size_t msg_len; for(uint8_t i=0;in;i++,ptr += msg_len) { if(ctr[i]==0) { msg_len = 0; continue; // no complaints against this peer } msg_len = sizeof(DKG_Message) + (1+dkg_noise_key_SIZE) * ctr[i]; const DKG_Message* msg = (const DKG_Message*) ptr; dkg_dump_msg(ptr, msg_len, 0); int ret = tp_recv_msg(ptr, msg_len, 11, i+1U, 0, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) { if(add_cheater(ctx, 18, 32+ret, i+1U, 0xfe) == NULL) return 4; continue; } // verify proofs const uint8_t *keyptr = msg->data; for(int k=0;kfrom; int j; for(j=0;jcomplaints_len;j++) { if(complaints[j] == ((complainer<<8) | accused)) { complaints[j]=0xffff; break; } } if(j==ctx->complaints_len) { // accused revealed a key that was not complained about if(add_cheater(ctx, 18, 6, accused, complainer) == NULL) return 4; continue; } const uint8_t *msg8_ptr = (*noisy_shares)[accused-1][complainer-1]; dkg_dump_msg(msg8_ptr, tpdkg_msg8_SIZE, 0); const DKG_Message *msg8 = (const DKG_Message *) msg8_ptr; uint64_t last_ts = ntohll(msg8->ts); ret = tp_recv_msg(msg8_ptr, tpdkg_msg8_SIZE, 8, accused, complainer, (*ctx->peer_sig_pks)[accused-1], ctx->sessionid, ctx->ts_epsilon, &last_ts); if(0!=ret) { // key reveal msg_recv failure if(add_cheater(ctx, 18, 16+ret, accused, complainer) == NULL) return 4; continue; } #ifdef UNITTEST dump(keyptr, dkg_noise_key_SIZE, "[!] key_%d,%d", accused, complainer); #endif //UNITTEST // verify key committing hmac first! #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) if(0!=crypto_auth_verify(msg8->data + noise_xk_handshake3_SIZE + sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, msg8->data + noise_xk_handshake3_SIZE, sizeof(TOPRF_Share) + crypto_secretbox_xchacha20poly1305_MACBYTES, keyptr)) { // failed to verify KC MAC on message if(add_cheater(ctx, 18, 3, accused, complainer) == NULL) return 4; continue; } #endif Noise_XK_error_code res0 = Noise_XK_aead_decrypt((uint8_t*)keyptr, 0, 0U, NULL, sizeof(share), (uint8_t*) &share, (uint8_t*) msg8->data + noise_xk_handshake3_SIZE); if (!(res0 == Noise_XK_CSuccess)) { // share decryption failure if(add_cheater(ctx, 18, 4, accused, complainer) == NULL) return 4; continue; } if(share.index != complainer) { // invalid share index TP_DKG_Cheater *cheater = add_cheater(ctx, 18, 5, accused, complainer); if(cheater == NULL) return 4; cheater->invalid_index = share.index; continue; } if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file, "[!] checking proof of peer %d for complaint by peer %d\n", msg->from, share.index); dump((void*) &share, sizeof(TOPRF_Share), "[!] share_%d,%d", msg->from, share.index); dump((*ctx->commitments)[(msg->from-1) * ctx->t], ctx->t * crypto_core_ristretto255_BYTES, "[!] commitments_%d", msg->from); } ret = dkg_verify_commitment(ctx->n, ctx->t, share.index, msg->from, (const uint8_t (*)[crypto_core_ristretto255_BYTES]) (*ctx->commitments)[(msg->from-1) * ctx->t], share); switch(ret) { case 0: { // verified correctly if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;32m[!] complaint against %d by %d invalid, proof correct\x1b[0m\n", msg->from, share.index); if(add_cheater(ctx, 18, 128+ret, accused, complainer) == NULL) return 4; break; } case 1: { // confirmed corrupt if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;31m[!] complaint against %d by %d valid, proof incorrect\x1b[0m\n", msg->from, share.index); if(add_cheater(ctx, 18, 128+ret, accused, complainer) == NULL) return 4; break; } case -1: { // invalid input if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;31m[!] complaint against %d by %d, cannot be verified, invalid input\x1b[0m\n", msg->from, share.index); if(add_cheater(ctx, 18, 128+ret, accused, complainer) == NULL) return 4; break; } } } } for(int i=0;icomplaints_len;i++) { if(complaints[i] != 0xffff) { if(add_cheater(ctx, 18, 7, (uint8_t) (complaints[i] >> 8), (uint8_t) (complaints[i] & 0xff)) == NULL) return 4; } } ctx->step=99; // we skip to the end return 3; } static int peer_step19_handler(TP_DKG_PeerState *ctx, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 19. send final transcript\x1b[0m\n", ctx->index); if(output_len != tpdkg_msg19_SIZE) return 2; DKG_Message* msg20 = (DKG_Message*) output; crypto_generichash_final(&ctx->transcript, msg20->data, crypto_generichash_BYTES); if(0!=tp_send_msg(output, tpdkg_msg19_SIZE, 20, ctx->index, 0, ctx->sig_sk, ctx->sessionid)) return 3; dkg_dump_msg(output, tpdkg_msg19_SIZE, ctx->index); return 0; } static int tp_step20_handler(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 20. collect and verify transcripts\x1b[0m\n"); if((tpdkg_msg19_SIZE * ctx->n) != input_len) return 1; if(output_len != tpdkg_msg20_SIZE) return 2; uint8_t transcript_hash[crypto_generichash_BYTES]; crypto_generichash_final(&ctx->transcript, transcript_hash, crypto_generichash_BYTES); uint8_t *wptr = ((DKG_Message *) output)->data; memcpy(wptr, "OK", 2); const uint8_t *ptr = input; for(uint8_t i=0;in;i++, ptr+=tpdkg_msg19_SIZE) { const DKG_Message* msg = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg19_SIZE, 0); int ret = tp_recv_msg(ptr, tpdkg_msg19_SIZE, 20, i+1U, 0, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) { if(add_cheater(ctx, 20, 1+ret, i+1U, 0) == NULL) return 4; memcpy(wptr,"NO",2); continue; } if(sodium_memcmp(transcript_hash, msg->data, sizeof(transcript_hash))!=0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to verify transcript from %d!\x1b[0m\n", i); } if(add_cheater(ctx, 20, 1, i+1U, 0) == NULL) return 4; memcpy(wptr,"NO",2); } } if(0!=tp_send_msg(output, output_len, 21, 0, 0xff, ctx->sig_sk, ctx->sessionid)) return 5; dkg_dump_msg(output, output_len, 0); if(ctx->cheater_len == 0) return 0; ctx->step = 99; // we finish here return 3; } static int peer_step21_handler(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[%d] step 21. get final approval\x1b[0m\n", ctx->index); if(input_len != tpdkg_msg20_SIZE) return 1; if(output_len != tpdkg_msg21_SIZE) return 2; // verify TP message envelope const DKG_Message* msg21 = (const DKG_Message*) input; dkg_dump_msg(input, input_len, ctx->index); int ret = tp_recv_msg(input, input_len, 21, 0, 0xff, ctx->tp_sig_pk, ctx->sessionid, ctx->ts_epsilon, &ctx->tp_last_ts); if(0!=ret) return 4+ret; int fail = (memcmp(msg21->data, "OK", 2) != 0); if(!fail) { ctx->share.index=ctx->index; if(0!=dkg_finish(ctx->n,*ctx->xshares,ctx->index,&ctx->share)) return 5; DKG_Message* msg22 = (DKG_Message*) output; memcpy(msg22->data, msg21->data, 2); if(0!=tp_send_msg(output, tpdkg_msg21_SIZE, 22, ctx->index, 0, ctx->sig_sk, ctx->sessionid)) return 3; dkg_dump_msg(output, tpdkg_msg21_SIZE, ctx->index); return 0; } return 4; } static int tp_step22_handler(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len) { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "\x1b[0;33m[!] step 22. collect acks from peers\x1b[0m\n"); if((tpdkg_msg21_SIZE * ctx->n) != input_len) return 1; const uint8_t *ptr = input; for(uint8_t i=0;in;i++, ptr+=tpdkg_msg21_SIZE) { const DKG_Message* msg = (const DKG_Message*) ptr; dkg_dump_msg(ptr, tpdkg_msg21_SIZE, 0); int ret = tp_recv_msg(ptr, tpdkg_msg21_SIZE, 22, i+1U, 0, (*ctx->peer_sig_pks)[i], ctx->sessionid, ctx->ts_epsilon, &ctx->last_ts[i]); if(0!=ret) { if(add_cheater(ctx, 22, 64+ret, i+1U, 0) == NULL) return 6; continue; } if(memcmp("OK", msg->data, 2)!=0) { if(liboprf_log_file!=NULL) { fprintf(liboprf_log_file,"\x1b[0;31m[!] failed to get ack from %d!\x1b[0m\n", i); } } } if(ctx->cheater_len>0) return 5; return 0; } int tpdkg_tp_next(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { int ret = 0; switch(ctx->step) { case 0: {ret = tp_step1_handler(ctx, output, output_len); break;} case 1: {ret = tp_step4_handler(ctx, input, input_len, output, output_len); break;} case 2: {ret = tp_step68_handler(ctx, input, input_len, output, output_len); break;} case 3: {ret = tp_step68_handler(ctx, input, input_len, output, output_len); break;} case 4: {ret = tp_step12_handler(ctx, input, input_len, output, output_len); break;} case 5: {ret = tp_step14_handler(ctx, input, input_len, output, output_len); break;} case 6: { ret = tp_step16_handler(ctx, input, input_len, output, output_len); ctx->prev = ctx->step; if(ctx->complaints_len == 0) { // we skip over to step 21 ctx->step++; } ctx->step++; return ret; } case 7: {ret = tp_step18_handler(ctx, input, input_len); break;} case 8: {ret = tp_step20_handler(ctx, input, input_len, output, output_len); break;} case 9: {ret = tp_step22_handler(ctx, input, input_len); break;} default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[!] invalid step\n"); return 99; } } ctx->prev=ctx->step++; if(ret!=0) ctx->step=99; // so that not_done reports done return ret; } int tpdkg_peer_next(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len) { int ret=0; switch(ctx->step) { case 0: {ret = peer_step23_handler(ctx, input, input_len, output, output_len); break;} case 1: {ret = peer_step5_handler(ctx, input, input_len, output, output_len); break;} case 2: {ret = peer_step7_handler(ctx, input, input_len, output, output_len); break;} case 3: {ret = peer_step911_handler(ctx, input, input_len, output, output_len); break;} case 4: {ret = peer_step13_handler(ctx, input, input_len, output, output_len); break;} case 5: {ret = peer_step15_handler(ctx, input, input_len, output, output_len); break;} case 6: {ret = peer_step17_handler(ctx, input, input_len); break;} case 7: {ret = peer_step17a_handler(ctx, output, output_len); break;} case 8: {ret = peer_step19_handler(ctx, output, output_len); break;} case 9: {ret = peer_step21_handler(ctx, input, input_len, output, output_len); break;} case 10: { // we are done ret = 0; break; } default: { if(liboprf_log_file!=NULL) fprintf(liboprf_log_file, "[%d] invalid step\n", ctx->index); ret = 99; } } ctx->prev=ctx->step++; if(ret!=0) ctx->step=99; // so that not_done reports done return ret; } uint8_t tpdkg_cheater_msg(const TP_DKG_Cheater *c, char *out, const size_t outlen) { if(c->error>65 && c->error<=70) { snprintf(out, outlen, "step %d message from peer %d for peer %d could not be validated: %s", c->step, c->peer, c->other_peer, dkg_recv_err(c->error & 0x3f)); return c->peer; } if(c->step==16) { if(c->error == 6) { snprintf(out, outlen, "more than t^2 complaints, most peers are cheating."); return 0; } else if(c->error == 7) { snprintf(out, outlen, "peer %d sent complaint about invalid peer %d.", c->peer, c->other_peer); return c->peer; } else if(c->error == 8) { snprintf(out, outlen, "peer %d sent a duplicate complaint about peer %d.", c->peer, c->other_peer); return c->peer; } snprintf(out,outlen, "invalid error code for step 16: %d", c->error); return 0; } else if(c->step==18) { if(c->error & 16) { snprintf(out, outlen, "message containing encrypted share from peer %d for peer %d could not be validated: %s", c->peer, c->other_peer, dkg_recv_err(c->error & 0xf)); return c->peer; } else if (c->error & 32) { snprintf(out, outlen, "message revealing key encrypting share from peer %d for peer %d could not be validated: %s", c->peer, c->other_peer, dkg_recv_err(c->error & 0x1f)); return c->peer; } switch(c->error) { case 3: { snprintf(out,outlen, "accused peer %d revealed a key (for peer %d) that was not complained about", c->peer, c->other_peer); return c->peer; } case 4: { snprintf(out,outlen, "verification of hmac of message from accused peer %d to complaining peer %d failed", c->peer, c->other_peer); return c->peer; } case 5: { snprintf(out,outlen, "accused peer %d sent an invalid share with index %d to complaining peer %d", c->peer, c->other_peer, c->invalid_index); return c->peer; } case 6: { snprintf(out,outlen, "accused peer %d revealed a key for happy peer %d", c->peer, c->other_peer); return c->peer; } case 7: { snprintf(out,outlen, "accused peer %d complained by peer %d was not verified", c->peer, c->other_peer); return c->peer; } case 127: { snprintf(out,outlen, "accused peer %d provided invalid parameters to complaint from peer %d", c->peer, c->other_peer); return c->peer; } case 128: { snprintf(out,outlen, "peer %d was falsely accused by peer %d", c->peer, c->other_peer); return c->other_peer; } case 129: { snprintf(out,outlen, "accused peer %d was caught cheating by peer %d", c->peer, c->other_peer); return c->peer; } default: { snprintf(out,outlen, "invalid error code for step 18: %d", c->error); return 0; } } } else if(c->step==20) { if(c->error == 1) { snprintf(out,outlen, "transcript mismatch peer %d", c->peer); return c->peer; } snprintf(out,outlen, "invalid error code for step 20: %d", c->error); return 0; } snprintf(out,outlen, "invalid step %d", c->step); return 0; } liboprf-0.9.4/src/tp-dkg.h000066400000000000000000000642631514673400200153240ustar00rootroot00000000000000#ifndef tp_dkg_h #define tp_dkg_h /** * @file tp-dkg.h * @brief API for the Trusted Party Distributed Key Generation (TP-DKG) * Protocol * SPDX-FileCopyrightText: 2024-25, Marsiske Stefan * SPDX-License-Identifier: LGPL-3.0-or-later * * This file implements a Distributed Key Generation (DKG) protocol. * There are two roles: the trusted party (TP) and peers. * * The trusted party connects to all the peers and orchestrates the * protocol, which communicate only via the TP with each other . This * way, the TP acts as a broadcast medium which is an essential part of * all DKG protocols. * * In this protocol, the trusted party is - as the name implies - * trusted, but does not learn the result of the DKG. If the trusted party * is so trusted that it can learn the result of the DKG, then it is much * simpler to just randomly generate a secret and then share it using * Shamir's secret sharing. * * The peers only identify themselves towards the TP using long-term * keys, but use ephemeral keys when communicating with each other. This * makes the peers unaware of the identities of each other. However, the * peers might be using the ephemeral public keys, or any of the generated * random values to use as a side-channel to leak their identity to the * other peers. * * The protocol consists of over 20 internal steps, but the API hides this * complexity behind a state-driven loop, which any user can call iteratively * while implementing the networking communication themselves. This simplifies * the network model and enables usage across different communication channels * like TCP/IP, Bluetooth, USB, UART, etc. * The STP must support all communication channels that its peers require, * while each peer only needs to support its chosen medium. * * ## Common Protocol Flow * * Both the peers and the TP share a similar API schema: * * - For peers: * ``` * msg0 = read(); // from the TP * start_peer(state, ...); * peer_set_bufs(); * while (peer_not_done(state)) { * input = allocate_memory( dkg_peer_input_size(state) ) * output = allocate_memory( dkg_peer_output_size(state) ) * input = read() * res = peer_next_step(state, input, output) * if res!=0: fail&abort * msg = output * send(msg) * } * store share * peer_free(state); * ``` * * - For the TP: * ``` * start_tp(state, ...); * set_bufs(...); * send(msg0); // to all peers * while (!tp_done(state)) { * input = allocate_memory( dkg_tp_input_size(state) ) * output = allocate_memory( dkg_tp_output_size(state) ) * input = read() * res = tp_next_step(state, input, output) * if res!=0: fail&abort * dkg_tp_peer_msg(state, output, peer_index, msg) * msg = output * send(msg) * } * ``` */ #include #include #include "XK.h" #include "dkg.h" typedef DKG_Cheater TP_DKG_Cheater; #define tpdkg_msg0_SIZE ( sizeof(DKG_Message) \ + crypto_generichash_BYTES/*dst*/ \ + 2 /*n,t*/ \ + crypto_sign_PUBLICKEYBYTES /* tp_sign_pk */ ) #define tpdkg_msg8_SIZE (sizeof(DKG_Message) /* header */ \ + noise_xk_handshake3_SIZE /* 4th&final noise handshake */ \ + sizeof(TOPRF_Share) /* msg: the noise_xk wrapped share */ \ + crypto_secretbox_xchacha20poly1305_MACBYTES /* mac of msg */ \ + crypto_auth_hmacsha256_BYTES /* key-committing mac over msg*/ ) /** * @struct TP_DKG_PeerState * @brief State of a peer during the execution the TP DKG protocol * * Some fields in this struct are internal variables and should not * be used. The following fields are useful and can be accessed by * users of the API: * * @var TP_DKG_PeerState::n Total number of peers participating in * this protocol * @var TP_DKG_PeerState::t The threshold, the minimum number of * peers required to use the shared secret generated by this DKG * @var TP_DKG_PeerState::index Index of this peer (1-based). This value * is between 1 to `n` inclusive * @var TP_DKG_PeerState::share Resulting secret share output of the DKG for * a peer. This value should probably be persisted for later usage * */ typedef struct { int step; int prev; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t n; uint8_t t; uint8_t index; uint8_t lt_sk[crypto_sign_SECRETKEYBYTES]; uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t sig_sk[crypto_sign_SECRETKEYBYTES]; uint8_t noise_pk[crypto_scalarmult_BYTES]; uint8_t noise_sk[crypto_scalarmult_SCALARBYTES]; uint8_t tp_sig_pk[crypto_sign_PUBLICKEYBYTES]; uint64_t tp_last_ts; uint64_t *last_ts; uint64_t ts_epsilon; uint8_t (*peer_sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*peer_noise_pks)[][crypto_scalarmult_BYTES]; Noise_XK_device_t *dev; Noise_XK_session_t *(*noise_outs)[]; Noise_XK_session_t *(*noise_ins)[]; uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]; TOPRF_Share (*shares)[]; TOPRF_Share (*xshares)[]; uint16_t complaints_len; uint16_t *complaints; uint8_t my_complaints_len; uint8_t *my_complaints; crypto_generichash_state transcript; TOPRF_Share share; } TP_DKG_PeerState; size_t tpdkg_peerstate_size(void); /** * @brief Get the total number of peers (`n`) in this DKG session */ uint8_t tpdkg_peerstate_n(const TP_DKG_PeerState *ctx); /** * @brief Get the threshold parameter (`t`) for this DKG session */ uint8_t tpdkg_peerstate_t(const TP_DKG_PeerState *ctx); /** * @brief Get the session ID (unique identifier) for this DKG run */ const uint8_t *tpdkg_peerstate_sessionid(const TP_DKG_PeerState *ctx); /** * @brief Get this peer's long-term signing secret key */ const uint8_t *tpdkg_peerstate_lt_sk(const TP_DKG_PeerState *ctx); /** * @brief Get the final secret share after successful protocol completion */ const uint8_t *tpdkg_peerstate_share(const TP_DKG_PeerState *ctx); /** * @brief Get current internal protocol step counter */ int tpdkg_peerstate_step(const TP_DKG_PeerState *ctx); /** * @brief TP-DKG Error Codes * * ## `recv_msg()` error codes: * 1 - Invalid message length. * 2 - Unexpected message number. * 3 - Invalid 'from' field. * 4 - Invalid 'to' field. * 5 - Message expired. * 6 - Signature verification failed. * * ## Step 18 error codes (verification phase failures): * 3 - HMAC verification failure. * 4 - Share decryption failure. * 5 - Invalid share index. * 6 - Accused revealed an unexpected key (not part of complaint). * 7 - Unchecked complaint (incomplete complaint resolution). * 16 + `recv_msg()` error code - Invalid msg8 (final Noise + HMAC-ed share). * 32 + `recv_msg()` error code - Invalid msg11 (key reveal message). * 127 - Invalid verification parameters from accused peer. * 128 - False complaint (complaint rejected, peer honest). * 129 - Correct complaint (peer found cheating). */ /** * @struct TP_DKG_TPState * @brief Internal state structure for the TP during TP DKG execution * * Some fields in this struct are internal variables and should not * be used. The following fields are useful and can be accessed by * users of the API: * * @var STP_DKG_STPState::n Total number of peers participating in * this protocol * * @var STP_DKG_STPState::t The threshold, the minimum number of * peers required to use the shared secret generated by this DKG * * @var STP_DKG_STPState::cheaters List of detected cheaters and protocol * violators at the end of a failed protocol run * * @var STP_DKG_STPState::cheater_len Length of the `cheaters` list */ typedef struct { int step; int prev; uint8_t sessionid[dkg_sessionid_SIZE]; uint8_t n; uint8_t t; uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES]; uint8_t sig_sk[crypto_sign_SECRETKEYBYTES]; uint64_t *last_ts; uint64_t ts_epsilon; uint8_t (*peer_sig_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*peer_lt_pks)[][crypto_sign_PUBLICKEYBYTES]; uint8_t (*commitments)[][crypto_core_ristretto255_BYTES]; // note this could be optimized by only storing the encrypted share and the hmac // and also dropping all items where i==j uint8_t (*encrypted_shares)[][tpdkg_msg8_SIZE]; uint16_t complaints_len; uint16_t (*complaints)[]; size_t cheater_len; TP_DKG_Cheater (*cheaters)[]; size_t cheater_max; crypto_generichash_state transcript; } TP_DKG_TPState; /** * @brief Gets the size needed for allocation of a TP_DKG_TPState struct * * @return The size in bytes required for TP_DKG_TPState */ size_t tpdkg_tpstate_size(void); /** * @brief Gets the number of peers (`n`) participating in the protocol * from the TP state * * @param[in] ctx Pointer to an initialized TP_DKG_TPState struct * * @return The number of peers (`n`) */ uint8_t tpdkg_tpstate_n(const TP_DKG_TPState *ctx); /** * @brief Gets the threshold (`t`) required for the DKG from the TP state * * @param[in] ctx Pointer to an initialized TP_DKG_TPState struct * * @return The threshold value (`t`) */ uint8_t tpdkg_tpstate_t(const TP_DKG_TPState *ctx); /** * @brief Gets the number of cheaters detected in the protocol from * the TP state * * @param[in] ctx Pointer to an initialized TP_DKG_TPState struct * * @return The number of cheaters detected (length of the `cheaters` list) */ size_t tpdkg_tpstate_cheater_len(const TP_DKG_TPState *ctx); /** * @brief Gets the session ID associated with the current TP state * * @param[in] ctx Pointer to an initialized TP_DKG_TPState struct * * @return Pointer to the session ID buffer */ const uint8_t *tpdkg_tpstate_sessionid(const TP_DKG_TPState *ctx); /** * @brief Gets the current step number in the protocol * * @param[in] ctx Pointer to an initialized TP_DKG_TPState struct * * @return The current step as an integer */ int tpdkg_tpstate_step(const TP_DKG_TPState *ctx); /* * Trusted Party functions */ /** * @brief Starts a new execution of a TP DKG protocol for the TP * * This function initializes the state of the TP and creates an * initial message containing the parameters for the peers. * * @param[in] ctx Pointer to a TP_DKG_TPState struct. This struct * will be initialized by this function * @param[in] ts_epsilon Maximum allowed message age in seconds before * it is considered stale and rejected. This value is used to * prevent replay attacks and enforce freshness. For small, * local setups (e.g., 2-out-of-3 participants), values as low * as 2–3 seconds may suffice. For large-scale deployments * (e.g., 126-out-of-127), this may need to be increased to * several hours * @param[in] n Number of peers participating in this execution * @param[in] t Threshold necessary to use the results of this DKG * @param[in] proto_name a list of bytes used as a domain separation tag * (DST). Set it to the name of your application * @param[in] proto_name_len The size of the array `proto_name`, to allow * non-zero terminated DSTs * @param[in] sig_pks Pointer to a (n+1)-element array of signing public * keys. The STP's public key must be at index 0. The rest of the * items must be in order * @param[in] ltssk STP’s private long-term signing key * @param[in] msg0_len Size of allocated memory for the output message, * `msg0`. Should be exactly `tpdkg_msg0_SIZE` long * @param[out] msg0 Output parameter, the message to be sent to peers * to initialize them * * @return 0 on success, non-zero on error */ int tpdkg_start_tp(TP_DKG_TPState *ctx, const uint64_t ts_epsilon, const uint8_t n, const uint8_t t, const char *proto_name, const size_t proto_name_len, const size_t msg0_len, DKG_Message *msg0); /** * @brief Sets all the variable sized buffers in the TP_DKG_TPState structure * * This function sets all the variable-sized buffers in the STP_DKG_STPState * struct. These buffers must be preallocated by the caller, typically on * the stack, based on the number of participants `n` and the threshold `t` * * A number of buffers are needed in the STP state that depend on the `n` * (number of participants) and `t` (threshold) parameters. * These can be allocated on the stack as follows: * @code * uint8_t tp_commitments[n*t][crypto_core_ristretto255_BYTES]; * uint16_t tp_complaints[n*n]; * uint8_t encrypted_shares[n*n][tpdkg_msg8_SIZE]; * TP_DKG_Cheater cheaters[t*t - 1]; * uint8_t tp_peers_sig_pks[n][crypto_sign_PUBLICKEYBYTES]; * uint8_t peer_lt_pks[n][crypto_sign_PUBLICKEYBYTES]; * * tpdkg_tp_set_bufs(&tp, &tp_commitments, &tp_complaints, &encrypted_shares, * &cheaters, sizeof(cheaters) / sizeof(TP_DKG_Cheater), * &tp_peers_sig_pks, &peer_lt_pks, &last_ts); * @endcode * * @param[in] ctx Pointer to the STP_DKG_STPState structure being * initialized * @param[in] commitments Pointer to a list of curve points representing * commitments * @param[in] complaints Pointer to a list of share complaint flags * @param[in] encrypted_shares Buffer of encrypted shares * @param[in] cheaters List of detected cheaters and protocol violators at * the end of a failed protocol run * @param[in] cheater_max Maximum number of cheat attempts to be recorded. * Normally, the maximum number of cheaters is `t * t - 1`, where * `t` is the threshold parameter. It should be provided as * (sizeof(cheaters) / sizeof(TP_DKG_Cheater)) * @param[in] tp_peers_sig_pks Buffer of peers' long-term signing public keys * @param[in] peer_lt_pks Buffer of peers' long-term public keys. This array * must be populated in the correct order before the first call * to `tpdkg_tp_next()` * @param[in] last_ts Array to store latest message timestamps for * freshness checks * */ void tpdkg_tp_set_bufs(TP_DKG_TPState *ctx, uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint16_t (*complaints)[], uint8_t (*encrypted_shares)[][tpdkg_msg8_SIZE], TP_DKG_Cheater (*cheaters)[], const size_t cheater_max, uint8_t (*tp_peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peer_lt_pks)[][crypto_sign_PUBLICKEYBYTES], uint64_t *last_ts); /** * @brief Calculate the input buffer size required for the next TP step * * This function calculates the size of the buffer needed to hold all outputs from the peers serving as input to the next step of the TP. * * An implementer should allocate a buffer of this size and concatenate * all messages from all peers in the order of the peers' indices. * The allocated buffer is to be passed as an input `tpdkg_tp_next()`. * After this, the buffer SHOULD be deallocated. * * @param[in] ctx An initialized TP_DKG_TPState structure * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t tpdkg_tp_input_size(const TP_DKG_TPState *ctx); /** * @brief Calculates the size of the message from each peer to be * received by the TP * * This function determines, for the current step, how many bytes the TP * expects from each individual peer. The result is written into the * provided `sizes` array. * * @param[in] ctx An initialized TP_DKG_TPState structure * @param[out] sizes a list of size `n` (number of peers), where the input * sizes for each peer will be written * * @return 0 on if the sizes differ from peer to peer, otherwise all peers * will be sending messages of equal size. In the latter case, all * items of the `sizes` array hold the same valid value */ int tpdkg_tp_input_sizes(const TP_DKG_TPState *ctx, size_t *sizes); /** * @brief Gets the output buffer size needed for the next TP step * * Determines the buffer size required to hold the output of * `tpdkg_tp_next()` for the current protocol step. An implementer * should allocate a buffer of this size and pass it as the `output` * parameter to `tpdkg_tp_next()` * * @param[in] ctx An initialized TP_DKG_TPState struct * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t tpdkg_tp_output_size(const TP_DKG_TPState *ctx); /** * @brief Executes the next step of the TP DKG protocol for the TP * * Processes the current protocol step using the provided input buffer * and writes the result to the output buffer. Then, it advances the * protocol state. * * This is an example of how to use this function in concert with * `tpdkg_tp_input_size()` and `tpdkg_tp_output_size()`: * @code * uint8_t tp_out[tpdkg_tp_output_size(&tp)]; * uint8_t tp_in[tpdkg_tp_input_size(&tp)]; * recv(socket, tp_in, sizeof(tp_in)); * ret = tpdkg_tp_next(&tp, tp_in, sizeof(tp_in), tp_out, sizeof tp_out); * @endcode * * @param[in] ctx Pointer to a valid TP_DKG_TPState * @param[in] input Buffer containing all peer inputs for the current step * @param[in] input_len Size of the input buffer * @param[out] output Output buffer for messages generated by the current * step * @param[in] output_len Size of the output buffer * * @return 0 on success, non-zero on error */ int tpdkg_tp_next(TP_DKG_TPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); /** * @brief Extracts the message to be sent to a specific peer from TP output * * This function converts the output of `stp_dkg_stp_next()` into a message * to be sent to the i-th peer. * Depending on the current STP step, the * output may be a broadcast (same messages to all) or dedicated and * unique messages for each peer. * This function returns a pointer to a message and the size of the message * to be sent for a particular peer specified as a parameter. * * This is an example of how to use this function in concert with * `stp_dkg_stp_next()`: * @code * ret = tpdkg_tp_next(&tp, tp_in, sizeof(tp_in), tp_out, sizeof(tp_out)); * for (int i = 0; i < tp.n; i++) { * const uint8_t *msg; * size_t len; * if (tpdkg_tp_peer_msg(&tp, tp_out, sizeof(tp_out), i, &msg, &len) != 0) * return 1; * send(i, msg, len); * } * @endcode * * @param[in] ctx Pointer to a valid TP_DKG_TPState * @param[in] base Pointer to the output buffer from `tpdkg_tp_next()` * @param[in] base_size Size of the output buffer of `tpdkg_tp_next()` * @param[in] peer Index of the peer (0-based) * @param[out] msg Pointer to the message to be sent to the i-th peer * @param[out] len Pointer to the length of the message to be sent to the * i-th peer * * @return 0 on success, non-zero on error */ int tpdkg_tp_peer_msg(const TP_DKG_TPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len); /** * @brief Checks if the TP protocol has more steps to execute or more * `tpdkg_tp_next()` calls are necessary * * @param[in] tp An initialized TP_DKG_TPState struct * * @return 1 if more steps are outstanding */ int tpdkg_tp_not_done(const TP_DKG_TPState *tp); /** * @brief Converts a cheater record to a human-readable string * * This function takes a TP_DKG_Cheater object (produced when cheating * behavior is detected) and formats a descriptive string explaining the * nature of the cheating incident. * * This variant is used for cheater objects created by TP * * @param[in] c Pointer to the cheater object * @param[out] out The pointer to the pre-allocated Buffer to receive the * formatted string * @param[in] outlen Size of the pre-allocated `out` buffer * * @return The index of the cheating peer */ uint8_t tpdkg_cheater_msg(const TP_DKG_Cheater *c, char *out, const size_t outlen); /* * Peer functions */ /** * @brief Starts a new execution of a TP DKG protocol for a peer * * This function initializes the state of the peer. * * @param[out] ctx Pointer to a TP_DKG_PeerState struct. This struct * will be initialized by this function * @param[in] ts_epsilon Maximum allowed message age in seconds before * it is considered stale and rejected. This value is used to * prevent replay attacks and enforce freshness. For small, * local setups (e.g., 2-out-of-3 participants), values as low * as 2–3 seconds may suffice. For large-scale deployments * (e.g., 126-out-of-127), this may need to be increased to * several hours * @param[in] peer_lt_sk The long-term private signing secret key of the * peer * @param[in] msg0 The initiating message, `msg0`, received from the STP, * created after running `tpdkg_tp_start()` * * @return 0 on success, non-zero on error **/ int tpdkg_start_peer(TP_DKG_PeerState *ctx, const uint64_t ts_epsilon, const uint8_t peer_lt_sk[crypto_sign_SECRETKEYBYTES], const DKG_Message *msg0); /** * @brief Sets all the variable-sized buffers in the TP_DKG_PeerState * structure * * The buffer sizes depend on the `n` and `t` parameters of the DKG * protocol, which could be known in advance. If not, these parameters * are announced by the TP in `msg0`, which is an input to the * `tpdkg_start_peer()` function. After this `tpdkg_start_peer()` call, * the peer state is initialized and can be used to find out the `n` and * `t` parameters. * * To allocate all the buffers on the stack: * @code * uint8_t peers_sig_pks[peerstate.n][crypto_sign_PUBLICKEYBYTES]; * uint8_t peers_noise_pks[peerstate.n][crypto_scalarmult_BYTES]; * Noise_XK_session_t *noise_outs[peerstate.n]; * Noise_XK_session_t *noise_ins[peerstate.n]; * TOPRF_Share ishares[peerstate.n]; * TOPRF_Share xshares[peerstate.n]; * uint8_t commitments[peerstate.n *peerstate.t][crypto_core_ristretto255_BYTES]; * uint16_t peer_complaints[peerstate.n *peerstate.n]; * uint8_t peer_my_complaints[peerstate.n]; * @endcode * * @param[in] ctx An initialized TP_DKG_PeerState struct * @param[in] peers_sig_pks Pointer to a list of peers' signature public keys * @param[in] peers_noise_pks Pointer to a list of peers' Noise_XK protocol * public keys * @param[in] commitments Pointer to a list of commitments * @param[in] complaints Pointer to a list holding complaint indices * @param[in] my_complaints Pointer to a list holding this peer's complaints * @param[in] last_ts Pointer to a list of last timestamps for each peer */ void tpdkg_peer_set_bufs(TP_DKG_PeerState *ctx, uint8_t (*peers_sig_pks)[][crypto_sign_PUBLICKEYBYTES], uint8_t (*peers_noise_pks)[][crypto_scalarmult_BYTES], Noise_XK_session_t *(*noise_outs)[], Noise_XK_session_t *(*noise_ins)[], TOPRF_Share (*shares)[], TOPRF_Share (*xshares)[], uint8_t (*commitments)[][crypto_core_ristretto255_BYTES], uint16_t *complaints, uint8_t *my_complaints, uint64_t *last_ts); /** * @brief Calculates the required size of the input buffer for the next * step of the peer * * This function returns the size in bytes of the input buffer that the peer * will expect for the upcoming protocol step. The input buffer holds the * data that the TP sends to the peer. * * The caller should allocate a buffer of at least this size and pass it as * input to `tpdkg_peer_next()`. After the step completes, the buffer SHOULD * be deallocated. * * @param[in] ctx An initialized TP_DKG_PeerState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t tpdkg_peer_input_size(const TP_DKG_PeerState *ctx); /** * @brief Calculates the size of the buffer needed to hold the output from * the `tpdkg_peer_next()` function * * Determines the buffer size required to hold the output of * `tpdkg_peer_next()` for the current protocol step. * An implementer should allocate a buffer of this size and pass it as the * `output` parameter to `tpdkg_peer_next()`. * * @param[in] ctx An initialized TP_DKG_PeerState struct * * @return 1 on error, otherwise the size to be allocated (can be 0) */ size_t tpdkg_peer_output_size(const TP_DKG_PeerState *ctx); /** * @brief Executes the next step of the TP DKG protocol for a peer * * Processes the current protocol step using the provided input buffer * and writes the result to the output buffer. Then, it advances the * protocol state. * * This is an example of how to use this function in concert with * `tpdkg_peer_output_size()` and `tpdkg_peer_output_size()`: * @code * uint8_t peers_out[tpdkg_peer_output_size(&peer)]; * * uint8_t peer_in[tpdkg_peer_input_size(&peer)]; * recv(socket, peer_in, sizeof(peer_in)); * ret = tpdkg_peer_next(&peer, * peer_in, sizeof(peer_in), * peers_out, sizeof(peers_out)); * @endcode * * @param[in] ctx Pointer to a valid TP_DKG_PeerState * @param[in] input Buffer containing input data for the current step * @param[in] input_len Size of the input buffer * @param[out] output Buffer to receive the output of the current step * @param[in] output_len Size of the output buffer * * @return 0 on success, non-zero on error * */ int tpdkg_peer_next(TP_DKG_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len); /** * @brief Checks if the STP protocol has more steps to execute or more * `tpdk_peer_next()` calls are necessary * * @param peer Pointer to a valid TP_DKG_PeerState * * @return 1 if more steps are outstanding */ int tpdkg_peer_not_done(const TP_DKG_PeerState *peer); /** * @brief Frees all resources allocated by the peer state * * This function MUST be called before a peer's state is deallocated. * The underlying Noise_XK implementation allocates a lot of internal * state on the heap, which must be freed manually to avoid memory leaks. * * * @param ctx Pointer to the TP_DKG_PeerState to free */ void tpdkg_peer_free(TP_DKG_PeerState *ctx); extern FILE* log_file; #endif //tp_dkg_h liboprf-0.9.4/src/utils.c000066400000000000000000000037131514673400200152620ustar00rootroot00000000000000#include #include #include #include #if (defined UNIT_TEST || defined UNITTEST_CORRUPT) int liboprf_debug = 1; #else int liboprf_debug = 0; #endif FILE *liboprf_log_file=NULL; #ifdef UNIT_TEST void debian_rng_scalar(uint8_t *scalar) { static int warned=0; static uint8_t rng_i[4]={1,0,0,0}; if(!warned) { fprintf(stderr, "\x1b[0;31mWARNING! This version of liboprf DKG is compiled with a *NON* random generator for UNIT_TESTS\x1b[0m\n"); warned=1; } memset(scalar,0,crypto_core_ristretto255_SCALARBYTES); sodium_increment(rng_i,4); memcpy(scalar,rng_i,4); //static uint16_t rng_i=0; //uint16_t tmp[64 / sizeof(uint16_t)]; //for(unsigned j=0;j<(64/ sizeof(uint16_t));j++) { // tmp[j]=rng_i++; //} //crypto_core_ristretto255_scalar_reduce(scalar,(uint8_t*)tmp); } #endif void __attribute__((visibility("hidden"))) dump(const uint8_t *p, const size_t len, const char* msg, ...) { FILE* lf = stderr; if(!liboprf_debug) return; if(liboprf_log_file!=NULL) lf = liboprf_log_file; va_list args; va_start(args, msg); vfprintf(lf, msg, args); va_end(args); fprintf(lf," "); for(size_t i=0;i uint64_t __attribute__((visibility("hidden"))) htonll(uint64_t n) { #if __BYTE_ORDER == __BIG_ENDIAN return n; #else return (((uint64_t)htonl((uint32_t)n)) << 32) + htonl((uint32_t) (n >> 32)); #endif } #endif // htonll #ifndef ntohll uint64_t __attribute__((visibility("hidden"))) ntohll(uint64_t n) { #if __BYTE_ORDER == __BIG_ENDIAN return n; #else return (((uint64_t)ntohl((uint32_t)n)) << 32) + ntohl((uint32_t)(n >> 32)); #endif } #endif // ntohll liboprf-0.9.4/src/utils.h000066400000000000000000000010401514673400200152560ustar00rootroot00000000000000#ifndef TOPRF_UTILS_H #define TOPRF_UTILS_H #include #include #include extern int liboprf_debug; extern FILE* liboprf_log_file; #define RED "\x1b[0;31m" #define NORMAL "\x1b[0m" #define GREEN "\x1b[0;32m" #ifdef UNIT_TEST void debian_rng_scalar(uint8_t *scalar); #endif //UNIT_TEST void dump(const uint8_t *p, const size_t len, const char* msg, ...); void fail(char* msg, ...); #ifndef htonll uint64_t htonll(uint64_t n); #endif #ifndef ntohll uint64_t ntohll(uint64_t n); #endif #endif // TOPRF_UTILS_H liboprf-0.9.4/zephyr/000077500000000000000000000000001514673400200145045ustar00rootroot00000000000000liboprf-0.9.4/zephyr/CMakeLists.txt000066400000000000000000000021211514673400200172400ustar00rootroot00000000000000# CMakeLists.txt for liboprf integration with Zephyr if(CONFIG_LIBOPRF) # Create a Zephyr library for the platform-specific adaptations zephyr_library_named(oprf_zephyr) # Define source directories set(LIBOPRF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) set(LIBOPRF_SRC_DIR ${LIBOPRF_DIR}/src) # Build liboprf directly (no external build process) #zephyr_library_named(oprf) # Add necessary includes zephyr_include_directories( ${LIBOPRF_DIR}/src ${LIBOPRF_DIR}/src/noise_xk/include ${LIBOPRF_DIR}/src/noise_xk/include/karmel ${LIBOPRF_DIR}/src/noise_xk/include/karmel/minimal ) # Make the includes available to applications zephyr_library_include_directories( ${LIBOPRF_DIR}/src ${LIBOPRF_DIR}/src/noise_xk/include ${LIBOPRF_DIR}/src/noise_xk/include/karmel ${LIBOPRF_DIR}/src/noise_xk/include/karmel/minimal ) FILE(GLOB lib_sources ${LIBOPRF_SRC_DIR}/*.c ${LIBOPRF_SRC_DIR}/noise_xk/src/*.c) # Add all necessary source files zephyr_library_sources(${lib_sources}) zephyr_compile_definitions(NO_TIME) endif() liboprf-0.9.4/zephyr/Kconfig000066400000000000000000000002611514673400200160060ustar00rootroot00000000000000# Kconfig - liboprf configuration options menuconfig LIBOPRF bool "liboprf OPRF library" depends on LIBSODIUM help This option enables the liboprf cryptographic library. liboprf-0.9.4/zephyr/module.yml000066400000000000000000000000611514673400200165110ustar00rootroot00000000000000build: cmake: zephyr kconfig: zephyr/Kconfig