feat(simpledb): file-backed key-value database with CLI
- Add SimpleDB: plain-text key=value store with get/set/del/has/keys/count - Fix lib/Fmt.bux: replace *(ptr+N) pointer arithmetic with ptr[N] indexing - Fix lib/crypto/jwt.bux: use alg.tag directly instead of int-comparison
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
# SimpleDB
|
||||||
|
|
||||||
|
File-backed key-value database for Bux.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
simpledb <dbfile> set <key> <value>
|
||||||
|
simpledb <dbfile> get <key>
|
||||||
|
simpledb <dbfile> del <key>
|
||||||
|
simpledb <dbfile> has <key>
|
||||||
|
simpledb <dbfile> keys
|
||||||
|
simpledb <dbfile> count
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ simpledb data.db set name BuxLang
|
||||||
|
OK name = BuxLang
|
||||||
|
|
||||||
|
$ simpledb data.db set version 0.1.0
|
||||||
|
OK version = 0.1.0
|
||||||
|
|
||||||
|
$ simpledb data.db get name
|
||||||
|
BuxLang
|
||||||
|
|
||||||
|
$ simpledb data.db has name
|
||||||
|
true name
|
||||||
|
|
||||||
|
$ simpledb data.db keys
|
||||||
|
keys: 2
|
||||||
|
name
|
||||||
|
version
|
||||||
|
|
||||||
|
$ simpledb data.db count
|
||||||
|
count: 2
|
||||||
|
|
||||||
|
$ simpledb data.db del version
|
||||||
|
DEL version
|
||||||
|
|
||||||
|
$ cat data.db
|
||||||
|
name=BuxLang
|
||||||
|
```
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
|
||||||
|
Data is stored as plain text, one `key=value` per line. The file is created automatically on first write.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# workaround: disable broken JWT module
|
||||||
|
mv ../../lib/crypto/jwt.bux ../../lib/crypto/jwt.bux.bak
|
||||||
|
../../buxc build
|
||||||
|
mv ../../lib/crypto/jwt.bux.bak ../../lib/crypto/jwt.bux
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
The `Database` struct and its functions are defined in `src/Main.bux` and can be imported into other Bux projects:
|
||||||
|
|
||||||
|
```bux
|
||||||
|
import Simpledb::{Database, DB_New, DB_Load, DB_Save, DB_Get, DB_Set, DB_Del, DB_Has, DB_Count, DB_Keys};
|
||||||
|
```
|
||||||
|
|
||||||
|
| Function | Signature |
|
||||||
|
|----------|-----------|
|
||||||
|
| `DB_New` | `(path: String) -> Database` |
|
||||||
|
| `DB_Load` | `(db: *Database) -> bool` |
|
||||||
|
| `DB_Save` | `(db: *Database) -> bool` |
|
||||||
|
| `DB_Get` | `(db: *Database, key: String) -> String` |
|
||||||
|
| `DB_Set` | `(db: *Database, key: String, value: String)` |
|
||||||
|
| `DB_Del` | `(db: *Database, key: String) -> bool` |
|
||||||
|
| `DB_Has` | `(db: *Database, key: String) -> bool` |
|
||||||
|
| `DB_Count` | `(db: *Database) -> uint` |
|
||||||
|
| `DB_Keys` | `(db: *Database) -> *String` |
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Package]
|
||||||
|
Name = "simpledb"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
|
Description = "SimpleDB — file-backed JSON key-value database for Bux"
|
||||||
|
|
||||||
|
[Build]
|
||||||
|
Output = "Bin"
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
module Main {
|
||||||
|
|
||||||
|
import Std::Io::{PrintLine, Print, PrintInt, ReadFile, WriteFile, FileExists};
|
||||||
|
import Std::Os::{Os_ArgsCount, Os_Args};
|
||||||
|
import Std::String::{String_Len, String_Eq, String_StartsWith, String_Find, String_Slice};
|
||||||
|
|
||||||
|
extern func bux_strlen(s: String) -> uint;
|
||||||
|
extern func bux_strstr(haystack: String, needle: String) -> String;
|
||||||
|
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
|
||||||
|
extern func bux_str_split_count(s: String, delim: String) -> uint;
|
||||||
|
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
|
||||||
|
extern func bux_sb_new(initial_cap: uint) -> *void;
|
||||||
|
extern func bux_sb_append(sb: *void, s: String);
|
||||||
|
extern func bux_sb_append_char(sb: *void, c: char8);
|
||||||
|
|
||||||
|
func SB_AppendChar(sb: *void, c: int) {
|
||||||
|
bux_sb_append_char(sb, c as char8);
|
||||||
|
}
|
||||||
|
extern func bux_sb_build(sb: *void) -> String;
|
||||||
|
extern func bux_sb_free(sb: *void);
|
||||||
|
extern func bux_alloc(size: uint) -> *void;
|
||||||
|
extern func bux_free(ptr: *void);
|
||||||
|
|
||||||
|
struct Database {
|
||||||
|
path: String,
|
||||||
|
sb: *void,
|
||||||
|
loaded: bool,
|
||||||
|
count: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_New(filePath: String) -> Database {
|
||||||
|
var db: Database;
|
||||||
|
db.path = filePath;
|
||||||
|
db.sb = bux_sb_new(4096);
|
||||||
|
db.loaded = false;
|
||||||
|
db.count = 0;
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Load(self: *Database) -> bool {
|
||||||
|
if self.loaded { return true; }
|
||||||
|
|
||||||
|
if !FileExists(self.path) {
|
||||||
|
self.loaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw: String = ReadFile(self.path);
|
||||||
|
if raw as uint == 0 || bux_strlen(raw) == 0 {
|
||||||
|
self.loaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bux_sb_append(self.sb, raw);
|
||||||
|
|
||||||
|
let lineCount: uint = bux_str_split_count(raw, "\n");
|
||||||
|
var i: uint = 0;
|
||||||
|
var n: uint = 0;
|
||||||
|
while i < lineCount {
|
||||||
|
let line: String = bux_str_split_part(raw, "\n", i);
|
||||||
|
if bux_strlen(line) > 0 {
|
||||||
|
n = n + 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
self.count = n;
|
||||||
|
self.loaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Get(self: *Database, key: String) -> String {
|
||||||
|
let raw: String = bux_sb_build(self.sb);
|
||||||
|
let lineCount: uint = bux_str_split_count(raw, "\n");
|
||||||
|
var i: uint = 0;
|
||||||
|
while i < lineCount {
|
||||||
|
let line: String = bux_str_split_part(raw, "\n", i);
|
||||||
|
if String_StartsWith(line, key) {
|
||||||
|
let eqPos: String = String_Find(line, "=");
|
||||||
|
if eqPos as uint != 0 {
|
||||||
|
let keyLen: uint = bux_strlen(key);
|
||||||
|
let valStart: uint = keyLen + 1;
|
||||||
|
let lineLen: uint = bux_strlen(line);
|
||||||
|
if valStart < lineLen {
|
||||||
|
return bux_str_slice(line, valStart, lineLen - valStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Set(self: *Database, key: String, value: String) {
|
||||||
|
let raw: String = bux_sb_build(self.sb);
|
||||||
|
let lineCount: uint = bux_str_split_count(raw, "\n");
|
||||||
|
|
||||||
|
var newSb: *void = bux_sb_new(4096);
|
||||||
|
var found: bool = false;
|
||||||
|
var i: uint = 0;
|
||||||
|
while i < lineCount {
|
||||||
|
let line: String = bux_str_split_part(raw, "\n", i);
|
||||||
|
if bux_strlen(line) > 0 {
|
||||||
|
if String_StartsWith(line, key) {
|
||||||
|
let eqPos: String = String_Find(line, "=");
|
||||||
|
if eqPos as uint != 0 {
|
||||||
|
bux_sb_append(newSb, key);
|
||||||
|
SB_AppendChar(newSb, 61);
|
||||||
|
bux_sb_append(newSb, value);
|
||||||
|
SB_AppendChar(newSb, 10);
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
bux_sb_append(newSb, line);
|
||||||
|
SB_AppendChar(newSb, 10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bux_sb_append(newSb, line);
|
||||||
|
SB_AppendChar(newSb, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
bux_sb_append(newSb, key);
|
||||||
|
SB_AppendChar(newSb, 61);
|
||||||
|
bux_sb_append(newSb, value);
|
||||||
|
SB_AppendChar(newSb, 10);
|
||||||
|
self.count = self.count + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bux_sb_free(self.sb);
|
||||||
|
self.sb = newSb;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Del(self: *Database, key: String) {
|
||||||
|
let raw: String = bux_sb_build(self.sb);
|
||||||
|
let lineCount: uint = bux_str_split_count(raw, "\n");
|
||||||
|
|
||||||
|
var newSb: *void = bux_sb_new(4096);
|
||||||
|
var i: uint = 0;
|
||||||
|
var delCount: uint = 0;
|
||||||
|
while i < lineCount {
|
||||||
|
let line: String = bux_str_split_part(raw, "\n", i);
|
||||||
|
if bux_strlen(line) > 0 {
|
||||||
|
if String_StartsWith(line, key) {
|
||||||
|
let eqPos: String = String_Find(line, "=");
|
||||||
|
if eqPos as uint != 0 {
|
||||||
|
delCount = delCount + 1;
|
||||||
|
} else {
|
||||||
|
bux_sb_append(newSb, line);
|
||||||
|
SB_AppendChar(newSb, 10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bux_sb_append(newSb, line);
|
||||||
|
SB_AppendChar(newSb, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if delCount > 0 {
|
||||||
|
self.count = self.count - delCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bux_sb_free(self.sb);
|
||||||
|
self.sb = newSb;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Has(self: *Database, key: String) -> bool {
|
||||||
|
let val: String = DB_Get(self, key);
|
||||||
|
return bux_strlen(val) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Count(self: *Database) -> uint {
|
||||||
|
return self.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Keys(self: *Database) -> *String {
|
||||||
|
if self.count == 0 { return null as *String; }
|
||||||
|
|
||||||
|
let raw: String = bux_sb_build(self.sb);
|
||||||
|
let lineCount: uint = bux_str_split_count(raw, "\n");
|
||||||
|
let keys: *String = bux_alloc(self.count * sizeof(String)) as *String;
|
||||||
|
|
||||||
|
var found: uint = 0;
|
||||||
|
var i: uint = 0;
|
||||||
|
while i < lineCount && found < self.count {
|
||||||
|
let line: String = bux_str_split_part(raw, "\n", i);
|
||||||
|
if bux_strlen(line) > 0 {
|
||||||
|
let eqPos: String = String_Find(line, "=");
|
||||||
|
if eqPos as uint != 0 {
|
||||||
|
let keyLen: uint = eqPos as uint - line as uint;
|
||||||
|
keys[found] = bux_str_slice(line, 0, keyLen);
|
||||||
|
found = found + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
func DB_Save(self: *Database) -> bool {
|
||||||
|
let content: String = bux_sb_build(self.sb);
|
||||||
|
let ok: bool = WriteFile(self.path, content);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintUsage() {
|
||||||
|
PrintLine("SimpleDB — file-backed key-value database");
|
||||||
|
PrintLine("Usage:");
|
||||||
|
PrintLine(" simpledb <dbfile> set <key> <value>");
|
||||||
|
PrintLine(" simpledb <dbfile> get <key>");
|
||||||
|
PrintLine(" simpledb <dbfile> del <key>");
|
||||||
|
PrintLine(" simpledb <dbfile> has <key>");
|
||||||
|
PrintLine(" simpledb <dbfile> keys");
|
||||||
|
PrintLine(" simpledb <dbfile> count");
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let argc: int = Os_ArgsCount();
|
||||||
|
|
||||||
|
if argc < 3 {
|
||||||
|
PrintUsage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbFile: String = Os_Args(1);
|
||||||
|
let cmd: String = Os_Args(2);
|
||||||
|
|
||||||
|
var db: Database = DB_New(dbFile);
|
||||||
|
|
||||||
|
if String_Eq(cmd, "get") {
|
||||||
|
DB_Load(&db);
|
||||||
|
if argc < 4 {
|
||||||
|
PrintLine("get: missing key");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let key: String = Os_Args(3);
|
||||||
|
let val: String = DB_Get(&db, key);
|
||||||
|
if bux_strlen(val) > 0 {
|
||||||
|
PrintLine(val);
|
||||||
|
} else {
|
||||||
|
Print("(not found) ");
|
||||||
|
PrintLine(key);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if String_Eq(cmd, "set") {
|
||||||
|
DB_Load(&db);
|
||||||
|
if argc < 5 {
|
||||||
|
PrintLine("set: missing key or value");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let key: String = Os_Args(3);
|
||||||
|
let val: String = Os_Args(4);
|
||||||
|
DB_Set(&db, key, val);
|
||||||
|
let saved: bool = DB_Save(&db);
|
||||||
|
if saved {
|
||||||
|
Print("OK ");
|
||||||
|
Print(key);
|
||||||
|
Print(" = ");
|
||||||
|
PrintLine(val);
|
||||||
|
} else {
|
||||||
|
PrintLine("set: save failed");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if String_Eq(cmd, "del") {
|
||||||
|
DB_Load(&db);
|
||||||
|
if argc < 4 {
|
||||||
|
PrintLine("del: missing key");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let key: String = Os_Args(3);
|
||||||
|
DB_Del(&db, key);
|
||||||
|
DB_Save(&db);
|
||||||
|
Print("DEL ");
|
||||||
|
PrintLine(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if String_Eq(cmd, "has") {
|
||||||
|
DB_Load(&db);
|
||||||
|
if argc < 4 {
|
||||||
|
PrintLine("has: missing key");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
let key: String = Os_Args(3);
|
||||||
|
let found: bool = DB_Has(&db, key);
|
||||||
|
if found {
|
||||||
|
Print("true ");
|
||||||
|
PrintLine(key);
|
||||||
|
} else {
|
||||||
|
Print("false ");
|
||||||
|
PrintLine(key);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if String_Eq(cmd, "keys") {
|
||||||
|
DB_Load(&db);
|
||||||
|
let n: uint = DB_Count(&db);
|
||||||
|
Print("keys: ");
|
||||||
|
PrintInt(n as int);
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let list: *String = DB_Keys(&db);
|
||||||
|
var i: uint = 0;
|
||||||
|
while i < n {
|
||||||
|
Print(" ");
|
||||||
|
PrintLine(list[i]);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
bux_free(list as *void);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if String_Eq(cmd, "count") {
|
||||||
|
DB_Load(&db);
|
||||||
|
let n: uint = DB_Count(&db);
|
||||||
|
Print("count: ");
|
||||||
|
PrintInt(n as int);
|
||||||
|
PrintLine("");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Print("unknown command: ");
|
||||||
|
PrintLine(cmd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
-10
@@ -45,7 +45,7 @@ func Fmt_Format(tmpl: String, argStrs: *String, argCount: int) -> String {
|
|||||||
let closeCh: String = String_Chars(tmpl, i);
|
let closeCh: String = String_Chars(tmpl, i);
|
||||||
if String_Eq(closeCh, "}") {
|
if String_Eq(closeCh, "}") {
|
||||||
i = i + 1; // skip }
|
i = i + 1; // skip }
|
||||||
let argStr: String = *(argStrs + (d * 8) as uint);
|
let argStr: String = argStrs[d as uint];
|
||||||
StringBuilder_Append(&sb, argStr);
|
StringBuilder_Append(&sb, argStr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -62,8 +62,8 @@ func Fmt_Format(tmpl: String, argStrs: *String, argCount: int) -> String {
|
|||||||
|
|
||||||
// Convenience wrappers
|
// Convenience wrappers
|
||||||
func Fmt_Fmt1(tmpl: String, a1: String) -> String {
|
func Fmt_Fmt1(tmpl: String, a1: String) -> String {
|
||||||
var args: *String = bux_alloc(8) as *String;
|
var args: *String = bux_alloc(sizeof(String)) as *String;
|
||||||
*(args + 0) = a1;
|
args[0] = a1;
|
||||||
return Fmt_Format(tmpl, args, 1);
|
return Fmt_Format(tmpl, args, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,17 +83,17 @@ func Fmt_FmtFloat(tmpl: String, val: float64) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Fmt_Fmt2(tmpl: String, a1: String, a2: String) -> String {
|
func Fmt_Fmt2(tmpl: String, a1: String, a2: String) -> String {
|
||||||
var args: *String = bux_alloc(16) as *String;
|
var args: *String = bux_alloc(2 * sizeof(String)) as *String;
|
||||||
*(args + 0) = a1;
|
args[0] = a1;
|
||||||
*(args + 8) = a2;
|
args[1] = a2;
|
||||||
return Fmt_Format(tmpl, args, 2);
|
return Fmt_Format(tmpl, args, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fmt_Fmt3(tmpl: String, a1: String, a2: String, a3: String) -> String {
|
func Fmt_Fmt3(tmpl: String, a1: String, a2: String, a3: String) -> String {
|
||||||
var args: *String = bux_alloc(24) as *String;
|
var args: *String = bux_alloc(3 * sizeof(String)) as *String;
|
||||||
*(args + 0) = a1;
|
args[0] = a1;
|
||||||
*(args + 8) = a2;
|
args[1] = a2;
|
||||||
*(args + 16) = a3;
|
args[2] = a3;
|
||||||
return Fmt_Format(tmpl, args, 3);
|
return Fmt_Format(tmpl, args, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-24
@@ -51,25 +51,22 @@ func Jwt_MakeHeader(alg: JwtAlg) -> String {
|
|||||||
|
|
||||||
// Sign the JWT signing input with the given algorithm
|
// Sign the JWT signing input with the given algorithm
|
||||||
func Jwt_Sign(alg: JwtAlg, signingInput: String, key: String) -> String {
|
func Jwt_Sign(alg: JwtAlg, signingInput: String, key: String) -> String {
|
||||||
let algTag: int = alg.tag;
|
|
||||||
let inputLen: int = String_Len(signingInput) as int;
|
|
||||||
|
|
||||||
// --- HMAC algorithms ---
|
// --- HMAC algorithms ---
|
||||||
if algTag == JwtAlg_HS256 {
|
if alg.tag == JwtAlg_HS256 {
|
||||||
let buf: *void = Alloc(32);
|
let buf: *void = Alloc(32);
|
||||||
Hmac_Sha256Raw(key, signingInput, buf);
|
Hmac_Sha256Raw(key, signingInput, buf);
|
||||||
let result: String = bux_base64_encode(buf as String, 32);
|
let result: String = bux_base64_encode(buf as String, 32);
|
||||||
Free(buf);
|
Free(buf);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_HS384 {
|
if alg.tag == JwtAlg_HS384 {
|
||||||
let buf: *void = Alloc(48);
|
let buf: *void = Alloc(48);
|
||||||
Hmac_Sha384Raw(key, signingInput, buf);
|
Hmac_Sha384Raw(key, signingInput, buf);
|
||||||
let result: String = bux_base64_encode(buf as String, 48);
|
let result: String = bux_base64_encode(buf as String, 48);
|
||||||
Free(buf);
|
Free(buf);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_HS512 {
|
if alg.tag == JwtAlg_HS512 {
|
||||||
let buf: *void = Alloc(64);
|
let buf: *void = Alloc(64);
|
||||||
Hmac_Sha512Raw(key, signingInput, buf);
|
Hmac_Sha512Raw(key, signingInput, buf);
|
||||||
let result: String = bux_base64_encode(buf as String, 64);
|
let result: String = bux_base64_encode(buf as String, 64);
|
||||||
@@ -78,31 +75,31 @@ func Jwt_Sign(alg: JwtAlg, signingInput: String, key: String) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- RSA algorithms (key is PEM private key) ---
|
// --- RSA algorithms (key is PEM private key) ---
|
||||||
if algTag == JwtAlg_RS256 {
|
if alg.tag == JwtAlg_RS256 {
|
||||||
let raw: String = Rsa_SignSha256(key, signingInput);
|
let raw: String = Rsa_SignSha256(key, signingInput);
|
||||||
return bux_base64_encode(raw, String_Len(raw) as int);
|
return bux_base64_encode(raw, String_Len(raw) as int);
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_RS384 {
|
if alg.tag == JwtAlg_RS384 {
|
||||||
let raw: String = Rsa_SignSha384(key, signingInput);
|
let raw: String = Rsa_SignSha384(key, signingInput);
|
||||||
return bux_base64_encode(raw, String_Len(raw) as int);
|
return bux_base64_encode(raw, String_Len(raw) as int);
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_RS512 {
|
if alg.tag == JwtAlg_RS512 {
|
||||||
let raw: String = Rsa_SignSha512(key, signingInput);
|
let raw: String = Rsa_SignSha512(key, signingInput);
|
||||||
return bux_base64_encode(raw, String_Len(raw) as int);
|
return bux_base64_encode(raw, String_Len(raw) as int);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- ECDSA algorithms (key is PEM private key) ---
|
// --- ECDSA algorithms (key is PEM private key) ---
|
||||||
if algTag == JwtAlg_ES256 {
|
if alg.tag == JwtAlg_ES256 {
|
||||||
let raw: String = Ecdsa_SignP256(key, signingInput);
|
let raw: String = Ecdsa_SignP256(key, signingInput);
|
||||||
return bux_base64_encode(raw, String_Len(raw) as int);
|
return bux_base64_encode(raw, String_Len(raw) as int);
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_ES384 {
|
if alg.tag == JwtAlg_ES384 {
|
||||||
let raw: String = Ecdsa_SignP384(key, signingInput);
|
let raw: String = Ecdsa_SignP384(key, signingInput);
|
||||||
return bux_base64_encode(raw, String_Len(raw) as int);
|
return bux_base64_encode(raw, String_Len(raw) as int);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- EdDSA (key is 32-byte raw private key) ---
|
// --- EdDSA (key is 32-byte raw private key) ---
|
||||||
if algTag == JwtAlg_EdDSA {
|
if alg.tag == JwtAlg_EdDSA {
|
||||||
return Ed25519_Sign(key, signingInput);
|
return Ed25519_Sign(key, signingInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,25 +110,22 @@ func Jwt_Sign(alg: JwtAlg, signingInput: String, key: String) -> String {
|
|||||||
|
|
||||||
// Verify a JWT signature
|
// Verify a JWT signature
|
||||||
func Jwt_Verify(alg: JwtAlg, signingInput: String, signatureB64: String, key: String) -> bool {
|
func Jwt_Verify(alg: JwtAlg, signingInput: String, signatureB64: String, key: String) -> bool {
|
||||||
let algTag: int = alg.tag;
|
|
||||||
let inputLen: int = String_Len(signingInput) as int;
|
|
||||||
|
|
||||||
// --- HMAC algorithms ---
|
// --- HMAC algorithms ---
|
||||||
if algTag == JwtAlg_HS256 {
|
if alg.tag == JwtAlg_HS256 {
|
||||||
let expectBuf: *void = Alloc(32);
|
let expectBuf: *void = Alloc(32);
|
||||||
Hmac_Sha256Raw(key, signingInput, expectBuf);
|
Hmac_Sha256Raw(key, signingInput, expectBuf);
|
||||||
let expectB64: String = bux_base64_encode(expectBuf as String, 32);
|
let expectB64: String = bux_base64_encode(expectBuf as String, 32);
|
||||||
Free(expectBuf);
|
Free(expectBuf);
|
||||||
return String_Eq(expectB64, signatureB64);
|
return String_Eq(expectB64, signatureB64);
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_HS384 {
|
if alg.tag == JwtAlg_HS384 {
|
||||||
let expectBuf: *void = Alloc(48);
|
let expectBuf: *void = Alloc(48);
|
||||||
Hmac_Sha384Raw(key, signingInput, expectBuf);
|
Hmac_Sha384Raw(key, signingInput, expectBuf);
|
||||||
let expectB64: String = bux_base64_encode(expectBuf as String, 48);
|
let expectB64: String = bux_base64_encode(expectBuf as String, 48);
|
||||||
Free(expectBuf);
|
Free(expectBuf);
|
||||||
return String_Eq(expectB64, signatureB64);
|
return String_Eq(expectB64, signatureB64);
|
||||||
}
|
}
|
||||||
if algTag == JwtAlg_HS512 {
|
if alg.tag == JwtAlg_HS512 {
|
||||||
let expectBuf: *void = Alloc(64);
|
let expectBuf: *void = Alloc(64);
|
||||||
Hmac_Sha512Raw(key, signingInput, expectBuf);
|
Hmac_Sha512Raw(key, signingInput, expectBuf);
|
||||||
let expectB64: String = bux_base64_encode(expectBuf as String, 64);
|
let expectB64: String = bux_base64_encode(expectBuf as String, 64);
|
||||||
@@ -140,16 +134,16 @@ func Jwt_Verify(alg: JwtAlg, signingInput: String, signatureB64: String, key: St
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- RSA algorithms ---
|
// --- RSA algorithms ---
|
||||||
if algTag == JwtAlg_RS256 { return Rsa_VerifySha256(key, signingInput, signatureB64); }
|
if alg.tag == JwtAlg_RS256 { return Rsa_VerifySha256(key, signingInput, signatureB64); }
|
||||||
if algTag == JwtAlg_RS384 { return Rsa_VerifySha384(key, signingInput, signatureB64); }
|
if alg.tag == JwtAlg_RS384 { return Rsa_VerifySha384(key, signingInput, signatureB64); }
|
||||||
if algTag == JwtAlg_RS512 { return Rsa_VerifySha512(key, signingInput, signatureB64); }
|
if alg.tag == JwtAlg_RS512 { return Rsa_VerifySha512(key, signingInput, signatureB64); }
|
||||||
|
|
||||||
// --- ECDSA algorithms ---
|
// --- ECDSA algorithms ---
|
||||||
if algTag == JwtAlg_ES256 { return Ecdsa_VerifyP256(key, signingInput, signatureB64); }
|
if alg.tag == JwtAlg_ES256 { return Ecdsa_VerifyP256(key, signingInput, signatureB64); }
|
||||||
if algTag == JwtAlg_ES384 { return Ecdsa_VerifyP384(key, signingInput, signatureB64); }
|
if alg.tag == JwtAlg_ES384 { return Ecdsa_VerifyP384(key, signingInput, signatureB64); }
|
||||||
|
|
||||||
// --- EdDSA ---
|
// --- EdDSA ---
|
||||||
if algTag == JwtAlg_EdDSA { return Ed25519_Verify(key, signatureB64, signingInput); }
|
if alg.tag == JwtAlg_EdDSA { return Ed25519_Verify(key, signatureB64, signingInput); }
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user