├── .gitignore ├── FFI.md ├── LICENSE ├── README.md ├── RUST_WISHLIST.md ├── aeonflux ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── amacs.rs │ ├── credential.rs │ ├── elgamal.rs │ ├── errors.rs │ ├── issuer.rs │ ├── lib.rs │ ├── macros.rs │ ├── nonces.rs │ ├── parameters.rs │ ├── pedersen.rs │ ├── prelude.rs │ ├── proofs.rs │ ├── proofs_macros.rs │ └── user.rs ├── build-test ├── README.md ├── alloc │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── aos │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── electron │ ├── Cargo.toml │ ├── README.md │ ├── electron.wasm │ ├── site │ │ ├── electron.d.ts │ │ ├── electron.html │ │ ├── electron.js │ │ └── electron_bg.wasm │ └── src │ │ └── lib.rs ├── ios │ ├── Cargo.toml │ ├── README.md │ ├── libbuildtest.a │ ├── src │ │ ├── buildtest.h │ │ └── lib.rs │ └── xcode │ │ ├── BuildTest.swift │ │ ├── RustBuildTest.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ ├── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── xcuserdata │ │ │ │ └── isis.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── isis.xcuserdatad │ │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ │ ├── RustBuildTest │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── ViewController.swift │ │ ├── RustBuildTestTests │ │ ├── Info.plist │ │ └── RustBuildTestTests.swift │ │ ├── RustBuildTestUITests │ │ ├── Info.plist │ │ └── RustBuildTestUITests.swift │ │ ├── buildtest-bridging-header.h │ │ ├── buildtest.h │ │ └── libbuildtest.a └── maths │ ├── Cargo.toml │ └── src │ └── lib.rs ├── credential.podspec ├── ffi ├── BUILD-ANDROID.md ├── Cargo.toml ├── Cargo.toml.template ├── Makefile ├── README.md ├── create-ndk-standalone.sh ├── ffi-api.md ├── src │ ├── c.rs │ ├── include │ │ └── credential.h │ ├── lib.rs │ └── macros.rs └── target │ ├── release │ ├── libcredential.a │ └── libcredential.so │ └── universal │ └── release │ └── libcredential.a ├── java └── src │ └── main │ └── java │ └── org │ └── signal │ └── credential │ ├── credential.java │ └── util.java ├── jni └── Credential │ ├── .gitignore │ ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ └── runConfigurations.xml │ ├── app │ ├── .gitignore │ ├── CMakeLists.txt │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── org │ │ │ └── signal │ │ │ └── credential │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ │ └── native-lib.cpp │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── org │ │ └── signal │ │ └── credential │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── rust │ ├── .gitignore │ ├── build.gradle │ ├── build │ │ └── outputs │ │ │ └── aar │ │ │ └── rust-debug.aar │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── org │ │ │ └── signal │ │ │ └── rust │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── jniLibs │ │ │ ├── arm64-v8a │ │ │ │ └── libcredential.so │ │ │ ├── armeabi-v7a │ │ │ │ └── libcredential.so │ │ │ └── x86 │ │ │ │ └── libcredential.so │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── org │ │ └── signal │ │ └── rust │ │ └── ExampleUnitTest.java │ └── settings.gradle ├── signal-credential ├── Cargo.toml ├── benches │ ├── credential-benches.rs │ └── credential-benchmarks.rs ├── src │ ├── credential.rs │ ├── errors.rs │ ├── issuer.rs │ ├── lib.rs │ ├── phone_number.rs │ └── user.rs └── tests │ └── full-protocol.rs ├── swift ├── Credential.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Credential.xcworkspace │ └── contents.xcworkspacedata ├── Credential │ ├── Credential.swift │ ├── credential-bridging-header.h │ └── credential.h ├── LICENSE ├── Makefile ├── Podfile ├── Pods │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ └── Pods-Credential │ │ ├── Info.plist │ │ ├── Pods-Credential-acknowledgements.markdown │ │ ├── Pods-Credential-acknowledgements.plist │ │ ├── Pods-Credential-dummy.m │ │ ├── Pods-Credential-resources.sh │ │ ├── Pods-Credential-umbrella.h │ │ ├── Pods-Credential.debug.xcconfig │ │ ├── Pods-Credential.modulemap │ │ └── Pods-Credential.release.xcconfig ├── Products │ └── libCredential.a └── libcredential.a ├── wasm ├── Cargo.toml ├── Makefile ├── README.md └── src │ ├── credential.d.ts │ ├── credential.js │ ├── credential.wasm │ ├── credential_bg.wasm │ ├── js.rs │ └── lib.rs └── zkp-expand ├── .gitignore ├── .rustfmt.toml ├── Cargo.toml ├── Makefile └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | 4 | *~ 5 | \#* 6 | .\#* 7 | *.swp 8 | *.orig 9 | *.bak 10 | 11 | *.s 12 | 13 | ._* 14 | .DS_Store 15 | 16 | **/xcuserdata/* 17 | 18 | **/private/* 19 | -------------------------------------------------------------------------------- /FFI.md: -------------------------------------------------------------------------------- 1 | 2 | Instructions for Using the FFI code 3 | ===================================== 4 | 5 | A high level, language agnostic walkthrough of the APIs are as follows. 6 | Function names in each language are more or less the same (the Swift API is 7 | class-oriented, but it still should be hopefully clear which function is which). 8 | 9 | Server Protocol 10 | ----------------- 11 | 12 | For all APIs¹, the current steps to create a new issuer are: 13 | 14 | system_parameters_create(bytes) -> system_parameters 15 | issuer_create(system_parameters, seed) -> amacs_keypair 16 | issuer_new(system_parameters, amacs_keypair) 17 | 18 | Next, the current steps to go through the entire protocol on the issuer side are: 19 | 20 | issuer_issue(issuer, seed, credential_request, phone_number) -> credential_issuance 21 | issuer_verify(issuer, credential_presentation) -> verified_credential 22 | 23 | Finally, to see if a user is a member of a certain group, the issuer does: 24 | 25 | issuer_verify_roster_membership(issuer, verified_credential) 26 | 27 | ¹ This functionality is currently included in the FFI for all languages to 28 | enable testing clients, even though none of that code will ever run on the 29 | actual server. 30 | 31 | Client Protocol 32 | ----------------- 33 | 34 | For the client side APIs, the corresponding steps for the full protocol are: 35 | 36 | user_obtain_finish(phone_number, system_parameters, issuer_parameters, credential_issuance) -> user 37 | user_show(user, roster_entry_commitment, seed) -> credential_presentation 38 | 39 | ² The `elgamal_keypair` may be `NULL`, `nil`, `undefined`, `null` to signify 40 | that it doesn't exist. (It doesn't yet because we don't need blinded credential 41 | issuance for anything yet.) 42 | 43 | Cryptographic Utility Functions 44 | --------------------------------- 45 | 46 | To create a commitment and its opening to some phone number, do: 47 | 48 | roster_entry_commitment_create(phone_number, system_parameters, seed) -> roster_entry_commitment 49 | 50 | Note that `roster_entry_commitment` contains the opening, the latter 51 | of which should not be given directly to the server/issuer. 52 | 53 | To remove the opening, do: 54 | 55 | roster_entry_commitment_remove_opening(roster_entry_commitment) -> roster_entry_commitment_sans_opening 56 | 57 | To verify that a commitment (and its opening) is a commitment to a 58 | phone number, do: 59 | 60 | roster_entry_commitment_open(roster_entry_commitment, phone_number, system_parameters) -> bool 61 | 62 | Javascript/Wasm API for Electron Client 63 | ----------------------------------------- 64 | 65 | The Javascript API is contained in wasm/src/credential.js (or credential.d.ts if 66 | Typescript is preferred). 67 | 68 | Swift/Objective-C API for iOS Client 69 | -------------------------------------- 70 | 71 | The Swift API is in swift/Credential. 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Signal Foundation, isis agora lovecruft. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # groupzk 2 | 3 | Components: 4 | 5 | * `aeonflux`: A generic library implementing algebraic message authentication 6 | codes, (currently single-attribute) composable anonymous credentials, and 7 | various zero-knowledge proofs regarding statements on the credential attributes. 8 | 9 | * `build-test`: Various crates expirimenting with FFI in various 10 | languages and for testing cross-compilation and linkage on various platforms. 11 | 12 | * `ffi`: An FFI API for C-like languages to use the functionality of the 13 | `signal-credential` library. 14 | 15 | * `java`: An FFI API for JNI Java code. **Work in progress.** 16 | 17 | * `signal-credential`: Signal-specfic library for creating anonymous credentials 18 | using phone numbers as blinded attributes and zero-knowledge proofs for 19 | demonstrating anonymously whether a user has certain privileges 20 | (i.e. owner/admin/member) within a Signal group conversation. 21 | 22 | * `swift`: A FFI API for Swift to use the functionality of the 23 | `signal-credential` library, its xcode project settings, and cocoapods specs. 24 | 25 | * `wasm`: A FFI API for Javascript to use the functionality of the 26 | `signal-credential` library. 27 | 28 | * `zkp-expand`: A small utitily to expand and clean up the `*_macros.rs` files 29 | containing pseudo-Camenisch-Stadler notated non-interactive zero-knowledge 30 | proofs from `aeonflux` and `signal-credential`. 31 | 32 | 33 | Build Artefacts 34 | ----------------- 35 | 36 | * C/C++: A copy of the copiled C/C++ FFI is at `ffi/target/release/libcredential.{a,so}`. 37 | * C Headers: The headers are at `ffi/src/include/credential.h`. 38 | * Java/JNI: The compiled AAR is at `jni/Credential/rust/build/outputs/aar/rust-debug.aar`. 39 | * Swift: A copy of the compiled Swift library is at `swift/Products/libCredential.a`. 40 | * Wasm/JS: A copy of the compiled Wasm is at 41 | `wasm/src/credential{_bg}.wasm` and its JS wrapper module is at `wasm/src/credential.js`. 42 | 43 | 44 | TODO 45 | ------ 46 | 47 | * The podspec for the Swift CocoaPod (`credential.podspec`) isn't working, 48 | however as noted above in the "Build Artefacts" section, the code does compile 49 | in Xcode. It might be that there is a build/linker setting somewhere in the 50 | `.xcodeproj` settings that isn't replicated in the podspec. 51 | 52 | * The Android/Java AAR is just a repackaging into an AAR of the C API, compiled for 53 | `arm64-v8a`, `armeabi-v7a`, and `x86` ABIs. There is no JNI interface to the 54 | C yet, as I wasn't sure whether Android clients and/or the server could just 55 | directly call the C. 56 | 57 | * Unittests and benchmarks should be written in C, Java, Swift, and JS. (There 58 | are currently only tests in Rust and C-like Rust). 59 | 60 | 61 | Useful resources 62 | ------------------ 63 | 64 | Java/JNI: 65 | 66 | * https://www.codepool.biz/build-so-aar-android-studio.html 67 | * https://dominoc925.blogspot.com/2015/09/how-to-create-and-use-android-archive.html 68 | * https://github.com/tomaka/android-rs-glue 69 | * https://hub.docker.com/r/tomaka/cargo-apk/ 70 | * https://github.com/signalapp/curve25519-java/blob/master/java/src/main/java/org/whispersystems/curve25519/NativeCurve25519Provider.java 71 | * https://github.com/signalapp/curve25519-java/blob/master/android/jni/curve25519-jni.c 72 | * https://docs.rs/jni/0.10.2/src/jni/wrapper/jnienv.rs.html#1063-1079 73 | * https://github.com/jni-rs/jni-rs/blob/master/example/mylib/src/lib.rs 74 | * https://docs.rs/jni/0.10.2/jni/ 75 | * https://github.com/signalapp/ContactDiscoveryService/blob/41ea6e87fa5410aee1891e9a83c46afe8aaf58c5/service/src/main/java/org/whispersystems/contactdiscovery/util/NativeUtils.java 76 | 77 | 78 | -------------------------------------------------------------------------------- /RUST_WISHLIST.md: -------------------------------------------------------------------------------- 1 | 2 | Things We Want in (Or Stabilised in) Rust 3 | =========================================== 4 | 5 | 1. It seems that Apple forked LLVM before Rust did, and then Apple's 6 | LLVM IR outputs differently formatted bitcode, but then also Apple 7 | didn't contribute the patches back to upstream LLVM before Rust 8 | ended up forking it: 9 | 10 | https://github.com/Geal/rust_on_mobile#how-can-we-generate-a-static-library-with-apples-bitcode-format 11 | 12 | It seems like this might be impossible (or an otherwise enormous 13 | amount of effort) to upgrade Rust's LLVM version, but it would be 14 | nice if iOS applications written in Rust could take advantage of 15 | the smaller library/binary sizes provided by bitcode. 16 | 17 | 2. Stabilise #![feature(asm)]. 18 | 19 | This is currently used in the `subtle` 20 | [crate](https://github.com/dalek-cryptography/subtle) (a library for 21 | constant-time functions, which is used by curve25519-dalek, which is then 22 | used by aeonflux, which is then used by signal-credential) to prevent LLVM 23 | optimising a `u8` which is always guaranteed to be either `0` or `1` to the 24 | internal one-bit `i1` type. 25 | 26 | It's also used by the `clear_on_drop` 27 | [crate](https://github.com/cesarb/clear_on_drop) (a library for zeroing out 28 | secrets from memory when the type's destructor is called) to prevent 29 | optimisers from eliding memory zeroing code after determining that the 30 | operand is no longer used, and for preventing optimisers from inlining a 31 | called function and the zeroing code and using separate stack areas for each. 32 | -------------------------------------------------------------------------------- /aeonflux/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aeonflux" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | readme = "README.md" 6 | license = "BSD-3-Clause" 7 | categories = ["cryptography", "no-std"] 8 | keywords = ["MAC", "zero-knowledge", "anonymous", "credential", "algebraic-MAC"] 9 | description = "Composable, lightweight, fast attribute-based anonymous credentials with infinite (aeon) rerandomised (flux) presentations using algebraic message authentication codes (aMACs)" 10 | exclude = [ 11 | "**/.gitignore", 12 | ".gitignore", 13 | ".travis.yml", 14 | ] 15 | autobenches = false 16 | 17 | ## XXX criterion can't find the crate unless the following is commented out? 18 | [lib] 19 | name = "aeonflux" 20 | #crate-type = ["staticlib", "rlib", "cdylib"] 21 | 22 | # Heck yeah, XSS As A Service. 23 | [package.metadata.docs.rs] 24 | rustdoc-args = ["--html-in-header", ".cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-0.13.2/rustdoc-include-katex-header.html"] 25 | features = ["nightly"] 26 | 27 | [dependencies] 28 | bincode = { version = "1" } 29 | clear_on_drop = { version = "0.2" } 30 | curve25519-dalek = { version = "0.21", default-features = false, features = ["serde"] } 31 | failure = { version = "0.1", default-features = false } 32 | merlin = { version = "0.2" } 33 | rand = { version = "0.5", default-features = false } 34 | rand_core = { version = "0.2.1", default-features = false } 35 | # TODO The zkp crate currently requires both serde and serde_derive. 36 | serde = { version = "1" } 37 | serde_derive = { version = "1" } 38 | sha2 = { version = "0.7" } 39 | subtle = { version = "1" } 40 | # zkp = { version = "0.4", default-features = false } 41 | 42 | [dev-dependencies] 43 | criterion = { version = "0.2" } 44 | 45 | # [replace] 46 | # "zkp:0.4.3" = { git = "https://github.com/isislovecruft/zkp", branch = "fix/stuff" } 47 | 48 | # [[bench]] 49 | # name = "aeonflux-benchmarks" 50 | # harness = false 51 | 52 | [features] 53 | default = [ "std", "nightly", "u64_backend" ] 54 | asm = [ "sha2/asm" ] 55 | # std = [ "curve25519-dalek/std", "zkp/std" ] 56 | # nightly = [ "clear_on_drop/nightly", "curve25519-dalek/nightly", "zkp/nightly", "subtle/nightly" ] 57 | # alloc = [ "curve25519-dalek/alloc", "zkp/alloc" ] 58 | # u32_backend = [ "curve25519-dalek/u32_backend", "zkp/u32_backend" ] 59 | # u64_backend = [ "curve25519-dalek/u64_backend", "zkp/u64_backend" ] 60 | # avx2_backend = [ "curve25519-dalek/avx2_backend", "zkp/avx2_backend" ] 61 | std = [ "curve25519-dalek/std" ] 62 | nightly = [ "clear_on_drop/nightly", "curve25519-dalek/nightly", "subtle/nightly" ] 63 | alloc = [ "curve25519-dalek/alloc" ] 64 | u32_backend = [ "curve25519-dalek/u32_backend" ] 65 | u64_backend = [ "curve25519-dalek/u64_backend" ] 66 | avx2_backend = [ "curve25519-dalek/avx2_backend" ] 67 | -------------------------------------------------------------------------------- /aeonflux/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Signal Foundation, isis agora lovecruft. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /aeonflux/README.md: -------------------------------------------------------------------------------- 1 | 2 | aeonflux 3 | ======== 4 | 5 | Composable, lightweight, fast attribute-based anonymous credentials with 6 | infinite (aeon) rerandomised (flux) presentations using algebraic message 7 | authentication codes (aMACs). 8 | 9 | TODO 10 | ---- 11 | 12 | * Support two-attribute credentials. 13 | * Make a more generalised composition mechanism. 14 | -------------------------------------------------------------------------------- /aeonflux/src/elgamal.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[cfg(not(feature = "std"))] 11 | use core::ops::Add; 12 | 13 | #[cfg(feature = "std")] 14 | use std::ops::Add; 15 | 16 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 17 | use alloc::vec::Vec; 18 | #[cfg(all(not(feature = "alloc"), feature = "std"))] 19 | use std::vec::Vec; 20 | 21 | use clear_on_drop::clear::Clear; 22 | 23 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE; 24 | use curve25519_dalek::ristretto::CompressedRistretto; 25 | use curve25519_dalek::ristretto::RistrettoPoint; 26 | use curve25519_dalek::scalar::Scalar; 27 | 28 | use rand_core::CryptoRng; 29 | use rand_core::RngCore; 30 | 31 | use serde::{self, Serialize, Deserialize, Serializer, Deserializer}; 32 | use serde::de::Visitor; 33 | 34 | use errors::CredentialError; 35 | 36 | pub use nonces::Ephemeral; 37 | 38 | pub const SIZEOF_PUBLIC_KEY: usize = 32; 39 | pub const SIZEOF_SECRET_KEY: usize = 32; 40 | pub const SIZEOF_KEYPAIR: usize = SIZEOF_PUBLIC_KEY + SIZEOF_SECRET_KEY; 41 | pub const SIZEOF_ENCRYPTION: usize = 64; 42 | 43 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 44 | #[repr(C)] 45 | pub struct PublicKey(pub(crate) RistrettoPoint); 46 | 47 | #[derive(Clone, Debug, Default, Eq, PartialEq)] 48 | #[repr(C)] 49 | pub struct SecretKey(pub(crate) Scalar); 50 | 51 | #[derive(Clone, Debug, Eq, PartialEq)] 52 | #[repr(C)] 53 | pub struct Keypair { 54 | pub secret: SecretKey, 55 | pub public: PublicKey, 56 | } 57 | 58 | /// A plaintext elGamal message. 59 | /// 60 | /// ElGamal cryptosystems in the elliptic curve context require a canonical, 61 | /// invertible, isomorphic mapping from messages as scalars to messages as group 62 | /// elements. One such construction is given in "Elliptic Curve Cryptosystems" 63 | /// (1987) by Neal Koblitz. 64 | /// 65 | /// Rather than dealing with mapping scalars to group elements, instead we 66 | /// require that the user save their plaintext while giving the encryption to 67 | /// the credential issuer. Later, rather than decrypt and map back to the 68 | /// original scalar, they simply use the original plaintext. For this reason, 69 | /// we are able to map scalars to group elements by simply multiplying them by 70 | /// the basepoint, which is obviously not invertible but works for the 71 | /// algebraic-MAC-based anonymous credential blind issuance use-case. 72 | pub struct Message(pub(crate) RistrettoPoint); 73 | 74 | impl<'a> From<&'a Scalar> for Message { 75 | fn from(source: &'a Scalar) -> Message { 76 | Message(source * &RISTRETTO_BASEPOINT_TABLE) 77 | } 78 | } 79 | 80 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 81 | pub struct Encryption { 82 | pub commitment: RistrettoPoint, 83 | pub encryption: RistrettoPoint, 84 | } 85 | 86 | impl<'a, 'b> Add<&'b Encryption> for &'a Encryption { 87 | type Output = Encryption; 88 | 89 | fn add(self, other: &'b Encryption) -> Encryption { 90 | Encryption { 91 | commitment: self.commitment + other.commitment, 92 | encryption: self.encryption + other.encryption, 93 | } 94 | } 95 | } 96 | 97 | impl PublicKey { 98 | pub fn from_bytes(bytes: &[u8]) -> Result { 99 | assert!(bytes.len() == 32); 100 | 101 | let mut tmp = [0u8; 32]; 102 | 103 | tmp.copy_from_slice(bytes); 104 | 105 | let point = CompressedRistretto(tmp).decompress()?; 106 | 107 | Ok(PublicKey(point)) 108 | } 109 | 110 | pub fn to_bytes(&self) -> Vec { 111 | let mut v: Vec = Vec::with_capacity(32); 112 | 113 | v.extend(self.0.compress().to_bytes().iter()); 114 | 115 | v 116 | } 117 | } 118 | 119 | impl_serde_with_to_bytes_and_from_bytes!(PublicKey, "A valid byte sequence representing an elgamal::PublicKey"); 120 | 121 | impl PublicKey { 122 | pub fn encrypt(&self, message: &Message, nonce: &Ephemeral) 123 | -> Encryption 124 | { 125 | // The mapping to the point representing the message must be invertible 126 | let commitment: RistrettoPoint = &RISTRETTO_BASEPOINT_TABLE * nonce; 127 | let encryption: RistrettoPoint = &message.0 + (&self.0 * nonce); 128 | 129 | Encryption{ commitment, encryption } 130 | } 131 | } 132 | 133 | impl From for RistrettoPoint { 134 | fn from(public: PublicKey) -> RistrettoPoint { 135 | public.0 136 | } 137 | } 138 | 139 | impl<'a> From<&'a SecretKey> for PublicKey { 140 | fn from(secret: &'a SecretKey) -> PublicKey { 141 | PublicKey(&RISTRETTO_BASEPOINT_TABLE * &secret.0) 142 | } 143 | } 144 | 145 | impl SecretKey { 146 | pub fn from_bytes(bytes: &[u8]) -> Result { 147 | assert!(bytes.len() == 32); 148 | 149 | let mut tmp = [0u8; 32]; 150 | 151 | tmp.copy_from_slice(bytes); 152 | 153 | let s = Scalar::from_canonical_bytes(tmp)?; 154 | 155 | Ok(SecretKey(s)) 156 | } 157 | 158 | pub fn to_bytes(&self) -> Vec { 159 | let mut v: Vec = Vec::with_capacity(32); 160 | 161 | v.extend(self.0.to_bytes().iter()); 162 | 163 | v 164 | } 165 | } 166 | 167 | impl_serde_with_to_bytes_and_from_bytes!(SecretKey, "A valid byte sequence representing an elamal::SecretKey"); 168 | 169 | impl SecretKey { 170 | pub fn generate(csprng: &mut C) -> SecretKey 171 | where 172 | C: CryptoRng + RngCore, 173 | { 174 | SecretKey(Scalar::random(csprng)) 175 | } 176 | 177 | pub fn decrypt(&self, encryption: &Encryption) -> RistrettoPoint { 178 | let secret: RistrettoPoint = &encryption.commitment * &self.0; 179 | 180 | &encryption.encryption - &secret 181 | } 182 | } 183 | 184 | impl From for Scalar { 185 | fn from(secret: SecretKey) -> Scalar { 186 | secret.0 187 | } 188 | } 189 | 190 | /// Overwrite secret key material with null bytes when it goes out of scope. 191 | impl Drop for SecretKey { 192 | fn drop(&mut self) { 193 | self.0.clear(); 194 | } 195 | } 196 | 197 | impl Keypair { 198 | pub fn from_bytes(bytes: &[u8]) -> Result { 199 | assert!(bytes.len() == 64); 200 | 201 | let secret = SecretKey::from_bytes(&bytes[00..32])?; 202 | let public = PublicKey::from_bytes(&bytes[32..64])?; 203 | 204 | Ok(Keypair{ secret, public }) 205 | } 206 | 207 | pub fn to_bytes(&self) -> Vec { 208 | let mut v: Vec = Vec::with_capacity(64); 209 | 210 | v.extend(self.secret.to_bytes()); 211 | v.extend(self.public.to_bytes()); 212 | 213 | v 214 | } 215 | } 216 | 217 | impl_serde_with_to_bytes_and_from_bytes!(Keypair, "A valid byte sequence representing an elgamal::Keypair"); 218 | 219 | impl Keypair { 220 | pub fn generate(csprng: &mut C) -> Keypair 221 | where 222 | C: CryptoRng + RngCore, 223 | { 224 | let secret: SecretKey = SecretKey::generate(csprng); 225 | let public: PublicKey = PublicKey::from(&secret); 226 | 227 | Keypair{ secret, public } 228 | } 229 | 230 | pub fn encrypt(&self, message: &Message, nonce: &Ephemeral) -> Encryption 231 | { 232 | self.public.encrypt(message, nonce) 233 | } 234 | } 235 | 236 | #[cfg(test)] 237 | mod test { 238 | use super::*; 239 | 240 | use rand::thread_rng; 241 | 242 | #[test] 243 | fn roundtrip() { 244 | let mut csprng = thread_rng(); 245 | let nonce = Ephemeral::new(&mut csprng); 246 | let msg = Message(&RISTRETTO_BASEPOINT_TABLE * &nonce); 247 | let keypair = Keypair::generate(&mut csprng); 248 | let enc = keypair.public.encrypt(&msg, &nonce); 249 | 250 | assert!(keypair.secret.decrypt(&enc) == msg.0); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /aeonflux/src/errors.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[cfg(feature = "std")] 11 | use std::fmt; 12 | #[cfg(feature = "std")] 13 | use std::fmt::Display; 14 | #[cfg(feature = "std")] 15 | use std::option::NoneError; 16 | 17 | #[cfg(not(feature = "std"))] 18 | use core::fmt; 19 | #[cfg(not(feature = "std"))] 20 | use core::fmt::Display; 21 | #[cfg(not(feature = "std"))] 22 | use core::option::NoneError; 23 | 24 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 25 | pub enum MacError { 26 | KeypairDeserialisation, 27 | PointDecompressionError, 28 | ScalarFormatError, 29 | /// An error in the length of bytes handed to a constructor. 30 | /// 31 | /// To use this, pass the `length` in bytes which its constructor expects. 32 | MessageLengthError{ length: usize }, 33 | /// The MAC could not be authenticated. 34 | AuthenticationError, 35 | } 36 | 37 | impl Display for MacError { 38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 | match *self { 40 | MacError::KeypairDeserialisation 41 | => write!(f, "Cannot deserialise keypair"), 42 | MacError::PointDecompressionError 43 | => write!(f, "Cannot decompress Ristretto point"), 44 | MacError::ScalarFormatError 45 | => write!(f, "Cannot use scalar with high-bit set"), 46 | MacError::MessageLengthError{ length: l } 47 | => write!(f, "Message must be {} bytes in length", l), 48 | MacError::AuthenticationError 49 | => write!(f, "MAC could not be authenticated"), 50 | } 51 | } 52 | } 53 | 54 | impl ::failure::Fail for MacError {} 55 | 56 | impl From for MacError { 57 | fn from(_source: NoneError) -> MacError { 58 | MacError::PointDecompressionError 59 | } 60 | } 61 | 62 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 63 | pub enum CredentialError { 64 | BadAttribute, 65 | CredentialIssuance, 66 | MacCreation, 67 | MacVerification, 68 | MissingData, 69 | NoIssuerKey, 70 | NoIssuerParameters, 71 | NoSystemParameters, 72 | PointDecompressionError, 73 | ScalarFormatError, 74 | WrongNumberOfAttributes, 75 | WrongNumberOfBytes, 76 | VerificationFailure, 77 | } 78 | 79 | impl fmt::Display for CredentialError { 80 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 81 | match *self { 82 | CredentialError::BadAttribute 83 | => write!(f, "An attribute was unacceptable"), 84 | CredentialError::CredentialIssuance 85 | => write!(f, "Failed to get a credential issued"), 86 | CredentialError::MacCreation 87 | => write!(f, "Could not create a MAC"), 88 | CredentialError::MacVerification 89 | => write!(f, "Could not verify a MAC"), 90 | CredentialError::MissingData 91 | => write!(f, "Some data, such as a key or zkproof, was missing"), 92 | CredentialError::NoIssuerKey 93 | => write!(f, "The issuer was not initialised properly and has no secret key"), 94 | CredentialError::NoIssuerParameters 95 | => write!(f, "The issuer was not initialised properly and has no parameters"), 96 | CredentialError::NoSystemParameters 97 | => write!(f, "The system parameters were not initialised"), 98 | CredentialError::PointDecompressionError 99 | => write!(f, "Cannot decompress Ristretto point"), 100 | CredentialError::ScalarFormatError 101 | => write!(f, "Cannot use scalar with high-bit set"), 102 | CredentialError::WrongNumberOfAttributes 103 | => write!(f, "The credential did not have the correct number of attributes"), 104 | CredentialError::WrongNumberOfBytes 105 | => write!(f, "The credential could not be deserialised because it was not a multiple of 32 bytes"), 106 | CredentialError::VerificationFailure 107 | => write!(f, "The proof could not be verified"), 108 | } 109 | } 110 | } 111 | 112 | impl ::failure::Fail for CredentialError { } 113 | 114 | impl From for CredentialError { 115 | fn from(_source: NoneError) -> CredentialError { 116 | CredentialError::MissingData 117 | } 118 | } 119 | 120 | impl From for CredentialError { 121 | fn from(source: MacError) -> CredentialError { 122 | match source { 123 | MacError::KeypairDeserialisation 124 | => CredentialError::NoIssuerKey, 125 | MacError::PointDecompressionError 126 | => CredentialError::NoIssuerParameters, 127 | MacError::ScalarFormatError 128 | => CredentialError::ScalarFormatError, 129 | MacError::MessageLengthError{ length: _ } 130 | => CredentialError::MacCreation, 131 | MacError::AuthenticationError 132 | => CredentialError::MacVerification, 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /aeonflux/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #![no_std] 11 | 12 | // TODO Get rid of the syntax that uses the nightly-only try_trait. 13 | #![feature(try_trait)] 14 | // We denote group elements with capital and scalars with lowercased names. 15 | #![allow(non_snake_case)] 16 | 17 | #![cfg_attr(any(not(feature = "std"), feature = "alloc"), feature(alloc))] 18 | 19 | #[cfg(feature = "std")] 20 | #[macro_use] 21 | extern crate std; 22 | #[cfg(any(not(feature = "std"), feature = "alloc"))] 23 | extern crate alloc; 24 | 25 | extern crate bincode; 26 | extern crate clear_on_drop; 27 | extern crate curve25519_dalek; 28 | extern crate failure; 29 | extern crate merlin; 30 | extern crate rand; 31 | extern crate rand_core; 32 | extern crate serde; 33 | #[macro_use] 34 | extern crate serde_derive; 35 | extern crate sha2; 36 | extern crate subtle; 37 | 38 | // The macros have to come first. 39 | #[macro_use] 40 | pub mod macros; 41 | 42 | pub mod amacs; 43 | pub mod credential; 44 | pub mod elgamal; 45 | pub mod errors; 46 | pub mod issuer; 47 | pub mod nonces; 48 | pub mod parameters; 49 | pub mod pedersen; 50 | pub mod prelude; 51 | pub mod proofs; 52 | pub mod user; 53 | -------------------------------------------------------------------------------- /aeonflux/src/macros.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[macro_export] 11 | macro_rules! impl_serde_with_to_bytes_and_from_bytes { 12 | ($t:tt, $expecting:expr) => { 13 | impl Serialize for $t { 14 | fn serialize(&self, serializer: S) -> Result 15 | where S: Serializer 16 | { 17 | serializer.serialize_bytes(&self.to_bytes()[..]) 18 | } 19 | } 20 | 21 | impl<'de> Deserialize<'de> for $t { 22 | fn deserialize(deserializer: D) -> Result 23 | where D: Deserializer<'de> 24 | { 25 | struct AeonfluxVisitor; 26 | 27 | impl<'de> Visitor<'de> for AeonfluxVisitor { 28 | type Value = $t; 29 | 30 | fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 31 | formatter.write_str($expecting) 32 | } 33 | 34 | fn visit_bytes(self, v: &[u8]) -> Result<$t, E> 35 | where E: serde::de::Error 36 | { 37 | match $t::from_bytes(v) { 38 | Ok(x) => Ok(x), 39 | Err(_x) => { 40 | #[cfg(feature = "std")] 41 | println!("Error while deserialising {}: {:?}", stringify!($t), _x); 42 | Err(serde::de::Error::invalid_length(v.len(), &self)) 43 | }, 44 | } 45 | } 46 | } 47 | deserializer.deserialize_bytes(AeonfluxVisitor) 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /aeonflux/src/nonces.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[cfg(not(feature = "std"))] 11 | use core::ops::{Neg, Mul, Index}; 12 | 13 | #[cfg(feature = "std")] 14 | use std::ops::{Neg, Mul, Index}; 15 | 16 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 17 | use alloc::vec::Vec; 18 | #[cfg(all(not(feature = "alloc"), feature = "std"))] 19 | use std::vec::Vec; 20 | 21 | use clear_on_drop::clear::Clear; 22 | 23 | use curve25519_dalek::ristretto::RistrettoBasepointTable; 24 | use curve25519_dalek::ristretto::RistrettoPoint; 25 | use curve25519_dalek::scalar::Scalar; 26 | 27 | use rand_core::CryptoRng; 28 | use rand_core::RngCore; 29 | 30 | 31 | /// An ephemeral key or nonce, used in elGamal encryptions and then discarded. 32 | #[derive(Clone, Debug, Default)] 33 | pub struct Ephemeral(Scalar); 34 | 35 | impl From for Ephemeral { 36 | fn from(source: Scalar) -> Ephemeral { 37 | Ephemeral(source) 38 | } 39 | } 40 | 41 | impl Ephemeral { 42 | pub fn new( 43 | csprng: &mut R, 44 | ) -> Ephemeral 45 | where 46 | R: CryptoRng + RngCore 47 | { 48 | Ephemeral(Scalar::random(csprng)) 49 | } 50 | 51 | pub fn to_bytes(&self) -> Vec { 52 | self.0.to_bytes().to_vec() 53 | } 54 | } 55 | 56 | impl<'s, 'e: 's> From<&'e Ephemeral> for &'s Scalar { 57 | fn from(source: &'e Ephemeral) -> &'s Scalar { 58 | &source.0 59 | } 60 | } 61 | 62 | /// Overwrite secret key material with null bytes when it goes out of scope. 63 | impl Drop for Ephemeral { 64 | fn drop(&mut self) { 65 | self.0.clear(); 66 | } 67 | } 68 | 69 | impl<'a, 'b> Mul<&'b RistrettoBasepointTable> for &'a Ephemeral { 70 | type Output = RistrettoPoint; 71 | 72 | fn mul(self, other: &'b RistrettoBasepointTable) -> RistrettoPoint { 73 | &self.0 * other 74 | } 75 | } 76 | 77 | impl<'a, 'b> Mul<&'a Ephemeral> for &'b RistrettoBasepointTable { 78 | type Output = RistrettoPoint; 79 | 80 | fn mul(self, other: &'a Ephemeral) -> RistrettoPoint { 81 | self * &other.0 82 | } 83 | } 84 | 85 | impl<'a, 'b> Mul<&'a Ephemeral> for &'b RistrettoPoint { 86 | type Output = RistrettoPoint; 87 | 88 | fn mul(self, other: &'a Ephemeral) -> RistrettoPoint { 89 | self * &other.0 90 | } 91 | } 92 | 93 | impl<'a, 'b> Mul<&'b RistrettoPoint> for &'a Ephemeral { 94 | type Output = RistrettoPoint; 95 | 96 | fn mul(self, other: &'b RistrettoPoint) -> RistrettoPoint { 97 | &self.0 * other 98 | } 99 | } 100 | 101 | impl<'a, > Mul<&'a Ephemeral> for RistrettoPoint { 102 | type Output = RistrettoPoint; 103 | 104 | fn mul(self, other: &'a Ephemeral) -> RistrettoPoint { 105 | self * &other.0 106 | } 107 | } 108 | 109 | impl<'b> Mul<&'b RistrettoPoint> for Ephemeral { 110 | type Output = RistrettoPoint; 111 | 112 | fn mul(self, other: &'b RistrettoPoint) -> RistrettoPoint { 113 | &self.0 * other 114 | } 115 | } 116 | 117 | impl<'a> Mul for &'a Ephemeral { 118 | type Output = RistrettoPoint; 119 | 120 | fn mul(self, other: RistrettoPoint) -> RistrettoPoint { 121 | &self.0 * other 122 | } 123 | } 124 | 125 | impl<'b> Mul for &'b RistrettoPoint { 126 | type Output = RistrettoPoint; 127 | 128 | fn mul(self, other: Ephemeral) -> RistrettoPoint { 129 | self * &other.0 130 | } 131 | } 132 | 133 | impl Mul for Ephemeral { 134 | type Output = RistrettoPoint; 135 | 136 | fn mul(self, other: RistrettoPoint) -> RistrettoPoint { 137 | &self.0 * other 138 | } 139 | } 140 | 141 | impl Mul for RistrettoPoint { 142 | type Output = RistrettoPoint; 143 | 144 | fn mul(self, other: Ephemeral) -> RistrettoPoint { 145 | self * &other.0 146 | } 147 | } 148 | 149 | impl Neg for Ephemeral { 150 | type Output = Ephemeral; 151 | 152 | fn neg(self) -> Ephemeral { 153 | Ephemeral(-self.0) 154 | } 155 | } 156 | 157 | #[derive(Clone, Debug, Default)] 158 | pub struct Nonces(pub(crate) Vec); 159 | 160 | impl Drop for Nonces { 161 | fn drop(&mut self) { 162 | for x in self.0.iter_mut() { 163 | x.clear(); 164 | } 165 | } 166 | } 167 | 168 | impl Index for Nonces { 169 | type Output = Ephemeral; 170 | 171 | fn index(&self, idx: usize) -> &Ephemeral { 172 | &self.0[idx] 173 | } 174 | } 175 | 176 | impl Nonces { 177 | pub fn new( 178 | csprng: &mut R, 179 | size: usize, 180 | ) -> Nonces 181 | where 182 | R: CryptoRng + RngCore 183 | { 184 | let mut v: Vec = Vec::with_capacity(size); 185 | 186 | for _ in 0..size { 187 | v.push(Ephemeral::new(csprng)); 188 | } 189 | 190 | Nonces(v) 191 | } 192 | 193 | pub fn iter(&self) -> impl Iterator { 194 | self.0.iter() 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /aeonflux/src/parameters.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 11 | use alloc::vec::Vec; 12 | #[cfg(all(not(feature = "alloc"), feature = "std"))] 13 | use std::vec::Vec; 14 | 15 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_COMPRESSED; 16 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; 17 | use curve25519_dalek::ristretto::CompressedRistretto; 18 | use curve25519_dalek::ristretto::RistrettoPoint; 19 | 20 | use serde::{self, Serialize, Deserialize, Serializer, Deserializer}; 21 | use serde::de::Visitor; 22 | 23 | use rand_core::CryptoRng; 24 | use rand_core::RngCore; 25 | 26 | use errors::CredentialError; 27 | 28 | pub const NUMBER_OF_ATTRIBUTES: usize = 1; 29 | pub const SIZEOF_SYSTEM_PARAMETERS: usize = 64; 30 | 31 | /// The `SystemParameters` define the system-wide context in which the anonymous 32 | /// credentials scheme and its proofs are constructed within. 33 | /// 34 | /// They are defined as \\( \( \mathbb{G}, p, G, H \) \\) where: 35 | /// 36 | /// * \\( \mathbb{G} \\) is a group with order \\( p \\), where 37 | /// `p` is a `k`-bit prime (`k = 255` in the case of using the Ristretto255 38 | /// group), 39 | /// * `g` and `h` are generators of `G`, 40 | /// * `log_g(h)` is unknown, that is `h` is chosen as a distinguished basepoint 41 | /// which is orthogonal to `g`. 42 | /// 43 | // 44 | // DOCDOC fix above to use latex 45 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 46 | pub struct SystemParameters { 47 | pub g: RistrettoPoint, 48 | pub h: RistrettoPoint, 49 | } 50 | 51 | impl SystemParameters { 52 | pub fn from_bytes(bytes: &[u8]) -> Result { 53 | if bytes.len() != SIZEOF_SYSTEM_PARAMETERS { 54 | return Err(CredentialError::NoSystemParameters); 55 | } 56 | 57 | let mut g_bytes = [0u8; 32]; 58 | let mut h_bytes = [0u8; 32]; 59 | 60 | g_bytes.copy_from_slice(&bytes[00..32]); 61 | h_bytes.copy_from_slice(&bytes[32..64]); 62 | 63 | let g: RistrettoPoint = match CompressedRistretto(g_bytes).decompress() { 64 | Some(x) => x, 65 | None => { 66 | #[cfg(feature = "std")] 67 | println!("Could not decode G from bytes: {:?}", g_bytes); 68 | return Err(CredentialError::PointDecompressionError); 69 | }, 70 | }; 71 | let h: RistrettoPoint = match CompressedRistretto(h_bytes).decompress() { 72 | Some(x) => x, 73 | None => { 74 | #[cfg(feature = "std")] 75 | println!("Could not decode H from bytes: {:?}", h_bytes); 76 | return Err(CredentialError::PointDecompressionError); 77 | }, 78 | }; 79 | 80 | Ok(SystemParameters { g, h }) 81 | } 82 | 83 | pub fn to_bytes(&self) -> Vec { 84 | let mut v: Vec = Vec::with_capacity(64); 85 | 86 | v.extend(self.g.compress().to_bytes().iter()); 87 | v.extend(self.h.compress().to_bytes().iter()); 88 | 89 | v 90 | } 91 | } 92 | 93 | impl_serde_with_to_bytes_and_from_bytes!(SystemParameters, 94 | "A valid byte sequence representing a SystemParameters"); 95 | 96 | impl SystemParameters { 97 | /// Generate the `SystemParameters` randomly via an RNG. 98 | /// 99 | /// In order to never have a secret scalar in memory for generating the 100 | /// orthogonal basepoint, this method can be used to obtain bytes from the 101 | /// `csprng` and attempt to decompress them into a basepoint. 102 | pub fn hunt_and_peck( 103 | csprng: &mut R, 104 | ) -> SystemParameters 105 | where 106 | R: RngCore + CryptoRng, 107 | { 108 | let mut tmp: [u8; 32] = [0u8; 32]; 109 | let mut H: Option = None; 110 | 111 | while H.is_none() { 112 | csprng.fill_bytes(&mut tmp); 113 | 114 | // Extremely unlikely but we may as well be careful. 115 | if CompressedRistretto(tmp) != RISTRETTO_BASEPOINT_COMPRESSED { 116 | H = CompressedRistretto(tmp).decompress(); 117 | } 118 | } 119 | 120 | SystemParameters { 121 | g: RISTRETTO_BASEPOINT_POINT, 122 | h: H.unwrap(), 123 | } 124 | } 125 | } 126 | 127 | // XXX use hyphae notation 128 | impl From for SystemParameters { 129 | /// Construct new system parameters from a chosen basepoint, `h`. 130 | /// 131 | /// # Inputs 132 | /// 133 | /// * `h`, a generator of the group `G`, which also has generator `g`, where 134 | /// `h` is chosen orthogonally such that `log_g(h)` is unknown. 135 | /// 136 | /// # Returns 137 | /// 138 | /// The `SystemParameters` for an anonymous credential protocol. 139 | fn from(h: RistrettoPoint) -> SystemParameters { 140 | debug_assert!(h != RISTRETTO_BASEPOINT_POINT); 141 | 142 | SystemParameters { 143 | g: RISTRETTO_BASEPOINT_POINT, 144 | h: h, 145 | } 146 | } 147 | } 148 | 149 | impl From<[u8; 32]> for SystemParameters { 150 | /// Construct the `SystemParameters` from a `CompressedRistretto` point. 151 | /// 152 | /// # Inputs 153 | /// 154 | /// * `h`, the compressed form a generator of the group `G`, which also has 155 | /// generator `g`, where h is chosen orthogonally such that `log_g(h)` is 156 | /// unknown. 157 | /// 158 | /// # Panics 159 | /// 160 | /// If `h` cannot be decompressed. 161 | /// 162 | /// # Returns 163 | /// 164 | /// The `SystemParameters` for an anonymous credential protocol. 165 | fn from(h: [u8; 32]) -> SystemParameters { 166 | debug_assert!(CompressedRistretto(h) != RISTRETTO_BASEPOINT_COMPRESSED); 167 | 168 | SystemParameters { 169 | g: RISTRETTO_BASEPOINT_POINT, 170 | h: CompressedRistretto(h).decompress().unwrap(), 171 | } 172 | } 173 | } 174 | 175 | #[cfg(test)] 176 | mod test { 177 | use super::*; 178 | 179 | use rand::thread_rng; 180 | 181 | const H: [u8; 32] = [ 184, 238, 220, 64, 5, 247, 91, 135, 182 | 93, 125, 218, 60, 36, 165, 166, 178, 183 | 118, 188, 77, 27, 133, 146, 193, 133, 184 | 234, 95, 69, 227, 213, 197, 84, 98, ]; 185 | 186 | #[test] 187 | fn system_parameters_serialize_deserialize() { 188 | let system_parameters: SystemParameters = H.into(); 189 | 190 | let serialized = system_parameters.to_bytes(); 191 | let deserialized = SystemParameters::from_bytes(&serialized).unwrap(); 192 | 193 | assert!(system_parameters == deserialized); 194 | assert!(&serialized[32..64] == &H); 195 | } 196 | 197 | #[test] 198 | fn system_parameters_from_bytes() { 199 | let bytes: [u8; 64] = [115, 121, 115, 116, 101, 109, 95, 112, 200 | 97, 114, 97, 109, 101, 116, 101, 114, 201 | 115, 95, 108, 101, 110, 103, 116, 104, 202 | 32, 105, 115, 32, 54, 52, 10, 115, 203 | 111, 109, 101, 32, 115, 104, 105, 116, 204 | 32, 119, 97, 115, 32, 109, 105, 115, 205 | 115, 105, 110, 103, 10, 146, 193, 133, 206 | 234, 95, 69, 227, 213, 197, 84, 98, ]; 207 | let system_parameters = SystemParameters::from_bytes(&bytes); 208 | 209 | assert!(system_parameters.is_err()); 210 | } 211 | 212 | #[test] 213 | fn hunt_and_peck() { 214 | let mut rng = thread_rng(); 215 | 216 | SystemParameters::hunt_and_peck(&mut rng); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /aeonflux/src/pedersen.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[cfg(not(feature = "std"))] 11 | use core::ops::{Mul, SubAssign}; 12 | 13 | #[cfg(feature = "std")] 14 | use std::ops::{Mul, SubAssign}; 15 | 16 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 17 | use alloc::vec::Vec; 18 | #[cfg(all(not(feature = "alloc"), feature = "std"))] 19 | use std::vec::Vec; 20 | 21 | use curve25519_dalek::ristretto::CompressedRistretto; 22 | use curve25519_dalek::ristretto::RistrettoPoint; 23 | use curve25519_dalek::scalar::Scalar; 24 | 25 | use serde::{self, Serialize, Deserialize, Serializer, Deserializer}; 26 | use serde::de::Visitor; 27 | 28 | use errors::CredentialError; 29 | 30 | use nonces::Ephemeral; 31 | 32 | pub const SIZEOF_COMMITMENT: usize = 32; 33 | 34 | /// A Pedersen commitment. 35 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 36 | pub struct Commitment(RistrettoPoint); 37 | 38 | impl From for RistrettoPoint { 39 | fn from(source: Commitment) -> RistrettoPoint { 40 | source.0 41 | } 42 | } 43 | 44 | impl<'a, 'b> Mul<&'b Scalar> for &'a Commitment { 45 | type Output = RistrettoPoint; 46 | 47 | fn mul(self, other: &'b Scalar) -> RistrettoPoint { 48 | self.0 * other 49 | } 50 | } 51 | 52 | impl<'a, 'b> Mul<&'a Commitment> for &'b Scalar { 53 | type Output = RistrettoPoint; 54 | 55 | fn mul(self, other: &'a Commitment) -> RistrettoPoint { 56 | self * other.0 57 | } 58 | } 59 | 60 | impl SubAssign for RistrettoPoint { 61 | fn sub_assign(&mut self, other: Commitment) { 62 | *self -= other.0; 63 | } 64 | } 65 | 66 | impl Commitment { 67 | pub fn from_bytes(bytes: &[u8]) -> Result { 68 | let mut tmp: [u8; 32] = [0u8; 32]; 69 | 70 | tmp.copy_from_slice(&bytes[0..32]); 71 | 72 | Ok(Commitment(CompressedRistretto(tmp).decompress()?)) 73 | } 74 | 75 | pub fn to_bytes(&self) -> Vec { 76 | let mut v: Vec = Vec::with_capacity(32); 77 | 78 | v.extend(self.0.compress().to_bytes().iter()); 79 | 80 | v 81 | } 82 | } 83 | 84 | impl_serde_with_to_bytes_and_from_bytes!(Commitment, 85 | "A valid byte sequence representing a pedersen::Commitment"); 86 | 87 | impl Commitment { 88 | /// Create a Pedersen commitment to some `value` using the specified `nonce` 89 | /// and `basepoint`. 90 | /// 91 | /// # Returns 92 | /// 93 | /// A `Commitment`. 94 | pub fn to(value: &RistrettoPoint, nonce: &Ephemeral, basepoint: &RistrettoPoint) -> Commitment { 95 | Commitment(value + &(nonce * basepoint)) 96 | } 97 | 98 | pub fn open( 99 | &self, 100 | value: &RistrettoPoint, 101 | nonce: &Ephemeral, 102 | basepoint: &RistrettoPoint, 103 | ) -> Result<(), ()> 104 | { 105 | match *value == &self.0 - &(nonce * basepoint) { 106 | true => Ok(()), 107 | false => Err(()), 108 | } 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod test { 114 | use super::*; 115 | 116 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE; 117 | use curve25519_dalek::ristretto::CompressedRistretto; 118 | 119 | use rand::thread_rng; 120 | 121 | pub const H: CompressedRistretto = CompressedRistretto( 122 | [ 76, 178, 52, 156, 167, 219, 63, 134, 123 | 191, 228, 77, 182, 140, 65, 148, 163, 124 | 247, 169, 129, 154, 54, 20, 36, 75, 125 | 89, 50, 60, 243, 104, 44, 214, 50, ]); 126 | 127 | #[test] 128 | fn good_commitment() { 129 | let mut csprng = thread_rng(); 130 | let nonce: Ephemeral = Ephemeral::new(&mut csprng); 131 | let value: RistrettoPoint = &Scalar::random(&mut csprng) * &RISTRETTO_BASEPOINT_TABLE; 132 | let basepoint: RistrettoPoint = H.decompress().unwrap(); 133 | 134 | let cmt: Commitment = Commitment::to(&value, &nonce, &basepoint); 135 | 136 | assert!(cmt.open(&value, &nonce, &basepoint).is_ok()); 137 | } 138 | 139 | #[test] 140 | fn bad_commitment() { 141 | let mut csprng = thread_rng(); 142 | let nonce: Ephemeral = Ephemeral::new(&mut csprng); 143 | let value: RistrettoPoint = &Scalar::random(&mut csprng) * &RISTRETTO_BASEPOINT_TABLE; 144 | let other_value: RistrettoPoint = &Scalar::random(&mut csprng) * &RISTRETTO_BASEPOINT_TABLE; 145 | let basepoint: RistrettoPoint = H.decompress().unwrap(); 146 | 147 | let cmt: Commitment = Commitment::to(&value, &nonce, &basepoint); 148 | 149 | assert!(cmt.open(&other_value, &nonce, &basepoint).is_err()); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /aeonflux/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | pub use amacs::{self}; 11 | pub use elgamal::{self}; 12 | pub use issuer::IssuerParameters; 13 | pub use parameters::SystemParameters; 14 | -------------------------------------------------------------------------------- /aeonflux/src/proofs_macros.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | //! Zero-knowledge proofs. 11 | //! 12 | //! # Note 13 | //! 14 | //! The notation and variable names used throughout this module are that of 15 | //! [LdV'17](https://patternsinthevoid.net/hyphae/hyphae.pdf), not those of 16 | //! [CMZ'13](https://eprint.iacr.org/2013/516.pdf) because the latter was 17 | //! missing signification details of the construction. 18 | 19 | /// A NIPK showing correct non-blinded issuance. 20 | /// 21 | /// # Inputs 22 | /// 23 | /// Secrets: 24 | /// 25 | /// * `x0, x1, x2` are the `Issuer`'s private key material. 26 | /// * `x0_tilde` is a blinding factor for the secret key. 27 | /// * `m1x1` is the message `m1` multiplied by the secret key `x1`. 28 | /// * `m2x2` is the message `m2` multiplied by the secret key `x2`. 29 | /// 30 | /// Publics: 31 | /// 32 | /// * `u` is the aMAC nonce. 33 | /// * `Cx0` is a Pedersen commitment to the secret key `x0`. 34 | /// * `B` and `A` are generators of the group, where `A` is chosen orthogonally 35 | /// such that `log_B(A)` is intractible. 36 | /// * `X1, X2` are the issuer's public key material. 37 | /// * `u_prime` is the aMAC tag. 38 | // 39 | // TODO The "m1x1" is `m1*x1` and is a hack because the zkp crate doesn't 40 | // currently support multiplying to scalars together before multiplying them by 41 | // the public point, so we multiply them before passing them into the 42 | // macro-generated code as an additional secret value (since it depend on x1). 43 | create_nipk!(issuance_revealed, 44 | (x0, x1, x0_tilde, m1x1), 45 | (P, Q, Cx0, B, A, X1) 46 | : 47 | Q = (P * x0 + P * m1x1), 48 | Cx0 = (B * x0 + A * x0_tilde), 49 | X1 = (A * x1) 50 | ); 51 | 52 | /// A NIPK proving that the blinded attributes are valid elGamal encryptions 53 | /// created with the user's public key. 54 | /// 55 | /// # Inputs 56 | /// 57 | /// Secrets: 58 | /// 59 | /// * `d` the `User`'s private elGamal encryption key, 60 | /// * `e0`...`en` the nonces used to form the elGamal encrypted attributes, 61 | /// * `m0`...`mn` the plaintext attributes, 62 | /// 63 | /// Publics: 64 | /// 65 | /// * `B` the basepoint, 66 | /// * `D` the `User`'s public elGamal encryption key, 67 | /// * `encrypted_attribute_0`...`encrypted_attribute_n` the encrypted attributes, 68 | /// 69 | /// # Proof Statements 70 | /// 71 | // DOCDOC 72 | create_nipk!(attributes_blinded, 73 | (d, e0, m0, nonce), 74 | (B, A, D, encrypted_attribute_0_0, encrypted_attribute_0_1) 75 | : 76 | D = (B * d), 77 | encrypted_attribute_0_0 = (B * e0), 78 | encrypted_attribute_0_1 = (B * m0 + D * e0) 79 | ); 80 | 81 | // XXX The T0_0 and T0_1, etc., should be the same points but we need to pass 82 | // them in twice because the zkp macro won't let us pass in AND proofs 83 | // w.r.t. the same value, e.g. 84 | // 85 | // T0 = (X0 * b), T0 = (h * t0), 86 | // T1 = (X1 * b), T1 = (h * t1), 87 | create_nipk!(issuance_blinded, 88 | (x0_tilde, x0, x1, s, b, t0), 89 | (B, A, X0, X1, D, P, T0_0, T0_1, 90 | EQ_commitment, EQ_encryption, 91 | encrypted_attribute_0_0, encrypted_attribute_0_1) 92 | : 93 | X0 = (B * x0 + A * x0_tilde), 94 | X1 = (A * x1), 95 | P = (B * b), 96 | T0_0 = (X0 * b), T0_1 = (A * t0), // XXX the zkp crate doesn't like this, hack around it 97 | EQ_commitment = (B * s + encrypted_attribute_0_0 * t0), 98 | EQ_encryption = (D * s + encrypted_attribute_0_1 * t0 99 | // This part is only if there were revealed attributes: 100 | // + x0 * P + x1m1 * P + x2m2 * P 101 | ) 102 | ); 103 | 104 | create_nipk!(valid_credential, 105 | (m0, z0, minus_zQ), 106 | (B, A, X0, P, V, Cm0) 107 | : 108 | Cm0 = (P * m0 + A * z0), 109 | V = (X0 * z0 + A * minus_zQ) 110 | ); 111 | 112 | /// Prove that the committed attribute in a credential, `Cm0`, is a commitment 113 | /// to the same underlying value as in another commitment, `Cm1`. 114 | create_nipk!(committed_values_equal, 115 | (m0, z0, z1), 116 | (B, A, P, Cm0, Cm1) 117 | : 118 | // ECDLEQ over the two entries to prove equivalence of committed values: 119 | Cm0 = (P * m0 + A * z0), 120 | Cm1 = (A * m0 + B * z1) 121 | ); 122 | -------------------------------------------------------------------------------- /aeonflux/src/user.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | // We denote group elements with capital and scalars with lowercased names. 11 | #![allow(non_snake_case)] 12 | 13 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 14 | use alloc::vec::Vec; 15 | #[cfg(all(not(feature = "alloc"), feature = "std"))] 16 | use std::vec::Vec; 17 | 18 | use curve25519_dalek::ristretto::RistrettoPoint; 19 | use curve25519_dalek::traits::Identity; 20 | 21 | use merlin::Transcript; 22 | 23 | use rand_core::RngCore; 24 | use rand_core::CryptoRng; 25 | 26 | use serde::{self, Serialize, Deserialize, Serializer, Deserializer}; 27 | use serde::de::Visitor; 28 | 29 | use amacs; 30 | use credential::Credential; 31 | use credential::CredentialIssuance; 32 | use credential::CredentialPresentation; 33 | use credential::CredentialRequest; 34 | use credential::RevealedAttribute; 35 | use elgamal; 36 | use errors::CredentialError; 37 | use issuer::IssuerParameters; 38 | use nonces::Ephemeral; 39 | use nonces::Nonces; 40 | use parameters::SystemParameters; 41 | use pedersen; 42 | use proofs::issuance_revealed; 43 | use proofs::valid_credential; 44 | 45 | /// DOCDOC 46 | #[derive(Debug, Eq, PartialEq)] 47 | pub struct User { 48 | pub system_parameters: SystemParameters, 49 | pub issuer_parameters: IssuerParameters, 50 | pub key: Option, 51 | pub credential: Option, 52 | } 53 | 54 | impl User { 55 | pub fn from_bytes(bytes: &[u8]) -> Result { 56 | if bytes.len() != 256 { 57 | return Err(CredentialError::MissingData); 58 | } 59 | 60 | let system_parameters = SystemParameters::from_bytes(&bytes[00..64])?; 61 | let issuer_parameters = IssuerParameters::from_bytes(&bytes[64..96])?; 62 | 63 | let key: Option; 64 | 65 | if &bytes[96..160] == &[0u8; 64][..] { 66 | key = None; 67 | } else { 68 | key = Some(elgamal::Keypair::from_bytes(&bytes[96..160])?); 69 | } 70 | 71 | let credential: Option; 72 | 73 | if &bytes[160..256] == &[0u8; 96][..] { 74 | credential = None; 75 | } else { 76 | credential = Some(Credential::from_bytes(&bytes[160..])?); 77 | } 78 | 79 | Ok(User { 80 | system_parameters, 81 | issuer_parameters, 82 | key, 83 | credential, 84 | }) 85 | } 86 | 87 | pub fn to_bytes(&self) -> Vec { 88 | let mut v: Vec = Vec::new(); 89 | 90 | v.extend(self.system_parameters.to_bytes()); 91 | v.extend(self.issuer_parameters.to_bytes()); 92 | 93 | match self.key { 94 | None => v.extend([0u8; 64].iter()), 95 | Some(ref x) => v.extend(x.to_bytes().iter()), 96 | } 97 | 98 | match self.credential { 99 | None => v.extend([0u8; 96].iter()), 100 | Some(ref x) => v.extend(x.to_bytes().iter()), 101 | } 102 | 103 | v 104 | } 105 | } 106 | 107 | impl_serde_with_to_bytes_and_from_bytes!(User, "A valid byte sequence representing a User"); 108 | 109 | impl User { 110 | /// DOCDOC 111 | pub fn new( 112 | system_parameters: SystemParameters, 113 | issuer_parameters: IssuerParameters, 114 | key: Option, 115 | ) -> User 116 | { 117 | User { 118 | system_parameters: system_parameters, 119 | issuer_parameters: issuer_parameters, 120 | key: key, 121 | credential: None, 122 | } 123 | } 124 | 125 | /// DOCDOC 126 | /// 127 | /// # Returns 128 | /// 129 | pub fn obtain( 130 | &self, 131 | attributes_revealed: Vec, 132 | ) -> CredentialRequest 133 | { 134 | CredentialRequest { 135 | attributes_revealed, 136 | } 137 | } 138 | 139 | /// DOCDOC 140 | pub fn obtain_finish( 141 | &mut self, 142 | issuance: Option<&CredentialIssuance>, 143 | ) -> Result<(), CredentialError> 144 | { 145 | let mut transcript = Transcript::new(b"AEONFLUX ISSUANCE"); 146 | 147 | let issue: &CredentialIssuance = match issuance { 148 | Some(i) => i, 149 | None => return Err(CredentialError::CredentialIssuance), 150 | }; 151 | let X1: RistrettoPoint = match self.issuer_parameters.Xn.get(0) { 152 | None => return Err(CredentialError::NoIssuerParameters), 153 | Some(x) => *x, 154 | }; 155 | 156 | let publics = issuance_revealed::Publics { 157 | P: &issue.credential.mac.nonce, 158 | Q: &issue.credential.mac.mac, 159 | Cx0: &issue.secret_key_commitment.into(), 160 | B: &self.system_parameters.g, 161 | A: &self.system_parameters.h, 162 | X1: &X1, 163 | }; 164 | 165 | if issue.proof.verify(&mut transcript, publics).is_err() { 166 | Err(CredentialError::CredentialIssuance) 167 | } else { 168 | self.credential = Some(issue.credential.clone()); 169 | 170 | Ok(()) 171 | } 172 | } 173 | 174 | // We also pass in the nonces here in order to allow reusing them in 175 | // proofs regarding the committed attributes. 176 | pub fn show( 177 | &self, 178 | nonces: &Nonces, 179 | rng: &mut R, 180 | ) -> Result 181 | where 182 | R: RngCore + CryptoRng, 183 | { 184 | let credential: &Credential = match self.credential { 185 | Some(ref x) => x, 186 | None => return Err(CredentialError::MissingData), 187 | }; 188 | 189 | let mut transcript = Transcript::new(b"AEONFLUX SHOW"); 190 | let mut csprng = transcript.fork_transcript().reseed_from_rng(rng); 191 | 192 | const N_ATTRIBUTES: usize = 1; 193 | 194 | // Rerandomise the aMAC to prevent trivial linkages. 195 | // 196 | // XXX do we want to pass in a merlin transcript instead of using the rng here? 197 | let rerandomized_mac: amacs::Tag = amacs::Rerandomization::new(&mut csprng).apply_to_tag(&credential.mac); 198 | 199 | let A = self.system_parameters.h; 200 | let B = self.system_parameters.g; 201 | let P = rerandomized_mac.nonce; 202 | let Q = rerandomized_mac.mac; 203 | 204 | // Commit to the rerandomised aMAC. 205 | let zQ: Ephemeral = Ephemeral::new(&mut csprng); 206 | let CQ: pedersen::Commitment = pedersen::Commitment::to(&Q, &zQ, &A); 207 | 208 | // Commit to the hidden attributes. 209 | let mut commitments: Vec = Vec::with_capacity(N_ATTRIBUTES); 210 | 211 | for (zi, mi) in nonces.iter().zip(credential.attributes.iter()) { 212 | let Cmi: pedersen::Commitment = pedersen::Commitment::to(&(mi * P), zi, &A); 213 | 214 | commitments.push(Cmi); 215 | } 216 | 217 | // Calculate the error factor. 218 | let mut V: RistrettoPoint = RistrettoPoint::identity(); 219 | 220 | for (index, zi) in nonces.iter().enumerate() { 221 | V += &(zi * self.issuer_parameters.Xn[index]); 222 | } 223 | V -= &zQ * &A; 224 | 225 | let minus_zQ = -zQ; 226 | 227 | let valid_credential_secrets = valid_credential::Secrets { 228 | m0: &credential.attributes[0], 229 | z0: (&nonces[0]).into(), 230 | minus_zQ: (&minus_zQ).into(), 231 | }; 232 | let valid_credential_publics = valid_credential::Publics { 233 | B: &B, 234 | A: &A, 235 | X0: &self.issuer_parameters.Xn[0], 236 | P: &rerandomized_mac.nonce, 237 | V: &V, 238 | Cm0: &commitments[0].into(), 239 | }; 240 | let valid_credential_proof = valid_credential::Proof::create(&mut transcript, 241 | valid_credential_publics, 242 | valid_credential_secrets); 243 | 244 | Ok(CredentialPresentation { 245 | proof: valid_credential_proof, 246 | rerandomized_mac_commitment: CQ, 247 | rerandomized_nonce: rerandomized_mac.nonce, 248 | attributes_revealed: Vec::with_capacity(0), 249 | attributes_blinded: commitments, 250 | }) 251 | } 252 | } 253 | 254 | impl User { 255 | pub fn blind_request( 256 | &mut self, 257 | csprng: &mut C, 258 | ) -> CredentialBlindRequest 259 | where 260 | C: CryptoRng + RngCore, 261 | { 262 | if self.key.is_none() { 263 | self.key = elgamal::Keypair::generate::(&mut csprng); 264 | } 265 | 266 | unimplemented!(); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /build-test/README.md: -------------------------------------------------------------------------------- 1 | 2 | build-test 3 | ============ 4 | 5 | A test for building cryptographic Rust code across iOS, Android, Electron, 6 | and the Signal backend server. 7 | 8 | Usage 9 | ======= 10 | 11 | Install a rust compiler on the build machine which can target the selected platform. 12 | 13 | 14 | Android 15 | --------- 16 | 17 | iOS 18 | ----- 19 | 20 | Electron 21 | ---------- 22 | 23 | rustup target add wasm32-unknown-unknown 24 | cargo build --target wasm32-unknown-unknown 25 | 26 | AWS 27 | ----- 28 | 29 | -------------------------------------------------------------------------------- /build-test/alloc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alloc" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | publish = false 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /build-test/alloc/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | use std::mem::size_of; 11 | use std::mem::size_of_val; 12 | use std::ptr; 13 | use std::slice; 14 | use std::os::raw::c_char; 15 | use std::os::raw::c_void; 16 | 17 | // Use the same allocator as Swift/Objective-C or Java would use. 18 | extern "C" { 19 | fn malloc(size: usize) -> *mut c_void; 20 | } 21 | 22 | pub fn allocate_buffer_for_bytes(bytes: &[u8]) -> *mut c_char { 23 | let size = size_of_val::<[u8]>(bytes); 24 | let bytesize = size_of::(); 25 | 26 | // Avoid integer overflow when adding one to the calculated size: 27 | let size_with_null = match size.checked_add(bytesize) { 28 | Some(n) => n, 29 | None => return ptr::null_mut(), 30 | }; 31 | 32 | let dest = unsafe { malloc(size_with_null) as *mut u8 }; 33 | 34 | if dest.is_null() { 35 | return ptr::null_mut(); 36 | } 37 | 38 | unsafe { 39 | ptr::copy_nonoverlapping(bytes.as_ptr(), dest, size); 40 | } 41 | 42 | // Let slice::from_raw_parts_mut do the pointer arithmetic for us: 43 | let s = unsafe { slice::from_raw_parts_mut(dest, size_with_null) }; 44 | s[size] = 0; // Add a null terminator 45 | 46 | dest as *mut c_char 47 | } 48 | -------------------------------------------------------------------------------- /build-test/aos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aos" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | publish = false 6 | 7 | [lib] 8 | name = "buildtest" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | maths = { version = "0.1.0", path = "../maths", features = ["u64_backend"] } 13 | alloc = { version = "0.1.0", path = "../alloc" } 14 | -------------------------------------------------------------------------------- /build-test/aos/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | extern crate maths; 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | #[test] 15 | fn it_works() { 16 | assert_eq!(2 + 2, 4); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build-test/electron/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "electron" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | publish = false 6 | 7 | [lib] 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | maths = { version = "0.1.0", path = "../maths", features = ["u32_backend"] } 12 | wasm-bindgen = { version = "0.2" } 13 | -------------------------------------------------------------------------------- /build-test/electron/README.md: -------------------------------------------------------------------------------- 1 | 2 | Electron 3 | ========== 4 | 5 | This is test code for determining the complexity of building/running 6 | cryptographic Rust code for Electron clients. 7 | 8 | 9 | Usage 10 | ------- 11 | 12 | Install `rustup` either via brew or via the rustup shell script: 13 | 14 | ```sh 15 | brew install rustup-init 16 | curl https://sh.rustup.rs -sSf | sh 17 | ``` 18 | 19 | Install a nightly Rust compiler and Cargo (Rust's package manager), 20 | and add wasm32 as a build target platform: 21 | 22 | ```sh 23 | rustup install nightly 24 | rustup default nightly 25 | rustup target add wasm32-unknown-unknown --toolchain nightly 26 | cargo +nightly build --target wasm32-unknown-unknown --release 27 | ``` 28 | 29 | This gives us a wasm file at `target/wasm32-unknown-unknown/release/electron.wasm`: 30 | 31 | ```sh 32 | cp target/wasm32-unknown-unknown/release/electron.wasm ./ 33 | ``` 34 | 35 | Now run the `wasm-bindgen` tool on it to generate a new wasm file and 36 | a set of Javascript bindings: 37 | 38 | ```sh 39 | cargo install wasm-bindgen-cli 40 | wasm-bindgen electron.wasm --out-dir site 41 | ``` 42 | 43 | That should create an `electron.js` module in the `site` directory which exports 44 | a Javascript FFI to the `do_things_with_maths()` Rust function in the `maths` 45 | crate, which will call the Javascript `alert()` function with the result of some 46 | cryptographic computations (it should display 32 random-ish looking bytes). 47 | 48 | 49 | Notes 50 | ----- 51 | 52 | I followed roughly 53 | [this post](https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/), 54 | but it even though it's only 3 months old it was pretty out of date 55 | and required me digging around in the proc-macro2 crate and some 56 | compiler internals. 57 | 58 | [This book](https://rustwasm.github.io/wasm-bindgen/introduction.html) 59 | was more helpful, but I ended up looking at both and cross-referencing. 60 | -------------------------------------------------------------------------------- /build-test/electron/electron.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/build-test/electron/electron.wasm -------------------------------------------------------------------------------- /build-test/electron/site/electron.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export function do_things_with_maths(): void; 3 | 4 | -------------------------------------------------------------------------------- /build-test/electron/site/electron.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /build-test/electron/site/electron.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import * as wasm from './electron_bg'; 3 | 4 | const TextDecoder = typeof self === 'object' && self.TextDecoder 5 | ? self.TextDecoder 6 | : require('util').TextDecoder; 7 | 8 | let cachedDecoder = new TextDecoder('utf-8'); 9 | 10 | let cachegetUint8Memory = null; 11 | function getUint8Memory() { 12 | if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { 13 | cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); 14 | } 15 | return cachegetUint8Memory; 16 | } 17 | 18 | function getStringFromWasm(ptr, len) { 19 | return cachedDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); 20 | } 21 | 22 | export function __wbg_alert_94554a01bf7038ef(arg0, arg1) { 23 | let varg0 = getStringFromWasm(arg0, arg1); 24 | alert(varg0); 25 | } 26 | /** 27 | * @returns {void} 28 | */ 29 | export function do_things_with_maths() { 30 | return wasm.do_things_with_maths(); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /build-test/electron/site/electron_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/build-test/electron/site/electron_bg.wasm -------------------------------------------------------------------------------- /build-test/electron/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #![feature(custom_attribute)] 11 | // #![feature(proc_macro)] // Required for nightly rustc<1.29 12 | 13 | extern crate maths; 14 | extern crate wasm_bindgen; 15 | 16 | use wasm_bindgen::prelude::*; 17 | 18 | #[wasm_bindgen] 19 | extern { 20 | /// Call the JS alert function from Rust. 21 | fn alert(s: &str); 22 | } 23 | 24 | #[wasm_bindgen] 25 | pub fn do_things_with_maths() { 26 | alert(&format!("{:?}", maths::do_things_with_maths())); 27 | } 28 | -------------------------------------------------------------------------------- /build-test/ios/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ios" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | publish = false 6 | 7 | [lib] 8 | name = "buildtest" 9 | crate-type = ["staticlib"] 10 | 11 | [dependencies] 12 | maths = { version = "0.1.0", path = "../maths", features = ["u64_backend"] } 13 | alloc = { version = "0.1.0", path = "../alloc" } 14 | -------------------------------------------------------------------------------- /build-test/ios/README.md: -------------------------------------------------------------------------------- 1 | 2 | iOS 3 | ===== 4 | 5 | This is test code for determining the complexity of building/running 6 | cryptographic Rust code for iOS clients. 7 | 8 | 9 | Usage 10 | ------- 11 | 12 | Install Xcode build tools: 13 | 14 | ```sh 15 | xcode-select --install 16 | ``` 17 | 18 | Install `rustup` either via brew or via the rustup shell script: 19 | 20 | ```sh 21 | brew install rustup-init && rustup-init 22 | curl https://sh.rustup.rs -sSf | sh 23 | ``` 24 | 25 | Add the iOS architectures to rustup so we can use them during cross 26 | compilation: 27 | 28 | ```sh 29 | rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios 30 | ``` 31 | 32 | Use cargo (Rust's package manager) to install `cargo-lipo`. This is a 33 | cargo subcommand which automatically creates a universal library for 34 | use with iOS. Without this crate, cross compiling Rust to work on iOS 35 | is infinitely harder. 36 | 37 | ```sh 38 | cargo install cargo-lipo 39 | ``` 40 | 41 | Now build with lipo (the `DEVELOPER_DIR` setting is a 42 | [workaround for a known rustc issue](https://github.com/rust-lang/rust/issues/36156#issuecomment-373201676), 43 | and the `env` part is specific to fish so if using bash or sh leave 44 | the `env` part out): 45 | 46 | ```sh 47 | env DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer cargo lipo --release 48 | ``` 49 | 50 | (The other workaround, which permanently changes Xcode's active developer 51 | directory, is `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`. 52 | I'm not an iOS developer so maybe that's normal, but it seemed rude to permanently 53 | change a setting just to build some measly Rust code.) 54 | 55 | There should now be a universal iOS library at `target/universal/release/libbuildtest.a`. 56 | 57 | This can be linked into an existing Xcode project by navigating the project 58 | settings to `General > Linked Frameworks and Libraries` and clicking the `+` 59 | button. You'll also need to link in the `libresolv.tbd` framework (search for 60 | it in the list of libraries/frameworks which appear after clicking the `+`). 61 | 62 | I did somewhat crazy things with raw pointers and allocators on the Rust side 63 | (cf. `allocate_buffer_for_bytes`) to be able to pass an actual pointer to Swift 64 | in a manner that Swift's garbage collector should be able to safely `free()` it 65 | later, but we should probably check for memory leaks to ensure it's actually 66 | getting freed (and hopefully not getting freed early and/or twice). 67 | 68 | See the `xcode` directory for an example iOS app which calls Rust from Swift code. 69 | 70 | Notes 71 | ----- 72 | 73 | I followed roughly 74 | [this post](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html). 75 | 76 | There's currently 77 | [a bug](https://github.com/rust-lang/rust/issues/36156#issuecomment-330971277) 78 | in rustc that prevents building for iOS from anything but macOS. This 79 | should be too much of an issue since we also need to use Xcode, which 80 | only works on macOS anyway. 81 | -------------------------------------------------------------------------------- /build-test/ios/libbuildtest.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/build-test/ios/libbuildtest.a -------------------------------------------------------------------------------- /build-test/ios/src/buildtest.h: -------------------------------------------------------------------------------- 1 | // This file is part of groupzk. 2 | // Copyright (c) 2018 Signal Foundation 3 | // See LICENSE for licensing information. 4 | // 5 | // Authors: 6 | // - isis agora lovecruft 7 | 8 | #include 9 | 10 | const char* do_things_with_maths(); 11 | -------------------------------------------------------------------------------- /build-test/ios/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | extern crate allocator; 11 | extern crate maths; 12 | 13 | use std::os::raw::c_char; 14 | 15 | use allocator::allocate_buffer_for_bytes; 16 | 17 | #[no_mangle] 18 | pub extern "C" fn do_things_with_maths() -> *mut c_char { 19 | let point = maths::do_things_with_maths(); 20 | 21 | // We can't just use a core::ffi::CString here because the compressed point 22 | // could have null bytes in it, hence the insane allocation utility 23 | // functions above. 24 | // 25 | // The plus side, however, is that we can safely free these bytes in Swift or 26 | // Objective-C (unlike a CString). 27 | allocate_buffer_for_bytes(point.as_bytes()) 28 | } 29 | -------------------------------------------------------------------------------- /build-test/ios/xcode/BuildTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BuildTest.swift 3 | // 4 | // 5 | // Created by isis on 8/28/18. 6 | // 7 | 8 | import Foundation 9 | 10 | class DoThingsWithMaths { 11 | func doThings() -> Data { 12 | let result = do_things_with_maths() 13 | let swift_result = Data.init(bytes: result!, count: 32) 14 | 15 | // free(result) 16 | 17 | return swift_result 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest.xcodeproj/project.xcworkspace/xcuserdata/isis.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/build-test/ios/xcode/RustBuildTest.xcodeproj/project.xcworkspace/xcuserdata/isis.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest.xcodeproj/xcuserdata/isis.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RustBuildTest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RustBuildTest 4 | // 5 | // Created by isis on 8/28/18. 6 | // Copyright © 2018 Signal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIRequiresFullScreen 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTest/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RustBuildTest 4 | // 5 | // Created by isis on 8/28/18. 6 | // Copyright © 2018 Signal. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var textBox: UITextView! 14 | @IBOutlet weak var buttonHandler: UIButton! 15 | @IBAction func buttonAction(_ sender: Any) { 16 | self.doMaths() 17 | } 18 | 19 | func doMaths() { 20 | let rustyStuff = DoThingsWithMaths() 21 | let point = rustyStuff.doThings() 22 | 23 | let encoded = point.map{ String(format: "0x%02X ", $0) }.joined() 24 | 25 | print("The calculated Ristretto point was:\n\(encoded)") 26 | 27 | self.textBox.text = "The calculated Ristretto point was:\n\n\(encoded)" 28 | } 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | 33 | self.buttonHandler.layer.cornerRadius = 10 34 | self.buttonHandler.clipsToBounds = true 35 | 36 | self.textBox.font = UIFont(name: "Courier", size: 14) 37 | 38 | self.doMaths() 39 | } 40 | 41 | override func didReceiveMemoryWarning() { 42 | super.didReceiveMemoryWarning() 43 | // Dispose of any resources that can be recreated. 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTestTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTestTests/RustBuildTestTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RustBuildTestTests.swift 3 | // RustBuildTestTests 4 | // 5 | // Created by isis on 8/28/18. 6 | // Copyright © 2018 Signal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import RustBuildTest 11 | 12 | class RustBuildTestTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTestUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /build-test/ios/xcode/RustBuildTestUITests/RustBuildTestUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RustBuildTestUITests.swift 3 | // RustBuildTestUITests 4 | // 5 | // Created by isis on 8/28/18. 6 | // Copyright © 2018 Signal. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class RustBuildTestUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /build-test/ios/xcode/buildtest-bridging-header.h: -------------------------------------------------------------------------------- 1 | // 2 | // buildtest-bridging-header.h 3 | // 4 | // 5 | // Created by isis on 8/28/18. 6 | // 7 | 8 | #ifndef buildtest_bridging_header_h 9 | #define buildtest_bridging_header_h 10 | 11 | #import "buildtest.h" 12 | 13 | #endif /* buildtest_bridging_header_h */ 14 | -------------------------------------------------------------------------------- /build-test/ios/xcode/buildtest.h: -------------------------------------------------------------------------------- 1 | // This file is part of groupzk. 2 | // Copyright (c) 2018 Signal Foundation 3 | // See LICENSE for licensing information. 4 | // 5 | // Authors: 6 | // - isis agora lovecruft 7 | 8 | #include 9 | 10 | const char* do_things_with_maths(); 11 | -------------------------------------------------------------------------------- /build-test/ios/xcode/libbuildtest.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/build-test/ios/xcode/libbuildtest.a -------------------------------------------------------------------------------- /build-test/maths/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "maths" 3 | version = "0.1.0" 4 | authors = ["isis lovecruft "] 5 | readme = "README.md" 6 | license = "BSD-3-Clause" 7 | repository = "https://github.com/signalapp/groupzk" 8 | categories = ["no-std"] 9 | description = "A small function to test building cryptographic Rust code on various platforms" 10 | exclude = [ 11 | "**/.gitignore", 12 | ".gitignore", 13 | ".travis.yml", 14 | ] 15 | 16 | [dependencies] 17 | curve25519-dalek = { version = "0.19", default-features = false } 18 | rand = { version = "0.5" } 19 | 20 | [features] 21 | default = [ ] 22 | u32_backend = [ "curve25519-dalek/u32_backend" ] 23 | u64_backend = [ "curve25519-dalek/u64_backend" ] 24 | avx2_backend = [ "curve25519-dalek/avx2_backend" ] 25 | -------------------------------------------------------------------------------- /build-test/maths/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | extern crate curve25519_dalek; 11 | extern crate rand; 12 | 13 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE; 14 | use curve25519_dalek::scalar::Scalar; 15 | use curve25519_dalek::ristretto::CompressedRistretto; 16 | use curve25519_dalek::ristretto::RistrettoPoint; 17 | 18 | use rand::thread_rng; 19 | 20 | pub fn do_things_with_maths() -> CompressedRistretto { 21 | let mut csprng = thread_rng(); 22 | let phone_number: Scalar = 4155551234u64.into(); 23 | let blinding: Scalar = Scalar::random(&mut csprng); 24 | let nonce: RistrettoPoint = &RISTRETTO_BASEPOINT_TABLE * &Scalar::random(&mut csprng); 25 | let exponent: Scalar = Scalar::one() + (blinding * phone_number); 26 | 27 | (nonce * exponent).compress() 28 | } 29 | 30 | #[cfg(test)] 31 | mod test { 32 | use super::*; 33 | 34 | use curve25519_dalek::traits::Identity; 35 | 36 | #[test] 37 | fn test_do_things_with_maths() { 38 | let x: CompressedRistretto = do_things_with_maths(); 39 | let id: CompressedRistretto = RistrettoPoint::identity().compress(); 40 | 41 | assert!(x != id); 42 | 43 | println!("Hooray, it works! You did some maths and made a thing:\n{:?}", x); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /credential.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint credential.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "credential" 11 | s.version = "0.0.3" 12 | s.summary = "A Swift FFI API for working with the Rust signal-credential crate." 13 | s.homepage = "https://signal.org/" 14 | s.license = "MIT (example)" 15 | s.license = { :type => "BSD", :file => "LICENSE" } 16 | s.authors = { "isis lovecruft" => "isis@patternsinthevoid.net" } 17 | s.source = { :git => "https://github.com/signalapp/groupzk.git", :tag => "swift-credential-#{s.version}" } 18 | s.source_files = "swift/Credential/**/*.{h,swift}", "swift/Credential/*credential*.{h,a}" 19 | 20 | s.exclude_files = "aeonflux/**/*", "build-test/**/*", "java/**/*", "jni/**/*", "ffi/**/*", "signal-credential/**/*", "wasm/**/*", "zkp-expand/**/*" 21 | 22 | s.ios.deployment_target = "8.0" 23 | s.osx.deployment_target = "10.10" 24 | 25 | s.ios.vendored_library = "swift/libcredential.a" 26 | s.osx.vendored_library = "swift/libcredential.a" 27 | 28 | s.private_header_files = "swift/Credential/credential.h" 29 | 30 | # s.library = "credential" 31 | # s.libraries = "iconv", "xml2" 32 | s.libraries = "resolv" 33 | 34 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 35 | # 36 | # If your library depends on compiler flags you can set them in the xcconfig hash 37 | # where they will only apply to your library. If you depend on other Podspecs 38 | # you can include multiple dependencies to ensure it works. 39 | 40 | # s.requires_arc = true 41 | 42 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 43 | # s.dependency "JSONKit", "~> 1.4" 44 | 45 | end 46 | -------------------------------------------------------------------------------- /ffi/BUILD-ANDROID.md: -------------------------------------------------------------------------------- 1 | 2 | AOS 3 | ===== 4 | 5 | This is test code for determining the complexity of building/running 6 | cryptographic Rust code for AOS clients. 7 | 8 | 9 | Usage 10 | ------- 11 | 12 | **NOTE**: I haven't actually done any of the following steps, but I 13 | believe the following *should* work. Ping me if you run into 14 | trouble. —isis 15 | 16 | Install `rustup` either via the rustup shell script: 17 | 18 | ```sh 19 | curl https://sh.rustup.rs -sSf | sh 20 | ``` 21 | 22 | Add the Android architectures to rustup so we can use them during cross 23 | compilation: 24 | 25 | ```sh 26 | rustup target add aarch64-linux-android arm-linux-androideabi armv7-linux-androideabi i686-linux-android x86_64-linux-android 27 | ``` 28 | 29 | Get Android NDK and set up our Cargo (Rust's package manager) config: 30 | 31 | ```sh 32 | sdkmanager --verbose ndk-bundle 33 | ./create-ndk-standalone.sh 34 | ``` 35 | 36 | Build for Android architectures: 37 | 38 | ```sh 39 | cargo build --target armv7-linux-androideabi --release 40 | cargo build --target arm-linux-androideabi --release 41 | cargo build --target x86_64-linux-android --release 42 | ``` 43 | 44 | Copy or link the `*.so` into the corresponding `Signal-Android/libs` directory: 45 | 46 | Copy from Rust | Copy to Android 47 | ---|--- 48 | `target/armv7-linux-androideabi/release/lib???.so` | `libs/armeabi-v7a/lib???.so` 49 | `target/arm-linux-androideabi/release/lib???.so` | `libs/armeabi/lib???.so` 50 | `target/x86_64-linux-android/release/lib???.so` | `libs/x86/lib???.so` 51 | 52 | TODO 53 | ---- 54 | 55 | * Are all AOS targets 64 bit? Should we build with the 32-bit code 56 | just in case? (The 64 bit code is roughly twice as fast.) 57 | 58 | Notes 59 | ----- 60 | 61 | I followed roughly the Android portion of 62 | [these directions](https://github.com/kennytm/rust-ios-android). 63 | -------------------------------------------------------------------------------- /ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffi" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | description = "C, Java JNI, and JS/Wasm FFI APIs for Signal's anonymous credentials" 6 | publish = false 7 | 8 | [lib] 9 | name = "credential" 10 | crate-type = [ "staticlib", "cdylib" ] 11 | 12 | [dependencies] 13 | libc = { version = "0.2", default-features = false } 14 | rand = { version = "0.5", default-features = false } 15 | signal-credential = { version = "*", path = "../signal-credential", default-features = false } 16 | 17 | [features] 18 | default = [ "nightly", "alloc" ] 19 | alloc = [ "signal-credential/alloc" ] 20 | std = [ "signal-credential/std" ] 21 | nightly = [ "signal-credential/nightly" ] 22 | u32_backend = [ "signal-credential/u32_backend" ] 23 | u64_backend = [ "signal-credential/u64_backend" ] 24 | avx2_backend = [ "signal-credential/avx2_backend" ] 25 | -------------------------------------------------------------------------------- /ffi/Cargo.toml.template: -------------------------------------------------------------------------------- 1 | [target.aarch64-linux-android] 2 | ar = "$PWD/NDK/arm64/bin/aarch64-linux-android-ar" 3 | linker = "$PWD/NDK/arm64/bin/aarch64-linux-android-clang" 4 | 5 | [target.armv7-linux-androideabi] 6 | ar = "$PWD/NDK/arm/bin/arm-linux-androideabi-ar" 7 | linker = "$PWD/NDK/arm/bin/arm-linux-androideabi-clang" 8 | 9 | [target.i686-linux-android] 10 | ar = "$PWD/NDK/x86/bin/i686-linux-android-ar" 11 | linker = "$PWD/NDK/x86/bin/i686-linux-android-clang" 12 | -------------------------------------------------------------------------------- /ffi/Makefile: -------------------------------------------------------------------------------- 1 | 2 | clean-android: 3 | -rm -rf .cargo 4 | -rm -rf NDK 5 | cargo clean 6 | 7 | android: 8 | cargo build --features=u32_backend --target armv7-linux-androideabi --release 9 | mkdir -p ../jni/Credential/rust/src/main/jniLibs/armeabi-v7a/ 10 | cp target/armv7-linux-androideabi/release/libcredential.so ../jni/Credential/rust/src/main/jniLibs/armeabi-v7a/ 11 | -cargo build --features=u32_backend --target arm-linux-androideabi --release 12 | -mkdir -p ../jni/Credential/rust/src/main/jniLibs/armeabi/ 13 | -cp target/arm-linux-androideabi/release/libcredential.so ../jni/Credential/rust/src/main/jniLibs/armeabi/ 14 | -cargo build --features=u64_backend --target x86_64-linux-android --release 15 | -mkdir -p ../jni/Credential/rust/src/main/jniLibs/x86_64/ 16 | -cp target/x86_64-linux-android/release/libcredential.so ../jni/Credential/rust/src/main/jniLibs/x86_64/ 17 | cargo build --features=u64_backend --target aarch64-linux-android --release 18 | mkdir -p ../jni/Credential/rust/src/main/jniLibs/arm64-v8a/ 19 | cp target/aarch64-linux-android/release/libcredential.so ../jni/Credential/rust/src/main/jniLibs/arm64-v8a/ 20 | cargo build --features=u32_backend --target i686-linux-android --release 21 | mkdir -p ../jni/Credential/rust/src/main/jniLibs/x86/ 22 | cp target/i686-linux-android/release/libcredential.so ../jni/Credential/rust/src/main/jniLibs/x86/ 23 | 24 | clean-ios: 25 | cargo clean 26 | 27 | override platform=$(shell uname -s) 28 | 29 | ios: 30 | ifneq ($(platform),Darwin) 31 | $(error WARNING: Running `make ios` only works on MacOS machines) 32 | endif 33 | ifndef SIGNAL_CREDENTIAL_IOS_DIR 34 | $(error Please set the SIGNAL_CREDENTIAL_IOS_DIR environment variable to the output directory for compiled library and header files, e.g. .../signal-credential-swift/Credential/libcredential) 35 | endif 36 | cargo lipo --release --features=u64_backend 37 | cp target/universal/release/libcredential.a $(SIGNAL_CREDENTIAL_IOS_DIR)/libcredential-ios.a 38 | cp src/include/credential.h $(SIGNAL_CREDENTIAL_IOS_DIR) 39 | -------------------------------------------------------------------------------- /ffi/README.md: -------------------------------------------------------------------------------- 1 | 2 | C-like FFI for signal-credential 3 | ================================== 4 | 5 | iOS 6 | ----- 7 | 8 | Install Xcode build tools: 9 | 10 | ```sh 11 | xcode-select --install 12 | ``` 13 | 14 | Install `rustup` either via brew or via the rustup shell script: 15 | 16 | ```sh 17 | brew install rustup-init && rustup-init 18 | curl https://sh.rustup.rs -sSf | sh 19 | ``` 20 | 21 | Add the iOS architectures to rustup so we can use them during cross 22 | compilation: 23 | 24 | ```sh 25 | rustup target add aarch64-apple-ios armv7-apple-ios armv7s-apple-ios x86_64-apple-ios i386-apple-ios 26 | ``` 27 | 28 | Use cargo (Rust's package manager) to install `cargo-lipo`. This is a 29 | cargo subcommand which automatically creates a universal library for 30 | use with iOS. Without this crate, cross compiling Rust to work on iOS 31 | is infinitely harder. 32 | 33 | ```sh 34 | cargo install cargo-lipo 35 | ``` 36 | 37 | Now build with lipo (the `DEVELOPER_DIR` setting is a 38 | [workaround for a known rustc issue](https://github.com/rust-lang/rust/issues/36156#issuecomment-373201676)): 39 | 40 | ```sh 41 | DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer \ 42 | SIGNAL_CREDENTIAL_IOS_DIR=.../path/to/signal-credential-ios/Credential/libcredential/ \ 43 | make ios 44 | ``` 45 | 46 | (The other workaround, which permanently changes Xcode's active developer 47 | directory, is `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`. 48 | I'm not an iOS developer so maybe that's normal, but it seemed rude to permanently 49 | change a setting just to build some measly Rust code.) 50 | 51 | There should now be a universal iOS library at 52 | `$SIGNAL_CREDENTIAL_IOS_DIR/libcredential-ios.a` and a header at 53 | `$SIGNAL_CREDENTIAL_IOS_DIR/credential.h`. 54 | -------------------------------------------------------------------------------- /ffi/create-ndk-standalone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | if [ -d NDK ]; then 6 | printf '\033[33;1mStandalone NDK already exists... Delete the NDK folder to make a new one.\033[0m\n\n' 7 | printf ' $ rm -rf NDK\n' 8 | exit 0 9 | fi 10 | 11 | if [ ! -d "${ANDROID_SDK_ROOT-}" ]; then 12 | ANDROID_SDK_ROOT="$HOME/Android/Sdk" 13 | fi 14 | if [ ! -d "${ANDROID_HOME-}" ]; then 15 | ANDROID_HOME="$ANDROID_SDK_ROOT" 16 | fi 17 | if [ ! -d "${ANDROID_NDK_HOME-}" ]; then 18 | ANDROID_NDK_HOME="$ANDROID_HOME/ndk-bundle" 19 | fi 20 | MAKER="${ANDROID_NDK_HOME}/build/tools/make_standalone_toolchain.py" 21 | 22 | if [ -x "$MAKER" ]; then 23 | echo 'Creating standalone NDK...' 24 | else 25 | printf '\033[91;1mPlease install Android NDK!\033[0m\n\n' 26 | printf ' $ sdkmanager ndk-bundle\n\n' 27 | printf "\033[33;1mnote\033[0m: file \033[34;4m$MAKER\033[0m not found.\n" 28 | printf 'If you have installed the NDK in non-standard location, please define the \033[1m$ANDROID_NDK_HOME\033[0m variable.\n' 29 | exit 1 30 | fi 31 | 32 | mkdir NDK 33 | 34 | create_ndk() { 35 | echo "($1)..." 36 | "$MAKER" --api "$2" --arch "$1" --install-dir "NDK/$1" 37 | } 38 | 39 | create_ndk arm64 21 40 | create_ndk arm 16 41 | create_ndk x86 16 42 | 43 | echo 'Updating cargo-config.toml...' 44 | 45 | mkdir .cargo 46 | sed 's|$PWD|'"${PWD}"'|g' Cargo.toml.template >> ./.cargo/config 47 | -------------------------------------------------------------------------------- /ffi/ffi-api.md: -------------------------------------------------------------------------------- 1 | common api 2 | ---------- 3 | 4 | system_parameters_create(seed) -> system_parameters; 5 | 6 | issuer api 7 | ---------- 8 | 9 | issuer_create(system_parameters, seed) -> amacs_keypair; 10 | issuer_new(system_parameters, amacs_keypair) -> issuer; 11 | issuer_get_issuer_parameters(issuer) -> issuer_parameters; 12 | issuer_issue(issuer, phone_number, seed) -> issuance; 13 | 14 | user api 15 | -------- 16 | 17 | user_obtain_finish(phone_number, system_parameters, issuer_parameters, issuance) -> user; 18 | user_show(user, commitment_and_opening, seed) -> presentation; 19 | user_create_roster_entry_commitment(phone_number, system_parametes, seed) -> commitment_and_opening; 20 | user_open_roster_entry_commitment(commitment_and_opening, system_parameters) -> bool; 21 | -------------------------------------------------------------------------------- /ffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #![no_std] 11 | #![allow(non_snake_case)] 12 | #![cfg_attr(all(not(feature = "std"), feature = "alloc"), feature(alloc))] 13 | 14 | #[cfg(feature = "std")] 15 | #[macro_use] 16 | extern crate std; 17 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 18 | extern crate alloc; 19 | 20 | extern crate libc; 21 | extern crate rand; 22 | extern crate signal_credential; 23 | 24 | // The macros have to come first. 25 | #[macro_use] 26 | pub mod macros; 27 | 28 | pub mod c; 29 | -------------------------------------------------------------------------------- /ffi/src/macros.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[macro_export] 11 | macro_rules! slice_to_len_and_ptr { 12 | ($x:expr) => {{ 13 | let x: &[u8] = $x; 14 | 15 | buf_t { 16 | len: x.len() as uint64_t, 17 | ptr: x.as_ptr() as *const uint8_t, 18 | } 19 | }} 20 | } 21 | 22 | #[macro_export] 23 | macro_rules! zero_len_and_ptr { 24 | () => { 25 | slice_to_len_and_ptr!(&[]) 26 | } 27 | } 28 | 29 | #[macro_export] 30 | macro_rules! len_and_ptr_to_slice { 31 | ($len:expr, $ptr:ident) => {{ 32 | if $ptr.is_null() || $len == 0 { 33 | return zero_len_and_ptr!(); 34 | } else { 35 | unsafe { slice::from_raw_parts($ptr, $len as size_t) } // XXX dangerous downcast 36 | } 37 | }} 38 | } 39 | 40 | #[macro_export] 41 | macro_rules! ok_or_return { 42 | ($expr:expr) => { 43 | match $expr { 44 | Ok(x) => x, 45 | Err(_x) => { 46 | #[cfg(feature = "std")] 47 | println!("{:?}", _x); 48 | return zero_len_and_ptr!(); 49 | }, 50 | }; 51 | } 52 | } 53 | 54 | #[macro_export] 55 | macro_rules! csprng_from_seed { 56 | ($seed:ident) => {{ 57 | let seed_array: [u8; LENGTH_SEED] = ok_or_return!(uint8_to_array!($seed, LENGTH_SEED)); 58 | 59 | SignalRng::from_seed(seed_array) 60 | }} 61 | } 62 | 63 | #[macro_export] 64 | macro_rules! uint8_to_array { 65 | ($ptr:ident, $array_length:expr) => {{ 66 | if $ptr.is_null() || $array_length == 0 { 67 | Err(()) 68 | } else { 69 | let bytes: &[u8] = unsafe { slice::from_raw_parts($ptr, $array_length as size_t) }; 70 | 71 | if bytes.len() != $array_length { 72 | Err(()) 73 | } else { 74 | let mut array: [u8; $array_length] = [0u8; $array_length]; 75 | 76 | // This will panic if the bytes.len() isn't equal to the array_length, 77 | // hence the explicit double-checks on the lengths above. 78 | array.copy_from_slice(bytes); 79 | 80 | Ok(array) 81 | } 82 | } 83 | }} 84 | } 85 | 86 | #[macro_export] 87 | macro_rules! deserialize_or_return { 88 | ($t:tt, $len:expr, $ptr:ident) => {{ 89 | let bytes: &[u8] = len_and_ptr_to_slice!($len, $ptr); 90 | 91 | ok_or_return!($t::from_bytes(bytes)) 92 | }} 93 | } 94 | 95 | #[macro_export] 96 | macro_rules! serialize_or_return { 97 | ($t:expr) => {{ 98 | $t.to_bytes() 99 | }} 100 | } 101 | 102 | // The following code was shamelessly stolen from the libc crate. 103 | 104 | /// A macro for defining #[cfg] if-else statements. 105 | /// 106 | /// This is similar to the `if/elif` C preprocessor macro by allowing definition 107 | /// of a cascade of `#[cfg]` cases, emitting the implementation which matches 108 | /// first. 109 | /// 110 | /// This allows you to conveniently provide a long list #[cfg]'d blocks of code 111 | /// without having to rewrite each clause multiple times. 112 | #[macro_export] 113 | macro_rules! cfg_if { 114 | ($( 115 | if #[cfg($($meta:meta),*)] { $($it:item)* } 116 | ) else * else { 117 | $($it2:item)* 118 | }) => { 119 | __cfg_if_items! { 120 | () ; 121 | $( ( ($($meta),*) ($($it)*) ), )* 122 | ( () ($($it2)*) ), 123 | } 124 | } 125 | } 126 | 127 | macro_rules! __cfg_if_items { 128 | (($($not:meta,)*) ; ) => {}; 129 | (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { 130 | __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* } 131 | __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } 132 | } 133 | } 134 | 135 | macro_rules! __cfg_if_apply { 136 | ($m:meta, $($it:item)*) => { 137 | $(#[$m] $it)* 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /ffi/target/release/libcredential.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/ffi/target/release/libcredential.a -------------------------------------------------------------------------------- /ffi/target/release/libcredential.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/ffi/target/release/libcredential.so -------------------------------------------------------------------------------- /ffi/target/universal/release/libcredential.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/ffi/target/universal/release/libcredential.a -------------------------------------------------------------------------------- /java/src/main/java/org/signal/credential/credential.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/java/src/main/java/org/signal/credential/credential.java -------------------------------------------------------------------------------- /java/src/main/java/org/signal/credential/util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Open Whisper Systems 3 | * Copyright (C) 2018 Signal 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package org.signal.credential.util; 20 | 21 | import java.io.File; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.io.OutputStream; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | 29 | public class NativeUtils { 30 | 31 | public static File extractNativeResource(String resource) throws IOException { 32 | File tempFile = Files.createTempFile("resource", "so").toFile(); 33 | tempFile.deleteOnExit(); 34 | 35 | OutputStream out = new FileOutputStream(tempFile); 36 | InputStream in = NativeUtils.class.getResourceAsStream(resource); 37 | 38 | if (in == null) throw new IOException("No such resource: " + resource); 39 | 40 | FileUtils.copy(in, out); 41 | 42 | return tempFile; 43 | } 44 | 45 | public static void loadNativeResource(String resource) throws IOException { 46 | File extracted = extractNativeResource(resource); 47 | System.load(extracted.getAbsolutePath()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jni/Credential/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /jni/Credential/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /jni/Credential/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /jni/Credential/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /jni/Credential/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /jni/Credential/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /jni/Credential/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | native-lib 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | src/main/cpp/native-lib.cpp ) 21 | 22 | # Searches for a specified prebuilt library and stores the path as a 23 | # variable. Because CMake includes system libraries in the search path by 24 | # default, you only need to specify the name of the public NDK library 25 | # you want to add. CMake verifies that the library exists before 26 | # completing its build. 27 | 28 | find_library( # Sets the name of the path variable. 29 | log-lib 30 | 31 | # Specifies the name of the NDK library that 32 | # you want CMake to locate. 33 | log ) 34 | 35 | # Specifies libraries CMake should link to your target library. You 36 | # can link multiple libraries, such as libraries you define in this 37 | # build script, prebuilt third-party libraries, or system libraries. 38 | 39 | target_link_libraries( # Specifies the target library. 40 | native-lib 41 | 42 | # Links the target library to the log library 43 | # included in the NDK. 44 | ${log-lib} ) -------------------------------------------------------------------------------- /jni/Credential/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "org.signal.credential" 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | externalNativeBuild { 13 | cmake { 14 | cppFlags "" 15 | } 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | externalNativeBuild { 25 | cmake { 26 | path "CMakeLists.txt" 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(dir: 'libs', include: ['*.jar']) 33 | implementation 'com.android.support:appcompat-v7:28.0.0' 34 | testImplementation 'junit:junit:4.12' 35 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 36 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 37 | } 38 | -------------------------------------------------------------------------------- /jni/Credential/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /jni/Credential/app/src/androidTest/java/org/signal/credential/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package org.signal.credential; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("org.signal.credential", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Credential 3 | 4 | -------------------------------------------------------------------------------- /jni/Credential/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /jni/Credential/app/src/test/java/org/signal/credential/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.signal.credential; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /jni/Credential/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.2.1' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /jni/Credential/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /jni/Credential/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /jni/Credential/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /jni/Credential/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /jni/Credential/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /jni/Credential/rust/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /jni/Credential/rust/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | 30 | implementation 'com.android.support:appcompat-v7:28.0.0' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 34 | } 35 | -------------------------------------------------------------------------------- /jni/Credential/rust/build/outputs/aar/rust-debug.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/rust/build/outputs/aar/rust-debug.aar -------------------------------------------------------------------------------- /jni/Credential/rust/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /jni/Credential/rust/src/androidTest/java/org/signal/rust/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package org.signal.rust; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("org.signal.rust.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jni/Credential/rust/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /jni/Credential/rust/src/main/jniLibs/arm64-v8a/libcredential.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/rust/src/main/jniLibs/arm64-v8a/libcredential.so -------------------------------------------------------------------------------- /jni/Credential/rust/src/main/jniLibs/armeabi-v7a/libcredential.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/rust/src/main/jniLibs/armeabi-v7a/libcredential.so -------------------------------------------------------------------------------- /jni/Credential/rust/src/main/jniLibs/x86/libcredential.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/jni/Credential/rust/src/main/jniLibs/x86/libcredential.so -------------------------------------------------------------------------------- /jni/Credential/rust/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | rust 3 | 4 | -------------------------------------------------------------------------------- /jni/Credential/rust/src/test/java/org/signal/rust/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.signal.rust; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /jni/Credential/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':rust' 2 | -------------------------------------------------------------------------------- /signal-credential/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-credential" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | publish = false 6 | readme = "README.md" 7 | license = "BSD-3-Clause" 8 | repository = "https://github.com/signalapp/groupzk" 9 | categories = ["no-std", "cryptography"] 10 | keywords = ["zero-knowledge", "anonymous-credentials"] 11 | description = "A pure-Rust library for anonymous attribute-based credentials using algebraic message authentication codes (aMACs)" 12 | exclude = [ 13 | "**/.gitignore", 14 | ".gitignore", 15 | ".travis.yml", 16 | ] 17 | autobenches = false 18 | 19 | ## XXX criterion can't find the signal-credential crate unless the following is commented out? 20 | # 21 | #[lib] 22 | #name = "credential" 23 | #crate-type = ["staticlib", "rlib", "cdylib"] 24 | 25 | [dependencies] 26 | aeonflux = { version = "0.1.0", path = "../aeonflux", default-features = false } 27 | bincode = { version = "1" } 28 | curve25519-dalek = { version = "0.21", default-features = false, features = ["serde"] } 29 | failure = { version = "0.1", default-features = false } 30 | merlin = { version = "0.2" } 31 | rand = { version = "0.5", default-features = false } 32 | rand_core = { version = "0.2.1", default-features = false } 33 | serde = { version = "1" } 34 | 35 | [dev-dependencies] 36 | criterion = { version = "0.2" } 37 | 38 | [[bench]] 39 | name = "credential-benchmarks" 40 | harness = false 41 | 42 | [features] 43 | default = [ "std", "nightly", "u64_backend" ] 44 | asm = [ "aeonflux/asm" ] 45 | std = [ "aeonflux/std", "curve25519-dalek/std" ] 46 | nightly = [ "aeonflux/nightly", "curve25519-dalek/nightly" ] 47 | alloc = [ "aeonflux/alloc", "curve25519-dalek/alloc" ] 48 | u32_backend = [ "aeonflux/u32_backend", "curve25519-dalek/u32_backend" ] 49 | u64_backend = [ "aeonflux/u64_backend", "curve25519-dalek/u64_backend" ] 50 | avx2_backend = [ "aeonflux/avx2_backend", "curve25519-dalek/avx2_backend" ] 51 | -------------------------------------------------------------------------------- /signal-credential/benches/credential-benches.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[macro_use] 4 | extern crate criterion; 5 | extern crate rand; 6 | 7 | use criterion::Criterion; 8 | 9 | use rand::thread_rng; 10 | 11 | use signal_credential::SystemParameters; 12 | use signal_credential::IssuerSecretKey; 13 | use signal_credential::SignalIssuer; 14 | use signal_credential::IssuerParameters; 15 | 16 | mod credential_benches { 17 | fn credential_request_client(c: &mut Criterion) { 18 | let system_parameters: SystemParameters = SystemParameters::from(H); 19 | let issuer_secret_key: IssuerSecretKey = IssuerSecretKey::new(NUMBER_OF_ATTRIBUTES); 20 | let issuer: SignalIssuer = SignalIssuer::new(system_parameters, Some(&issuer_secret_key)); 21 | let issuer_parameters: IssuerParameters = issuer.issuer_parameters.clone(); 22 | let alice_phone_number_input: &str = "14155551234"; 23 | 24 | c.bench_function("Credential request client", move |b| { 25 | b.iter(|| SignalUser::new(system_parameters, 26 | issuer_parameters.clone(), 27 | None, // no encrypted attributes so the key isn't needed 28 | String::from(alice_phone_number_input))) 29 | }); 30 | } 31 | 32 | criterion_group!{ 33 | name = credential_benches; 34 | config = Criterion::default(); 35 | targets = 36 | credential_request_client, 37 | } 38 | } 39 | 40 | criterion_main!( 41 | credential_benches::credential_benches, 42 | ); 43 | -------------------------------------------------------------------------------- /signal-credential/src/credential.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | //! An implementation of CMZ'13 MAC_GGM based anonymous credentials for Signal. 11 | 12 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 13 | use alloc::vec::Vec; 14 | #[cfg(all(not(feature = "alloc"), feature = "std"))] 15 | use std::vec::Vec; 16 | 17 | use aeonflux::credential::SIZEOF_CREDENTIAL_PRESENTATION; 18 | use aeonflux::credential::Credential; 19 | use aeonflux::credential::CredentialIssuance; 20 | use aeonflux::credential::CredentialPresentation; 21 | use aeonflux::errors::CredentialError; 22 | use aeonflux::proofs::committed_values_equal; 23 | 24 | use bincode::{deserialize, serialize}; 25 | 26 | use serde::{self, Serialize, Deserialize, Serializer, Deserializer}; 27 | use serde::de::Visitor; 28 | 29 | use phone_number::SIZEOF_COMMITTED_PHONE_NUMBER; 30 | use phone_number::CommittedPhoneNumber; 31 | 32 | /// The number of revealed attributes on a `SignalCredential` during issuance. 33 | pub const ISSUANCE_NUMBER_OF_REVEALED_ATTRIBUTES: usize = 1; 34 | 35 | /// The number of encrypted attributes on a `SignalCredential` during issuance. 36 | pub const ISSUANCE_NUMBER_OF_BLINDED_ATTRIBUTES: usize = 0; 37 | 38 | /// The number of revealed attributes on a `SignalCredential` during presentation. 39 | pub const PRESENTATION_NUMBER_OF_REVEALED_ATTRIBUTES: usize = 0; 40 | 41 | /// The number of encrypted attributes on a `SignalCredential` during presentation. 42 | pub const PRESENTATION_NUMBER_OF_BLINDED_ATTRIBUTES: usize = 1; 43 | 44 | /// The total number of attributes on a `SignalCredentia`. 45 | pub const NUMBER_OF_ATTRIBUTES: usize = 46 | ISSUANCE_NUMBER_OF_REVEALED_ATTRIBUTES + 47 | ISSUANCE_NUMBER_OF_BLINDED_ATTRIBUTES; 48 | 49 | pub type SignalCredentialIssuance = CredentialIssuance; 50 | 51 | #[derive(Clone, Debug, Eq, PartialEq)] 52 | pub struct SignalCredentialPresentation { 53 | /// The user's corresponding committed phone number in a roster entry. 54 | pub roster_entry_commitment: CommittedPhoneNumber, 55 | /// A `CredentialPresentation` showing that this credential is valid. 56 | pub presentation: CredentialPresentation, 57 | /// Create a zero-knowledge proof showing that if the aMAC on our 58 | /// credential verifies successfully, that the underlying value in the 59 | /// commitment to our credential attribute is the same as the underlying 60 | /// committed value the `roster_entry_commitment`. 61 | pub roster_membership_proof: committed_values_equal::Proof, 62 | } 63 | 64 | impl SignalCredentialPresentation { 65 | pub fn from_bytes(bytes: &[u8]) -> Result { 66 | const RE: usize = SIZEOF_COMMITTED_PHONE_NUMBER; 67 | 68 | if bytes.len() < RE + SIZEOF_CREDENTIAL_PRESENTATION { 69 | #[cfg(feature = "std")] 70 | println!("The SignalCredentialPresentation bytes were not long enough, got {} bytes", bytes.len()); 71 | return Err(CredentialError::MissingData); 72 | } 73 | 74 | let roster_entry_commitment = CommittedPhoneNumber::from_bytes(&bytes[00..RE])?; 75 | let presentation = CredentialPresentation::from_bytes(&bytes[RE..RE+SIZEOF_CREDENTIAL_PRESENTATION])?; 76 | 77 | let roster_membership_proof: committed_values_equal::Proof = 78 | match deserialize(&bytes[RE+SIZEOF_CREDENTIAL_PRESENTATION..]) 79 | { 80 | Ok(x) => x, 81 | Err(_x) => { 82 | #[cfg(feature = "std")] 83 | println!("Error while deserializing SignalCredentialPresentation: {}", _x); 84 | return Err(CredentialError::MissingData); 85 | }, 86 | }; 87 | 88 | Ok(SignalCredentialPresentation { roster_entry_commitment, presentation, roster_membership_proof }) 89 | } 90 | 91 | pub fn to_bytes(&self) -> Vec { 92 | let mut v: Vec = Vec::with_capacity(512); 93 | 94 | v.extend(self.roster_entry_commitment.to_bytes()); 95 | v.extend(self.presentation.to_bytes()); 96 | 97 | let serialized = match serialize(&self.roster_membership_proof) { 98 | Ok(x) => x, 99 | Err(_x) => { 100 | #[cfg(feature = "std")] 101 | println!("Error while serializing SignalCredentialPresentation: {}", _x); 102 | panic!(); // XXX clean this up 103 | }, 104 | }; 105 | 106 | v.extend(serialized); 107 | v 108 | } 109 | } 110 | 111 | impl_serde_with_to_bytes_and_from_bytes!(SignalCredentialPresentation, 112 | "A valid byte sequence representing a SignalCredentialPresentation"); 113 | 114 | /// An anonymous credential belonging to a `SignalUser` and issued and verified 115 | /// by a `SignalIssuer`. 116 | pub type SignalCredential = Credential; 117 | 118 | /// A `SignalCredential` which has already been verified. 119 | /// 120 | /// # Note 121 | /// 122 | /// This type is used to cause the additional proof methods called by the issuer 123 | /// to only be callable if the issuer has previously successfully called 124 | /// `SignalIssuer.verify()`. 125 | #[derive(Clone, Debug, Eq, PartialEq)] 126 | pub struct VerifiedSignalCredential(pub(crate) SignalCredentialPresentation); 127 | 128 | impl VerifiedSignalCredential { 129 | pub fn from_bytes(bytes: &[u8]) -> Result { 130 | Ok(VerifiedSignalCredential(SignalCredentialPresentation::from_bytes(bytes)?)) 131 | } 132 | 133 | pub fn to_bytes(&self) -> Vec { 134 | self.0.to_bytes() 135 | } 136 | } 137 | 138 | impl_serde_with_to_bytes_and_from_bytes!(VerifiedSignalCredential, 139 | "A valid byte sequence representing a VerifiedSignalCredential"); 140 | 141 | #[cfg(test)] 142 | mod test { 143 | use super::*; 144 | 145 | use issuer::SignalIssuer; 146 | use issuer::IssuerParameters; 147 | use parameters::SystemParameters; 148 | use phone_number::RosterEntryCommitment; 149 | use user::SignalUser; 150 | 151 | use rand::thread_rng; 152 | 153 | #[test] 154 | fn verified_credential_serialize_deserialize() { 155 | let mut issuer_rng = thread_rng(); 156 | let mut alice_rng = thread_rng(); 157 | 158 | let system_parameters: SystemParameters = SystemParameters::hunt_and_peck(&mut issuer_rng); 159 | let issuer: SignalIssuer = SignalIssuer::create(system_parameters, &mut issuer_rng); 160 | let issuer_parameters: IssuerParameters = issuer.get_issuer_parameters(); 161 | let alice_phone_number_input: &[u8] = &[1, 4, 1, 5, 5, 5, 5, 1, 2, 3, 4]; 162 | let mut alice: SignalUser = SignalUser::new(system_parameters, 163 | issuer_parameters.clone(), 164 | None, // no enncrypted attributes so the key isn't needed 165 | alice_phone_number_input.clone()).unwrap(); 166 | let alice_issuance: SignalCredentialIssuance = issuer.issue(&alice_phone_number_input, 167 | &mut issuer_rng).unwrap(); 168 | 169 | alice.obtain_finish(Some(&alice_issuance)).unwrap(); 170 | 171 | let entry = RosterEntryCommitment::create(&alice_phone_number_input, &system_parameters, &mut alice_rng).unwrap(); 172 | let alice_presentation: SignalCredentialPresentation = alice.show(&mut alice_rng, &entry).unwrap(); 173 | let verified: VerifiedSignalCredential = issuer.verify(alice_presentation).unwrap(); 174 | 175 | let serialized = verified.to_bytes(); 176 | let deserialized = VerifiedSignalCredential::from_bytes(&serialized); 177 | 178 | assert!(deserialized.is_ok()); 179 | assert!(deserialized.unwrap() == verified); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /signal-credential/src/errors.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #[cfg(not(feature = "std"))] 11 | use core::fmt; 12 | 13 | #[cfg(feature = "std")] 14 | use std::fmt; 15 | 16 | #[cfg(not(feature = "std"))] 17 | use core::option::NoneError; 18 | 19 | #[cfg(feature = "std")] 20 | use std::option::NoneError; 21 | 22 | use aeonflux::errors::CredentialError; 23 | 24 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 25 | pub enum PhoneNumberError { 26 | LengthExceeded, 27 | InvalidPhoneNumber, 28 | } 29 | 30 | impl fmt::Display for PhoneNumberError { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | match *self { 33 | PhoneNumberError::LengthExceeded 34 | => write!(f, "A canonicalised phone number cannot be more than 32 bytes"), 35 | PhoneNumberError::InvalidPhoneNumber 36 | => write!(f, "The user's proof of roster membership could not be verified"), 37 | } 38 | } 39 | } 40 | 41 | impl ::failure::Fail for PhoneNumberError { } 42 | 43 | impl From for PhoneNumberError { 44 | fn from(_source: NoneError) -> PhoneNumberError { 45 | PhoneNumberError::InvalidPhoneNumber 46 | } 47 | } 48 | 49 | impl From for CredentialError { 50 | fn from(_source: PhoneNumberError) -> CredentialError { 51 | NoneError.into() 52 | } 53 | } 54 | 55 | impl From for PhoneNumberError { 56 | fn from(_source: CredentialError) -> PhoneNumberError { 57 | NoneError.into() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /signal-credential/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | //! Implementation of the anonymous credentials scheme in CMZ'13. 11 | 12 | #![no_std] 13 | 14 | // TODO Get rid of the syntax that uses the nightly-only try_trait. 15 | #![feature(try_trait)] 16 | // We denote group elements with capital and scalars with lowercased names. 17 | #![allow(non_snake_case)] 18 | 19 | #![cfg_attr(all(not(feature = "std"), feature = "alloc"), feature(alloc))] 20 | 21 | #[cfg(feature = "std")] 22 | #[macro_use] 23 | extern crate std; 24 | #[cfg(all(not(feature = "std"), feature = "alloc"))] 25 | extern crate alloc; 26 | 27 | #[macro_use] 28 | extern crate aeonflux; 29 | extern crate bincode; 30 | extern crate curve25519_dalek; 31 | extern crate merlin; 32 | extern crate failure; 33 | extern crate rand; 34 | extern crate rand_core; 35 | extern crate serde; 36 | 37 | pub mod credential; 38 | pub mod errors; 39 | pub mod issuer; 40 | pub mod phone_number; 41 | pub mod user; 42 | 43 | pub use credential::*; 44 | pub use errors::*; 45 | pub use issuer::*; 46 | pub use phone_number::*; 47 | pub use user::*; 48 | 49 | // Re-export common externally-used types from aeonflux. 50 | pub use aeonflux::prelude::*; 51 | 52 | pub mod parameters { 53 | pub use aeonflux::prelude::SystemParameters; 54 | } 55 | -------------------------------------------------------------------------------- /signal-credential/tests/full-protocol.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of signal-credential. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | extern crate aeonflux; 11 | extern crate rand; 12 | extern crate signal_credential; 13 | 14 | use aeonflux::issuer::IssuerParameters; 15 | use aeonflux::parameters::SystemParameters; 16 | use rand::thread_rng; 17 | use signal_credential::credential::*; 18 | use signal_credential::issuer::*; 19 | use signal_credential::phone_number::CommittedPhoneNumber; 20 | use signal_credential::phone_number::RosterEntryCommitment; 21 | use signal_credential::user::SignalUser; 22 | 23 | #[test] 24 | fn credential_issuance_and_presentation() { 25 | // Create RNGs for each party. 26 | let mut issuer_rng = thread_rng(); 27 | let mut alice_rng = thread_rng(); 28 | let mut bob_rng = thread_rng(); 29 | 30 | // Create an issuer 31 | let system_parameters: SystemParameters = SystemParameters::hunt_and_peck(&mut issuer_rng); 32 | let issuer: SignalIssuer = SignalIssuer::create(system_parameters, &mut issuer_rng); 33 | 34 | // Get the issuer's parameters so we can advertise them to new users: 35 | let issuer_parameters: IssuerParameters = issuer.issuer.keypair.public.clone(); 36 | 37 | // Create a couple users 38 | let alice_phone_number_input: &[u8] = &[1, 4, 1, 5, 5, 5, 5, 1, 2, 3, 4]; 39 | let mut alice: SignalUser = SignalUser::new(system_parameters, 40 | issuer_parameters.clone(), 41 | None, // no encrypted attributes so the key isn't needed 42 | alice_phone_number_input.clone()).unwrap(); 43 | 44 | let bob_phone_number_input: &[u8] = &[1, 4, 1, 5, 5, 5, 5, 6, 6, 6, 6]; 45 | let mut bob: SignalUser = SignalUser::new(system_parameters, 46 | issuer_parameters.clone(), 47 | None, // no encrypted attributes so the key isn't needed 48 | bob_phone_number_input.clone()).unwrap(); 49 | 50 | // Try to get the issuer to give Alice a new credential 51 | let alice_issuance: SignalCredentialIssuance = issuer.issue(&alice_phone_number_input, 52 | &mut issuer_rng).unwrap(); 53 | 54 | // Give the result back to Alice for processing 55 | alice.obtain_finish(Some(&alice_issuance)).unwrap(); 56 | 57 | // And the same for Bob: 58 | let bob_issuance: SignalCredentialIssuance = issuer.issue(&bob_phone_number_input, 59 | &mut issuer_rng).unwrap(); 60 | 61 | bob.obtain_finish(Some(&bob_issuance)).unwrap(); 62 | 63 | let bob_entry: RosterEntryCommitment = RosterEntryCommitment::create(&bob_phone_number_input, 64 | &system_parameters, 65 | &mut bob_rng).unwrap(); 66 | 67 | // Pretend that Bob had previously made a Signal group with a key: 68 | let mut roster_admins: Vec = Vec::new(); 69 | let mut roster_users: Vec = Vec::new(); 70 | 71 | roster_admins.push(bob_entry.commitment); 72 | 73 | let alice_entry: RosterEntryCommitment = RosterEntryCommitment::create(&alice_phone_number_input, 74 | &system_parameters, 75 | &mut alice_rng).unwrap(); 76 | 77 | // Now Bob adds Alice: 78 | roster_users.push(alice_entry.commitment); 79 | 80 | // Alice wants to prove they're in the roster: 81 | let alice_presentation: SignalCredentialPresentation = alice.show(&mut alice_rng, 82 | &alice_entry).unwrap(); 83 | let verified_credential: VerifiedSignalCredential = issuer.verify(alice_presentation).unwrap(); 84 | 85 | let user_proof = issuer.verify_roster_membership(&verified_credential); 86 | assert!(user_proof.is_ok()); 87 | 88 | let server_copy_alice_roster_entry_commitment = user_proof.unwrap(); 89 | 90 | // Now the issuer can check whether Alice was an admin or a user: 91 | assert!(! roster_admins.contains(&server_copy_alice_roster_entry_commitment)); 92 | assert!(roster_users.contains(&server_copy_alice_roster_entry_commitment)); 93 | } 94 | -------------------------------------------------------------------------------- /swift/Credential.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /swift/Credential.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /swift/Credential.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /swift/Credential/Credential.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Credential.swift 3 | // Credential 4 | // 5 | // Created by isis on 10/9/18, with the utmost apologies to my future readers 6 | // as this was my first time writing Swift. 7 | // 8 | // Copyright © 2018 Signal. All rights reserved. 9 | // 10 | 11 | import Foundation 12 | 13 | class SystemParameters { 14 | var data = [UInt8](repeating: 0, count: Int(LENGTH_SYSTEM_PARAMETERS)) 15 | 16 | init?(withBytes bytes: [UInt8]) { 17 | guard bytes.count == LENGTH_SYSTEM_PARAMETERS else { return } 18 | 19 | self.data = bytes 20 | } 21 | 22 | func create(seed: [UInt8]) -> Self? { 23 | guard seed.count == 32 else { return nil } 24 | 25 | let buffer = system_parameters_create(seed) 26 | 27 | self.data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 28 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 29 | } 30 | return self 31 | } 32 | } 33 | 34 | class AlgebraicMACKeypair { 35 | var data = [UInt8](repeating: 0, count: Int(LENGTH_ISSUER_KEYPAIR)) 36 | 37 | init?(withBytes bytes: [UInt8]) { 38 | guard bytes.count == LENGTH_ISSUER_KEYPAIR else { return } 39 | 40 | self.data = bytes 41 | } 42 | } 43 | 44 | class RosterEntryCommitment { 45 | var data = [UInt8](repeating: 0, count: Int(LENGTH_ROSTER_ENTRY_COMMITMENT)) 46 | 47 | init?(withBytes bytes: [UInt8]) { 48 | guard bytes.count == LENGTH_ROSTER_ENTRY_COMMITMENT else { return } 49 | 50 | self.data = bytes 51 | } 52 | 53 | init?(withSeed seed: [UInt8], 54 | phone_number: [UInt8], 55 | system_parameters: SystemParameters) { 56 | guard seed.count == 32 else { return nil } 57 | 58 | let buffer = roster_entry_commitment_create(phone_number, 59 | UInt64(phone_number.count), 60 | &system_parameters.data, 61 | UInt64(system_parameters.data.count), 62 | seed) 63 | self.data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 64 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 65 | } 66 | } 67 | 68 | func open(phone_number: [UInt8], system_parameters: SystemParameters) -> Bool { 69 | let buffer = roster_entry_commitment_open(&self.data, 70 | UInt64(self.data.count), 71 | phone_number, 72 | UInt64(phone_number.count), 73 | &system_parameters.data, 74 | UInt64(system_parameters.data.count)); 75 | 76 | if buffer.len == 1 { 77 | return true 78 | } else { 79 | return false 80 | } 81 | } 82 | } 83 | 84 | class IssuerParameters { 85 | var data = [UInt8](repeating: 0, count: Int(LENGTH_ISSUER_PARAMETERS)) 86 | 87 | init?(withBytes bytes: [UInt8]) { 88 | guard bytes.count == LENGTH_ISSUER_KEYPAIR else { return } 89 | 90 | self.data = bytes 91 | } 92 | } 93 | 94 | class CredentialIssuance { 95 | var data = [UInt8](repeating: 0, count: Int(LENGTH_CREDENTIAL_ISSUANCE)) 96 | 97 | init?(withBytes bytes: [UInt8]) { 98 | guard bytes.count == LENGTH_CREDENTIAL_ISSUANCE else { return } 99 | 100 | self.data = bytes 101 | } 102 | } 103 | 104 | class CredentialPresentation { 105 | var data = [UInt8](repeating: 0, count: Int(LENGTH_CREDENTIAL_PRESENTATION)) 106 | 107 | init?(withBytes bytes: [UInt8]) { 108 | guard bytes.count == LENGTH_CREDENTIAL_PRESENTATION else { return } 109 | 110 | self.data = bytes 111 | } 112 | } 113 | 114 | class VerifiedCredential { 115 | var data = [UInt8](repeating: 0, count: Int(LENGTH_VERIFIED_CREDENTIAL)) 116 | 117 | init?(withBytes bytes: [UInt8]) { 118 | guard bytes.count == LENGTH_VERIFIED_CREDENTIAL else { return } 119 | 120 | self.data = bytes 121 | } 122 | } 123 | 124 | class SignalIssuer { 125 | var keypair: AlgebraicMACKeypair 126 | var data = [UInt8](repeating: 0, count: Int(LENGTH_ISSUER)) 127 | 128 | init?(withSeed seed: [UInt8], system_parameters: SystemParameters) { 129 | guard seed.count == 32 else { return nil } 130 | 131 | let keypair_buffer = issuer_create(&system_parameters.data, 132 | UInt64(system_parameters.data.count), seed) 133 | data = keypair_buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(keypair_buffer.len)) { 134 | Array(UnsafeBufferPointer(start: $0, count: Int(keypair_buffer.len))) 135 | } 136 | self.keypair = AlgebraicMACKeypair(withBytes: data)! 137 | 138 | let buffer = issuer_new(&system_parameters.data, UInt64(system_parameters.data.count), 139 | &self.keypair.data, UInt64(self.keypair.data.count)) 140 | self.data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 141 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 142 | } 143 | } 144 | 145 | init(withKeypair keypair: AlgebraicMACKeypair, system_parameters: SystemParameters) { 146 | let buffer = issuer_new(&system_parameters.data, UInt64(system_parameters.data.count), 147 | &keypair.data, UInt64(keypair.data.count)) 148 | self.data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 149 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 150 | } 151 | self.keypair = keypair 152 | } 153 | 154 | func get_parameters() -> IssuerParameters? { 155 | let buffer = issuer_get_issuer_parameters(&self.data, UInt64(self.data.count)) 156 | let data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 157 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 158 | } 159 | let issuer_parameters = IssuerParameters(withBytes: data) 160 | 161 | return issuer_parameters 162 | } 163 | 164 | func issue(phone_number: [UInt8], seed: [UInt8]) -> CredentialIssuance? { 165 | guard seed.count == 32 else { return nil } 166 | 167 | let buffer = issuer_issue(&self.data, UInt64(self.data.count), 168 | phone_number, UInt64(phone_number.count), seed) 169 | let data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 170 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 171 | } 172 | let issuance = CredentialIssuance(withBytes: data) 173 | 174 | return issuance 175 | } 176 | 177 | func verify(presentation: CredentialPresentation) -> VerifiedCredential? { 178 | let buffer = issuer_verify(&self.data, UInt64(self.data.count), 179 | &presentation.data, UInt64(presentation.data.count)) 180 | let data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 181 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 182 | } 183 | let verified = VerifiedCredential(withBytes: data) 184 | 185 | return verified 186 | } 187 | } 188 | 189 | 190 | class User { 191 | var data = [UInt8](repeating: 0, count: Int(LENGTH_USER)) 192 | 193 | init?(phone_number: [UInt8], 194 | system_parameters: SystemParameters, 195 | issuer_parameters: IssuerParameters, 196 | issuance: CredentialIssuance) { 197 | let buffer = user_obtain_finish(phone_number, UInt64(phone_number.count), 198 | &system_parameters.data, UInt64(system_parameters.data.count), 199 | &issuer_parameters.data, UInt64(issuer_parameters.data.count), 200 | &issuance.data, UInt64(issuance.data.count)) 201 | 202 | self.data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 203 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 204 | } 205 | } 206 | 207 | func show(roster_entry_commitment: RosterEntryCommitment, seed: [UInt8]) -> CredentialPresentation? { 208 | guard seed.count == 32 else { return nil } 209 | 210 | let buffer = user_show(&self.data, UInt64(self.data.count), 211 | &roster_entry_commitment.data, UInt64(roster_entry_commitment.data.count), seed) 212 | let data = buffer.ptr.withMemoryRebound(to: UInt8.self, capacity: Int(buffer.len)) { 213 | Array(UnsafeBufferPointer(start: $0, count: Int(buffer.len))) 214 | } 215 | let presentation = CredentialPresentation(withBytes: data) 216 | 217 | return presentation 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /swift/Credential/credential-bridging-header.h: -------------------------------------------------------------------------------- 1 | // 2 | // credential-bridging-header.h 3 | // Credential 4 | // 5 | // Created by isis on 10/9/18. 6 | // Copyright © 2018 Signal. All rights reserved. 7 | // 8 | 9 | #ifndef credential_bridging_header_h 10 | #define credential_bridging_header_h 11 | 12 | #import "credential.h" 13 | 14 | #endif /* credential_bridging_header_h */ 15 | -------------------------------------------------------------------------------- /swift/Credential/credential.h: -------------------------------------------------------------------------------- 1 | ../../ffi/src/include/credential.h -------------------------------------------------------------------------------- /swift/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Signal Foundation, isis agora lovecruft. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /swift/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: ios 3 | 4 | ios: 5 | cargo lipo --release --features=u64_backend 6 | -------------------------------------------------------------------------------- /swift/Podfile: -------------------------------------------------------------------------------- 1 | project 'Credential.xcodeproj' 2 | 3 | # Uncomment the next line to define a global platform for your project 4 | # platform :ios, '9.0' 5 | 6 | target 'Credential' do 7 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 8 | use_frameworks! 9 | 10 | # Pods for Credential 11 | 12 | end 13 | -------------------------------------------------------------------------------- /swift/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: 66ef1282e3e5ac569d277391deab88ca8e3a9edf 2 | 3 | COCOAPODS: 1.5.2 4 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Credential : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Credential 5 | @end 6 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_CredentialVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_CredentialVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks' 3 | PODS_BUILD_DIR = ${BUILD_DIR} 4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 6 | PODS_ROOT = ${SRCROOT}/Pods 7 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Credential { 2 | umbrella header "Pods-Credential-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /swift/Pods/Target Support Files/Pods-Credential/Pods-Credential.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' '@executable_path/../../Frameworks' 3 | PODS_BUILD_DIR = ${BUILD_DIR} 4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 6 | PODS_ROOT = ${SRCROOT}/Pods 7 | -------------------------------------------------------------------------------- /swift/Products/libCredential.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/swift/Products/libCredential.a -------------------------------------------------------------------------------- /swift/libcredential.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/swift/libcredential.a -------------------------------------------------------------------------------- /wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | 6 | # NOTE: Having a cdylib crate (required for wasm) requires a dynamically linked 7 | # std/core Rust library, which is compiled to rely on the behaviour of 8 | # panics, and thus we cannot use `[profile.release] panic = abort`. 9 | [lib] 10 | name = "credential" 11 | crate-type = ["cdylib"] 12 | 13 | [dependencies.signal-credential] 14 | version = "0.1" 15 | path = "../signal-credential" 16 | default-features = false 17 | features = ["std", "nightly", "u32_backend"] 18 | 19 | [dependencies] 20 | rand = { version = "0.5" } 21 | wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } 22 | 23 | [replace] 24 | "zkp:0.4.3" = { git = "https://github.com/isislovecruft/zkp", branch = "fix/stuff", default-features = false } 25 | -------------------------------------------------------------------------------- /wasm/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: wasm 3 | 4 | wasm: 5 | #rustup target add wasm32-unknown-unknown --toolchain nightly 6 | #-cargo install wasm-bindgen-cli 7 | cargo +nightly build --target wasm32-unknown-unknown --release 8 | cp target/wasm32-unknown-unknown/release/credential.wasm src 9 | wasm-bindgen --no-modules src/credential.wasm --out-dir src 10 | -------------------------------------------------------------------------------- /wasm/README.md: -------------------------------------------------------------------------------- 1 | 2 | wasm 3 | ====== 4 | 5 | Web Assembly and Javascript/Typescript bindings for the `signal-credential` 6 | library, for use in the Signal desktop client. 7 | 8 | 9 | Producing Updated Bindings 10 | ---------------------------- 11 | 12 | Install `rustup` either via brew or via the rustup shell script: 13 | 14 | ```sh 15 | brew install rustup-init 16 | curl https://sh.rustup.rs -sSf | sh 17 | ``` 18 | 19 | Install a nightly Rust compiler and Cargo (Rust's package manager): 20 | 21 | ```sh 22 | rustup install nightly 23 | ``` 24 | 25 | Probably, you can just run `make` at this point. The `.wasm` and 26 | `.js` module should end up in `src/`. Details of what the `Makefile` 27 | is doing are as follows. 28 | 29 | Details 30 | --------- 31 | 32 | Add wasm32 as a build target platform: 33 | 34 | ```sh 35 | rustup target add wasm32-unknown-unknown --toolchain nightly 36 | cargo +nightly build --target wasm32-unknown-unknown --release 37 | ``` 38 | 39 | This gives us a wasm file at `target/wasm32-unknown-unknown/release/electron.wasm`: 40 | 41 | ```sh 42 | cp target/wasm32-unknown-unknown/release/electron.wasm ./ 43 | ``` 44 | 45 | Now run the `wasm-bindgen` tool on it to generate a new wasm file and 46 | a set of Javascript bindings: 47 | 48 | ```sh 49 | cargo install wasm-bindgen-cli 50 | wasm-bindgen src/credential.wasm --out-dir src 51 | ``` 52 | 53 | That should create a `src/credential.js` module which exports a 54 | Javascript FFI to the `signal-credential` Rust crate. 55 | 56 | 57 | Notes 58 | ----- 59 | 60 | I followed roughly 61 | [this post](https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/), 62 | but it even though it's only 3 months old it was pretty out of date 63 | and required me digging around in the proc-macro2 crate and some 64 | compiler internals. 65 | 66 | [This book](https://rustwasm.github.io/wasm-bindgen/introduction.html) 67 | was more helpful, but I ended up looking at both and cross-referencing. 68 | 69 | The most important part of the above book is likely the 70 | [documentation](https://rustwasm.github.io/wasm-bindgen/reference/arbitrary-data-with-serde.html) 71 | on the `JsValue` type, which I've used liberally here. 72 | -------------------------------------------------------------------------------- /wasm/src/credential.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | export function system_parameters_create(arg0: Uint8Array): any; 3 | 4 | export function issuer_create(arg0: any, arg1: Uint8Array): any; 5 | 6 | export function issuer_get_issuer_parameters(arg0: any): any; 7 | 8 | export function issuer_new(arg0: any, arg1: any): any; 9 | 10 | export function issuer_issue(arg0: any, arg1: Uint8Array, arg2: Uint8Array): any; 11 | 12 | export function issuer_verify(arg0: any, arg1: any): any; 13 | 14 | export function issuer_verify_roster_membership(arg0: any, arg1: any): any; 15 | 16 | export function user_obtain_finish(arg0: Uint8Array, arg1: any, arg2: any, arg3: any): any; 17 | 18 | export function user_show(arg0: any, arg1: any, arg2: Uint8Array): any; 19 | 20 | -------------------------------------------------------------------------------- /wasm/src/credential.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/wasm/src/credential.wasm -------------------------------------------------------------------------------- /wasm/src/credential_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isislovecruft/signal-anonymous-credentials/fd59033d8d84aa1d5eeff8b38fd0acae73b510a0/wasm/src/credential_bg.wasm -------------------------------------------------------------------------------- /wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of groupzk. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #![allow(non_snake_case)] 11 | 12 | #![feature(custom_attribute)] 13 | // #![feature(proc_macro)] // Required for nightly rustc<1.29 14 | 15 | extern crate rand; 16 | extern crate signal_credential; 17 | extern crate wasm_bindgen; 18 | 19 | pub mod js; 20 | -------------------------------------------------------------------------------- /zkp-expand/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | src/proofs.rs 3 | -------------------------------------------------------------------------------- /zkp-expand/.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 100 2 | hard_tabs = false 3 | tab_spaces = 4 4 | newline_style = "Auto" 5 | use_small_heuristics = "Max" 6 | indent_style = "Block" 7 | wrap_comments = false 8 | comment_width = 100 9 | normalize_comments = false 10 | normalize_doc_attributes = false 11 | format_strings = false 12 | format_macro_matchers = false 13 | format_macro_bodies = true 14 | empty_item_single_line = true 15 | struct_lit_single_line = true 16 | fn_single_line = false 17 | where_single_line = false 18 | imports_indent = "Block" 19 | imports_layout = "Mixed" 20 | merge_imports = false 21 | reorder_imports = true 22 | reorder_modules = true 23 | reorder_impl_items = false 24 | type_punctuation_density = "Wide" 25 | space_before_colon = false 26 | space_after_colon = true 27 | spaces_around_ranges = false 28 | binop_separator = "Front" 29 | remove_nested_parens = true 30 | combine_control_expr = true 31 | struct_field_align_threshold = 0 32 | match_arm_blocks = true 33 | force_multiline_blocks = false 34 | fn_args_density = "Tall" 35 | brace_style = "SameLineWhere" 36 | control_brace_style = "AlwaysSameLine" 37 | trailing_semicolon = true 38 | trailing_comma = "Vertical" 39 | match_block_trailing_comma = false 40 | blank_lines_upper_bound = 1 41 | blank_lines_lower_bound = 0 42 | edition = "2015" 43 | merge_derives = true 44 | use_try_shorthand = false 45 | use_field_init_shorthand = false 46 | force_explicit_abi = true 47 | condense_wildcard_suffixes = false 48 | color = "Auto" 49 | required_version = "0.99.5" 50 | unstable_features = false 51 | disable_all_formatting = false 52 | skip_children = false 53 | hide_parse_errors = false 54 | error_on_line_overflow = false 55 | error_on_unformatted = false 56 | report_todo = "Never" 57 | report_fixme = "Never" 58 | ignore = [] 59 | emit_mode = "Files" 60 | make_backup = false 61 | -------------------------------------------------------------------------------- /zkp-expand/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zkp-expand" 3 | version = "0.1.0" 4 | authors = ["Isis Lovecruft "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | serde = { version = "1" } 9 | serde_derive = { version = "1" } 10 | zkp = { version = "0.4", default-features = false } 11 | 12 | [replace] 13 | "zkp:0.4.3" = { git = "https://github.com/isislovecruft/zkp", branch = "fix/stuff" } 14 | 15 | [features] 16 | default = [ "nightly"] 17 | std = [ "zkp/std" ] 18 | nightly = [ "zkp/nightly" ] 19 | alloc = [ "zkp/alloc" ] 20 | u32_backend = [ "zkp/u32_backend" ] 21 | u64_backend = [ "zkp/u64_backend" ] 22 | avx2_backend = [ "zkp/avx2_backend" ] 23 | -------------------------------------------------------------------------------- /zkp-expand/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: zkp-expand 3 | 4 | FEATURES:=u64_backend 5 | 6 | dependencies: 7 | rustup component add rustfmt-preview --toolchain nightly 8 | 9 | expand-aeonflux: 10 | cp ../aeonflux/src/proofs.rs ./src/ 11 | cargo expand --features="$(FEATURES)" > aeonflux_proofs.rs 12 | 13 | expand-signal-credential: 14 | cp ../signal-credential/src/proofs.rs ./src/ 15 | cargo expand --features="$(FEATURES)" > signal_credential_proofs.rs 16 | 17 | expand: expand-aeonflux expand-signal-credential 18 | 19 | format: 20 | rustfmt aeonflux_proofs.rs 21 | rustfmt signal_credential_proofs.rs 22 | 23 | zkp-expand: expand clean 24 | 25 | clean: 26 | -rm src/proofs.rs 27 | -------------------------------------------------------------------------------- /zkp-expand/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of aeonflux. 4 | // Copyright (c) 2018 Signal Foundation 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | #![no_std] 11 | 12 | #[cfg(feature = "std")] 13 | extern crate std; 14 | #[cfg(any(not(feature = "std"), feature = "alloc"))] 15 | extern crate alloc; 16 | 17 | extern crate serde; 18 | #[macro_use] 19 | extern crate serde_derive; 20 | #[macro_use] 21 | extern crate zkp; 22 | 23 | pub mod proofs; 24 | --------------------------------------------------------------------------------