feat: add Std::Crypto — SHA-256, HMAC-SHA256, Base64, random bytes
- C runtime (OpenSSL): bux_sha256, bux_hmac_sha256, bux_random_bytes, bux_base64_encode, bux_base64_decode, bux_bytes_to_hex - library/std/Crypto.bux: Crypto_Sha256, Crypto_HmacSha256, Crypto_HmacSha256Raw, Crypto_RandomBytes, Crypto_Base64Encode, Crypto_Base64Decode - examples/jwt.bux: full JWT creation with HS256 signature - Link with -lcrypto in C backend
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
[Package]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import Std::Io::{PrintLine};
|
||||||
|
import Std::Crypto;
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("SHA256:");
|
||||||
|
PrintLine(Crypto_Sha256("hello"));
|
||||||
|
|
||||||
|
PrintLine("HMAC-SHA256:");
|
||||||
|
PrintLine(Crypto_HmacSha256("secret", "message"));
|
||||||
|
|
||||||
|
PrintLine("Base64 encode:");
|
||||||
|
PrintLine(Crypto_Base64Encode("hello"));
|
||||||
|
|
||||||
|
PrintLine("Random bytes (base64):");
|
||||||
|
PrintLine(Crypto_RandomBytes(16));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -536,7 +536,7 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
# Compile with cc
|
# Compile with cc
|
||||||
let outputName = if man.name != "": man.name else: "bux_out"
|
let outputName = if man.name != "": man.name else: "bux_out"
|
||||||
let outputFile = buildDir / outputName
|
let outputFile = buildDir / outputName
|
||||||
let ccCmd = &"cc -O2 -pthread -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm 2>&1"
|
let ccCmd = &"cc -O2 -pthread -o {outputFile} {cFile} {runtimeDst} {ioDst} -lm -lcrypto 2>&1"
|
||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
printInfo(&"running: {ccCmd}", useColor)
|
printInfo(&"running: {ccCmd}", useColor)
|
||||||
let (output, exitCode) = execCmdEx(ccCmd)
|
let (output, exitCode) = execCmdEx(ccCmd)
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// JWT - Create a JSON Web Token
|
||||||
|
import Std::Io::{PrintLine};
|
||||||
|
import Std::Json;
|
||||||
|
import Std::Crypto;
|
||||||
|
import Std::String::{String_Replace, String_Concat, String_EndsWith, String_Slice, String_Len};
|
||||||
|
|
||||||
|
func Base64UrlEncode(s: String) -> String {
|
||||||
|
let b64: String = Crypto_Base64Encode(s);
|
||||||
|
let r1: String = String_Replace(b64, "+", "-");
|
||||||
|
let r2: String = String_Replace(r1, "/", "_");
|
||||||
|
var result: String = r2;
|
||||||
|
while String_EndsWith(result, "=") {
|
||||||
|
result = String_Slice(result, 0, String_Len(result) - 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Base64UrlEncodeSig(b64: String) -> String {
|
||||||
|
let r1: String = String_Replace(b64, "+", "-");
|
||||||
|
let r2: String = String_Replace(r1, "/", "_");
|
||||||
|
var result: String = r2;
|
||||||
|
while String_EndsWith(result, "=") {
|
||||||
|
result = String_Slice(result, 0, String_Len(result) - 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Jwt_Create(payload: JsonValue, secret: String) -> String {
|
||||||
|
let header: JsonValue = Json_Object();
|
||||||
|
Json_ObjectSet(&header, "alg", Json_String("HS256"));
|
||||||
|
Json_ObjectSet(&header, "typ", Json_String("JWT"));
|
||||||
|
|
||||||
|
let headerB64: String = Base64UrlEncode(Json_Stringify(header));
|
||||||
|
let payloadB64: String = Base64UrlEncode(Json_Stringify(payload));
|
||||||
|
|
||||||
|
let signingInput: String = String_Concat(headerB64, ".");
|
||||||
|
signingInput = String_Concat(signingInput, payloadB64);
|
||||||
|
|
||||||
|
let sigRaw: String = Crypto_HmacSha256Raw(secret, signingInput);
|
||||||
|
let sigB64: String = Base64UrlEncodeSig(sigRaw);
|
||||||
|
|
||||||
|
return String_Concat(String_Concat(signingInput, "."), sigB64);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("=== JWT Demo ===");
|
||||||
|
|
||||||
|
let payload: JsonValue = Json_Object();
|
||||||
|
Json_ObjectSet(&payload, "sub", Json_String("user123"));
|
||||||
|
Json_ObjectSet(&payload, "name", Json_String("Bux Developer"));
|
||||||
|
Json_ObjectSet(&payload, "admin", Json_Bool(true));
|
||||||
|
|
||||||
|
let token: String = Jwt_Create(payload, "my-secret-key");
|
||||||
|
PrintLine("Token:");
|
||||||
|
PrintLine(token);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1248,3 +1248,59 @@ int bux_socket_close(int fd) {
|
|||||||
const char* bux_socket_error(void) {
|
const char* bux_socket_error(void) {
|
||||||
return strerror(errno);
|
return strerror(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Cryptography primitives (OpenSSL)
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
|
||||||
|
void bux_sha256(const char* data, int len, unsigned char* out) {
|
||||||
|
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
|
||||||
|
EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
|
||||||
|
EVP_DigestUpdate(ctx, data, (size_t)len);
|
||||||
|
EVP_DigestFinal_ex(ctx, out, NULL);
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bux_hmac_sha256(const char* key, int keylen, const char* msg, int msglen, unsigned char* out) {
|
||||||
|
unsigned int outlen = 32;
|
||||||
|
HMAC(EVP_sha256(), key, keylen, (const unsigned char*)msg, (size_t)msglen, out, &outlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_random_bytes(unsigned char* buf, int len) {
|
||||||
|
return RAND_bytes(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_base64_encode(const unsigned char* in, int inlen) {
|
||||||
|
int outlen = 4 * ((inlen + 2) / 3);
|
||||||
|
char* out = (char*)bux_alloc(outlen + 1);
|
||||||
|
int elen = EVP_EncodeBlock((unsigned char*)out, in, inlen);
|
||||||
|
out[elen] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_base64_decode(const char* in, int inlen, int* outlen) {
|
||||||
|
int maxlen = 3 * inlen / 4;
|
||||||
|
char* out = (char*)bux_alloc(maxlen + 1);
|
||||||
|
*outlen = EVP_DecodeBlock((unsigned char*)out, (const unsigned char*)in, inlen);
|
||||||
|
if (*outlen < 0) {
|
||||||
|
*outlen = 0;
|
||||||
|
out[0] = '\0';
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_bytes_to_hex(const unsigned char* data, int len) {
|
||||||
|
char* out = (char*)bux_alloc((size_t)len * 2 + 1);
|
||||||
|
const char* hex = "0123456789abcdef";
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
out[i * 2] = hex[(data[i] >> 4) & 0x0F];
|
||||||
|
out[i * 2 + 1] = hex[data[i] & 0x0F];
|
||||||
|
}
|
||||||
|
out[len * 2] = '\0';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
module Std::Crypto {
|
||||||
|
extern func bux_sha256(data: String, len: int, out: *void);
|
||||||
|
extern func bux_hmac_sha256(key: String, keylen: int, msg: String, msglen: int, out: *void);
|
||||||
|
extern func bux_random_bytes(buf: *void, len: int) -> int;
|
||||||
|
extern func bux_base64_encode(data: String, len: int) -> String;
|
||||||
|
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
|
||||||
|
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
|
||||||
|
|
||||||
|
/* SHA-256: returns lowercase hex string of the hash */
|
||||||
|
func Crypto_Sha256(data: String) -> String {
|
||||||
|
let len: int = String_Len(data) as int;
|
||||||
|
let hashBuf: *void = Alloc(32);
|
||||||
|
bux_sha256(data, len, hashBuf);
|
||||||
|
let result: String = bux_bytes_to_hex(hashBuf as *void, 32);
|
||||||
|
Free(hashBuf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HMAC-SHA256: returns lowercase hex string */
|
||||||
|
func Crypto_HmacSha256(key: String, message: String) -> String {
|
||||||
|
let keylen: int = String_Len(key) as int;
|
||||||
|
let msglen: int = String_Len(message) as int;
|
||||||
|
let hmacBuf: *void = Alloc(32);
|
||||||
|
bux_hmac_sha256(key, keylen, message, msglen, hmacBuf);
|
||||||
|
let result: String = bux_bytes_to_hex(hmacBuf as *void, 32);
|
||||||
|
Free(hmacBuf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Random bytes: returns a string of n random bytes */
|
||||||
|
func Crypto_RandomBytes(n: int) -> String {
|
||||||
|
if n <= 0 { return ""; }
|
||||||
|
let buf: *void = Alloc(n as uint);
|
||||||
|
if bux_random_bytes(buf, n) != 1 {
|
||||||
|
Free(buf);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
let result: String = bux_base64_encode(buf as String, n);
|
||||||
|
Free(buf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base64 encode */
|
||||||
|
func Crypto_Base64Encode(s: String) -> String {
|
||||||
|
return bux_base64_encode(s, String_Len(s) as int);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HMAC-SHA256 raw: returns base64 of raw binary hmac */
|
||||||
|
func Crypto_HmacSha256Raw(key: String, message: String) -> String {
|
||||||
|
let keylen: int = String_Len(key) as int;
|
||||||
|
let msglen: int = String_Len(message) as int;
|
||||||
|
let hmacBuf: *void = Alloc(32);
|
||||||
|
bux_hmac_sha256(key, keylen, message, msglen, hmacBuf);
|
||||||
|
let result: String = bux_base64_encode(hmacBuf as String, 32);
|
||||||
|
Free(hmacBuf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base64 decode */
|
||||||
|
func Crypto_Base64Decode(s: String) -> String {
|
||||||
|
let outlen: int = 0;
|
||||||
|
return bux_base64_decode(s, String_Len(s) as int, &outlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user