Skip to content

Commit 6291941

Browse files
committed
[accless] Add Decrypt FFI Wrapper For abe4
1 parent 195fe31 commit 6291941

File tree

6 files changed

+124
-1
lines changed

6 files changed

+124
-1
lines changed

accless/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Accless Core Library
2+
3+
To build:
4+
5+
```bash
6+
accli docker run --mount --cwd /code/accless/accless python3 build.py [-- --clean]
7+
```
8+
9+
To test:
10+
11+
```bash
12+
accli docker run --mount --cwd /code/accless/accless/build-native ctest -- --output-on-failure
13+
```

accless/build.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sys import argv, exit
77

88
ACCLESS_ROOT = dirname(realpath(__file__))
9+
PROJ_ROOT = dirname(ACCLESS_ROOT)
910

1011

1112
def compile(wasm=False, native=False, debug=False, time=False):
@@ -54,6 +55,8 @@ def compile(wasm=False, native=False, debug=False, time=False):
5455
elif len(argv) == 2 and argv[1] == "--clean":
5556
rmtree(join(ACCLESS_ROOT, "build-native"), ignore_errors=True)
5657
rmtree(join(ACCLESS_ROOT, "build-wasm"), ignore_errors=True)
58+
run("cargo clean -p accless-abe4", shell=True, check=True, cwd=PROJ_ROOT)
59+
run("cargo clean -p accless-jwt", shell=True, check=True, cwd=PROJ_ROOT)
5760

5861
# Build the microbenchmarks
5962
compile(wasm=True, debug=debug)

accless/libs/abe4/cpp-bindings/abe4.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <cstring> // For std::memcpy
55
#include <nlohmann/json.hpp>
6+
#include <optional>
67

78
namespace accless::abe4 {
89

@@ -53,6 +54,22 @@ EncryptOutput encrypt(const std::string &mpk, const std::string &policy) {
5354
return {result_json["gt"], result_json["ciphertext"]};
5455
}
5556

57+
std::optional<std::string> decrypt(const std::string &usk,
58+
const std::string &gid,
59+
const std::string &policy,
60+
const std::string &ct) {
61+
char *result =
62+
decrypt_abe4(usk.c_str(), gid.c_str(), policy.c_str(), ct.c_str());
63+
if (!result) {
64+
return std::nullopt;
65+
}
66+
67+
std::string gt_b64(result);
68+
free_string(result);
69+
70+
return gt_b64;
71+
}
72+
5673
std::map<std::string, std::vector<uint8_t>>
5774
unpackFullKey(const std::vector<uint8_t> &full_key_bytes) {
5875
std::map<std::string, std::vector<uint8_t>> result;

accless/libs/abe4/cpp-bindings/abe4.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <cstdint>
44
#include <map>
5+
#include <optional>
56
#include <string>
67
#include <vector>
78

@@ -11,6 +12,9 @@ char *setup_abe4(const char *auths_json);
1112
char *keygen_abe4(const char *gid, const char *msk_b64,
1213
const char *user_attrs_json);
1314
char *encrypt_abe4(const char *mpk_b64, const char *policy_str);
15+
char *decrypt_abe4(const char *usk_b64, const char *gid, const char *policy_str,
16+
const char *ct_b64);
17+
1418
} // extern "C"
1519

1620
namespace accless::abe4 {
@@ -65,6 +69,29 @@ std::string keygen(const std::string &gid, const std::string &msk,
6569
*/
6670
EncryptOutput encrypt(const std::string &mpk, const std::string &policy);
6771

72+
/**
73+
* @brief Decrypts a ciphertext using a User Secret Key (USK), group ID, policy,
74+
* and ciphertext.
75+
*
76+
* This function acts as a C++ wrapper around the Rust `decrypt` FFI function.
77+
* It takes a base64 encoded User Secret Key, group ID, policy string, and
78+
* base64 encoded ciphertext. It calls the Rust FFI function, and returns an
79+
* `std::optional<std::string>` containing the base64 encoded `Gt` if decryption
80+
* is successful, or `std::nullopt` otherwise.
81+
*
82+
* @param usk A base64 encoded string representing the User Secret Key.
83+
* @param gid The group ID associated with the decryption.
84+
* @param policy A string representing the access policy used for encryption.
85+
* @param ct A base64 encoded string representing the ciphertext to be
86+
* decrypted.
87+
* @return An `std::optional<std::string>` containing the base64 encoded `Gt` on
88+
* success, or `std::nullopt` on failure.
89+
*/
90+
std::optional<std::string> decrypt(const std::string &usk,
91+
const std::string &gid,
92+
const std::string &policy,
93+
const std::string &ct);
94+
6895
/**
6996
* @brief Unpacks a serialized FullKey (e.g., MPK or MSK) into a map of
7097
* authority to its partial key.

accless/libs/abe4/cpp-bindings/tests.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "abe4.h"
22
#include "base64.h" // New include
33
#include <gtest/gtest.h>
4+
#include <optional>
45

56
TEST(abe4, setup) {
67
std::vector<std::string> auths = {"auth1", "auth2"};
@@ -64,3 +65,23 @@ TEST(Abe4Test, Encrypt) {
6465
EXPECT_FALSE(encrypt_output.gt.empty());
6566
EXPECT_FALSE(encrypt_output.ciphertext.empty());
6667
}
68+
69+
TEST(Abe4Test, Decrypt) {
70+
std::vector<std::string> auths = {"auth1", "auth2"};
71+
accless::abe4::SetupOutput setup_output = accless::abe4::setup(auths);
72+
73+
std::string gid = "test_gid";
74+
std::vector<accless::abe4::UserAttribute> user_attrs = {
75+
{"auth1", "label1", "attr1"}, {"auth2", "label2", "attr2"}};
76+
std::string usk_b64 =
77+
accless::abe4::keygen(gid, setup_output.msk, user_attrs);
78+
79+
std::string policy = "auth1.label1:attr1 and auth2.label2:attr2";
80+
accless::abe4::EncryptOutput encrypt_output =
81+
accless::abe4::encrypt(setup_output.mpk, policy);
82+
83+
std::optional<std::string> decrypted_gt =
84+
accless::abe4::decrypt(usk_b64, gid, policy, encrypt_output.ciphertext);
85+
ASSERT_TRUE(decrypted_gt.has_value());
86+
EXPECT_EQ(decrypted_gt.value(), encrypt_output.gt);
87+
}

accless/libs/abe4/src/lib.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub use scheme::{decrypt, encrypt, iota, keygen, setup, tau};
1111
use scheme::{
1212
iota::Iota,
1313
tau::Tau,
14-
types::{Ciphertext, MPK, MSK},
14+
types::{Ciphertext, MPK, MSK, USK},
1515
};
1616
use serde::{Deserialize, Serialize};
1717
use std::{
@@ -141,3 +141,45 @@ pub unsafe extern "C" fn encrypt_abe4(
141141
let output_json = serde_json::to_string(&output).unwrap();
142142
CString::new(output_json).unwrap().into_raw()
143143
}
144+
145+
#[allow(clippy::missing_safety_doc)]
146+
#[unsafe(no_mangle)]
147+
pub unsafe extern "C" fn decrypt_abe4(
148+
usk_b64: *const c_char,
149+
gid: *const c_char,
150+
policy_str: *const c_char,
151+
ct_b64: *const c_char,
152+
) -> *mut c_char {
153+
let usk_b64_cstr = unsafe { CStr::from_ptr(usk_b64) };
154+
let gid_cstr = unsafe { CStr::from_ptr(gid) };
155+
let policy_cstr = unsafe { CStr::from_ptr(policy_str) };
156+
let ct_b64_cstr = unsafe { CStr::from_ptr(ct_b64) };
157+
158+
let usk_bytes = general_purpose::STANDARD
159+
.decode(usk_b64_cstr.to_str().unwrap())
160+
.unwrap();
161+
let usk: USK = USK::deserialize_compressed(&usk_bytes[..]).unwrap();
162+
163+
let policy = Policy::parse(policy_cstr.to_str().unwrap()).unwrap();
164+
let tau = Tau::new(&policy);
165+
166+
let user_attrs = usk.get_user_attributes();
167+
let iota = Iota::new(&user_attrs);
168+
169+
let ct_bytes = general_purpose::STANDARD
170+
.decode(ct_b64_cstr.to_str().unwrap())
171+
.unwrap();
172+
let ct: Ciphertext = Ciphertext::deserialize_compressed(&ct_bytes[..]).unwrap();
173+
174+
let result = decrypt(&usk, gid_cstr.to_str().unwrap(), &iota, &tau, &policy, &ct);
175+
176+
match result {
177+
Some(gt) => {
178+
let mut gt_bytes = Vec::new();
179+
gt.serialize_compressed(&mut gt_bytes).unwrap();
180+
let gt_b64 = general_purpose::STANDARD.encode(&gt_bytes);
181+
CString::new(gt_b64).unwrap().into_raw()
182+
}
183+
None => std::ptr::null_mut(),
184+
}
185+
}

0 commit comments

Comments
 (0)