Skip to content

Commit

Permalink
Internally create ArenaAllocator
Browse files Browse the repository at this point in the history
  • Loading branch information
malcolmstill committed Mar 31, 2024
1 parent 6eac992 commit 5dfe893
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 81 deletions.
44 changes: 23 additions & 21 deletions biscuit-samples/src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ const Result = @import("sample.zig").Result;

const log = std.log.scoped(.samples);

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};

pub fn main() anyerror!void {
defer _ = gpa.deinit();
defer _ = gpa_state.deinit();

var arena = std.heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();
const gpa = gpa_state.allocator();

const alloc = arena.allocator();
var arena_state = std.heap.ArenaAllocator.init(gpa);
defer arena_state.deinit();

var args = try std.process.argsWithAllocator(alloc);
const arena = arena_state.allocator();

var args = try std.process.argsWithAllocator(arena);
defer args.deinit();
_ = args.skip();

Expand All @@ -27,8 +29,8 @@ pub fn main() anyerror!void {
// 2. Parse json
const json_string = @embedFile("samples/samples.json");

const dynamic_tree = try std.json.parseFromSliceLeaky(std.json.Value, alloc, json_string, .{});
const r = try std.json.parseFromValueLeaky(Samples, alloc, dynamic_tree, .{});
const dynamic_tree = try std.json.parseFromSliceLeaky(std.json.Value, arena, json_string, .{});
const r = try std.json.parseFromValueLeaky(Samples, arena, dynamic_tree, .{});

var public_key_mem: [32]u8 = undefined;
_ = try std.fmt.hexToBytes(&public_key_mem, r.root_public_key);
Expand All @@ -40,30 +42,30 @@ pub fn main() anyerror!void {
if (!mem.eql(u8, name, testcase.filename)) continue;
}

const token = try std.fs.cwd().readFileAlloc(alloc, testcase.filename, 0xFFFFFFF);
const token = try std.fs.cwd().readFileAlloc(arena, testcase.filename, 0xFFFFFFF);

for (testcase.validations.map.values(), 0..) |validation, i| {
errdefer log.err("Error on validation {} of {s}\n", .{ i, testcase.filename });
try validate(alloc, token, public_key, validation.result, validation.authorizer_code);
try validate(gpa, token, public_key, validation.result, validation.authorizer_code);
}
}
}

pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.sign.Ed25519.PublicKey, result: Result, authorizer_code: []const u8) !void {
var errors = std.ArrayList(AuthorizerError).init(alloc);
pub fn validate(allocator: mem.Allocator, token: []const u8, public_key: std.crypto.sign.Ed25519.PublicKey, result: Result, authorizer_code: []const u8) !void {
var errors = std.ArrayList(AuthorizerError).init(allocator);
defer errors.deinit();

switch (result) {
.Ok => try runValidation(alloc, token, public_key, authorizer_code, &errors),
.Ok => try runValidation(allocator, token, public_key, authorizer_code, &errors),
.Err => |e| {
switch (e) {
.Format => |f| switch (f) {
.InvalidSignatureSize => runValidation(alloc, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
.InvalidSignatureSize => runValidation(allocator, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
error.IncorrectBlockSignatureLength => return,
else => return err,
},
.Signature => |s| switch (s) {
.InvalidSignature => runValidation(alloc, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
.InvalidSignature => runValidation(allocator, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
error.SignatureVerificationFailed,
error.InvalidEncoding,
=> return,
Expand All @@ -72,7 +74,7 @@ pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.
},
},
.FailedLogic => |f| switch (f) {
.Unauthorized => |u| runValidation(alloc, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
.Unauthorized => |u| runValidation(allocator, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
error.AuthorizationFailed => {

// Check that we have expected check failures
Expand Down Expand Up @@ -121,7 +123,7 @@ pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.
},
else => return err,
},
.InvalidBlockRule => |_| runValidation(alloc, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
.InvalidBlockRule => |_| runValidation(allocator, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
error.AuthorizationFailed => {
for (errors.items) |found_failed_check| {
switch (found_failed_check) {
Expand All @@ -136,7 +138,7 @@ pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.
else => return err,
},
},
.Execution => runValidation(alloc, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
.Execution => runValidation(allocator, token, public_key, authorizer_code, &errors) catch |err| switch (err) {
error.Overflow => return,
else => return err,
},
Expand All @@ -147,11 +149,11 @@ pub fn validate(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.
}
}

pub fn runValidation(alloc: mem.Allocator, token: []const u8, public_key: std.crypto.sign.Ed25519.PublicKey, authorizer_code: []const u8, errors: *std.ArrayList(AuthorizerError)) !void {
var b = try Biscuit.fromBytes(alloc, token, public_key);
pub fn runValidation(allocator: mem.Allocator, token: []const u8, public_key: std.crypto.sign.Ed25519.PublicKey, authorizer_code: []const u8, errors: *std.ArrayList(AuthorizerError)) !void {
var b = try Biscuit.fromBytes(allocator, token, public_key);
defer b.deinit();

var a = try b.authorizer(alloc);
var a = try b.authorizer();
defer a.deinit();

var it = std.mem.split(u8, authorizer_code, ";");
Expand Down
46 changes: 20 additions & 26 deletions biscuit/src/authorizer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@ const PolicyResult = @import("biscuit-builder").PolicyResult;
const log = std.log.scoped(.authorizer);

pub const Authorizer = struct {
allocator: mem.Allocator,
arena: mem.Allocator,
checks: std.ArrayList(builder.Check),
policies: std.ArrayList(builder.Policy),
biscuit: ?Biscuit,
biscuit: ?*Biscuit,
world: World,
symbols: SymbolTable,
public_key_to_block_id: std.AutoHashMap(usize, std.ArrayList(usize)),
scopes: std.ArrayList(Scope),

pub fn init(arena: std.mem.Allocator, biscuit: Biscuit) !Authorizer {
var symbols = SymbolTable.init("authorizer", arena);
pub fn init(allocator: mem.Allocator, arena: std.mem.Allocator, biscuit: *Biscuit) !Authorizer {
var symbols = SymbolTable.init("authorizer", allocator);
var public_key_to_block_id = std.AutoHashMap(usize, std.ArrayList(usize)).init(arena);

// Map public key symbols into authorizer symbols and public_key_to_block_id map
Expand All @@ -37,10 +38,15 @@ pub const Authorizer = struct {

const authorizer_public_key_index = try symbols.insertPublicKey(public_key);

try public_key_to_block_id.put(authorizer_public_key_index, try block_ids.clone());
var list = try std.ArrayList(usize).initCapacity(allocator, block_ids.items.len);

for (block_ids.items) |id| try list.append(id);

try public_key_to_block_id.put(authorizer_public_key_index, list);
}

return .{
.allocator = allocator,
.arena = arena,
.checks = std.ArrayList(builder.Check).init(arena),
.policies = std.ArrayList(builder.Policy).init(arena),
Expand All @@ -52,28 +58,16 @@ pub const Authorizer = struct {
};
}

pub fn deinit(_: *Authorizer) void {
// authorizer.world.deinit();
// authorizer.symbols.deinit();
// authorizer.scopes.deinit();

// for (authorizer.checks.items) |check| {
// check.deinit();
// }
// authorizer.checks.deinit();

// for (authorizer.policies.items) |policy| {
// policy.deinit();
// }
// authorizer.policies.deinit();

// {
// var it = authorizer.public_key_to_block_id.valueIterator();
// while (it.next()) |block_ids| {
// block_ids.deinit();
// }
// authorizer.public_key_to_block_id.deinit();
// }
pub fn deinit(authorizer: *Authorizer) void {
authorizer.symbols.deinit();

{
var it = authorizer.public_key_to_block_id.valueIterator();
while (it.next()) |block_ids| {
block_ids.deinit();
}
authorizer.public_key_to_block_id.deinit();
}
}

/// Authorize token with authorizer
Expand Down
80 changes: 69 additions & 11 deletions biscuit/src/biscuit.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ const World = @import("biscuit-datalog").world.World;
const SerializedBiscuit = @import("biscuit-format").SerializedBiscuit;
const builder = @import("biscuit-builder");

const ArenaAllocator = std.heap.ArenaAllocator;

const log = std.log.scoped(.biscuit);

pub const Biscuit = struct {
allocator: mem.Allocator,
arena_state: *ArenaAllocator,
serialized: SerializedBiscuit,
authority: Block,
blocks: std.ArrayList(Block),
Expand All @@ -24,24 +28,43 @@ pub const Biscuit = struct {
unreachable;
}

/// Deserialize a token from byte array into a Biscuit.
pub fn fromBytes(allocator: mem.Allocator, token_bytes: []const u8, root_public_key: Ed25519.PublicKey) !Biscuit {
var serialized = try SerializedBiscuit.deserialize(allocator, token_bytes, root_public_key);
errdefer serialized.deinit();

// The calls to Block.fromBytes further down allocate into this arena. After this function (Biscuit.fromBytes)
// returns, we create new rules, facts, terms etc. from those generated in Block.fromBytes. If those, use
// e.g. clone, that clone will be using the allocator used here which would have a reference to the ArenaAllocator
// stack as it was on the stack which would now be invalid.
//
// What we do here instead is create the ArenaAllocator on the heap such that is has a stable address.
//
// TODO: alternatively we could require this function takes a *ArenaAllocator (in place or in addition to
// allocator: mem.Allocator) and not deal with this allocation.
var arena_state = try allocator.create(ArenaAllocator);
arena_state.* = ArenaAllocator.init(allocator);
errdefer {
arena_state.deinit();
allocator.destroy(arena_state);
}

const arena = arena_state.allocator();

// For each block we will temporarily store the external public key (where it exists).
var block_external_keys = try std.ArrayList(?Ed25519.PublicKey).initCapacity(allocator, 1 + serialized.blocks.items.len);
defer block_external_keys.deinit();
defer std.debug.assert(block_external_keys.items.len == 1 + serialized.blocks.items.len);

var token_symbols = SymbolTable.init("biscuit", allocator);

const authority = try Block.fromBytes(allocator, serialized.authority, &token_symbols);
const authority = try Block.fromBytes(arena, serialized.authority, &token_symbols);
try block_external_keys.append(null);
log.debug("authority {any}", .{authority});

var blocks = std.ArrayList(Block).init(allocator);
var blocks = try std.ArrayList(Block).initCapacity(arena, serialized.blocks.items.len);
for (serialized.blocks.items) |signed_block| {
const block = try Block.fromBytes(allocator, signed_block, &token_symbols);
const block = try Block.fromBytes(arena, signed_block, &token_symbols);
log.debug("{any}", .{block});

const external_key = if (signed_block.external_signature) |external_signature| external_signature.public_key else null;
Expand Down Expand Up @@ -75,6 +98,8 @@ pub const Biscuit = struct {
}

return .{
.allocator = allocator,
.arena_state = arena_state,
.serialized = serialized,
.authority = authority,
.blocks = blocks,
Expand All @@ -83,25 +108,24 @@ pub const Biscuit = struct {
};
}

/// Deinitialize the biscuit
pub fn deinit(biscuit: *Biscuit) void {
for (biscuit.blocks.items) |*block| {
block.deinit();
}
biscuit.blocks.deinit();
biscuit.authority.deinit();
biscuit.arena_state.deinit(); // Free all the facts, predicates, etc.

// FIXME: think about lifetimes for public_key_to_block_id
var it = biscuit.public_key_to_block_id.valueIterator();
while (it.next()) |block_ids| {
block_ids.deinit();
}
biscuit.public_key_to_block_id.deinit();

biscuit.symbols.deinit();
biscuit.serialized.deinit();

biscuit.allocator.destroy(biscuit.arena_state);
}

pub fn authorizer(biscuit: *Biscuit, allocator: std.mem.Allocator) !Authorizer {
return try Authorizer.init(allocator, biscuit.*);
pub fn authorizer(biscuit: *Biscuit) !Authorizer {
return try Authorizer.init(biscuit.allocator, biscuit.arena_state.allocator(), biscuit);
}

/// Append block to biscuit
Expand Down Expand Up @@ -172,6 +196,8 @@ pub const Biscuit = struct {
// var errors = std.ArrayList(AuthorizerError).init(allocator);
// defer errors.deinit();

// errdefer std.debug.print("error = {any}\n", .{errors.items});

// _ = try a.authorize(&errors);
// }
// }
Expand Down Expand Up @@ -214,3 +240,35 @@ pub const Biscuit = struct {
// try testing.expectError(error.AuthorizationFailed, a.authorize(&errors));
// }
// }

// test {
// const decode = @import("biscuit-format").decode;
// const testing = std.testing;
// var allocator = testing.allocator;

// // Key
// // private: 83e03c958f83085923f3cd091bab3c3b33a0c7f93f44889739fdb6c6fdb26f5b
// // public: 49fe7ec1972952c8c92119def96235ad622d0d024f3042a49c7317f7d5baf3da
// const token = "EpACCqUBCgFhCgFiCgFjCgFkCgdtYWxjb2xtCgRqb2huCgEwCgExGAMiCQoHCAISAxiACCIJCgcIAhIDGIEIIgkKBwgCEgMYgggiCQoHCAISAxiDCCIOCgwIBxIDGIQIEgMYgAgiDgoMCAcSAxiECBIDGIIIIg4KDAgHEgMYhQgSAxiBCCokCgsIBBIDCIYIEgIYABIHCAISAwiGCBIMCAcSAwiHCBIDCIYIEiQIABIgnSmYbzjEQ2n09JhlmGs6j_ZhKYgj3nRkEMdGJJqQimwaQD4UTmEDtu5G8kRJZbNTcNuGg8Izb5ja2BSV3Rlkv1Y6IV_Nd00sIstiEq1RPH-M8xfFdWaW1gixH54Y5deHzwYiIgogFmxoQyXPm8ccNBKKh0hv8eRwrYjS56s0OTQWZShHoVw=";

// var public_key_mem: [32]u8 = undefined;
// _ = try std.fmt.hexToBytes(&public_key_mem, "49fe7ec1972952c8c92119def96235ad622d0d024f3042a49c7317f7d5baf3da");
// const public_key = try Ed25519.PublicKey.fromBytes(public_key_mem);

// const bytes = try decode.urlSafeBase64ToBytes(allocator, token);
// defer allocator.free(bytes);

// var arena_state = std.heap.ArenaAllocator.init(testing.allocator);
// defer arena_state.deinit();

// const arena = arena_state.allocator();

// var b = try Biscuit.fromBytes(arena, bytes, public_key);
// defer b.deinit();

// var block = builder.Block.init(arena);
// try block.addFact("read(\"file1\")");

// try b.append(block);
// _ = try b.serialize();
// }
Loading

0 comments on commit 5dfe893

Please sign in to comment.