miniroon

Simplistic macaroon-based authorization for Unix systems
git clone https://ccx.te2000.cz/git/miniroon
Log | Files | Refs

commit fa6766202164222de301768454e9e3ae516418e1
parent cd541a42d498aaee7b0c010048539604184f02c4
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Sat, 15 Feb 2025 15:12:17 +0000

First blackbox test

Diffstat:
Dsrc/gen-miniroon.py | 81-------------------------------------------------------------------------------
Msrc/test_blackbox.c | 20+++++++++++++++++++-
Dtest/ccx_once.json | 9---------
Atest/gen-miniroon.py | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/json/example1.json | 11+++++++++++
Atest/json/example1_once.json | 9+++++++++
Dtest/miniroon/ccx/run | 2--
Atest/miniroon/example1/run | 2++
Rtest/miniroon/ccx/secret -> test/miniroon/example1/secret | 0
Rtest/miniroon/ccx/verify -> test/miniroon/example1/verify | 0
10 files changed, 122 insertions(+), 93 deletions(-)

diff --git a/src/gen-miniroon.py b/src/gen-miniroon.py @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import base64 -import hmac -import json -import os -import sys - - -class NetString(bytes): - @classmethod - def from_any(cls, data): - if isinstance(data, NetString): - return cls(data) - if isinstance(data, bytes): - return cls(to_ns(data)) - if isinstance(data, str): - return cls(to_ns(data.encode('ascii'))) - assert not isinstance(data, dict) - return cls(to_ns(to_ns_list(data))) - - -def to_ns(b): - assert isinstance(b, bytes) - return NetString(b'%d:%s,' % (len(b), b)) - - -def to_ns_list(data): - assert not isinstance(data, (dict, bytes, str)) - return b''.join(NetString.from_any(i) for i in data) - - -def miniroon_hmac(key, msg): - assert len(key) == 32 - print('miniroon_hmac%r' % ((key, msg),), file=sys.stderr) - #return hmac.digest(key, msg, 'blake2s') - return hmac.digest(key, msg, 'sha256') - - -def make_miniroon(name, action='invoke-once', secret=b'\0'*32, caveats=(), version='capv0', unwrap=False): - hdr = b''.join(NetString.from_any(i) for i in (version, name, action)) - caveats_ns = [to_ns_list(c) for c in caveats] - sig = miniroon_hmac(secret, hdr) - for c in caveats_ns: - sig = miniroon_hmac(sig, c) - if unwrap: - return b''.join(map(NetString.from_any, (hdr, caveats_ns, sig))) - else: - return NetString.from_any([hdr, caveats_ns, sig]) - - -def main_old(): - import os - # os.write(1, make_miniroon(name='ccx')) - os.write(1, make_miniroon(name='ccx', caveats=[ - ('env-is', 'var1', 'hello'), - ('env-absent', 'var2'), - ('env-glob', 'var3', '_*'), - # ('x-glob', 'var3', '_*'), - ('env-is', 'var3', '_hello'), - ])) - - -argument_parser = argparse.ArgumentParser() -argument_parser.add_argument('--unwrap', action='store_true', default=False) -argument_parser.add_argument('json_in', type=argparse.FileType(mode='r'), nargs="?", default=sys.stdin) - - -def main(): - args = argument_parser.parse_args() - data = json.load(args.json_in) - data['unwrap'] = args.unwrap - assert isinstance(data, dict) - if 'secret_b64' in data: - assert 'secret' not in data - data['secret'] = base64.b64decode(data['secret_b64']) - os.write(1, make_miniroon(**data)) - - -if __name__ == '__main__': - main() diff --git a/src/test_blackbox.c b/src/test_blackbox.c @@ -1,18 +1,36 @@ #include "tests.h" #include <sys/stat.h> +#include <stdio.h> /* Declare a local suite. */ SUITE(blackbox); TEST have_source_file(void) { struct stat sb; - int stat_result = stat("ccx_once.json", &sb); + int stat_result = stat("json/example1_once.json", &sb); ASSERT_EQ(0, stat_result); PASS(); } +const char expect_example1_stdout[] = "var1=hello\nvar2=\nvar3=_hello\n"; +TEST example1(void) { + FILE *stream = popen("./gen-miniroon.py json/example1.json | ../src/build/miniroon-read miniroon", "r"); + ASSERT(stream); + + char out[sizeof(expect_example1_stdout)]; + size_t read_len = fread(out, sizeof(out), 1, stream); + ASSERT_EQ(sizeof(out), read_len); + ASSERT_STRN_EQ(expect_example1_stdout, out, sizeof(out)); + + int ret = pclose(stream); + ASSERT_EQ(0, ret); + + PASS(); +} + /* Primary test suite. */ SUITE(blackbox) { RUN_TEST(have_source_file); + RUN_TEST(example1); } diff --git a/test/ccx_once.json b/test/ccx_once.json @@ -1,9 +0,0 @@ -{ - "name": "ccx", - "caveats": [ - ["env-is", "var1", "hello"], - ["env-absent", "var2"], - ["env-glob", "var3", "_*"], - ["env-is", "var3", "_hello"] - ] -} diff --git a/test/gen-miniroon.py b/test/gen-miniroon.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +import argparse +import base64 +import hmac +import json +import os +import sys + + +class NetString(bytes): + @classmethod + def from_any(cls, data): + if isinstance(data, NetString): + return cls(data) + if isinstance(data, bytes): + return cls(to_ns(data)) + if isinstance(data, str): + return cls(to_ns(data.encode('ascii'))) + assert not isinstance(data, dict) + return cls(to_ns(to_ns_list(data))) + + +def to_ns(b): + assert isinstance(b, bytes) + return NetString(b'%d:%s,' % (len(b), b)) + + +def to_ns_list(data): + assert not isinstance(data, (dict, bytes, str)) + return b''.join(NetString.from_any(i) for i in data) + + +def miniroon_hmac(key, msg): + assert len(key) == 32 + print('miniroon_hmac%r' % ((key, msg),), file=sys.stderr) + #return hmac.digest(key, msg, 'blake2s') + return hmac.digest(key, msg, 'sha256') + + +def make_miniroon(name, action='invoke-once', secret=b'\0'*32, caveats=(), version='capv0', unwrap=False): + hdr = b''.join(NetString.from_any(i) for i in (version, name, action)) + caveats_ns = [to_ns_list(c) for c in caveats] + sig = miniroon_hmac(secret, hdr) + for c in caveats_ns: + sig = miniroon_hmac(sig, c) + if unwrap: + return b''.join(map(NetString.from_any, (hdr, caveats_ns, sig))) + else: + return NetString.from_any([hdr, caveats_ns, sig]) + + +def main_old(): + import os + # os.write(1, make_miniroon(name='ccx')) + os.write(1, make_miniroon(name='ccx', caveats=[ + ('env-is', 'var1', 'hello'), + ('env-absent', 'var2'), + ('env-glob', 'var3', '_*'), + # ('x-glob', 'var3', '_*'), + ('env-is', 'var3', '_hello'), + ])) + + +argument_parser = argparse.ArgumentParser() +argument_parser.add_argument('--unwrap', action='store_true', default=False) +argument_parser.add_argument('json_in', type=argparse.FileType(mode='r'), nargs="?", default=sys.stdin) + + +def main(): + args = argument_parser.parse_args() + data = json.load(args.json_in) + data['unwrap'] = args.unwrap + assert isinstance(data, dict) + if 'secret_b64' in data: + assert 'secret' not in data + data['secret'] = base64.b64decode(data.pop('secret_b64')) + os.write(1, make_miniroon(**data)) + + +if __name__ == '__main__': + main() diff --git a/test/json/example1.json b/test/json/example1.json @@ -0,0 +1,11 @@ +{ + "action": "invoke", + "name": "example1", + "caveats": [ + ["env-is", "var1", "hello"], + ["env-absent", "var2"], + ["env-glob", "var3", "_*"], + ["env-is", "var3", "_hello"] + ], + "secret_b64": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" +} diff --git a/test/json/example1_once.json b/test/json/example1_once.json @@ -0,0 +1,9 @@ +{ + "name": "example1", + "caveats": [ + ["env-is", "var1", "hello"], + ["env-absent", "var2"], + ["env-glob", "var3", "_*"], + ["env-is", "var3", "_hello"] + ] +} diff --git a/test/miniroon/ccx/run b/test/miniroon/ccx/run @@ -1,2 +0,0 @@ -#!/bin/sh -exec env diff --git a/test/miniroon/example1/run b/test/miniroon/example1/run @@ -0,0 +1,2 @@ +#!/bin/sh +exec printf '%s\n' "var1=$var1" "var2=$var2" "var3=$var3" diff --git a/test/miniroon/ccx/secret b/test/miniroon/example1/secret Binary files differ. diff --git a/test/miniroon/ccx/verify b/test/miniroon/example1/verify