From aac6a473f73118d42b6d4a0836c57eb9b46bca41 Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Tue, 22 Oct 2024 15:53:09 +0200 Subject: [PATCH 1/6] refactor(capi): Move CAPI generation to its own crate --- .github/workflows/release.yml | 66 +- .github/workflows/rust.yml | 79 +- Cargo.toml | 2 +- README.md | 14 +- biscuit-auth/Cargo.toml | 28 +- biscuit-auth/src/lib.rs | 10 +- biscuit-auth/src/token/mod.rs | 8 +- biscuit-capi/Cargo.toml | 30 + biscuit-capi/LICENSE | 202 +++ biscuit-capi/README.md | 36 + {biscuit-auth => biscuit-capi}/cbindgen.toml | 0 biscuit-capi/src/lib.rs | 1251 ++++++++++++++++++ {biscuit-auth => biscuit-capi}/tests/capi.rs | 0 13 files changed, 1590 insertions(+), 136 deletions(-) create mode 100644 biscuit-capi/Cargo.toml create mode 100644 biscuit-capi/LICENSE create mode 100644 biscuit-capi/README.md rename {biscuit-auth => biscuit-capi}/cbindgen.toml (100%) create mode 100644 biscuit-capi/src/lib.rs rename {biscuit-auth => biscuit-capi}/tests/capi.rs (100%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc451516..507545e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release on: push: tags: - - '*' + - "*" permissions: contents: write @@ -12,43 +12,33 @@ env: CARGO_TERM_COLOR: always jobs: - build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macos-latest-large + target_arch: x86_64-apple-darwin + - os: macos-latest + target_arch: aarch64-apple-darwin + - os: ubuntu-latest + target_arch: x86_64-unknown-linux-gnu steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo - target/ - key: ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}-cargoc- - ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}- - - name: Install cargo-c - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-c - - name: Run cargo-c tests - uses: actions-rs/cargo@v1 - with: - command: ctest - args: --release - - name: Build - uses: actions-rs/cargo@v1 - with: - command: cinstall - args: --release --prefix=/usr --destdir=./build - - name: Compress - run: tar cvzf biscuit_c-${{github.ref_name}}-x86_64.tar.gz -C build/ . - - name: Release - uses: softprops/action-gh-release@v1 - with: - files: biscuit_c-${{github.ref_name}}-x86_64.tar.gz + - name: Checkout sources + uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + target: ${{ matrix.target_arch }} + - name: Install cargo-c + run: cargo install cargo-c --version 0.10.5+cargo-0.83.0 + - name: Run cargo-c tests + run: cargo ctest --release --features="capi" + - name: Build C-API + run: cargo cinstall --release --prefix=/usr --destdir=./build --features="capi" + - name: Compress + run: tar cvzf biscuit_capi-${{github.ref_name}}-${{matrix.target_arch}}.tar.gz -C build/ . + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: biscuit_capi-${{github.ref_name}}-${{matrix.target_arch}}.tar.gz diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 70b60f11..dc7c2006 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,69 +2,50 @@ name: Rust on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main, v5 ] + branches: [main, v5] env: CARGO_TERM_COLOR: always jobs: build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo - target/ - key: ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}- - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --features="serde-error,bwk" --verbose - - name: Check samples - run: | - cd biscuit-auth - cargo run --release --example testcases --features serde-error -- ./samples --json > ./samples/samples.json - git diff --exit-code - + - uses: actions/checkout@v2 + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/.cargo + target/ + key: ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} + restore-keys: | + ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} + ${{ runner.os }}- + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --features="serde-error,bwk" --verbose + - name: Check samples + run: | + cd biscuit-auth + cargo run --release --example testcases --features serde-error -- ./samples --json > ./samples/samples.json + git diff --exit-code capi: runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo - target/ - key: ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - restore-keys: | - ${{ runner.os }}-cargoc-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}-cargoc- - ${{ runner.os }}-${{ hashFiles('Cargo.toml') }} - ${{ runner.os }}- - - name: Install cargo-c - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-c --version 0.9.14+cargo-0.66 - - name: Run cargo-c tests - uses: actions-rs/cargo@v1 - with: - command: ctest - args: --release + - name: Checkout sources + uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Install cargo-c + run: cargo install cargo-c --version 0.10.5+cargo-0.83.0 + - name: Run cargo-c tests + run: cargo ctest --features="capi" --release coverage: name: Coverage @@ -94,4 +75,4 @@ jobs: uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} - slug: biscuit-auth/biscuit-rust \ No newline at end of file + slug: biscuit-auth/biscuit-rust diff --git a/Cargo.toml b/Cargo.toml index 28254783..9dcf7dc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["biscuit-auth", "biscuit-quote", "biscuit-parser"] +members = ["biscuit-auth", "biscuit-quote", "biscuit-parser", "biscuit-capi"] diff --git a/README.md b/README.md index 2ccb3b26..ce65f1e8 100644 --- a/README.md +++ b/README.md @@ -172,19 +172,7 @@ biscuit implementations come with a default symbol table to avoid transmitting f # C bindings -This project can generate C bindings with [cargo-c](https://crates.io/crates/cargo-c). - -compile it with: - -``` -cargo cinstall --prefix=/usr --destdir=./build -``` - -Run C integration tests with: - -``` -cargo ctest -``` +You can find the C bindings and documentation in the [`biscuit-capi`](./biscuit-capi/README.md) crate. ## License diff --git a/biscuit-auth/Cargo.toml b/biscuit-auth/Cargo.toml index 322cb555..334c7d68 100644 --- a/biscuit-auth/Cargo.toml +++ b/biscuit-auth/Cargo.toml @@ -12,9 +12,7 @@ repository = "https://github.com/biscuit-auth/biscuit-rust" [features] default = ["regex-full", "datalog-macro"] -regex-full = [ "regex/perf", "regex/unicode"] -# used by cargo-c to signal the compilation of C bindings -capi = ["inline-c"] +regex-full = ["regex/perf", "regex/unicode"] wasm = ["wasm-bindgen", "getrandom/wasm-bindgen"] # used by biscuit-wasm to serialize errors to JSON serde-error = ["serde", "biscuit-parser/serde-error"] @@ -33,12 +31,11 @@ sha2 = "^0.9" prost = "0.10" prost-types = "0.10" regex = { version = "1.5", default-features = false, features = ["std"] } -nom = {version = "7", default-features = false, features = ["std"] } +nom = { version = "7", default-features = false, features = ["std"] } hex = "0.4" zeroize = { version = "1", default-features = false } thiserror = "1" rand = { version = "0.8" } -inline-c = { version = "0.1", optional = true } wasm-bindgen = { version = "0.2", optional = true } base64 = "0.13.0" ed25519-dalek = { version = "2.0.0", features = ["rand_core", "zeroize"] } @@ -48,7 +45,9 @@ time = { version = "0.3.7", features = ["formatting", "parsing"] } uuid = { version = "1", optional = true } biscuit-parser = { version = "0.1.2", path = "../biscuit-parser" } biscuit-quote = { version = "0.2.2", optional = true, path = "../biscuit-quote" } -chrono = { version = "0.4.26", optional = true, default-features = false, features = ["serde"] } +chrono = { version = "0.4.26", optional = true, default-features = false, features = [ + "serde", +] } [dev-dependencies] @@ -64,23 +63,6 @@ codspeed-bencher-compat = "2.6.0" #[build-dependencies] #prost-build = "0.10" -[package.metadata.capi.library] -# Used as the library name and defaults to the crate name. This might get -# prefixed with `lib` depending on the target platform. -name = "biscuit_auth" - -include = [ - "Cargo.toml", - "cbindgen.toml", - "build.rs", - "examples/*.rs", - "LICENSE", - "README.md", - "src/*.rs", - "src/*/*.rs", - "tests/*.rs" -] - [[example]] name = "testcases" required-features = ["serde-error"] diff --git a/biscuit-auth/src/lib.rs b/biscuit-auth/src/lib.rs index d09ea33e..89577c27 100644 --- a/biscuit-auth/src/lib.rs +++ b/biscuit-auth/src/lib.rs @@ -219,12 +219,12 @@ //! biscuit implementations come with a default symbol table to avoid transmitting //! frequent values with every token. -mod crypto; +pub mod crypto; pub mod datalog; pub mod error; pub mod format; pub mod parser; -mod token; +pub mod token; pub use crypto::{KeyPair, PrivateKey, PublicKey}; pub use token::authorizer::{Authorizer, AuthorizerLimits}; @@ -235,12 +235,6 @@ pub use token::Biscuit; pub use token::RootKeyProvider; pub use token::{ThirdPartyBlock, ThirdPartyRequest}; -#[cfg(cargo_c)] -mod capi; - -#[cfg(cargo_c)] -pub use capi::*; - #[cfg(feature = "bwk")] mod bwk; #[cfg(feature = "bwk")] diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index fe1b089e..ce6fcb19 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -73,9 +73,9 @@ pub fn default_symbol_table() -> SymbolTable { #[derive(Clone, Debug)] pub struct Biscuit { pub(crate) root_key_id: Option, - pub(crate) authority: schema::Block, - pub(crate) blocks: Vec, - pub(crate) symbols: SymbolTable, + pub authority: schema::Block, + pub blocks: Vec, + pub symbols: SymbolTable, pub(crate) container: SerializedBiscuit, } @@ -500,7 +500,7 @@ impl Biscuit { 1 + self.blocks.len() } - pub(crate) fn block(&self, index: usize) -> Result { + pub fn block(&self, index: usize) -> Result { let block = if index == 0 { proto_block_to_token_block( &self.authority, diff --git a/biscuit-capi/Cargo.toml b/biscuit-capi/Cargo.toml new file mode 100644 index 00000000..14ab13ae --- /dev/null +++ b/biscuit-capi/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "biscuit-capi" +version = "5.0.0" # Should keep the same version as biscuit-auth +description = "C API for Biscuit" +authors = ["Geoffroy Couprie "] +edition = "2018" +license = "Apache-2.0" +documentation = "https://docs.rs/biscuit-auth" +homepage = "https://github.com/biscuit-auth/biscuit" +repository = "https://github.com/biscuit-auth/biscuit-rust" + +[dependencies] +biscuit-auth = { version = "5.0.0", path = "../biscuit-auth", features = [ + "datalog-macro", + "serde-error", +] } +inline-c = { version = "0.1", optional = true } +rand = "0.8" + +[features] +default = [] +capi = ["inline-c"] + +[package.metadata.capi.library] +# Used as the library name and defaults to the crate name. This might get +# prefixed with `lib` depending on the target platform. +name = "biscuit_auth" + +[package.metadata.capi.header] +name = "biscuit_auth" diff --git a/biscuit-capi/LICENSE b/biscuit-capi/LICENSE new file mode 100644 index 00000000..57bc88a1 --- /dev/null +++ b/biscuit-capi/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/biscuit-capi/README.md b/biscuit-capi/README.md new file mode 100644 index 00000000..06035e66 --- /dev/null +++ b/biscuit-capi/README.md @@ -0,0 +1,36 @@ +## Biscuit C API + +This crate provides a C API for the Biscuit token library. It is a wrapper around the Rust API, and is intended to be used by other languages that can interface with C. + +### Building from sources + +To build the C API, you need to have the Rust toolchain installed. You can then build the C API by running: + +```sh +cargo cinstall --release --features="capi" --prefix=/path/to/install --destdir=/path/to/destdir +``` +It will produce a shared library in the `--destdir` directory and also the C headers. +You can then link against the generated library in your C code. + +`cargo cinstall` is provided by the [`cargo-c`](https://github.com/lu-zero/cargo-c/) crate, which you can install with `cargo install cargo-c`. + +### Downloading pre-built binaries +In the [releases section](https://github.com/biscuit-auth/biscuit-rust/releases) of this repository, you can find pre-built binaries for the C API. +Currently, only Linux x86_64 and MacOS (x86 + arm) are provided. + +### Running the tests + +```sh +cargo ctest --features="capi" +``` + +## License + +Licensed under Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be licensed as above, without any additional terms or +conditions. \ No newline at end of file diff --git a/biscuit-auth/cbindgen.toml b/biscuit-capi/cbindgen.toml similarity index 100% rename from biscuit-auth/cbindgen.toml rename to biscuit-capi/cbindgen.toml diff --git a/biscuit-capi/src/lib.rs b/biscuit-capi/src/lib.rs new file mode 100644 index 00000000..5b400246 --- /dev/null +++ b/biscuit-capi/src/lib.rs @@ -0,0 +1,1251 @@ +use rand::prelude::*; +use std::{ + cell::RefCell, + ffi::{CStr, CString}, + fmt, + os::raw::c_char, +}; + +use biscuit_auth::datalog::SymbolTable; + +enum Error { + Biscuit(biscuit_auth::error::Token), + InvalidArgument, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidArgument => write!(f, "invalid argument"), + Error::Biscuit(e) => write!(f, "{}", e), + } + } +} + +impl From for Error { + fn from(error: biscuit_auth::error::Token) -> Self { + Error::Biscuit(error) + } +} + +thread_local! { + static LAST_ERROR: RefCell> = const { RefCell::new(None) }; +} + +fn update_last_error(err: Error) { + LAST_ERROR.with(|prev| { + *prev.borrow_mut() = Some(err); + }); +} + +#[no_mangle] +pub extern "C" fn error_message() -> *const c_char { + thread_local! { + static LAST: RefCell> = const { RefCell::new(None) }; + } + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(ref err) => { + let err = CString::new(err.to_string()).ok(); + LAST.with(|ret| { + *ret.borrow_mut() = err; + ret.borrow() + .as_ref() + .map(|x| x.as_ptr()) + .unwrap_or(std::ptr::null()) + }) + } + None => std::ptr::null(), + }) +} + +#[repr(C)] +pub enum ErrorKind { + None, + InvalidArgument, + InternalError, + FormatSignatureInvalidFormat, + FormatSignatureInvalidSignature, + FormatSealedSignature, + FormatEmptyKeys, + FormatUnknownPublicKey, + FormatDeserializationError, + FormatSerializationError, + FormatBlockDeserializationError, + FormatBlockSerializationError, + FormatVersion, + FormatInvalidBlockId, + FormatExistingPublicKey, + FormatSymbolTableOverlap, + FormatPublicKeyTableOverlap, + FormatUnknownExternalKey, + FormatUnknownSymbol, + AppendOnSealed, + LogicInvalidBlockRule, + LogicUnauthorized, + LogicAuthorizerNotEmpty, + LogicNoMatchingPolicy, + LanguageError, + TooManyFacts, + TooManyIterations, + Timeout, + ConversionError, + FormatInvalidKeySize, + FormatInvalidSignatureSize, + FormatInvalidKey, + FormatSignatureDeserializationError, + FormatBlockSignatureDeserializationError, + FormatSignatureInvalidSignatureGeneration, + AlreadySealed, + Execution, +} + +#[no_mangle] +pub extern "C" fn error_kind() -> ErrorKind { + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(ref err) => match err { + Error::InvalidArgument => ErrorKind::InvalidArgument, + Error::Biscuit(e) => { + use biscuit_auth::error::*; + match e { + Token::InternalError => ErrorKind::InternalError, + Token::Format(Format::Signature(Signature::InvalidFormat)) => { + ErrorKind::FormatSignatureInvalidFormat + } + Token::Format(Format::Signature(Signature::InvalidSignature(_))) => { + ErrorKind::FormatSignatureInvalidSignature + } + Token::Format(Format::Signature(Signature::InvalidSignatureGeneration(_))) => { + ErrorKind::FormatSignatureInvalidSignatureGeneration + } + Token::Format(Format::SealedSignature) => ErrorKind::FormatSealedSignature, + Token::Format(Format::EmptyKeys) => ErrorKind::FormatEmptyKeys, + Token::Format(Format::UnknownPublicKey) => ErrorKind::FormatUnknownPublicKey, + Token::Format(Format::DeserializationError(_)) => { + ErrorKind::FormatDeserializationError + } + Token::Format(Format::SerializationError(_)) => { + ErrorKind::FormatSerializationError + } + Token::Format(Format::BlockDeserializationError(_)) => { + ErrorKind::FormatBlockDeserializationError + } + Token::Format(Format::BlockSerializationError(_)) => { + ErrorKind::FormatBlockSerializationError + } + Token::Format(Format::Version { .. }) => ErrorKind::FormatVersion, + Token::Format(Format::InvalidKeySize(_)) => ErrorKind::FormatInvalidKeySize, + Token::Format(Format::InvalidSignatureSize(_)) => { + ErrorKind::FormatInvalidSignatureSize + } + Token::Format(Format::InvalidKey(_)) => ErrorKind::FormatInvalidKey, + Token::Format(Format::SignatureDeserializationError(_)) => { + ErrorKind::FormatSignatureDeserializationError + } + Token::Format(Format::BlockSignatureDeserializationError(_)) => { + ErrorKind::FormatBlockSignatureDeserializationError + } + Token::Format(Format::InvalidBlockId(_)) => ErrorKind::FormatInvalidBlockId, + Token::Format(Format::ExistingPublicKey(_)) => { + ErrorKind::FormatExistingPublicKey + } + Token::Format(Format::SymbolTableOverlap) => { + ErrorKind::FormatSymbolTableOverlap + } + Token::Format(Format::PublicKeyTableOverlap) => { + ErrorKind::FormatPublicKeyTableOverlap + } + Token::Format(Format::UnknownExternalKey) => { + ErrorKind::FormatUnknownExternalKey + } + Token::Format(Format::UnknownSymbol(_)) => ErrorKind::FormatUnknownSymbol, + Token::AppendOnSealed => ErrorKind::AppendOnSealed, + Token::AlreadySealed => ErrorKind::AlreadySealed, + Token::Language(_) => ErrorKind::LanguageError, + Token::FailedLogic(Logic::InvalidBlockRule(_, _)) => { + ErrorKind::LogicInvalidBlockRule + } + Token::FailedLogic(Logic::Unauthorized { .. }) => ErrorKind::LogicUnauthorized, + Token::FailedLogic(Logic::AuthorizerNotEmpty) => { + ErrorKind::LogicAuthorizerNotEmpty + } + Token::FailedLogic(Logic::NoMatchingPolicy { .. }) => { + ErrorKind::LogicNoMatchingPolicy + } + Token::RunLimit(RunLimit::TooManyFacts) => ErrorKind::TooManyFacts, + Token::RunLimit(RunLimit::TooManyIterations) => ErrorKind::TooManyIterations, + Token::RunLimit(RunLimit::Timeout) => ErrorKind::Timeout, + Token::ConversionError(_) => ErrorKind::ConversionError, + Token::Base64(_) => ErrorKind::FormatDeserializationError, + Token::Execution(_) => ErrorKind::Execution, + } + } + }, + None => ErrorKind::None, + }) +} + +#[no_mangle] +pub extern "C" fn error_check_count() -> u64 { + use biscuit_auth::error::*; + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) + | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { + checks.len() as u64 + } + _ => 0, + }) +} + +#[no_mangle] +pub extern "C" fn error_check_id(check_index: u64) -> u64 { + use biscuit_auth::error::*; + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) + | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { + if check_index >= checks.len() as u64 { + u64::MAX + } else { + match checks[check_index as usize] { + FailedCheck::Block(FailedBlockCheck { check_id, .. }) => check_id as u64, + FailedCheck::Authorizer(FailedAuthorizerCheck { check_id, .. }) => { + check_id as u64 + } + } + } + } + _ => u64::MAX, + }) +} + +#[no_mangle] +pub extern "C" fn error_check_block_id(check_index: u64) -> u64 { + use biscuit_auth::error::*; + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) + | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { + if check_index >= checks.len() as u64 { + u64::MAX + } else { + match checks[check_index as usize] { + FailedCheck::Block(FailedBlockCheck { block_id, .. }) => block_id as u64, + _ => u64::MAX, + } + } + } + _ => u64::MAX, + }) +} + +/// deallocation is handled by Biscuit +/// the string is overwritten on each call +#[no_mangle] +pub extern "C" fn error_check_rule(check_index: u64) -> *const c_char { + use biscuit_auth::error::*; + thread_local! { + static CAVEAT_RULE: RefCell> = const { RefCell::new(None) }; + } + + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) + | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { + if check_index >= checks.len() as u64 { + std::ptr::null() + } else { + let rule = match &checks[check_index as usize] { + FailedCheck::Block(FailedBlockCheck { rule, .. }) => rule, + FailedCheck::Authorizer(FailedAuthorizerCheck { rule, .. }) => rule, + }; + let err = CString::new(rule.clone()).ok(); + CAVEAT_RULE.with(|ret| { + *ret.borrow_mut() = err; + ret.borrow() + .as_ref() + .map(|x| x.as_ptr()) + .unwrap_or(std::ptr::null()) + }) + } + } + _ => std::ptr::null(), + }) +} + +#[no_mangle] +pub extern "C" fn error_check_is_authorizer(check_index: u64) -> bool { + use biscuit_auth::error::*; + LAST_ERROR.with(|prev| match *prev.borrow() { + Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) + | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { + if check_index >= checks.len() as u64 { + false + } else { + match checks[check_index as usize] { + FailedCheck::Block(FailedBlockCheck { .. }) => false, + FailedCheck::Authorizer(FailedAuthorizerCheck { .. }) => true, + } + } + } + _ => false, + }) +} + +pub struct Biscuit(biscuit_auth::token::Biscuit); +pub struct KeyPair(biscuit_auth::crypto::KeyPair); +pub struct PublicKey(biscuit_auth::crypto::PublicKey); +pub struct BiscuitBuilder(biscuit_auth::token::builder::BiscuitBuilder); +pub struct BlockBuilder(biscuit_auth::token::builder::BlockBuilder); +pub struct Authorizer(biscuit_auth::token::authorizer::Authorizer); + +#[no_mangle] +pub unsafe extern "C" fn key_pair_new<'a>( + seed_ptr: *const u8, + seed_len: usize, +) -> Option> { + let slice = std::slice::from_raw_parts(seed_ptr, seed_len); + if slice.len() != 32 { + update_last_error(Error::InvalidArgument); + return None; + } + + let mut seed = [0u8; 32]; + seed.copy_from_slice(slice); + + let mut rng: StdRng = SeedableRng::from_seed(seed); + + Some(Box::new(KeyPair( + biscuit_auth::crypto::KeyPair::new_with_rng(&mut rng), + ))) +} + +#[no_mangle] +pub unsafe extern "C" fn key_pair_public(kp: Option<&KeyPair>) -> Option> { + if kp.is_none() { + update_last_error(Error::InvalidArgument); + } + let kp = kp?; + + Some(Box::new(PublicKey((*kp).0.public()))) +} + +/// expects a 32 byte buffer +#[no_mangle] +pub unsafe extern "C" fn key_pair_serialize(kp: Option<&KeyPair>, buffer_ptr: *mut u8) -> usize { + if kp.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + let kp = kp.unwrap(); + + let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); + + output_slice.copy_from_slice(&kp.0.private().to_bytes()[..]); + 32 +} + +/// expects a 32 byte buffer +#[no_mangle] +pub unsafe extern "C" fn key_pair_deserialize(buffer_ptr: *mut u8) -> Option> { + let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); + + match biscuit_auth::crypto::PrivateKey::from_bytes(input_slice).ok() { + None => { + update_last_error(Error::InvalidArgument); + None + } + Some(privkey) => Some(Box::new(KeyPair(biscuit_auth::crypto::KeyPair::from( + &privkey, + )))), + } +} + +#[no_mangle] +pub unsafe extern "C" fn key_pair_free(_kp: Option>) {} + +/// expects a 32 byte buffer +#[no_mangle] +pub unsafe extern "C" fn public_key_serialize( + kp: Option<&PublicKey>, + buffer_ptr: *mut u8, +) -> usize { + if kp.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + let kp = kp.unwrap(); + + let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); + + output_slice.copy_from_slice(&kp.0.to_bytes()[..]); + 32 +} + +/// expects a 32 byte buffer +#[no_mangle] +pub unsafe extern "C" fn public_key_deserialize(buffer_ptr: *mut u8) -> Option> { + let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); + + match biscuit_auth::crypto::PublicKey::from_bytes(input_slice).ok() { + None => { + update_last_error(Error::InvalidArgument); + None + } + Some(pubkey) => Some(Box::new(PublicKey(pubkey))), + } +} + +#[no_mangle] +pub unsafe extern "C" fn public_key_free(_kp: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder() -> Option> { + Some(Box::new(BiscuitBuilder( + biscuit_auth::token::Biscuit::builder(), + ))) +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_set_context( + builder: Option<&mut BiscuitBuilder>, + context: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let context = CStr::from_ptr(context); + let s = context.to_str(); + match s { + Err(_) => { + update_last_error(Error::InvalidArgument); + false + } + Ok(context) => { + builder.0.set_context(context.to_string()); + true + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_set_root_key_id( + builder: Option<&mut BiscuitBuilder>, + root_key_id: u32, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + builder.0.set_root_key_id(root_key_id); + true +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_add_fact( + builder: Option<&mut BiscuitBuilder>, + fact: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let fact = CStr::from_ptr(fact); + let s = fact.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + builder + .0 + .add_fact(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_add_rule( + builder: Option<&mut BiscuitBuilder>, + rule: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let rule = CStr::from_ptr(rule); + let s = rule.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + builder + .0 + .add_rule(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_add_check( + builder: Option<&mut BiscuitBuilder>, + check: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let check = CStr::from_ptr(check); + let s = check.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + builder + .0 + .add_check(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_build( + builder: Option<&BiscuitBuilder>, + key_pair: Option<&KeyPair>, + seed_ptr: *const u8, + seed_len: usize, +) -> Option> { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + } + let builder = builder?; + + if key_pair.is_none() { + update_last_error(Error::InvalidArgument); + } + let key_pair = key_pair?; + + let slice = std::slice::from_raw_parts(seed_ptr, seed_len); + if slice.len() != 32 { + return None; + } + + let mut seed = [0u8; 32]; + seed.copy_from_slice(slice); + + let mut rng: StdRng = SeedableRng::from_seed(seed); + (*builder) + .0 + .clone() + .build_with_rng(&key_pair.0, SymbolTable::default(), &mut rng) + .map(Biscuit) + .map(Box::new) + .ok() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_builder_free<'a>(_builder: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_from<'a>( + biscuit_ptr: *const u8, + biscuit_len: usize, + root: Option<&'a PublicKey>, +) -> Option> { + let biscuit = std::slice::from_raw_parts(biscuit_ptr, biscuit_len); + if root.is_none() { + update_last_error(Error::InvalidArgument); + } + let root = root?; + + biscuit_auth::token::Biscuit::from(biscuit, root.0) + .map(Biscuit) + .map(Box::new) + .ok() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_serialized_size(biscuit: Option<&Biscuit>) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + match biscuit.0.serialized_size() { + Ok(sz) => sz, + Err(e) => { + update_last_error(Error::Biscuit(e)); + return 0; + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_sealed_size(biscuit: Option<&Biscuit>) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + match biscuit.0.serialized_size() { + Ok(sz) => sz, + Err(e) => { + update_last_error(Error::Biscuit(e)); + return 0; + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_serialize( + biscuit: Option<&Biscuit>, + buffer_ptr: *mut u8, +) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + match (*biscuit).0.to_vec() { + Ok(v) => { + let size = match biscuit.0.serialized_size() { + Ok(sz) => sz, + Err(e) => { + update_last_error(Error::Biscuit(e)); + return 0; + } + }; + + let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, size); + + output_slice.copy_from_slice(&v[..]); + v.len() + } + Err(e) => { + update_last_error(Error::Biscuit(e)); + 0 + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_serialize_sealed( + biscuit: Option<&Biscuit>, + buffer_ptr: *mut u8, +) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + match (*biscuit).0.seal() { + Ok(b) => match b.to_vec() { + Ok(v) => { + let size = match biscuit.0.serialized_size() { + Ok(sz) => sz, + Err(e) => { + update_last_error(Error::Biscuit(e)); + return 0; + } + }; + + let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, size); + + output_slice.copy_from_slice(&v[..]); + v.len() + } + Err(e) => { + update_last_error(Error::Biscuit(e)); + 0 + } + }, + + Err(e) => { + update_last_error(Error::Biscuit(e)); + 0 + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_count(biscuit: Option<&Biscuit>) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + biscuit.0.blocks.len() + 1 +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_fact_count( + biscuit: Option<&Biscuit>, + block_index: u32, +) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + let block = match biscuit.0.block(block_index as usize) { + Ok(block) => block, + Err(e) => { + update_last_error(e.into()); + return 0; + } + }; + + block.facts.len() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_rule_count( + biscuit: Option<&Biscuit>, + block_index: u32, +) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + let block = match biscuit.0.block(block_index as usize) { + Ok(block) => block, + Err(e) => { + update_last_error(e.into()); + return 0; + } + }; + + block.rules.len() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_check_count( + biscuit: Option<&Biscuit>, + block_index: u32, +) -> usize { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return 0; + } + + let biscuit = biscuit.unwrap(); + + let block = match biscuit.0.block(block_index as usize) { + Ok(block) => block, + Err(e) => { + update_last_error(e.into()); + return 0; + } + }; + + block.checks.len() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_fact( + biscuit: Option<&Biscuit>, + block_index: u32, + fact_index: u32, +) -> *mut c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + + let biscuit = biscuit.unwrap(); + + let block = match biscuit.0.block(block_index as usize) { + Ok(block) => block, + Err(e) => { + update_last_error(e.into()); + return std::ptr::null_mut(); + } + }; + + match block.facts.get(fact_index as usize) { + None => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + Some(fact) => match CString::new(biscuit.0.symbols.print_fact(fact)) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_rule( + biscuit: Option<&Biscuit>, + block_index: u32, + rule_index: u32, +) -> *mut c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + + let biscuit = biscuit.unwrap(); + + let block = match biscuit.0.block(block_index as usize) { + Ok(block) => block, + Err(e) => { + update_last_error(e.into()); + return std::ptr::null_mut(); + } + }; + + match block.rules.get(rule_index as usize) { + None => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + Some(rule) => match CString::new(biscuit.0.symbols.print_rule(rule)) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_check( + biscuit: Option<&Biscuit>, + block_index: u32, + check_index: u32, +) -> *mut c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + + let biscuit = biscuit.unwrap(); + + let block = match biscuit.0.block(block_index as usize) { + Ok(block) => block, + Err(e) => { + update_last_error(e.into()); + return std::ptr::null_mut(); + } + }; + + match block.checks.get(check_index as usize) { + None => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + Some(check) => match CString::new(biscuit.0.symbols.print_check(check)) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_block_context( + biscuit: Option<&Biscuit>, + block_index: u32, +) -> *mut c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + + let biscuit = biscuit.unwrap(); + + let block = if block_index == 0 { + &biscuit.0.authority + } else { + match biscuit.0.blocks.get(block_index as usize - 1) { + Some(b) => b, + None => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + } + }; + + match &block.context { + None => { + return std::ptr::null_mut(); + } + Some(context) => match CString::new(context.clone()) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn create_block() -> Box { + Box::new(BlockBuilder( + biscuit_auth::token::builder::BlockBuilder::new(), + )) +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_append_block( + biscuit: Option<&Biscuit>, + block_builder: Option<&BlockBuilder>, + key_pair: Option<&KeyPair>, +) -> Option> { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + } + let biscuit = biscuit?; + + if block_builder.is_none() { + update_last_error(Error::InvalidArgument); + } + let builder = block_builder?; + + if key_pair.is_none() { + update_last_error(Error::InvalidArgument); + } + let key_pair = key_pair?; + + match biscuit + .0 + .append_with_keypair(&key_pair.0, builder.0.clone()) + { + Ok(token) => Some(Box::new(Biscuit(token))), + Err(e) => { + update_last_error(Error::Biscuit(e)); + None + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_authorizer<'a>( + biscuit: Option<&'a Biscuit>, +) -> Option> { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + } + let biscuit = biscuit?; + + (*biscuit).0.authorizer().map(Authorizer).map(Box::new).ok() +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_free(_biscuit: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn block_builder_set_context( + builder: Option<&mut BlockBuilder>, + context: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let context = CStr::from_ptr(context); + let s = context.to_str(); + match s { + Err(_) => { + update_last_error(Error::InvalidArgument); + false + } + Ok(context) => { + builder.0.set_context(context.to_string()); + true + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn block_builder_add_fact( + builder: Option<&mut BlockBuilder>, + fact: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let fact = CStr::from_ptr(fact); + let s = fact.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + builder + .0 + .add_fact(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn block_builder_add_rule( + builder: Option<&mut BlockBuilder>, + rule: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let rule = CStr::from_ptr(rule); + let s = rule.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + builder + .0 + .add_rule(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn block_builder_add_check( + builder: Option<&mut BlockBuilder>, + check: *const c_char, +) -> bool { + if builder.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let builder = builder.unwrap(); + + let check = CStr::from_ptr(check); + let s = check.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + builder + .0 + .add_check(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn block_builder_free(_builder: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_add_fact( + authorizer: Option<&mut Authorizer>, + fact: *const c_char, +) -> bool { + if authorizer.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let authorizer = authorizer.unwrap(); + + let fact = CStr::from_ptr(fact); + let s = fact.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + authorizer + .0 + .add_fact(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_add_rule( + authorizer: Option<&mut Authorizer>, + rule: *const c_char, +) -> bool { + if authorizer.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let authorizer = authorizer.unwrap(); + + let rule = CStr::from_ptr(rule); + let s = rule.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + authorizer + .0 + .add_rule(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_add_check( + authorizer: Option<&mut Authorizer>, + check: *const c_char, +) -> bool { + if authorizer.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let authorizer = authorizer.unwrap(); + + let check = CStr::from_ptr(check); + let s = check.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + authorizer + .0 + .add_check(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_add_policy( + authorizer: Option<&mut Authorizer>, + policy: *const c_char, +) -> bool { + if authorizer.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let authorizer = authorizer.unwrap(); + + let policy = CStr::from_ptr(policy); + let s = policy.to_str(); + if s.is_err() { + update_last_error(Error::InvalidArgument); + return false; + } + + authorizer + .0 + .add_policy(s.unwrap()) + .map_err(|e| { + update_last_error(Error::Biscuit(e)); + }) + .is_ok() +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_authorize(authorizer: Option<&mut Authorizer>) -> bool { + if authorizer.is_none() { + update_last_error(Error::InvalidArgument); + return false; + } + let authorizer = authorizer.unwrap(); + + match authorizer.0.authorize() { + Ok(_index) => true, + Err(e) => { + update_last_error(Error::Biscuit(e)); + false + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_print(authorizer: Option<&mut Authorizer>) -> *mut c_char { + if authorizer.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + let authorizer = authorizer.unwrap(); + + match CString::new(authorizer.0.print_world()) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + return std::ptr::null_mut(); + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn authorizer_free(_authorizer: Option>) {} + +#[no_mangle] +pub unsafe extern "C" fn string_free(ptr: *mut c_char) { + if ptr != std::ptr::null_mut() { + drop(CString::from_raw(ptr)); + } +} + +#[no_mangle] +pub unsafe extern "C" fn biscuit_print(biscuit: Option<&Biscuit>) -> *const c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null(); + } + let biscuit = biscuit.unwrap(); + + match CString::new(biscuit.0.print()) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + return std::ptr::null(); + } + } +} diff --git a/biscuit-auth/tests/capi.rs b/biscuit-capi/tests/capi.rs similarity index 100% rename from biscuit-auth/tests/capi.rs rename to biscuit-capi/tests/capi.rs From dbaecbb0c18529e5a3d0151a40401389dcc405d5 Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Wed, 23 Oct 2024 22:01:47 +0200 Subject: [PATCH 2/6] reduce Biscuit visibility --- biscuit-auth/src/datalog/symbol.rs | 1 + biscuit-auth/src/lib.rs | 4 +- biscuit-auth/src/token/mod.rs | 19 +++++++-- biscuit-capi/src/lib.rs | 66 +++++++++++++++--------------- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/biscuit-auth/src/datalog/symbol.rs b/biscuit-auth/src/datalog/symbol.rs index ab2e4df3..a0b41ac2 100644 --- a/biscuit-auth/src/datalog/symbol.rs +++ b/biscuit-auth/src/datalog/symbol.rs @@ -206,6 +206,7 @@ impl SymbolTable { } } } + pub fn print_fact(&self, f: &Fact) -> String { self.print_predicate(&f.predicate) } diff --git a/biscuit-auth/src/lib.rs b/biscuit-auth/src/lib.rs index 89577c27..dc6bd64a 100644 --- a/biscuit-auth/src/lib.rs +++ b/biscuit-auth/src/lib.rs @@ -219,12 +219,12 @@ //! biscuit implementations come with a default symbol table to avoid transmitting //! frequent values with every token. -pub mod crypto; +mod crypto; pub mod datalog; pub mod error; pub mod format; pub mod parser; -pub mod token; +mod token; pub use crypto::{KeyPair, PrivateKey, PublicKey}; pub use token::authorizer::{Authorizer, AuthorizerLimits}; diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index ce6fcb19..8951b2e5 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -73,9 +73,9 @@ pub fn default_symbol_table() -> SymbolTable { #[derive(Clone, Debug)] pub struct Biscuit { pub(crate) root_key_id: Option, - pub authority: schema::Block, - pub blocks: Vec, - pub symbols: SymbolTable, + pub(crate) authority: schema::Block, + pub(crate) blocks: Vec, + pub(crate) symbols: SymbolTable, pub(crate) container: SerializedBiscuit, } @@ -221,6 +221,19 @@ impl Biscuit { }) } + /// prints the content of a block as a protobuf message + pub fn print_fact(&self, fact: &crate::datalog::Fact) -> String { + self.symbols.print_fact(fact) + } + + pub fn print_rule(&self, rule: &crate::datalog::Rule) -> String { + self.symbols.print_rule(rule) + } + + pub fn print_check(&self, check: &crate::datalog::Check) -> String { + self.symbols.print_check(check) + } + /// creates a new token, using a provided CSPRNG /// /// the public part of the root keypair must be used for verification diff --git a/biscuit-capi/src/lib.rs b/biscuit-capi/src/lib.rs index 5b400246..06ba8b6e 100644 --- a/biscuit-capi/src/lib.rs +++ b/biscuit-capi/src/lib.rs @@ -288,12 +288,12 @@ pub extern "C" fn error_check_is_authorizer(check_index: u64) -> bool { }) } -pub struct Biscuit(biscuit_auth::token::Biscuit); -pub struct KeyPair(biscuit_auth::crypto::KeyPair); -pub struct PublicKey(biscuit_auth::crypto::PublicKey); -pub struct BiscuitBuilder(biscuit_auth::token::builder::BiscuitBuilder); -pub struct BlockBuilder(biscuit_auth::token::builder::BlockBuilder); -pub struct Authorizer(biscuit_auth::token::authorizer::Authorizer); +pub struct Biscuit(biscuit_auth::Biscuit); +pub struct KeyPair(biscuit_auth::KeyPair); +pub struct PublicKey(biscuit_auth::PublicKey); +pub struct BiscuitBuilder(biscuit_auth::builder::BiscuitBuilder); +pub struct BlockBuilder(biscuit_auth::builder::BlockBuilder); +pub struct Authorizer(biscuit_auth::Authorizer); #[no_mangle] pub unsafe extern "C" fn key_pair_new<'a>( @@ -311,9 +311,9 @@ pub unsafe extern "C" fn key_pair_new<'a>( let mut rng: StdRng = SeedableRng::from_seed(seed); - Some(Box::new(KeyPair( - biscuit_auth::crypto::KeyPair::new_with_rng(&mut rng), - ))) + Some(Box::new(KeyPair(biscuit_auth::KeyPair::new_with_rng( + &mut rng, + )))) } #[no_mangle] @@ -346,14 +346,12 @@ pub unsafe extern "C" fn key_pair_serialize(kp: Option<&KeyPair>, buffer_ptr: *m pub unsafe extern "C" fn key_pair_deserialize(buffer_ptr: *mut u8) -> Option> { let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - match biscuit_auth::crypto::PrivateKey::from_bytes(input_slice).ok() { + match biscuit_auth::PrivateKey::from_bytes(input_slice).ok() { None => { update_last_error(Error::InvalidArgument); None } - Some(privkey) => Some(Box::new(KeyPair(biscuit_auth::crypto::KeyPair::from( - &privkey, - )))), + Some(privkey) => Some(Box::new(KeyPair(biscuit_auth::KeyPair::from(&privkey)))), } } @@ -383,7 +381,7 @@ pub unsafe extern "C" fn public_key_serialize( pub unsafe extern "C" fn public_key_deserialize(buffer_ptr: *mut u8) -> Option> { let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - match biscuit_auth::crypto::PublicKey::from_bytes(input_slice).ok() { + match biscuit_auth::PublicKey::from_bytes(input_slice).ok() { None => { update_last_error(Error::InvalidArgument); None @@ -397,9 +395,7 @@ pub unsafe extern "C" fn public_key_free(_kp: Option>) {} #[no_mangle] pub unsafe extern "C" fn biscuit_builder() -> Option> { - Some(Box::new(BiscuitBuilder( - biscuit_auth::token::Biscuit::builder(), - ))) + Some(Box::new(BiscuitBuilder(biscuit_auth::Biscuit::builder()))) } #[no_mangle] @@ -573,7 +569,7 @@ pub unsafe extern "C" fn biscuit_from<'a>( } let root = root?; - biscuit_auth::token::Biscuit::from(biscuit, root.0) + biscuit_auth::Biscuit::from(biscuit, root.0) .map(Biscuit) .map(Box::new) .ok() @@ -699,7 +695,7 @@ pub unsafe extern "C" fn biscuit_block_count(biscuit: Option<&Biscuit>) -> usize let biscuit = biscuit.unwrap(); - biscuit.0.blocks.len() + 1 + biscuit.0.block_count() } #[no_mangle] @@ -797,7 +793,7 @@ pub unsafe extern "C" fn biscuit_block_fact( update_last_error(Error::InvalidArgument); return std::ptr::null_mut(); } - Some(fact) => match CString::new(biscuit.0.symbols.print_fact(fact)) { + Some(fact) => match CString::new(biscuit.0.print_fact(fact)) { Ok(s) => s.into_raw(), Err(_) => { update_last_error(Error::InvalidArgument); @@ -833,7 +829,7 @@ pub unsafe extern "C" fn biscuit_block_rule( update_last_error(Error::InvalidArgument); return std::ptr::null_mut(); } - Some(rule) => match CString::new(biscuit.0.symbols.print_rule(rule)) { + Some(rule) => match CString::new(biscuit.0.print_rule(rule)) { Ok(s) => s.into_raw(), Err(_) => { update_last_error(Error::InvalidArgument); @@ -869,7 +865,7 @@ pub unsafe extern "C" fn biscuit_block_check( update_last_error(Error::InvalidArgument); return std::ptr::null_mut(); } - Some(check) => match CString::new(biscuit.0.symbols.print_check(check)) { + Some(check) => match CString::new(biscuit.0.print_check(check)) { Ok(s) => s.into_raw(), Err(_) => { update_last_error(Error::InvalidArgument); @@ -892,26 +888,30 @@ pub unsafe extern "C" fn biscuit_block_context( let biscuit = biscuit.unwrap(); let block = if block_index == 0 { - &biscuit.0.authority + match biscuit.0.block(0) { + Ok(b) => b, + Err(err) => { + update_last_error(Error::Biscuit(err)); + return std::ptr::null_mut(); + } + } } else { - match biscuit.0.blocks.get(block_index as usize - 1) { - Some(b) => b, - None => { - update_last_error(Error::InvalidArgument); + match biscuit.0.block(block_index as usize - 1) { + Ok(b) => b, + Err(err) => { + update_last_error(Error::Biscuit(err)); return std::ptr::null_mut(); } } }; match &block.context { - None => { - return std::ptr::null_mut(); - } + None => std::ptr::null_mut(), Some(context) => match CString::new(context.clone()) { Ok(s) => s.into_raw(), Err(_) => { update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); + std::ptr::null_mut() } }, } @@ -919,9 +919,7 @@ pub unsafe extern "C" fn biscuit_block_context( #[no_mangle] pub unsafe extern "C" fn create_block() -> Box { - Box::new(BlockBuilder( - biscuit_auth::token::builder::BlockBuilder::new(), - )) + Box::new(BlockBuilder(biscuit_auth::builder::BlockBuilder::new())) } #[no_mangle] From 90a21801c041f59eeff63f04cd5b85cc42b3b437 Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Thu, 24 Oct 2024 10:34:22 +0200 Subject: [PATCH 3/6] remove capi.rs --- biscuit-auth/src/capi.rs | 1245 -------------------------------------- 1 file changed, 1245 deletions(-) delete mode 100644 biscuit-auth/src/capi.rs diff --git a/biscuit-auth/src/capi.rs b/biscuit-auth/src/capi.rs deleted file mode 100644 index 3a80cbba..00000000 --- a/biscuit-auth/src/capi.rs +++ /dev/null @@ -1,1245 +0,0 @@ -use rand::prelude::*; -use std::{ - cell::RefCell, - ffi::{CStr, CString}, - fmt, - os::raw::c_char, -}; - -use crate::datalog::SymbolTable; - -enum Error { - Biscuit(crate::error::Token), - InvalidArgument, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::InvalidArgument => write!(f, "invalid argument"), - Error::Biscuit(e) => write!(f, "{}", e), - } - } -} - -impl From for Error { - fn from(error: crate::error::Token) -> Self { - Error::Biscuit(error) - } -} - -thread_local! { - static LAST_ERROR: RefCell> = RefCell::new(None); -} - -fn update_last_error(err: Error) { - LAST_ERROR.with(|prev| { - *prev.borrow_mut() = Some(err); - }); -} - -#[no_mangle] -pub extern "C" fn error_message() -> *const c_char { - thread_local! { - static LAST: RefCell> = RefCell::new(None); - } - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(ref err) => { - let err = CString::new(err.to_string()).ok(); - LAST.with(|ret| { - *ret.borrow_mut() = err; - ret.borrow() - .as_ref() - .map(|x| x.as_ptr()) - .unwrap_or(std::ptr::null()) - }) - } - None => std::ptr::null(), - }) -} - -#[repr(C)] -pub enum ErrorKind { - None, - InvalidArgument, - InternalError, - FormatSignatureInvalidFormat, - FormatSignatureInvalidSignature, - FormatSealedSignature, - FormatEmptyKeys, - FormatUnknownPublicKey, - FormatDeserializationError, - FormatSerializationError, - FormatBlockDeserializationError, - FormatBlockSerializationError, - FormatVersion, - FormatInvalidBlockId, - FormatExistingPublicKey, - FormatSymbolTableOverlap, - FormatPublicKeyTableOverlap, - FormatUnknownExternalKey, - FormatUnknownSymbol, - AppendOnSealed, - LogicInvalidBlockRule, - LogicUnauthorized, - LogicAuthorizerNotEmpty, - LogicNoMatchingPolicy, - LanguageError, - TooManyFacts, - TooManyIterations, - Timeout, - ConversionError, - FormatInvalidKeySize, - FormatInvalidSignatureSize, - FormatInvalidKey, - FormatSignatureDeserializationError, - FormatBlockSignatureDeserializationError, - FormatSignatureInvalidSignatureGeneration, - AlreadySealed, - Execution, -} - -#[no_mangle] -pub extern "C" fn error_kind() -> ErrorKind { - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(ref err) => match err { - Error::InvalidArgument => ErrorKind::InvalidArgument, - Error::Biscuit(e) => { - use crate::error::*; - match e { - Token::InternalError => ErrorKind::InternalError, - Token::Format(Format::Signature(Signature::InvalidFormat)) => { - ErrorKind::FormatSignatureInvalidFormat - } - Token::Format(Format::Signature(Signature::InvalidSignature(_))) => { - ErrorKind::FormatSignatureInvalidSignature - } - Token::Format(Format::Signature(Signature::InvalidSignatureGeneration(_))) => { - ErrorKind::FormatSignatureInvalidSignatureGeneration - } - Token::Format(Format::SealedSignature) => ErrorKind::FormatSealedSignature, - Token::Format(Format::EmptyKeys) => ErrorKind::FormatEmptyKeys, - Token::Format(Format::UnknownPublicKey) => ErrorKind::FormatUnknownPublicKey, - Token::Format(Format::DeserializationError(_)) => { - ErrorKind::FormatDeserializationError - } - Token::Format(Format::SerializationError(_)) => { - ErrorKind::FormatSerializationError - } - Token::Format(Format::BlockDeserializationError(_)) => { - ErrorKind::FormatBlockDeserializationError - } - Token::Format(Format::BlockSerializationError(_)) => { - ErrorKind::FormatBlockSerializationError - } - Token::Format(Format::Version { .. }) => ErrorKind::FormatVersion, - Token::Format(Format::InvalidKeySize(_)) => ErrorKind::FormatInvalidKeySize, - Token::Format(Format::InvalidSignatureSize(_)) => { - ErrorKind::FormatInvalidSignatureSize - } - Token::Format(Format::InvalidKey(_)) => ErrorKind::FormatInvalidKey, - Token::Format(Format::SignatureDeserializationError(_)) => { - ErrorKind::FormatSignatureDeserializationError - } - Token::Format(Format::BlockSignatureDeserializationError(_)) => { - ErrorKind::FormatBlockSignatureDeserializationError - } - Token::Format(Format::InvalidBlockId(_)) => ErrorKind::FormatInvalidBlockId, - Token::Format(Format::ExistingPublicKey(_)) => { - ErrorKind::FormatExistingPublicKey - } - Token::Format(Format::SymbolTableOverlap) => { - ErrorKind::FormatSymbolTableOverlap - } - Token::Format(Format::PublicKeyTableOverlap) => { - ErrorKind::FormatPublicKeyTableOverlap - } - Token::Format(Format::UnknownExternalKey) => { - ErrorKind::FormatUnknownExternalKey - } - Token::Format(Format::UnknownSymbol(_)) => ErrorKind::FormatUnknownSymbol, - Token::AppendOnSealed => ErrorKind::AppendOnSealed, - Token::AlreadySealed => ErrorKind::AlreadySealed, - Token::Language(_) => ErrorKind::LanguageError, - Token::FailedLogic(Logic::InvalidBlockRule(_, _)) => { - ErrorKind::LogicInvalidBlockRule - } - Token::FailedLogic(Logic::Unauthorized { .. }) => ErrorKind::LogicUnauthorized, - Token::FailedLogic(Logic::AuthorizerNotEmpty) => { - ErrorKind::LogicAuthorizerNotEmpty - } - Token::FailedLogic(Logic::NoMatchingPolicy { .. }) => { - ErrorKind::LogicNoMatchingPolicy - } - Token::RunLimit(RunLimit::TooManyFacts) => ErrorKind::TooManyFacts, - Token::RunLimit(RunLimit::TooManyIterations) => ErrorKind::TooManyIterations, - Token::RunLimit(RunLimit::Timeout) => ErrorKind::Timeout, - Token::ConversionError(_) => ErrorKind::ConversionError, - Token::Base64(_) => ErrorKind::FormatDeserializationError, - Token::Execution(_) => ErrorKind::Execution, - } - } - }, - None => ErrorKind::None, - }) -} - -#[no_mangle] -pub extern "C" fn error_check_count() -> u64 { - use crate::error::*; - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) - | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { - checks.len() as u64 - } - _ => 0, - }) -} - -#[no_mangle] -pub extern "C" fn error_check_id(check_index: u64) -> u64 { - use crate::error::*; - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) - | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { - if check_index >= checks.len() as u64 { - u64::MAX - } else { - match checks[check_index as usize] { - FailedCheck::Block(FailedBlockCheck { check_id, .. }) => check_id as u64, - FailedCheck::Authorizer(FailedAuthorizerCheck { check_id, .. }) => { - check_id as u64 - } - } - } - } - _ => u64::MAX, - }) -} - -#[no_mangle] -pub extern "C" fn error_check_block_id(check_index: u64) -> u64 { - use crate::error::*; - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) - | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { - if check_index >= checks.len() as u64 { - u64::MAX - } else { - match checks[check_index as usize] { - FailedCheck::Block(FailedBlockCheck { block_id, .. }) => block_id as u64, - _ => u64::MAX, - } - } - } - _ => u64::MAX, - }) -} - -/// deallocation is handled by Biscuit -/// the string is overwritten on each call -#[no_mangle] -pub extern "C" fn error_check_rule(check_index: u64) -> *const c_char { - use crate::error::*; - thread_local! { - static CAVEAT_RULE: RefCell> = RefCell::new(None); - } - - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) - | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { - if check_index >= checks.len() as u64 { - std::ptr::null() - } else { - let rule = match &checks[check_index as usize] { - FailedCheck::Block(FailedBlockCheck { rule, .. }) => rule, - FailedCheck::Authorizer(FailedAuthorizerCheck { rule, .. }) => rule, - }; - let err = CString::new(rule.clone()).ok(); - CAVEAT_RULE.with(|ret| { - *ret.borrow_mut() = err; - ret.borrow() - .as_ref() - .map(|x| x.as_ptr()) - .unwrap_or(std::ptr::null()) - }) - } - } - _ => std::ptr::null(), - }) -} - -#[no_mangle] -pub extern "C" fn error_check_is_authorizer(check_index: u64) -> bool { - use crate::error::*; - LAST_ERROR.with(|prev| match *prev.borrow() { - Some(Error::Biscuit(Token::FailedLogic(Logic::Unauthorized { ref checks, .. }))) - | Some(Error::Biscuit(Token::FailedLogic(Logic::NoMatchingPolicy { ref checks }))) => { - if check_index >= checks.len() as u64 { - false - } else { - match checks[check_index as usize] { - FailedCheck::Block(FailedBlockCheck { .. }) => false, - FailedCheck::Authorizer(FailedAuthorizerCheck { .. }) => true, - } - } - } - _ => false, - }) -} - -pub struct Biscuit(crate::token::Biscuit); -pub struct KeyPair(crate::crypto::KeyPair); -pub struct PublicKey(crate::crypto::PublicKey); -pub struct BiscuitBuilder(crate::token::builder::BiscuitBuilder); -pub struct BlockBuilder(crate::token::builder::BlockBuilder); -pub struct Authorizer(crate::token::authorizer::Authorizer); - -#[no_mangle] -pub unsafe extern "C" fn key_pair_new<'a>( - seed_ptr: *const u8, - seed_len: usize, -) -> Option> { - let slice = std::slice::from_raw_parts(seed_ptr, seed_len); - if slice.len() != 32 { - update_last_error(Error::InvalidArgument); - return None; - } - - let mut seed = [0u8; 32]; - seed.copy_from_slice(slice); - - let mut rng: StdRng = SeedableRng::from_seed(seed); - - Some(Box::new(KeyPair(crate::crypto::KeyPair::new_with_rng( - &mut rng, - )))) -} - -#[no_mangle] -pub unsafe extern "C" fn key_pair_public(kp: Option<&KeyPair>) -> Option> { - if kp.is_none() { - update_last_error(Error::InvalidArgument); - } - let kp = kp?; - - Some(Box::new(PublicKey((*kp).0.public()))) -} - -/// expects a 32 byte buffer -#[no_mangle] -pub unsafe extern "C" fn key_pair_serialize(kp: Option<&KeyPair>, buffer_ptr: *mut u8) -> usize { - if kp.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - let kp = kp.unwrap(); - - let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - - output_slice.copy_from_slice(&kp.0.private().to_bytes()[..]); - 32 -} - -/// expects a 32 byte buffer -#[no_mangle] -pub unsafe extern "C" fn key_pair_deserialize(buffer_ptr: *mut u8) -> Option> { - let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - - match crate::crypto::PrivateKey::from_bytes(input_slice).ok() { - None => { - update_last_error(Error::InvalidArgument); - None - } - Some(privkey) => Some(Box::new(KeyPair(crate::crypto::KeyPair::from(&privkey)))), - } -} - -#[no_mangle] -pub unsafe extern "C" fn key_pair_free(_kp: Option>) {} - -/// expects a 32 byte buffer -#[no_mangle] -pub unsafe extern "C" fn public_key_serialize( - kp: Option<&PublicKey>, - buffer_ptr: *mut u8, -) -> usize { - if kp.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - let kp = kp.unwrap(); - - let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - - output_slice.copy_from_slice(&kp.0.to_bytes()[..]); - 32 -} - -/// expects a 32 byte buffer -#[no_mangle] -pub unsafe extern "C" fn public_key_deserialize(buffer_ptr: *mut u8) -> Option> { - let input_slice = std::slice::from_raw_parts_mut(buffer_ptr, 32); - - match crate::crypto::PublicKey::from_bytes(input_slice).ok() { - None => { - update_last_error(Error::InvalidArgument); - None - } - Some(pubkey) => Some(Box::new(PublicKey(pubkey))), - } -} - -#[no_mangle] -pub unsafe extern "C" fn public_key_free(_kp: Option>) {} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder() -> Option> { - Some(Box::new(BiscuitBuilder(crate::token::Biscuit::builder()))) -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_set_context( - builder: Option<&mut BiscuitBuilder>, - context: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let context = CStr::from_ptr(context); - let s = context.to_str(); - match s { - Err(_) => { - update_last_error(Error::InvalidArgument); - false - } - Ok(context) => { - builder.0.set_context(context.to_string()); - true - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_set_root_key_id( - builder: Option<&mut BiscuitBuilder>, - root_key_id: u32, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - builder.0.set_root_key_id(root_key_id); - true -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_add_fact( - builder: Option<&mut BiscuitBuilder>, - fact: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let fact = CStr::from_ptr(fact); - let s = fact.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - builder - .0 - .add_fact(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_add_rule( - builder: Option<&mut BiscuitBuilder>, - rule: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let rule = CStr::from_ptr(rule); - let s = rule.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - builder - .0 - .add_rule(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_add_check( - builder: Option<&mut BiscuitBuilder>, - check: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let check = CStr::from_ptr(check); - let s = check.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - builder - .0 - .add_check(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_build( - builder: Option<&BiscuitBuilder>, - key_pair: Option<&KeyPair>, - seed_ptr: *const u8, - seed_len: usize, -) -> Option> { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - } - let builder = builder?; - - if key_pair.is_none() { - update_last_error(Error::InvalidArgument); - } - let key_pair = key_pair?; - - let slice = std::slice::from_raw_parts(seed_ptr, seed_len); - if slice.len() != 32 { - return None; - } - - let mut seed = [0u8; 32]; - seed.copy_from_slice(slice); - - let mut rng: StdRng = SeedableRng::from_seed(seed); - (*builder) - .0 - .clone() - .build_with_rng(&key_pair.0, SymbolTable::default(), &mut rng) - .map(Biscuit) - .map(Box::new) - .ok() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_builder_free<'a>(_builder: Option>) {} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_from<'a>( - biscuit_ptr: *const u8, - biscuit_len: usize, - root: Option<&'a PublicKey>, -) -> Option> { - let biscuit = std::slice::from_raw_parts(biscuit_ptr, biscuit_len); - if root.is_none() { - update_last_error(Error::InvalidArgument); - } - let root = root?; - - crate::token::Biscuit::from(biscuit, root.0) - .map(Biscuit) - .map(Box::new) - .ok() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_serialized_size(biscuit: Option<&Biscuit>) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - match biscuit.0.serialized_size() { - Ok(sz) => sz, - Err(e) => { - update_last_error(Error::Biscuit(e)); - return 0; - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_sealed_size(biscuit: Option<&Biscuit>) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - match biscuit.0.serialized_size() { - Ok(sz) => sz, - Err(e) => { - update_last_error(Error::Biscuit(e)); - return 0; - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_serialize( - biscuit: Option<&Biscuit>, - buffer_ptr: *mut u8, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - match (*biscuit).0.to_vec() { - Ok(v) => { - let size = match biscuit.0.serialized_size() { - Ok(sz) => sz, - Err(e) => { - update_last_error(Error::Biscuit(e)); - return 0; - } - }; - - let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, size); - - output_slice.copy_from_slice(&v[..]); - v.len() - } - Err(e) => { - update_last_error(Error::Biscuit(e)); - 0 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_serialize_sealed( - biscuit: Option<&Biscuit>, - buffer_ptr: *mut u8, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - match (*biscuit).0.seal() { - Ok(b) => match b.to_vec() { - Ok(v) => { - let size = match biscuit.0.serialized_size() { - Ok(sz) => sz, - Err(e) => { - update_last_error(Error::Biscuit(e)); - return 0; - } - }; - - let output_slice = std::slice::from_raw_parts_mut(buffer_ptr, size); - - output_slice.copy_from_slice(&v[..]); - v.len() - } - Err(e) => { - update_last_error(Error::Biscuit(e)); - 0 - } - }, - - Err(e) => { - update_last_error(Error::Biscuit(e)); - 0 - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_count(biscuit: Option<&Biscuit>) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - biscuit.0.blocks.len() + 1 -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_fact_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.facts.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_rule_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.rules.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_check_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.checks.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_fact( - biscuit: Option<&Biscuit>, - block_index: u32, - fact_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.facts.get(fact_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(fact) => match CString::new(biscuit.0.symbols.print_fact(fact)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_rule( - biscuit: Option<&Biscuit>, - block_index: u32, - rule_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.rules.get(rule_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(rule) => match CString::new(biscuit.0.symbols.print_rule(rule)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_check( - biscuit: Option<&Biscuit>, - block_index: u32, - check_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.checks.get(check_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(check) => match CString::new(biscuit.0.symbols.print_check(check)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_context( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = if block_index == 0 { - &biscuit.0.authority - } else { - match biscuit.0.blocks.get(block_index as usize - 1) { - Some(b) => b, - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - } - }; - - match &block.context { - None => { - return std::ptr::null_mut(); - } - Some(context) => match CString::new(context.clone()) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn create_block() -> Box { - Box::new(BlockBuilder(crate::token::builder::BlockBuilder::new())) -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_append_block( - biscuit: Option<&Biscuit>, - block_builder: Option<&BlockBuilder>, - key_pair: Option<&KeyPair>, -) -> Option> { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - } - let biscuit = biscuit?; - - if block_builder.is_none() { - update_last_error(Error::InvalidArgument); - } - let builder = block_builder?; - - if key_pair.is_none() { - update_last_error(Error::InvalidArgument); - } - let key_pair = key_pair?; - - match biscuit - .0 - .append_with_keypair(&key_pair.0, builder.0.clone()) - { - Ok(token) => Some(Box::new(Biscuit(token))), - Err(e) => { - update_last_error(Error::Biscuit(e)); - None - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_authorizer<'a>( - biscuit: Option<&'a Biscuit>, -) -> Option> { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - } - let biscuit = biscuit?; - - (*biscuit).0.authorizer().map(Authorizer).map(Box::new).ok() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_free(_biscuit: Option>) {} - -#[no_mangle] -pub unsafe extern "C" fn block_builder_set_context( - builder: Option<&mut BlockBuilder>, - context: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let context = CStr::from_ptr(context); - let s = context.to_str(); - match s { - Err(_) => { - update_last_error(Error::InvalidArgument); - false - } - Ok(context) => { - builder.0.set_context(context.to_string()); - true - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn block_builder_add_fact( - builder: Option<&mut BlockBuilder>, - fact: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let fact = CStr::from_ptr(fact); - let s = fact.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - builder - .0 - .add_fact(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn block_builder_add_rule( - builder: Option<&mut BlockBuilder>, - rule: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let rule = CStr::from_ptr(rule); - let s = rule.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - builder - .0 - .add_rule(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn block_builder_add_check( - builder: Option<&mut BlockBuilder>, - check: *const c_char, -) -> bool { - if builder.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let builder = builder.unwrap(); - - let check = CStr::from_ptr(check); - let s = check.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - builder - .0 - .add_check(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn block_builder_free(_builder: Option>) {} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_add_fact( - authorizer: Option<&mut Authorizer>, - fact: *const c_char, -) -> bool { - if authorizer.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let authorizer = authorizer.unwrap(); - - let fact = CStr::from_ptr(fact); - let s = fact.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - authorizer - .0 - .add_fact(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_add_rule( - authorizer: Option<&mut Authorizer>, - rule: *const c_char, -) -> bool { - if authorizer.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let authorizer = authorizer.unwrap(); - - let rule = CStr::from_ptr(rule); - let s = rule.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - authorizer - .0 - .add_rule(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_add_check( - authorizer: Option<&mut Authorizer>, - check: *const c_char, -) -> bool { - if authorizer.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let authorizer = authorizer.unwrap(); - - let check = CStr::from_ptr(check); - let s = check.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - authorizer - .0 - .add_check(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_add_policy( - authorizer: Option<&mut Authorizer>, - policy: *const c_char, -) -> bool { - if authorizer.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let authorizer = authorizer.unwrap(); - - let policy = CStr::from_ptr(policy); - let s = policy.to_str(); - if s.is_err() { - update_last_error(Error::InvalidArgument); - return false; - } - - authorizer - .0 - .add_policy(s.unwrap()) - .map_err(|e| { - update_last_error(Error::Biscuit(e)); - }) - .is_ok() -} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_authorize(authorizer: Option<&mut Authorizer>) -> bool { - if authorizer.is_none() { - update_last_error(Error::InvalidArgument); - return false; - } - let authorizer = authorizer.unwrap(); - - match authorizer.0.authorize() { - Ok(_index) => true, - Err(e) => { - update_last_error(Error::Biscuit(e)); - false - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_print(authorizer: Option<&mut Authorizer>) -> *mut c_char { - if authorizer.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - let authorizer = authorizer.unwrap(); - - match CString::new(authorizer.0.print_world()) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn authorizer_free(_authorizer: Option>) {} - -#[no_mangle] -pub unsafe extern "C" fn string_free(ptr: *mut c_char) { - if ptr != std::ptr::null_mut() { - drop(CString::from_raw(ptr)); - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_print(biscuit: Option<&Biscuit>) -> *const c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null(); - } - let biscuit = biscuit.unwrap(); - - match CString::new(biscuit.0.print()) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null(); - } - } -} From 00dfed05c8cf2af9cb69052ba9b6436ff2169758 Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Sun, 3 Nov 2024 16:03:39 +0100 Subject: [PATCH 4/6] add `print_block_source` c binding --- .github/workflows/release.yml | 1 + biscuit-auth/src/token/mod.rs | 13 ---- biscuit-capi/src/lib.rs | 137 +++++++--------------------------- 3 files changed, 30 insertions(+), 121 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 507545e3..df39d659 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,7 @@ jobs: run: cargo install cargo-c --version 0.10.5+cargo-0.83.0 - name: Run cargo-c tests run: cargo ctest --release --features="capi" + if: matrix.os == 'ubuntu-latest' && matrix.target_arch == 'x86_64-unknown-linux-gnu' - name: Build C-API run: cargo cinstall --release --prefix=/usr --destdir=./build --features="capi" - name: Compress diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index 8951b2e5..59458a5a 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -221,19 +221,6 @@ impl Biscuit { }) } - /// prints the content of a block as a protobuf message - pub fn print_fact(&self, fact: &crate::datalog::Fact) -> String { - self.symbols.print_fact(fact) - } - - pub fn print_rule(&self, rule: &crate::datalog::Rule) -> String { - self.symbols.print_rule(rule) - } - - pub fn print_check(&self, check: &crate::datalog::Check) -> String { - self.symbols.print_check(check) - } - /// creates a new token, using a provided CSPRNG /// /// the public part of the root keypair must be used for verification diff --git a/biscuit-capi/src/lib.rs b/biscuit-capi/src/lib.rs index 06ba8b6e..5f14b83c 100644 --- a/biscuit-capi/src/lib.rs +++ b/biscuit-capi/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::missing_safety_doc)] use rand::prelude::*; use std::{ cell::RefCell, @@ -767,114 +768,6 @@ pub unsafe extern "C" fn biscuit_block_check_count( block.checks.len() } -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_fact( - biscuit: Option<&Biscuit>, - block_index: u32, - fact_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.facts.get(fact_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(fact) => match CString::new(biscuit.0.print_fact(fact)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_rule( - biscuit: Option<&Biscuit>, - block_index: u32, - rule_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.rules.get(rule_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(rule) => match CString::new(biscuit.0.print_rule(rule)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_check( - biscuit: Option<&Biscuit>, - block_index: u32, - check_index: u32, -) -> *mut c_char { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return std::ptr::null_mut(); - } - }; - - match block.checks.get(check_index as usize) { - None => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - Some(check) => match CString::new(biscuit.0.print_check(check)) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - return std::ptr::null_mut(); - } - }, - } -} - #[no_mangle] pub unsafe extern "C" fn biscuit_block_context( biscuit: Option<&Biscuit>, @@ -1247,3 +1140,31 @@ pub unsafe extern "C" fn biscuit_print(biscuit: Option<&Biscuit>) -> *const c_ch } } } + +#[no_mangle] +pub unsafe extern "C" fn biscuit_print_block_source( + biscuit: Option<&Biscuit>, + block_index: u32, +) -> *const c_char { + if biscuit.is_none() { + update_last_error(Error::InvalidArgument); + return std::ptr::null(); + } + let biscuit = biscuit.unwrap(); + + let block_source = match biscuit.0.print_block_source(block_index as usize) { + Ok(s) => s, + Err(e) => { + update_last_error(Error::Biscuit(e)); + return std::ptr::null(); + } + }; + + match CString::new(block_source) { + Ok(s) => s.into_raw(), + Err(_) => { + update_last_error(Error::InvalidArgument); + std::ptr::null() + } + } +} From 4bb076cd583decb03be81867d095fb8112ea2f7e Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Sun, 3 Nov 2024 16:43:29 +0100 Subject: [PATCH 5/6] add test for `biscuit_print_block_source` + use `inttypes.h` macros for handling cross-platform `uint64_t` type --- biscuit-capi/tests/capi.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/biscuit-capi/tests/capi.rs b/biscuit-capi/tests/capi.rs index 9d0a1c61..1694c629 100644 --- a/biscuit-capi/tests/capi.rs +++ b/biscuit-capi/tests/capi.rs @@ -8,6 +8,7 @@ mod capi { (assert_c! { #include #include + #include #include "biscuit_auth.h" int main() { @@ -51,19 +52,19 @@ mod capi { if(error_kind() == LogicUnauthorized) { uint64_t error_count = error_check_count(); - printf("failed checks (%ld):\n", error_count); + printf("failed checks (%" PRIu64 "):\n", error_count); for(uint64_t i = 0; i < error_count; i++) { if(error_check_is_authorizer(i)) { uint64_t check_id = error_check_id(i); const char* rule = error_check_rule(i); - printf(" Authorizer check %ld: %s\n", check_id, rule); + printf(" Authorizer check %" PRIu64 ": %s\n", check_id, rule); } else { uint64_t check_id = error_check_id(i); uint64_t block_id = error_check_block_id(i); const char* rule = error_check_rule(i); - printf(" Block %ld, check %ld: %s\n", block_id, check_id, rule); + printf(" Block %" PRIu64 ", check %" PRIu64 ": %s\n", block_id, check_id, rule); } } @@ -76,10 +77,13 @@ mod capi { string_free(world_print); uint64_t sz = biscuit_serialized_size(b2); - printf("serialized size: %ld\n", sz); + printf("serialized size: %" PRIu64 "\n", sz); uint8_t * buffer = malloc(sz); uint64_t written = biscuit_serialize(b2, buffer); - printf("wrote %ld bytes\n", written); + printf("wrote %" PRIu64 " bytes\n", written); + + const char *biscuit_source = biscuit_print_block_source(b2, 0); + printf("biscuit block 0 source: %s\n", biscuit_source); free(buffer); authorizer_free(authorizer); @@ -126,6 +130,8 @@ allow if true; serialized size: 322 wrote 322 bytes +biscuit block 0 source: right("file1", "read"); + "#, ); } From 08f79006e062c840d4e7ca4baa15034081749bdf Mon Sep 17 00:00:00 2001 From: Pierre Tondereau Date: Sun, 3 Nov 2024 17:58:51 +0100 Subject: [PATCH 6/6] remove `block()` usage and C-API usage --- biscuit-auth/src/token/mod.rs | 2 +- biscuit-capi/src/lib.rs | 110 ++++++---------------------------- biscuit-capi/tests/capi.rs | 8 +++ 3 files changed, 26 insertions(+), 94 deletions(-) diff --git a/biscuit-auth/src/token/mod.rs b/biscuit-auth/src/token/mod.rs index 59458a5a..fe1b089e 100644 --- a/biscuit-auth/src/token/mod.rs +++ b/biscuit-auth/src/token/mod.rs @@ -500,7 +500,7 @@ impl Biscuit { 1 + self.blocks.len() } - pub fn block(&self, index: usize) -> Result { + pub(crate) fn block(&self, index: usize) -> Result { let block = if index == 0 { proto_block_to_token_block( &self.authority, diff --git a/biscuit-capi/src/lib.rs b/biscuit-capi/src/lib.rs index 5f14b83c..23a0b290 100644 --- a/biscuit-capi/src/lib.rs +++ b/biscuit-capi/src/lib.rs @@ -699,75 +699,6 @@ pub unsafe extern "C" fn biscuit_block_count(biscuit: Option<&Biscuit>) -> usize biscuit.0.block_count() } -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_fact_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.facts.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_rule_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.rules.len() -} - -#[no_mangle] -pub unsafe extern "C" fn biscuit_block_check_count( - biscuit: Option<&Biscuit>, - block_index: u32, -) -> usize { - if biscuit.is_none() { - update_last_error(Error::InvalidArgument); - return 0; - } - - let biscuit = biscuit.unwrap(); - - let block = match biscuit.0.block(block_index as usize) { - Ok(block) => block, - Err(e) => { - update_last_error(e.into()); - return 0; - } - }; - - block.checks.len() -} - #[no_mangle] pub unsafe extern "C" fn biscuit_block_context( biscuit: Option<&Biscuit>, @@ -780,31 +711,24 @@ pub unsafe extern "C" fn biscuit_block_context( let biscuit = biscuit.unwrap(); - let block = if block_index == 0 { - match biscuit.0.block(0) { - Ok(b) => b, - Err(err) => { - update_last_error(Error::Biscuit(err)); - return std::ptr::null_mut(); - } - } - } else { - match biscuit.0.block(block_index as usize - 1) { - Ok(b) => b, - Err(err) => { - update_last_error(Error::Biscuit(err)); - return std::ptr::null_mut(); - } - } - }; + let context = biscuit.0.context(); - match &block.context { - None => std::ptr::null_mut(), - Some(context) => match CString::new(context.clone()) { - Ok(s) => s.into_raw(), - Err(_) => { - update_last_error(Error::InvalidArgument); - std::ptr::null_mut() + match context.get(block_index as usize) { + None => { + update_last_error(Error::Biscuit(biscuit_auth::error::Token::Format( + biscuit_auth::error::Format::InvalidBlockId(block_index as usize), + ))); + + std::ptr::null_mut() + } + Some(context) => match context { + None => std::ptr::null_mut(), + Some(context) => { + let c = CString::new(context.clone()); + match c { + Err(_) => std::ptr::null_mut(), + Ok(context_cstring) => context_cstring.into_raw(), + } } }, } diff --git a/biscuit-capi/tests/capi.rs b/biscuit-capi/tests/capi.rs index 1694c629..af7f615a 100644 --- a/biscuit-capi/tests/capi.rs +++ b/biscuit-capi/tests/capi.rs @@ -85,6 +85,12 @@ mod capi { const char *biscuit_source = biscuit_print_block_source(b2, 0); printf("biscuit block 0 source: %s\n", biscuit_source); + uintptr_t count = biscuit_block_count(b2); + printf("biscuit block count: %" PRIuPTR "\n", count); + + char *block_context_0 = biscuit_block_context(b2, 0); + printf("biscuit block 0 context: %s\n", block_context_0); + free(buffer); authorizer_free(authorizer); block_builder_free(bb); @@ -132,6 +138,8 @@ serialized size: 322 wrote 322 bytes biscuit block 0 source: right("file1", "read"); +biscuit block count: 2 +biscuit block 0 context: (null) "#, ); }