Add bounded structured crypto operations
This commit is contained in:
@@ -14,6 +14,7 @@ hmac_sha256
|
||||
hmac_sha256_hex
|
||||
random_bytes
|
||||
crypto_equal
|
||||
crypto_operation
|
||||
password_hash
|
||||
password_verify
|
||||
password_needs_rehash
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
:sig
|
||||
DValue crypto_operation(DValue request)
|
||||
|
||||
:params
|
||||
request : structured operation, algorithm, and operation-specific fields
|
||||
return value : map with ok, bounded error code, and operation-specific output
|
||||
|
||||
:content
|
||||
Runs one explicitly supported structured asymmetric cryptographic operation. The initial allowlist is `key_generate` with `ES256` and `jwt_sign` with `ES256`. Unknown operations and algorithms fail closed.
|
||||
|
||||
ES256 signing validates that `x`, `y`, and `d` form one P-256 key, forces the protected `alg` to `ES256`, and emits a compact JWT with a 64-byte JOSE signature. Requests, nesting, values, and output are bounded. Header and claims roots must be JSON objects containing valid UTF-8 without raw control bytes.
|
||||
|
||||
This function does not replace typed digest, HMAC, password, randomness, or constant-time comparison APIs. It exposes no raw signing, arbitrary curve/digest selection, encryption, or generic OpenSSL access. Keep returned private JWKs secret.
|
||||
|
||||
:example
|
||||
DValue key_request;
|
||||
key_request["operation"] = "key_generate";
|
||||
key_request["algorithm"] = "ES256";
|
||||
DValue key = crypto_operation(key_request);
|
||||
|
||||
DValue sign_request;
|
||||
sign_request["operation"] = "jwt_sign";
|
||||
sign_request["algorithm"] = "ES256";
|
||||
sign_request["private_jwk"] = key["private_jwk"];
|
||||
sign_request["protected_header"]["typ"] = "JWT";
|
||||
sign_request["claims"]["iss"] = "https://client.example";
|
||||
DValue signed_jwt = crypto_operation(sign_request);
|
||||
print(signed_jwt["ok"].to_bool() ? "signed" : "failed", "\n");
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "testlib.h"
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
u64 passed = 0;
|
||||
u64 failed = 0;
|
||||
u64 skipped = 0;
|
||||
auto check = [&](String name, bool ok, String detail)
|
||||
{
|
||||
site_tests_case(name, ok ? "pass" : "fail", detail);
|
||||
if(ok) passed++; else failed++;
|
||||
};
|
||||
site_tests_page_start("Structured crypto operations", "Algorithm-selected P-256 JWK creation and ES256 JWT signing.");
|
||||
DValue key_request;
|
||||
key_request["operation"] = "key_generate";
|
||||
key_request["algorithm"] = "ES256";
|
||||
DValue key = crypto_operation(key_request);
|
||||
DValue header;
|
||||
header["alg"] = "none";
|
||||
header["kid"] = key["kid"];
|
||||
DValue claims;
|
||||
claims["iss"] = "https://client.example";
|
||||
claims["sub"] = "client.example";
|
||||
DValue sign_request;
|
||||
sign_request["operation"] = "jwt_sign";
|
||||
sign_request["algorithm"] = "ES256";
|
||||
sign_request["private_jwk"] = key["private_jwk"];
|
||||
sign_request["protected_header"] = header;
|
||||
sign_request["claims"] = claims;
|
||||
DValue signed_result = crypto_operation(sign_request);
|
||||
String jwt = signed_result["jwt"].to_string();
|
||||
StringList parts = split(jwt, ".");
|
||||
String header_b64 = parts.size() == 3 ? parts[0] : "";
|
||||
while(header_b64.size() % 4) header_b64 += "=";
|
||||
String decoded_header = base64_decode(header_b64);
|
||||
DValue wrong_curve = key["private_jwk"];
|
||||
wrong_curve["crv"] = "P-384";
|
||||
DValue malformed = key["private_jwk"];
|
||||
malformed["x"] = "not_base64url=";
|
||||
DValue wrong_request = sign_request;
|
||||
wrong_request["private_jwk"] = wrong_curve;
|
||||
DValue malformed_request = sign_request;
|
||||
malformed_request["private_jwk"] = malformed;
|
||||
DValue unsupported;
|
||||
unsupported["operation"] = "encrypt";
|
||||
unsupported["algorithm"] = "ES256";
|
||||
DValue list_header;
|
||||
list_header.set_array();
|
||||
DValue list_item;
|
||||
list_item = "not-an-object";
|
||||
list_header.push(list_item);
|
||||
DValue list_request = sign_request;
|
||||
list_request["protected_header"] = list_header;
|
||||
DValue control_request = sign_request;
|
||||
control_request["claims"]["bad"] = String("control\nbyte");
|
||||
DValue oversized = key_request;
|
||||
oversized["ignored"] = String(17000, 'x');
|
||||
check("crypto_operation() ES256 key generation and JWT signing", key["ok"].to_bool() && signed_result["ok"].to_bool() && key["private_jwk"]["kty"].to_string() == "EC" && key["private_jwk"]["crv"].to_string() == "P-256" && key["public_jwk"]["d"].to_string() == "" && key["kid"].to_string() == key["thumbprint"].to_string() && parts.size() == 3 && parts[2].size() == 86 && decoded_header.find("ES256") != String::npos && !crypto_operation(wrong_request)["ok"].to_bool() && !crypto_operation(malformed_request)["ok"].to_bool() && crypto_operation(unsupported)["error"].to_string() == "unsupported_operation" && !crypto_operation(list_request)["ok"].to_bool() && crypto_operation(control_request)["error"].to_string() == "invalid_request" && crypto_operation(oversized)["error"].to_string() == "invalid_request", key["kid"].to_string());
|
||||
site_tests_summary(passed, failed, skipped, "Structured crypto tests generate ephemeral P-256 keys and retain no key material.");
|
||||
site_tests_page_end();
|
||||
}
|
||||
@@ -8,6 +8,7 @@ markdown.uce|Markdown|Markdown parsing, rendering, and component hooks.|http sui
|
||||
units.uce|Units|unit_call(), lifecycle hooks, and unit metadata.|http suite uce public|Units|1|1
|
||||
websockets.ws.uce|WebSockets|Browser-driven WebSocket helper checks.|http suite uce public websocket|WebSockets|1|1
|
||||
io.uce|Filesystem|Filesystem helpers that are restricted outside trusted networks.|http suite uce internal|Filesystem|1|1
|
||||
crypto_operation.uce|Structured Crypto|Algorithm-selected key generation and JWT signing coverage.|http suite uce public crypto|Structured crypto operations|1|1
|
||||
sqlite.uce|SQLite|SQLite connector with prepared named parameters and DValue rows.|http suite uce internal sqlite|SQLite|1|1
|
||||
zip.uce|ZIP|Archive helpers that create and extract temporary server-side files.|http suite uce internal|ZIP|1|1
|
||||
services.uce|Sockets And Services|Network/service helpers that are restricted outside trusted networks.|http suite uce internal|Sockets And Services|1|1
|
||||
|
||||
Reference in New Issue
Block a user