feat(hsm-emulator): M10 crypto-surface, M11 conformance, deep-audit hardening
M10 (crypto-surface): RUP-safe buffered AES-GCM streaming (16 MiB cap), DigestEncrypt/SignEncrypt/DecryptDigest/DecryptVerify dual functions (decrypt-side CBC-only), RSA C_SignRecover/C_VerifyRecover (CKM_RSA_PKCS), digest-only Get/SetOperationState. M11 (conformance): spec-correct C_WaitForSlotEvent (DONT_BLOCK->CKR_NO_EVENT, blocking->FUNCTION_NOT_SUPPORTED, pReserved->ARGUMENTS_BAD) + CKF_DONT_BLOCK; public learn/CONFORMANCE.md documenting every deliberate N/A with its OASIS v2.40 section and exact return code. Deep project audit (5 read-only dimension agents, 0 Critical/High): - fix: C_Finalize-vs-fast-path use-after-free race -- replace lockless state.current() with state.acquire() (lock + verify-live atomically) across all ~57 entry points - fix: enforce CKR_PIN_LEN_RANGE on the new PIN in InitToken/InitPIN/SetPIN - fix: clear a stale sealed flag on Object.set replace (login-unseal wedge) - harden: zeroize CBC plaintext stack temporaries; Debug leak-assert at C_Finalize turns smoke into an API-layer leak gate - test: ABI layout-check PSS/OAEP param structs; +sealed-flag regression Proven: 76/76 unit (Debug + ReleaseSafe), in-process smoke (with PIN-length asserts), cross-process pkcs11-tool (init/pin/RSA keygen+sign/wrong-pin). .so exports only C_GetFunctionList.
This commit is contained in:
parent
0b8f8df8a3
commit
f70ca3f32c
|
|
@ -24,6 +24,7 @@ pub fn main() !void {
|
|||
var user_pin = "1234".*;
|
||||
var new_user_pin = "5678".*;
|
||||
var wrong_pin = "0000".*;
|
||||
var short_pin = "12".*;
|
||||
var label: [32]u8 = @splat(' ');
|
||||
@memcpy(label[0..11], "smoke-token");
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ pub fn main() !void {
|
|||
try check("C_GetMechanismList(size)", f.C_GetMechanismList.?(slot, null, &mech_count));
|
||||
if (mech_count == 0) return error.NoMechanisms;
|
||||
|
||||
if (f.C_InitToken.?(slot, &short_pin, short_pin.len, &label) != ck.CKR_PIN_LEN_RANGE) return error.ShortSoPinNotRejected;
|
||||
try check("C_InitToken", f.C_InitToken.?(slot, &so_pin, so_pin.len, &label));
|
||||
try check("C_GetTokenInfo(post-init)", f.C_GetTokenInfo.?(slot, &token_info));
|
||||
if (token_info.flags & ck.CKF_TOKEN_INITIALIZED == 0) return error.InitTokenDidNotInitialize;
|
||||
|
|
@ -80,6 +82,7 @@ pub fn main() !void {
|
|||
try check("C_GetSessionInfo(SO)", f.C_GetSessionInfo.?(h, &si));
|
||||
if (si.state != ck.CKS_RW_SO_FUNCTIONS) return error.UnexpectedSoState;
|
||||
|
||||
if (f.C_InitPIN.?(h, &short_pin, short_pin.len) != ck.CKR_PIN_LEN_RANGE) return error.ShortUserPinNotRejected;
|
||||
try check("C_InitPIN", f.C_InitPIN.?(h, &user_pin, user_pin.len));
|
||||
try check("C_Logout(SO)", f.C_Logout.?(h));
|
||||
|
||||
|
|
@ -90,6 +93,7 @@ pub fn main() !void {
|
|||
try check("C_GetSessionInfo(USER)", f.C_GetSessionInfo.?(h, &si));
|
||||
if (si.state != ck.CKS_RW_USER_FUNCTIONS) return error.UnexpectedUserState;
|
||||
|
||||
if (f.C_SetPIN.?(h, &user_pin, user_pin.len, &short_pin, short_pin.len) != ck.CKR_PIN_LEN_RANGE) return error.ShortNewPinNotRejected;
|
||||
try check("C_SetPIN", f.C_SetPIN.?(h, &user_pin, user_pin.len, &new_user_pin, new_user_pin.len));
|
||||
try check("C_Logout(USER)", f.C_Logout.?(h));
|
||||
|
||||
|
|
@ -492,9 +496,197 @@ pub fn main() !void {
|
|||
try check("C_Digest(value)", f.C_Digest.?(h, &dk_val, dk_val.len, &dk_d2, &dk_d2len));
|
||||
if (dk_d1len != 32 or !std.mem.eql(u8, dk_d1[0..32], dk_d2[0..32])) return error.DigestKeyMismatch;
|
||||
|
||||
var gcm_iv = [_]u8{ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB };
|
||||
var gcm_aad = "gcm-associated-data".*;
|
||||
var gcm_params = ck.CK_GCM_PARAMS{
|
||||
.pIv = &gcm_iv,
|
||||
.ulIvLen = gcm_iv.len,
|
||||
.ulIvBits = 96,
|
||||
.pAAD = &gcm_aad,
|
||||
.ulAADLen = gcm_aad.len,
|
||||
.ulTagBits = 128,
|
||||
};
|
||||
var gcm_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_AES_GCM, .pParameter = &gcm_params, .ulParameterLen = @sizeOf(ck.CK_GCM_PARAMS) };
|
||||
var gcm_pt = "GCM streaming over multiple update calls exceeds one block".*;
|
||||
|
||||
var gcm_ct1: [128]u8 = undefined;
|
||||
var gcm_ct1len: ck.CK_ULONG = gcm_ct1.len;
|
||||
try check("C_EncryptInit(GCM one-shot)", f.C_EncryptInit.?(h, &gcm_mech, h_aes));
|
||||
try check("C_Encrypt(GCM one-shot)", f.C_Encrypt.?(h, &gcm_pt, gcm_pt.len, &gcm_ct1, &gcm_ct1len));
|
||||
|
||||
var gcm_ct2: [128]u8 = undefined;
|
||||
var part_out: [128]u8 = undefined;
|
||||
var part_outlen: ck.CK_ULONG = part_out.len;
|
||||
try check("C_EncryptInit(GCM stream)", f.C_EncryptInit.?(h, &gcm_mech, h_aes));
|
||||
var off: usize = 0;
|
||||
while (off < gcm_pt.len) {
|
||||
const end = @min(off + 17, gcm_pt.len);
|
||||
part_outlen = part_out.len;
|
||||
try check("C_EncryptUpdate(GCM)", f.C_EncryptUpdate.?(h, gcm_pt[off..].ptr, @intCast(end - off), &part_out, &part_outlen));
|
||||
if (part_outlen != 0) return error.GcmUpdateEmittedEarly;
|
||||
off = end;
|
||||
}
|
||||
var gcm_ct2len: ck.CK_ULONG = gcm_ct2.len;
|
||||
try check("C_EncryptFinal(GCM)", f.C_EncryptFinal.?(h, &gcm_ct2, &gcm_ct2len));
|
||||
if (gcm_ct1len != gcm_ct2len or !std.mem.eql(u8, gcm_ct1[0..gcm_ct1len], gcm_ct2[0..gcm_ct2len])) return error.GcmStreamMismatch;
|
||||
|
||||
var gcm_back: [128]u8 = undefined;
|
||||
try check("C_DecryptInit(GCM stream)", f.C_DecryptInit.?(h, &gcm_mech, h_aes));
|
||||
off = 0;
|
||||
while (off < gcm_ct1len) {
|
||||
const end = @min(off + 19, gcm_ct1len);
|
||||
part_outlen = part_out.len;
|
||||
try check("C_DecryptUpdate(GCM)", f.C_DecryptUpdate.?(h, gcm_ct1[off..].ptr, @intCast(end - off), &part_out, &part_outlen));
|
||||
if (part_outlen != 0) return error.GcmDecryptEmittedEarly;
|
||||
off = end;
|
||||
}
|
||||
var gcm_backlen: ck.CK_ULONG = gcm_back.len;
|
||||
try check("C_DecryptFinal(GCM)", f.C_DecryptFinal.?(h, &gcm_back, &gcm_backlen));
|
||||
if (gcm_backlen != gcm_pt.len or !std.mem.eql(u8, gcm_back[0..gcm_backlen], &gcm_pt)) return error.GcmStreamRoundTripFailed;
|
||||
|
||||
var gcm_bad: [128]u8 = undefined;
|
||||
@memcpy(gcm_bad[0..gcm_ct1len], gcm_ct1[0..gcm_ct1len]);
|
||||
gcm_bad[0] ^= 0xff;
|
||||
var gcm_badlen: ck.CK_ULONG = gcm_back.len;
|
||||
try check("C_DecryptInit(GCM tamper)", f.C_DecryptInit.?(h, &gcm_mech, h_aes));
|
||||
if (f.C_Decrypt.?(h, &gcm_bad, gcm_ct1len, &gcm_back, &gcm_badlen) != ck.CKR_ENCRYPTED_DATA_INVALID) return error.GcmTamperNotDetected;
|
||||
|
||||
var de_sha = ck.CK_MECHANISM{ .mechanism = ck.CKM_SHA256, .pParameter = null, .ulParameterLen = 0 };
|
||||
var dual_pt = "dual-function plaintext spanning blocks".*;
|
||||
var de_iv = [_]u8{1} ** 16;
|
||||
var de_cbcpad = ck.CK_MECHANISM{ .mechanism = ck.CKM_AES_CBC_PAD, .pParameter = &de_iv, .ulParameterLen = de_iv.len };
|
||||
try check("C_DigestInit(DigestEncrypt)", f.C_DigestInit.?(h, &de_sha));
|
||||
try check("C_EncryptInit(DigestEncrypt)", f.C_EncryptInit.?(h, &de_cbcpad, h_aes));
|
||||
var de_ct: [80]u8 = undefined;
|
||||
var de_ctlen: usize = 0;
|
||||
off = 0;
|
||||
while (off < dual_pt.len) {
|
||||
const end = @min(off + 16, dual_pt.len);
|
||||
var seg: ck.CK_ULONG = @intCast(de_ct.len - de_ctlen);
|
||||
try check("C_DigestEncryptUpdate", f.C_DigestEncryptUpdate.?(h, dual_pt[off..].ptr, @intCast(end - off), de_ct[de_ctlen..].ptr, &seg));
|
||||
de_ctlen += @intCast(seg);
|
||||
off = end;
|
||||
}
|
||||
var de_finlen: ck.CK_ULONG = @intCast(de_ct.len - de_ctlen);
|
||||
try check("C_EncryptFinal(DigestEncrypt)", f.C_EncryptFinal.?(h, de_ct[de_ctlen..].ptr, &de_finlen));
|
||||
de_ctlen += @intCast(de_finlen);
|
||||
var de_dig: [32]u8 = undefined;
|
||||
var de_diglen: ck.CK_ULONG = de_dig.len;
|
||||
try check("C_DigestFinal(DigestEncrypt)", f.C_DigestFinal.?(h, &de_dig, &de_diglen));
|
||||
var de_ref: [32]u8 = undefined;
|
||||
var de_reflen: ck.CK_ULONG = de_ref.len;
|
||||
try check("C_DigestInit(DigestEncrypt ref)", f.C_DigestInit.?(h, &de_sha));
|
||||
try check("C_Digest(DigestEncrypt ref)", f.C_Digest.?(h, &dual_pt, dual_pt.len, &de_ref, &de_reflen));
|
||||
if (!std.mem.eql(u8, de_dig[0..32], de_ref[0..32])) return error.DualDigestMismatch;
|
||||
var de_back: [80]u8 = undefined;
|
||||
var de_backlen: ck.CK_ULONG = de_back.len;
|
||||
try check("C_DecryptInit(DigestEncrypt verify)", f.C_DecryptInit.?(h, &de_cbcpad, h_aes));
|
||||
try check("C_Decrypt(DigestEncrypt verify)", f.C_Decrypt.?(h, &de_ct, @intCast(de_ctlen), &de_back, &de_backlen));
|
||||
if (de_backlen != dual_pt.len or !std.mem.eql(u8, de_back[0..de_backlen], &dual_pt)) return error.DualEncryptRoundTrip;
|
||||
|
||||
var dd_pt = "thirty-two-byte aligned message!".*;
|
||||
var dd_iv = [_]u8{2} ** 16;
|
||||
var dd_cbc = ck.CK_MECHANISM{ .mechanism = ck.CKM_AES_CBC, .pParameter = &dd_iv, .ulParameterLen = dd_iv.len };
|
||||
var dd_ct: [48]u8 = undefined;
|
||||
var dd_ctlen: ck.CK_ULONG = dd_ct.len;
|
||||
try check("C_EncryptInit(DecryptDigest setup)", f.C_EncryptInit.?(h, &dd_cbc, h_aes));
|
||||
try check("C_Encrypt(DecryptDigest setup)", f.C_Encrypt.?(h, &dd_pt, dd_pt.len, &dd_ct, &dd_ctlen));
|
||||
try check("C_DecryptInit(DecryptDigest)", f.C_DecryptInit.?(h, &dd_cbc, h_aes));
|
||||
try check("C_DigestInit(DecryptDigest)", f.C_DigestInit.?(h, &de_sha));
|
||||
var dd_back: [48]u8 = undefined;
|
||||
var dd_seg: ck.CK_ULONG = dd_back.len;
|
||||
try check("C_DecryptDigestUpdate", f.C_DecryptDigestUpdate.?(h, &dd_ct, dd_ctlen, &dd_back, &dd_seg));
|
||||
var dd_backlen: usize = @intCast(dd_seg);
|
||||
var dd_finlen: ck.CK_ULONG = @intCast(dd_back.len - dd_backlen);
|
||||
try check("C_DecryptFinal(DecryptDigest)", f.C_DecryptFinal.?(h, dd_back[dd_backlen..].ptr, &dd_finlen));
|
||||
dd_backlen += @intCast(dd_finlen);
|
||||
var dd_dig: [32]u8 = undefined;
|
||||
var dd_diglen: ck.CK_ULONG = dd_dig.len;
|
||||
try check("C_DigestFinal(DecryptDigest)", f.C_DigestFinal.?(h, &dd_dig, &dd_diglen));
|
||||
if (dd_backlen != dd_pt.len or !std.mem.eql(u8, dd_back[0..dd_backlen], &dd_pt)) return error.DualDecryptRoundTrip;
|
||||
var dd_ref: [32]u8 = undefined;
|
||||
var dd_reflen: ck.CK_ULONG = dd_ref.len;
|
||||
try check("C_DigestInit(DecryptDigest ref)", f.C_DigestInit.?(h, &de_sha));
|
||||
try check("C_Digest(DecryptDigest ref)", f.C_Digest.?(h, &dd_pt, dd_pt.len, &dd_ref, &dd_reflen));
|
||||
if (!std.mem.eql(u8, dd_dig[0..32], dd_ref[0..32])) return error.DualDecryptDigestMismatch;
|
||||
|
||||
var sv_pt = "verify-after-decrypt aligned!!!!".*;
|
||||
var sv_iv = [_]u8{3} ** 16;
|
||||
var sv_cbc = ck.CK_MECHANISM{ .mechanism = ck.CKM_AES_CBC, .pParameter = &sv_iv, .ulParameterLen = sv_iv.len };
|
||||
try check("C_SignInit(SignEncrypt)", f.C_SignInit.?(h, &hmac_mech, h_hmac));
|
||||
try check("C_EncryptInit(SignEncrypt)", f.C_EncryptInit.?(h, &sv_cbc, h_aes));
|
||||
var sv_ct: [48]u8 = undefined;
|
||||
var sv_seg: ck.CK_ULONG = sv_ct.len;
|
||||
try check("C_SignEncryptUpdate", f.C_SignEncryptUpdate.?(h, &sv_pt, sv_pt.len, &sv_ct, &sv_seg));
|
||||
var sv_ctlen: usize = @intCast(sv_seg);
|
||||
var sv_finlen: ck.CK_ULONG = @intCast(sv_ct.len - sv_ctlen);
|
||||
try check("C_EncryptFinal(SignEncrypt)", f.C_EncryptFinal.?(h, sv_ct[sv_ctlen..].ptr, &sv_finlen));
|
||||
sv_ctlen += @intCast(sv_finlen);
|
||||
var sv_mac: [32]u8 = undefined;
|
||||
var sv_maclen: ck.CK_ULONG = sv_mac.len;
|
||||
try check("C_SignFinal(SignEncrypt)", f.C_SignFinal.?(h, &sv_mac, &sv_maclen));
|
||||
try check("C_DecryptInit(DecryptVerify)", f.C_DecryptInit.?(h, &sv_cbc, h_aes));
|
||||
try check("C_VerifyInit(DecryptVerify)", f.C_VerifyInit.?(h, &hmac_mech, h_hmac));
|
||||
var sv_back: [48]u8 = undefined;
|
||||
var sv_bseg: ck.CK_ULONG = sv_back.len;
|
||||
try check("C_DecryptVerifyUpdate", f.C_DecryptVerifyUpdate.?(h, sv_ct[0..sv_ctlen].ptr, @intCast(sv_ctlen), &sv_back, &sv_bseg));
|
||||
var sv_backlen: usize = @intCast(sv_bseg);
|
||||
var sv_bfinlen: ck.CK_ULONG = @intCast(sv_back.len - sv_backlen);
|
||||
try check("C_DecryptFinal(DecryptVerify)", f.C_DecryptFinal.?(h, sv_back[sv_backlen..].ptr, &sv_bfinlen));
|
||||
sv_backlen += @intCast(sv_bfinlen);
|
||||
try check("C_VerifyFinal(DecryptVerify)", f.C_VerifyFinal.?(h, &sv_mac, sv_maclen));
|
||||
if (sv_backlen != sv_pt.len or !std.mem.eql(u8, sv_back[0..sv_backlen], &sv_pt)) return error.DualSignVerifyRoundTrip;
|
||||
|
||||
var rec_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_RSA_PKCS, .pParameter = null, .ulParameterLen = 0 };
|
||||
var rec_msg = "recover me via RSA".*;
|
||||
var rec_sig: [256]u8 = undefined;
|
||||
var rec_siglen: ck.CK_ULONG = rec_sig.len;
|
||||
try check("C_SignRecoverInit", f.C_SignRecoverInit.?(h, &rec_mech, h_rsapriv));
|
||||
try check("C_SignRecover", f.C_SignRecover.?(h, &rec_msg, rec_msg.len, &rec_sig, &rec_siglen));
|
||||
if (rec_siglen != 256) return error.SignRecoverLenWrong;
|
||||
var rec_out: [256]u8 = undefined;
|
||||
var rec_outlen: ck.CK_ULONG = rec_out.len;
|
||||
try check("C_VerifyRecoverInit", f.C_VerifyRecoverInit.?(h, &rec_mech, h_rsapub));
|
||||
try check("C_VerifyRecover", f.C_VerifyRecover.?(h, &rec_sig, rec_siglen, &rec_out, &rec_outlen));
|
||||
if (rec_outlen != rec_msg.len or !std.mem.eql(u8, rec_out[0..rec_outlen], &rec_msg)) return error.VerifyRecoverMismatch;
|
||||
|
||||
var os_sha = ck.CK_MECHANISM{ .mechanism = ck.CKM_SHA256, .pParameter = null, .ulParameterLen = 0 };
|
||||
var os_p1 = "operation-".*;
|
||||
var os_p2 = "state".*;
|
||||
try check("C_DigestInit(opstate)", f.C_DigestInit.?(h, &os_sha));
|
||||
try check("C_DigestUpdate(opstate p1)", f.C_DigestUpdate.?(h, &os_p1, os_p1.len));
|
||||
var os_blob: [256]u8 = undefined;
|
||||
var os_bloblen: ck.CK_ULONG = 0;
|
||||
try check("C_GetOperationState(size)", f.C_GetOperationState.?(h, null, &os_bloblen));
|
||||
if (os_bloblen == 0 or os_bloblen > os_blob.len) return error.OpStateSizeWrong;
|
||||
try check("C_GetOperationState(fill)", f.C_GetOperationState.?(h, &os_blob, &os_bloblen));
|
||||
try check("C_DigestUpdate(opstate p2)", f.C_DigestUpdate.?(h, &os_p2, os_p2.len));
|
||||
var os_dA: [32]u8 = undefined;
|
||||
var os_dAlen: ck.CK_ULONG = os_dA.len;
|
||||
try check("C_DigestFinal(opstate A)", f.C_DigestFinal.?(h, &os_dA, &os_dAlen));
|
||||
try check("C_SetOperationState", f.C_SetOperationState.?(h, &os_blob, os_bloblen, ck.CK_INVALID_HANDLE, ck.CK_INVALID_HANDLE));
|
||||
try check("C_DigestUpdate(opstate p2 restored)", f.C_DigestUpdate.?(h, &os_p2, os_p2.len));
|
||||
var os_dB: [32]u8 = undefined;
|
||||
var os_dBlen: ck.CK_ULONG = os_dB.len;
|
||||
try check("C_DigestFinal(opstate B)", f.C_DigestFinal.?(h, &os_dB, &os_dBlen));
|
||||
if (!std.mem.eql(u8, os_dA[0..32], os_dB[0..32])) return error.OpStateRestoreMismatch;
|
||||
|
||||
var slot_evt: ck.CK_SLOT_ID = 0;
|
||||
var reserved_probe: u8 = 0;
|
||||
const reserved_ptr: ?*anyopaque = &reserved_probe;
|
||||
if (f.C_WaitForSlotEvent.?(ck.CKF_DONT_BLOCK, &slot_evt, null) != ck.CKR_NO_EVENT) return error.WaitSlotEventNotNoEvent;
|
||||
if (f.C_WaitForSlotEvent.?(0, &slot_evt, null) != ck.CKR_FUNCTION_NOT_SUPPORTED) return error.WaitSlotEventBlockingNotRefused;
|
||||
if (f.C_WaitForSlotEvent.?(ck.CKF_DONT_BLOCK, &slot_evt, reserved_ptr) != ck.CKR_ARGUMENTS_BAD) return error.WaitSlotEventReservedNotChecked;
|
||||
if (f.C_GetFunctionStatus.?(h) != ck.CKR_FUNCTION_NOT_PARALLEL) return error.GetFunctionStatusNotParallel;
|
||||
if (f.C_CancelFunction.?(h) != ck.CKR_FUNCTION_NOT_PARALLEL) return error.CancelFunctionNotParallel;
|
||||
var seed_probe = "seed-material".*;
|
||||
if (f.C_SeedRandom.?(h, &seed_probe, seed_probe.len) != ck.CKR_RANDOM_SEED_NOT_SUPPORTED) return error.SeedRandomNotRefused;
|
||||
|
||||
try check("C_CloseSession", f.C_CloseSession.?(h));
|
||||
try check("C_Finalize", f.C_Finalize.?(null));
|
||||
|
||||
if (f.C_WaitForSlotEvent.?(ck.CKF_DONT_BLOCK, &slot_evt, null) != ck.CKR_CRYPTOKI_NOT_INITIALIZED) return error.WaitSlotEventUninitNotRejected;
|
||||
|
||||
std.debug.print("smoke: OK\n", .{});
|
||||
std.debug.print(" cryptokiVersion = {d}.{d}\n", .{ info.cryptokiVersion.major, info.cryptokiVersion.minor });
|
||||
std.debug.print(" slots = {d}\n", .{count});
|
||||
|
|
@ -510,6 +702,11 @@ pub fn main() !void {
|
|||
std.debug.print(" derive = C_DeriveKey ECDH1 P-256: both parties agree (raw + DER-wrapped peer point)\n", .{});
|
||||
std.debug.print(" keywrap = C_WrapKey/C_UnwrapKey AES-KEY-WRAP + RSA-OAEP round-trips; unextractable target refused\n", .{});
|
||||
std.debug.print(" digestkey = C_DigestKey digest equals C_Digest of the same key bytes\n", .{});
|
||||
std.debug.print(" gcmstream = AES-GCM multipart enc/dec == one-shot (chunked); tamper -> ENCRYPTED_DATA_INVALID\n", .{});
|
||||
std.debug.print(" dual = DigestEncrypt/SignEncrypt + DecryptDigest/DecryptVerify lock-step round-trips\n", .{});
|
||||
std.debug.print(" recover = RSA C_SignRecover -> C_VerifyRecover recovers the exact message\n", .{});
|
||||
std.debug.print(" opstate = C_GetOperationState -> C_SetOperationState resumes the digest identically\n", .{});
|
||||
std.debug.print(" conformance = WaitForSlotEvent(DONT_BLOCK)->NO_EVENT, blocking->NOT_SUPPORTED, pReserved->ARGS_BAD; Get/CancelFunction->NOT_PARALLEL; SeedRandom->SEED_NOT_SUPPORTED\n", .{});
|
||||
}
|
||||
|
||||
fn check(name: []const u8, rv: ck.CK_RV) !void {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,301 @@
|
|||
<!-- ©AngelaMos | 2026 -->
|
||||
<!-- CONFORMANCE.md -->
|
||||
|
||||
# PKCS#11 v2.40 Conformance Statement
|
||||
|
||||
The AngelaMos HSM Emulator implements the full Cryptoki (PKCS#11) v2.40 C ABI: all
|
||||
68 functions in the canonical `CK_FUNCTION_LIST`, machine-checked against the
|
||||
vendored OASIS headers at build time. This document records every place where the
|
||||
module narrows behavior: the exact return code it gives, the spec clause that
|
||||
permits it, and why. Every narrowing here is a documented decision with a defined
|
||||
return value.
|
||||
|
||||
A function that is not applicable to a fixed software token returns the specific
|
||||
code the spec defines for that situation. The `CKR_FUNCTION_NOT_SUPPORTED` results
|
||||
that remain mark the boundary between single-shot and multi-part operation surfaces,
|
||||
covered in section 2.
|
||||
|
||||
**Specifications**
|
||||
|
||||
- PKCS#11 Base Specification v2.40 (OASIS, errata 01) — function semantics and return codes.
|
||||
- PKCS#11 Current Mechanisms v2.40 (OASIS, errata 01) — per-mechanism parameters.
|
||||
|
||||
Section numbers below refer to the Base specification unless a line names the
|
||||
Mechanisms document.
|
||||
|
||||
---
|
||||
|
||||
## 1. Function-level conformance
|
||||
|
||||
### 1.1 `C_WaitForSlotEvent` — §5.5 (slot and token management)
|
||||
|
||||
The module exposes a single fixed slot (ID 0) whose token is always present. No
|
||||
insertion or removal event can ever occur, so the function reports that fact
|
||||
precisely rather than pretending to support hardware slot events.
|
||||
|
||||
| Call | Return | Basis |
|
||||
|------|--------|-------|
|
||||
| `flags` has `CKF_DONT_BLOCK`, no event pending | `CKR_NO_EVENT` | §5.5: a non-blocking poll with no pending event returns `CKR_NO_EVENT`. For a fixed slot, no event is ever pending, so this is always the answer. |
|
||||
| `flags` clears `CKF_DONT_BLOCK` (blocking) | `CKR_FUNCTION_NOT_SUPPORTED` | A blocking wait must not return until an event occurs. For a fixed software slot no event can occur, so blocking would hang the caller forever. The module declines the blocking mode instead. |
|
||||
| `pReserved != NULL_PTR` | `CKR_ARGUMENTS_BAD` | §5.5: `pReserved` is reserved and must be `NULL_PTR` in v2.40. |
|
||||
| called before `C_Initialize` | `CKR_CRYPTOKI_NOT_INITIALIZED` | §5.4 general semantics. |
|
||||
|
||||
Interop note: `pkcs11-tool --wait` calls this in blocking mode and therefore
|
||||
receives `CKR_FUNCTION_NOT_SUPPORTED` immediately rather than blocking. Hosts that
|
||||
poll with `CKF_DONT_BLOCK` (the common case for slot enumeration) get the correct
|
||||
`CKR_NO_EVENT`.
|
||||
|
||||
### 1.2 `C_GetFunctionStatus`, `C_CancelFunction` — §5.15 (parallel function management)
|
||||
|
||||
Both are legacy functions from the era of parallel (asynchronous) Cryptoki calls.
|
||||
v2.40 has no parallel execution model, and the spec defines the canonical answer
|
||||
for a serial implementation:
|
||||
|
||||
| Call | Return | Basis |
|
||||
|------|--------|-------|
|
||||
| `C_GetFunctionStatus` | `CKR_FUNCTION_NOT_PARALLEL` | §5.15: the only meaningful return for a library that does not run functions in parallel. |
|
||||
| `C_CancelFunction` | `CKR_FUNCTION_NOT_PARALLEL` | §5.15: same. |
|
||||
|
||||
### 1.3 `C_SeedRandom` — §5.14 (random number generation)
|
||||
|
||||
| Call | Return | Basis |
|
||||
|------|--------|-------|
|
||||
| `C_SeedRandom` | `CKR_RANDOM_SEED_NOT_SUPPORTED` | The RNG is the operating-system CSPRNG, drawn through `std.Io.randomSecure` (`getrandom(2)`, `arc4random_buf`, or `/dev/urandom` depending on platform and libc). Caller-supplied seed material cannot meaningfully reseed it, so the module declines rather than silently discarding the seed (which would mislead the caller). |
|
||||
| `C_GenerateRandom` | fully supported | — |
|
||||
|
||||
---
|
||||
|
||||
## 2. Operation-surface boundaries
|
||||
|
||||
These are the deliberate edges of the multi-part operation surface. Each returns a
|
||||
specific code so a caller can distinguish "wrong call for this mechanism" from a
|
||||
runtime failure.
|
||||
|
||||
### 2.1 AES-GCM is RUP-safe buffered — §5.8, §5.9
|
||||
|
||||
`CKM_AES_GCM` multi-part encryption and decryption buffer the entire message and run
|
||||
the authenticated operation **once at `*Final`**:
|
||||
|
||||
- `C_EncryptUpdate` / `C_DecryptUpdate` append the part to an internal buffer and
|
||||
emit **0 bytes**. §5.8/§5.9 permit an Update to produce fewer output bytes than it
|
||||
consumes (block buffering); producing the whole result at `*Final` is conformant.
|
||||
- `C_EncryptFinal` emits ciphertext + 128-bit tag; `C_DecryptFinal` verifies the tag
|
||||
and only then releases plaintext.
|
||||
|
||||
This is a security decision. A streaming GCM *decrypt* built on incremental
|
||||
release would hand back **unverified plaintext** before the tag is checked (release
|
||||
of unverified plaintext, "RUP") — an anti-pattern for an HSM. Buffering until the
|
||||
tag verifies makes RUP impossible by construction.
|
||||
|
||||
Because the buffer holds the whole message, a single GCM message is bounded:
|
||||
|
||||
| Condition | Return |
|
||||
|-----------|--------|
|
||||
| buffered length would exceed 16 MiB (`max_gcm_stream_len`), encrypt | `CKR_DATA_LEN_RANGE` |
|
||||
| buffered length would exceed 16 MiB, decrypt | `CKR_ENCRYPTED_DATA_LEN_RANGE` |
|
||||
|
||||
Strict parameter validation (`CK_GCM_PARAMS`):
|
||||
|
||||
| Parameter | Accepted | Else |
|
||||
|-----------|----------|------|
|
||||
| `ulIvLen` | exactly 12 bytes | `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| `ulIvBits` | `0` or `96` | `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| `ulTagBits` | exactly `128` | `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| `pIv` | non-NULL | `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| `ulAADLen` | ≤ 256 bytes | `CKR_ARGUMENTS_BAD` |
|
||||
|
||||
Interop note: a host MUST request a 128-bit tag and supply a 12-byte IV. With
|
||||
`pkcs11-tool` that means `--iv <24 hex chars> --tag-bits-len 128`; omitting either
|
||||
trips `CKR_MECHANISM_PARAM_INVALID`.
|
||||
|
||||
### 2.2 Dual-function operations — §5.12
|
||||
|
||||
| Function | Supported modes | Else |
|
||||
|----------|-----------------|------|
|
||||
| `C_DigestEncryptUpdate`, `C_SignEncryptUpdate` (encrypt side) | AES-CBC, AES-CBC-PAD, AES-GCM | — |
|
||||
| `C_DecryptDigestUpdate`, `C_DecryptVerifyUpdate` (decrypt side) | AES-CBC only | non-CBC → `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| any dual-function leg using an RSA sign/verify operation | — | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
|
||||
The decrypt side couples the **recovered plaintext** of each `C_Decrypt*Update` into
|
||||
the digest/verify operation. That coupling is only exact when the cipher releases
|
||||
exactly the decrypted bytes on every call:
|
||||
|
||||
- **AES-CBC** releases each decrypted block immediately, so bytes-out equals
|
||||
bytes-to-digest per call. Supported.
|
||||
- **AES-CBC-PAD** holds back the final block until `C_DecryptFinal` (it cannot know
|
||||
the padding until the end), so the digest would miss the last block.
|
||||
- **AES-GCM** buffers everything and releases at `C_DecryptFinal` (§2.1), so the
|
||||
digest would receive nothing incrementally.
|
||||
|
||||
Coupling either of the latter would desynchronize the pair, so the module returns
|
||||
`CKR_FUNCTION_NOT_SUPPORTED` rather than producing a silently wrong digest. The
|
||||
encrypt side has no such constraint — it digests/signs the **input** plaintext,
|
||||
which is fully available on each call, so all three modes work.
|
||||
|
||||
### 2.3 RSA is single-shot — §5.8, §5.9, §5.11
|
||||
|
||||
Every RSA operation is a single modular exponentiation over the whole input, so RSA
|
||||
has no multi-part form:
|
||||
|
||||
| Call on an RSA operation | Return |
|
||||
|--------------------------|--------|
|
||||
| `C_EncryptUpdate` / `C_EncryptFinal` | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| `C_DecryptUpdate` / `C_DecryptFinal` | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| `C_SignUpdate` / `C_SignFinal` | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| `C_VerifyUpdate` / `C_VerifyFinal` | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
|
||||
Use the one-shot `C_Encrypt` / `C_Decrypt` / `C_Sign` / `C_Verify`. The hash-then-sign
|
||||
mechanisms (`CKM_SHA256_RSA_PKCS`, the PSS variants) still need the full message
|
||||
before the single RSA operation, so they too are one-shot.
|
||||
|
||||
### 2.4 Sign / Verify-Recover — §5.11
|
||||
|
||||
| Mechanism | Supported | Else |
|
||||
|-----------|-----------|------|
|
||||
| `CKM_RSA_PKCS` (EMSA-PKCS1-v1.5 type 1, message recoverable from the signature) | yes | other mechanism → `CKR_MECHANISM_INVALID` |
|
||||
|
||||
`C_SignRecover` rejects input that cannot fit the modulus with PKCS#1 v1.5 overhead
|
||||
(data length + 11 bytes > modulus) with `CKR_DATA_LEN_RANGE`. `C_VerifyRecover`
|
||||
returns the recovered message; a signature whose length is not the modulus length
|
||||
returns `CKR_SIGNATURE_LEN_RANGE`, and a malformed encoding returns
|
||||
`CKR_SIGNATURE_INVALID`. Raw recover (`CKM_RSA_X_509`) is not offered.
|
||||
|
||||
### 2.5 Get / SetOperationState — §5.6
|
||||
|
||||
Operation state is **digest-only**:
|
||||
|
||||
| Call | Return | Basis |
|
||||
|------|--------|-------|
|
||||
| `C_GetOperationState` with an active sign/verify/encrypt/decrypt/sign-recover/verify-recover operation | `CKR_STATE_UNSAVEABLE` | §5.6: a library may decline to save state it cannot serialize. Only digest state is saveable here. |
|
||||
| `C_GetOperationState` with no operation active | `CKR_OPERATION_NOT_INITIALIZED` | §5.6. |
|
||||
| `C_SetOperationState` with non-zero `hEncryptionKey` or `hAuthenticationKey` | `CKR_KEY_NOT_NEEDED` | §5.6: a digest needs no key, so passing one is an error. |
|
||||
| `C_SetOperationState` with a malformed blob (wrong version byte, unknown hasher tag, wrong length) | `CKR_SAVED_STATE_INVALID` | §5.6. |
|
||||
|
||||
The saved blob is `[version][hasher tag][raw hasher state]`, validated on restore.
|
||||
It is **opaque and same-build only**: it carries the raw standard-library hasher
|
||||
state, whose layout is not stable across builds. PKCS#11 does not promise
|
||||
operation-state portability across implementations or builds (§5.6). Restore is
|
||||
byte-exact within the same binary; any other input fails closed via the version,
|
||||
tag, and exact-length checks above.
|
||||
|
||||
---
|
||||
|
||||
## 3. Mechanism constraints
|
||||
|
||||
### 3.1 ECDH key derivation — §5.13, Mechanisms (CKM_ECDH1_DERIVE)
|
||||
|
||||
| Aspect | Value | Else |
|
||||
|--------|-------|------|
|
||||
| KDF | `CKD_NULL` only | other KDF → `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| peer public point | raw SEC1 uncompressed **or** DER `OCTET STRING`-wrapped | malformed → `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| curves | P-256, P-384 | — |
|
||||
| shared data | none (`CKD_NULL` carries no shared data) | — |
|
||||
|
||||
### 3.2 RSA — Mechanisms (CKM_RSA_PKCS, _PSS, _OAEP)
|
||||
|
||||
| Aspect | Value | Else |
|
||||
|--------|-------|------|
|
||||
| key size | 2048–4096 bits | outside range → `CKR_KEY_SIZE_RANGE` |
|
||||
| public exponent | fixed at 65537 (F4) | not selectable at keygen |
|
||||
| PSS / OAEP hash | SHA-256, SHA-384, SHA-512 | other → `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| MGF hash | must equal the content hash | mismatch → `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| OAEP label (source) | not supported | `ulSourceDataLen != 0` → `CKR_MECHANISM_PARAM_INVALID` |
|
||||
|
||||
Dedicated `CKM_SHA384_RSA_PKCS` / `CKM_SHA512_RSA_PKCS` mechanisms are not
|
||||
advertised; SHA-384 and SHA-512 are reachable through the `CKM_RSA_PKCS_PSS` and
|
||||
`CKM_RSA_PKCS_OAEP` parameter `hashAlg`.
|
||||
|
||||
### 3.3 AES — Mechanisms (CKM_AES_*)
|
||||
|
||||
AES-128 and AES-256 only. AES-192 is not implemented (the Zig standard library
|
||||
exposes no 192-bit AES). A key length outside {16, 32} bytes → `CKR_KEY_SIZE_RANGE`.
|
||||
|
||||
### 3.4 ECDSA — Mechanisms (CKM_ECDSA*)
|
||||
|
||||
Curves P-256 and P-384. Mechanisms `CKM_ECDSA` (pre-hashed input) and
|
||||
`CKM_ECDSA_SHA256`. `CKM_ECDSA_SHA384` and `CKM_ECDSA_SHA512` are out of scope.
|
||||
|
||||
### 3.5 Key wrap — §5.13
|
||||
|
||||
| Aspect | Value | Else |
|
||||
|--------|-------|------|
|
||||
| wrappable target | secret keys (`CKO_SECRET_KEY`) only | asymmetric target → `CKR_KEY_NOT_WRAPPABLE` |
|
||||
| wrapping mechanisms | `CKM_AES_KEY_WRAP` (RFC 3394), `CKM_RSA_PKCS_OAEP` | other → `CKR_MECHANISM_INVALID` |
|
||||
| unextractable target | refused | `CKR_KEY_UNEXTRACTABLE` |
|
||||
| tampered wrapped blob on unwrap | refused | `CKR_WRAPPED_KEY_INVALID` |
|
||||
|
||||
---
|
||||
|
||||
## 4. Object and token model (informative)
|
||||
|
||||
- **One fixed slot** (ID 0), always present, hosting **one token**. Login is required
|
||||
to see or use private objects.
|
||||
- **Encrypted at rest.** Token objects persist to a file under a selective envelope:
|
||||
only sensitive attribute *values* are sealed with AES-256-GCM under a per-token
|
||||
master key. The master key is wrapped under a single **User-PIN keyslot**
|
||||
(Argon2id-derived KEK). There is no SO keyslot for user secrets by design — the
|
||||
Security Officer must not be able to read user key material.
|
||||
- **Public objects and attributes stay in plaintext** and are visible before login,
|
||||
which is spec-correct: only private/sensitive material is gated by login.
|
||||
- In memory, sensitive attributes are plaintext only while the User is logged in;
|
||||
logout and session teardown re-seal them and zeroize the master key.
|
||||
|
||||
---
|
||||
|
||||
## 5. Advertised mechanism list
|
||||
|
||||
`C_GetMechanismList` returns these 21 mechanisms. Key-size units are
|
||||
mechanism-dependent per the spec (bits for RSA and EC, bytes for AES and HMAC).
|
||||
|
||||
| Mechanism | Min | Max | Flags |
|
||||
|-----------|-----|-----|-------|
|
||||
| `CKM_SHA256` | 0 | 0 | DIGEST |
|
||||
| `CKM_SHA384` | 0 | 0 | DIGEST |
|
||||
| `CKM_SHA512` | 0 | 0 | DIGEST |
|
||||
| `CKM_SHA256_HMAC` | 32 | 64 | SIGN, VERIFY |
|
||||
| `CKM_SHA384_HMAC` | 32 | 64 | SIGN, VERIFY |
|
||||
| `CKM_SHA512_HMAC` | 32 | 64 | SIGN, VERIFY |
|
||||
| `CKM_AES_KEY_GEN` | 16 | 32 | GENERATE |
|
||||
| `CKM_AES_CBC` | 16 | 32 | ENCRYPT, DECRYPT |
|
||||
| `CKM_AES_CBC_PAD` | 16 | 32 | ENCRYPT, DECRYPT |
|
||||
| `CKM_AES_GCM` | 16 | 32 | ENCRYPT, DECRYPT |
|
||||
| `CKM_EC_KEY_PAIR_GEN` | 256 | 384 | GENERATE_KEY_PAIR, EC_NAMEDCURVE |
|
||||
| `CKM_ECDSA` | 256 | 384 | SIGN, VERIFY, EC_NAMEDCURVE |
|
||||
| `CKM_ECDSA_SHA256` | 256 | 384 | SIGN, VERIFY, EC_NAMEDCURVE |
|
||||
| `CKM_ECDH1_DERIVE` | 256 | 384 | DERIVE, EC_NAMEDCURVE |
|
||||
| `CKM_RSA_PKCS_KEY_PAIR_GEN` | 2048 | 4096 | GENERATE_KEY_PAIR |
|
||||
| `CKM_RSA_PKCS` | 2048 | 4096 | SIGN, VERIFY, ENCRYPT, DECRYPT, SIGN_RECOVER, VERIFY_RECOVER |
|
||||
| `CKM_SHA256_RSA_PKCS` | 2048 | 4096 | SIGN, VERIFY |
|
||||
| `CKM_RSA_PKCS_PSS` | 2048 | 4096 | SIGN, VERIFY |
|
||||
| `CKM_SHA256_RSA_PKCS_PSS` | 2048 | 4096 | SIGN, VERIFY |
|
||||
| `CKM_RSA_PKCS_OAEP` | 2048 | 4096 | ENCRYPT, DECRYPT, WRAP, UNWRAP |
|
||||
| `CKM_AES_KEY_WRAP` | 16 | 32 | WRAP, UNWRAP |
|
||||
|
||||
---
|
||||
|
||||
## 6. Summary: deliberate return codes
|
||||
|
||||
| Boundary | Return code |
|
||||
|----------|-------------|
|
||||
| `C_WaitForSlotEvent`, non-blocking poll | `CKR_NO_EVENT` |
|
||||
| `C_WaitForSlotEvent`, blocking mode | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| `C_WaitForSlotEvent`, `pReserved != NULL` | `CKR_ARGUMENTS_BAD` |
|
||||
| `C_GetFunctionStatus`, `C_CancelFunction` | `CKR_FUNCTION_NOT_PARALLEL` |
|
||||
| `C_SeedRandom` | `CKR_RANDOM_SEED_NOT_SUPPORTED` |
|
||||
| GCM message over 16 MiB | `CKR_DATA_LEN_RANGE` / `CKR_ENCRYPTED_DATA_LEN_RANGE` |
|
||||
| GCM bad parameters | `CKR_MECHANISM_PARAM_INVALID` / `CKR_ARGUMENTS_BAD` |
|
||||
| dual-function decrypt side, non-CBC | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| RSA multi-part (`*Update` / `*Final`) | `CKR_FUNCTION_NOT_SUPPORTED` |
|
||||
| Sign/Verify-Recover, non-`CKM_RSA_PKCS` | `CKR_MECHANISM_INVALID` |
|
||||
| Get/SetOperationState, non-digest operation | `CKR_STATE_UNSAVEABLE` |
|
||||
| SetOperationState with a key handle | `CKR_KEY_NOT_NEEDED` |
|
||||
| SetOperationState, malformed blob | `CKR_SAVED_STATE_INVALID` |
|
||||
| ECDH non-`CKD_NULL` KDF | `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| OAEP with a label | `CKR_MECHANISM_PARAM_INVALID` |
|
||||
| AES key length not 16/32 bytes | `CKR_KEY_SIZE_RANGE` |
|
||||
| wrap of an asymmetric target | `CKR_KEY_NOT_WRAPPABLE` |
|
||||
| wrap of an unextractable key | `CKR_KEY_UNEXTRACTABLE` |
|
||||
|
||||
Every entry above is exercised by the unit tests, the in-process smoke harness, or a
|
||||
cross-process `pkcs11-tool` run; the corrected slot/parallel/RNG codes are asserted
|
||||
in `examples/smoke.zig` against the built shared object.
|
||||
|
|
@ -373,6 +373,60 @@ fn updateOutLen(op: *const cipher.Cipher, in_len: usize) ck.CK_ULONG {
|
|||
return @intCast(((op.partial_len + in_len) / config.aes_block_len) * config.aes_block_len);
|
||||
}
|
||||
|
||||
const NeedResult = union(enum) { ok: ck.CK_ULONG, err: ck.CK_RV };
|
||||
const EmitResult = union(enum) { ok: usize, err: ck.CK_RV };
|
||||
|
||||
fn encUpdateNeed(op: *const session.EncryptOp, in_len: usize) NeedResult {
|
||||
return switch (op.*) {
|
||||
.rsa => .{ .err = ck.CKR_FUNCTION_NOT_SUPPORTED },
|
||||
.aes => |*c| .{ .ok = updateOutLen(c, in_len) },
|
||||
.gcm => .{ .ok = 0 },
|
||||
};
|
||||
}
|
||||
|
||||
fn encUpdateEmit(inst: *state.Instance, op: *session.EncryptOp, in: []const u8, out: []u8) EmitResult {
|
||||
switch (op.*) {
|
||||
.rsa => return .{ .err = ck.CKR_FUNCTION_NOT_SUPPORTED },
|
||||
.aes => |*c| return .{ .ok = c.encryptUpdate(in, out) },
|
||||
.gcm => |*g| {
|
||||
g.append(inst.allocator(), in) catch |e| return .{ .err = switch (e) {
|
||||
error.OutOfMemory => ck.CKR_HOST_MEMORY,
|
||||
error.TooLarge => ck.CKR_DATA_LEN_RANGE,
|
||||
} };
|
||||
return .{ .ok = 0 };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn decUpdateNeed(op: *const session.DecryptOp, in_len: usize) NeedResult {
|
||||
return switch (op.*) {
|
||||
.rsa => .{ .err = ck.CKR_FUNCTION_NOT_SUPPORTED },
|
||||
.aes => |*c| .{ .ok = updateOutLen(c, in_len) },
|
||||
.gcm => .{ .ok = 0 },
|
||||
};
|
||||
}
|
||||
|
||||
fn decUpdateEmit(inst: *state.Instance, op: *session.DecryptOp, in: []const u8, out: []u8) EmitResult {
|
||||
switch (op.*) {
|
||||
.rsa => return .{ .err = ck.CKR_FUNCTION_NOT_SUPPORTED },
|
||||
.aes => |*c| return .{ .ok = c.decryptUpdate(in, out) },
|
||||
.gcm => |*g| {
|
||||
g.append(inst.allocator(), in) catch |e| return .{ .err = switch (e) {
|
||||
error.OutOfMemory => ck.CKR_HOST_MEMORY,
|
||||
error.TooLarge => ck.CKR_ENCRYPTED_DATA_LEN_RANGE,
|
||||
} };
|
||||
return .{ .ok = 0 };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn decryptSideDualOk(op: *const session.DecryptOp) bool {
|
||||
return switch (op.*) {
|
||||
.aes => |*c| c.mode == .cbc,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn emitDigest(sess: *session.Session, pDigest: ?[*]ck.CK_BYTE, pulDigestLen: *ck.CK_ULONG) ck.CK_RV {
|
||||
const op = &sess.digest_op.?;
|
||||
const dlen: ck.CK_ULONG = @intCast(op.digestLen());
|
||||
|
|
@ -415,16 +469,16 @@ fn rsaCryptInit(inst: *state.Instance, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK
|
|||
}
|
||||
|
||||
pub fn C_EncryptInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.encrypt_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
if (cipher.modeOf(pMechanism.mechanism) != null) {
|
||||
sess.encrypt_op = .{ .aes = switch (buildCipher(inst, pMechanism, hKey, true, ck.CKA_ENCRYPT)) {
|
||||
const c = switch (buildCipher(inst, pMechanism, hKey, true, ck.CKA_ENCRYPT)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |c| c,
|
||||
} };
|
||||
.ok => |built| built,
|
||||
};
|
||||
sess.encrypt_op = if (c.mode == .gcm) .{ .gcm = .{ .cipher = c } } else .{ .aes = c };
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (isRsaCryptMech(pMechanism.mechanism)) {
|
||||
|
|
@ -438,8 +492,7 @@ pub fn C_EncryptInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANIS
|
|||
}
|
||||
|
||||
pub fn C_Encrypt(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pEncryptedData: ?[*]ck.CK_BYTE, pulEncryptedDataLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.encrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -457,19 +510,28 @@ pub fn C_Encrypt(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen
|
|||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const out = pEncryptedData.?[0..@intCast(need)];
|
||||
var n: usize = 0;
|
||||
switch (c.mode) {
|
||||
.gcm => n = c.gcmEncrypt(in, out),
|
||||
.cbc, .cbc_pad => {
|
||||
n = c.encryptUpdate(in, out);
|
||||
n += c.encryptFinal(out[n..]) catch |e| {
|
||||
sess.endEncrypt();
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
},
|
||||
}
|
||||
var n = c.encryptUpdate(in, out);
|
||||
n += c.encryptFinal(out[n..]) catch |e| {
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulEncryptedDataLen.* = @intCast(n);
|
||||
sess.endEncrypt();
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
.gcm => |*g| {
|
||||
const need: ck.CK_ULONG = @intCast(cipher.encryptOutLen(.gcm, in.len));
|
||||
if (pEncryptedData == null) {
|
||||
pulEncryptedDataLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulEncryptedDataLen.* < need) {
|
||||
pulEncryptedDataLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = g.cipher.gcmEncrypt(in, pEncryptedData.?[0..@intCast(need)]);
|
||||
pulEncryptedDataLen.* = @intCast(n);
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
.rsa => |*r| {
|
||||
|
|
@ -484,36 +546,32 @@ pub fn C_Encrypt(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen
|
|||
}
|
||||
const pc = switch (rsaPublicComponents(inst, r.key, ck.CKA_ENCRYPT)) {
|
||||
.err => |rv| {
|
||||
sess.endEncrypt();
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |c| c,
|
||||
};
|
||||
const n = rsa.encrypt(pc, r.params, in, pEncryptedData.?[0..@intCast(need)]) catch {
|
||||
sess.endEncrypt();
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return ck.CKR_DATA_LEN_RANGE;
|
||||
};
|
||||
pulEncryptedDataLen.* = @intCast(n);
|
||||
sess.endEncrypt();
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn C_EncryptUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG, pEncryptedPart: ?[*]ck.CK_BYTE, pulEncryptedPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.encrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const c = switch (op.*) {
|
||||
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
|
||||
.aes => |*x| x,
|
||||
};
|
||||
if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
|
||||
const in = part(pPart, ulPartLen);
|
||||
const need = updateOutLen(c, in.len);
|
||||
const need = switch (encUpdateNeed(op, in.len)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |n| n,
|
||||
};
|
||||
if (pEncryptedPart == null) {
|
||||
pulEncryptedPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
|
|
@ -522,51 +580,71 @@ pub fn C_EncryptUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulP
|
|||
pulEncryptedPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
pulEncryptedPartLen.* = @intCast(c.encryptUpdate(in, pEncryptedPart.?[0..@intCast(need)]));
|
||||
const wrote = switch (encUpdateEmit(inst, op, in, pEncryptedPart.?[0..@intCast(need)])) {
|
||||
.err => |rv| {
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |n| n,
|
||||
};
|
||||
pulEncryptedPartLen.* = @intCast(wrote);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_EncryptFinal(hSession: ck.CK_SESSION_HANDLE, pLastEncryptedPart: ?[*]ck.CK_BYTE, pulLastEncryptedPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.encrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const c = switch (op.*) {
|
||||
switch (op.*) {
|
||||
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
|
||||
.aes => |*x| x,
|
||||
};
|
||||
if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
|
||||
const need: ck.CK_ULONG = if (c.mode == .cbc_pad) config.aes_block_len else 0;
|
||||
if (pLastEncryptedPart == null) {
|
||||
pulLastEncryptedPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
.aes => |*c| {
|
||||
const need: ck.CK_ULONG = if (c.mode == .cbc_pad) config.aes_block_len else 0;
|
||||
if (pLastEncryptedPart == null) {
|
||||
pulLastEncryptedPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulLastEncryptedPartLen.* < need) {
|
||||
pulLastEncryptedPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = c.encryptFinal(pLastEncryptedPart.?[0..@intCast(need)]) catch |e| {
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulLastEncryptedPartLen.* = @intCast(n);
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
.gcm => |*g| {
|
||||
const need: ck.CK_ULONG = @intCast(g.len + config.gcm_tag_len);
|
||||
if (pLastEncryptedPart == null) {
|
||||
pulLastEncryptedPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulLastEncryptedPartLen.* < need) {
|
||||
pulLastEncryptedPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = g.cipher.gcmEncrypt(g.data(), pLastEncryptedPart.?[0..@intCast(need)]);
|
||||
pulLastEncryptedPartLen.* = @intCast(n);
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
}
|
||||
if (pulLastEncryptedPartLen.* < need) {
|
||||
pulLastEncryptedPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = c.encryptFinal(pLastEncryptedPart.?[0..@intCast(need)]) catch |e| {
|
||||
sess.endEncrypt();
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulLastEncryptedPartLen.* = @intCast(n);
|
||||
sess.endEncrypt();
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_DecryptInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.decrypt_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
if (cipher.modeOf(pMechanism.mechanism) != null) {
|
||||
sess.decrypt_op = .{ .aes = switch (buildCipher(inst, pMechanism, hKey, false, ck.CKA_DECRYPT)) {
|
||||
const c = switch (buildCipher(inst, pMechanism, hKey, false, ck.CKA_DECRYPT)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |c| c,
|
||||
} };
|
||||
.ok => |built| built,
|
||||
};
|
||||
sess.decrypt_op = if (c.mode == .gcm) .{ .gcm = .{ .cipher = c } } else .{ .aes = c };
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (isRsaCryptMech(pMechanism.mechanism)) {
|
||||
|
|
@ -580,8 +658,7 @@ pub fn C_DecryptInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANIS
|
|||
}
|
||||
|
||||
pub fn C_Decrypt(hSession: ck.CK_SESSION_HANDLE, pEncryptedData: [*]ck.CK_BYTE, ulEncryptedDataLen: ck.CK_ULONG, pData: ?[*]ck.CK_BYTE, pulDataLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.decrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -599,22 +676,31 @@ pub fn C_Decrypt(hSession: ck.CK_SESSION_HANDLE, pEncryptedData: [*]ck.CK_BYTE,
|
|||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const out = pData.?[0..@intCast(need)];
|
||||
var n: usize = 0;
|
||||
switch (c.mode) {
|
||||
.gcm => n = c.gcmDecrypt(in, out) catch |e| {
|
||||
sess.endDecrypt();
|
||||
return mapCipherErr(e);
|
||||
},
|
||||
.cbc, .cbc_pad => {
|
||||
n = c.decryptUpdate(in, out);
|
||||
n += c.decryptFinal(out[n..]) catch |e| {
|
||||
sess.endDecrypt();
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
},
|
||||
}
|
||||
var n = c.decryptUpdate(in, out);
|
||||
n += c.decryptFinal(out[n..]) catch |e| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulDataLen.* = @intCast(n);
|
||||
sess.endDecrypt();
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
.gcm => |*g| {
|
||||
const need: ck.CK_ULONG = @intCast(cipher.decryptOutLen(.gcm, in.len));
|
||||
if (pData == null) {
|
||||
pulDataLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulDataLen.* < need) {
|
||||
pulDataLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = g.cipher.gcmDecrypt(in, pData.?[0..@intCast(need)]) catch |e| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulDataLen.* = @intCast(n);
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
.rsa => |*r| {
|
||||
|
|
@ -629,36 +715,32 @@ pub fn C_Decrypt(hSession: ck.CK_SESSION_HANDLE, pEncryptedData: [*]ck.CK_BYTE,
|
|||
}
|
||||
const sc = switch (rsaPrivateComponents(inst, r.key, ck.CKA_DECRYPT)) {
|
||||
.err => |rv| {
|
||||
sess.endDecrypt();
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |c| c,
|
||||
};
|
||||
const n = rsa.decrypt(sc, r.params, in, pData.?[0..@intCast(need)]) catch {
|
||||
sess.endDecrypt();
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return ck.CKR_ENCRYPTED_DATA_INVALID;
|
||||
};
|
||||
pulDataLen.* = @intCast(n);
|
||||
sess.endDecrypt();
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn C_DecryptUpdate(hSession: ck.CK_SESSION_HANDLE, pEncryptedPart: [*]ck.CK_BYTE, ulEncryptedPartLen: ck.CK_ULONG, pPart: ?[*]ck.CK_BYTE, pulPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.decrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const c = switch (op.*) {
|
||||
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
|
||||
.aes => |*x| x,
|
||||
};
|
||||
if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
|
||||
const in = part(pEncryptedPart, ulEncryptedPartLen);
|
||||
const need = updateOutLen(c, in.len);
|
||||
const need = switch (decUpdateNeed(op, in.len)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |n| n,
|
||||
};
|
||||
if (pPart == null) {
|
||||
pulPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
|
|
@ -667,43 +749,65 @@ pub fn C_DecryptUpdate(hSession: ck.CK_SESSION_HANDLE, pEncryptedPart: [*]ck.CK_
|
|||
pulPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
pulPartLen.* = @intCast(c.decryptUpdate(in, pPart.?[0..@intCast(need)]));
|
||||
const wrote = switch (decUpdateEmit(inst, op, in, pPart.?[0..@intCast(need)])) {
|
||||
.err => |rv| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |n| n,
|
||||
};
|
||||
pulPartLen.* = @intCast(wrote);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_DecryptFinal(hSession: ck.CK_SESSION_HANDLE, pLastPart: ?[*]ck.CK_BYTE, pulLastPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.decrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const c = switch (op.*) {
|
||||
switch (op.*) {
|
||||
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
|
||||
.aes => |*x| x,
|
||||
};
|
||||
if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
|
||||
const need: ck.CK_ULONG = if (c.mode == .cbc_pad) config.aes_block_len else 0;
|
||||
if (pLastPart == null) {
|
||||
pulLastPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
.aes => |*c| {
|
||||
const need: ck.CK_ULONG = if (c.mode == .cbc_pad) config.aes_block_len else 0;
|
||||
if (pLastPart == null) {
|
||||
pulLastPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulLastPartLen.* < need) {
|
||||
pulLastPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = c.decryptFinal(pLastPart.?[0..@intCast(need)]) catch |e| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulLastPartLen.* = @intCast(n);
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
.gcm => |*g| {
|
||||
const need: ck.CK_ULONG = @intCast(cipher.decryptOutLen(.gcm, g.len));
|
||||
if (pLastPart == null) {
|
||||
pulLastPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulLastPartLen.* < need) {
|
||||
pulLastPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = g.cipher.gcmDecrypt(g.data(), pLastPart.?[0..@intCast(need)]) catch |e| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulLastPartLen.* = @intCast(n);
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return ck.CKR_OK;
|
||||
},
|
||||
}
|
||||
if (pulLastPartLen.* < need) {
|
||||
pulLastPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const n = c.decryptFinal(pLastPart.?[0..@intCast(need)]) catch |e| {
|
||||
sess.endDecrypt();
|
||||
return mapCipherErr(e);
|
||||
};
|
||||
pulLastPartLen.* = @intCast(n);
|
||||
sess.endDecrypt();
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_DigestInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.digest_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
|
|
@ -712,8 +816,7 @@ pub fn C_DigestInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM
|
|||
}
|
||||
|
||||
pub fn C_Digest(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pDigest: ?[*]ck.CK_BYTE, pulDigestLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -731,8 +834,7 @@ pub fn C_Digest(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen:
|
|||
}
|
||||
|
||||
pub fn C_DigestUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -741,8 +843,7 @@ pub fn C_DigestUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPa
|
|||
}
|
||||
|
||||
pub fn C_DigestKey(hSession: ck.CK_SESSION_HANDLE, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -756,8 +857,7 @@ pub fn C_DigestKey(hSession: ck.CK_SESSION_HANDLE, hKey: ck.CK_OBJECT_HANDLE) ca
|
|||
}
|
||||
|
||||
pub fn C_DigestFinal(hSession: ck.CK_SESSION_HANDLE, pDigest: ?[*]ck.CK_BYTE, pulDigestLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -765,8 +865,7 @@ pub fn C_DigestFinal(hSession: ck.CK_SESSION_HANDLE, pDigest: ?[*]ck.CK_BYTE, pu
|
|||
}
|
||||
|
||||
pub fn C_SignInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
|
|
@ -778,8 +877,7 @@ pub fn C_SignInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM,
|
|||
}
|
||||
|
||||
pub fn C_Sign(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -818,8 +916,7 @@ pub fn C_Sign(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: c
|
|||
}
|
||||
|
||||
pub fn C_SignUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -832,8 +929,7 @@ pub fn C_SignUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPart
|
|||
}
|
||||
|
||||
pub fn C_SignFinal(hSession: ck.CK_SESSION_HANDLE, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -844,17 +940,57 @@ pub fn C_SignFinal(hSession: ck.CK_SESSION_HANDLE, pSignature: ?[*]ck.CK_BYTE, p
|
|||
return emitSign(inst, sess, pSignature, pulSignatureLen);
|
||||
}
|
||||
|
||||
pub fn C_SignRecoverInit(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_SignRecoverInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_recover_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
if (pMechanism.mechanism != ck.CKM_RSA_PKCS) return ck.CKR_MECHANISM_INVALID;
|
||||
const pc = switch (rsaPrivateComponents(inst, hKey, ck.CKA_SIGN_RECOVER)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |c| c,
|
||||
};
|
||||
sess.sign_recover_op = .{ .key = hKey, .out_len = pc.n.len };
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_SignRecover(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_SignRecover(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.sign_recover_op) |o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const need: ck.CK_ULONG = @intCast(op.out_len);
|
||||
if (pSignature == null) {
|
||||
pulSignatureLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulSignatureLen.* < need) {
|
||||
pulSignatureLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const in = part(pData, ulDataLen);
|
||||
if (in.len + rsa.pkcs1_v15_min_overhead > op.out_len) {
|
||||
sess.endSignRecover();
|
||||
return ck.CKR_DATA_LEN_RANGE;
|
||||
}
|
||||
const sc = switch (rsaPrivateComponents(inst, op.key, ck.CKA_SIGN_RECOVER)) {
|
||||
.err => |rv| {
|
||||
sess.endSignRecover();
|
||||
return rv;
|
||||
},
|
||||
.ok => |c| c,
|
||||
};
|
||||
const n = rsa.sign(sc, .{ .scheme = .pkcs1, .digest = .none }, in, pSignature.?[0..@intCast(need)]) catch {
|
||||
sess.endSignRecover();
|
||||
return ck.CKR_FUNCTION_FAILED;
|
||||
};
|
||||
pulSignatureLen.* = @intCast(n);
|
||||
sess.endSignRecover();
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_VerifyInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.verify_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
|
|
@ -866,8 +1002,7 @@ pub fn C_VerifyInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM
|
|||
}
|
||||
|
||||
pub fn C_Verify(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -894,8 +1029,7 @@ pub fn C_Verify(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen:
|
|||
}
|
||||
|
||||
pub fn C_VerifyUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -908,8 +1042,7 @@ pub fn C_VerifyUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPa
|
|||
}
|
||||
|
||||
pub fn C_VerifyFinal(hSession: ck.CK_SESSION_HANDLE, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
|
|
@ -920,26 +1053,188 @@ pub fn C_VerifyFinal(hSession: ck.CK_SESSION_HANDLE, pSignature: [*]ck.CK_BYTE,
|
|||
return finalizeVerify(sess, pSignature, ulSignatureLen);
|
||||
}
|
||||
|
||||
pub fn C_VerifyRecoverInit(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_VerifyRecoverInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.verify_recover_op != null) return ck.CKR_OPERATION_ACTIVE;
|
||||
if (pMechanism.mechanism != ck.CKM_RSA_PKCS) return ck.CKR_MECHANISM_INVALID;
|
||||
const pc = switch (rsaPublicComponents(inst, hKey, ck.CKA_VERIFY_RECOVER)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |c| c,
|
||||
};
|
||||
sess.verify_recover_op = .{ .key = hKey, .out_len = pc.n.len };
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_VerifyRecover(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_VerifyRecover(hSession: ck.CK_SESSION_HANDLE, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG, pData: ?[*]ck.CK_BYTE, pulDataLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
const op = if (sess.verify_recover_op) |o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
if (pData == null) {
|
||||
pulDataLen.* = @intCast(op.out_len);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (ulSignatureLen != op.out_len) {
|
||||
sess.endVerifyRecover();
|
||||
return ck.CKR_SIGNATURE_LEN_RANGE;
|
||||
}
|
||||
const pc = switch (rsaPublicComponents(inst, op.key, ck.CKA_VERIFY_RECOVER)) {
|
||||
.err => |rv| {
|
||||
sess.endVerifyRecover();
|
||||
return rv;
|
||||
},
|
||||
.ok => |c| c,
|
||||
};
|
||||
var tmp: [rsa.max_modulus_bytes]u8 = undefined;
|
||||
defer std.crypto.secureZero(u8, &tmp);
|
||||
const m = rsa.recover(pc, part(pSignature, ulSignatureLen), &tmp) catch {
|
||||
sess.endVerifyRecover();
|
||||
return ck.CKR_SIGNATURE_INVALID;
|
||||
};
|
||||
if (pulDataLen.* < m) {
|
||||
pulDataLen.* = @intCast(m);
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
@memcpy(pData.?[0..m], tmp[0..m]);
|
||||
pulDataLen.* = @intCast(m);
|
||||
sess.endVerifyRecover();
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_DigestEncryptUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_DigestEncryptUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG, pEncryptedPart: ?[*]ck.CK_BYTE, pulEncryptedPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.digest_op == null or sess.encrypt_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const op = &sess.encrypt_op.?;
|
||||
const in = part(pPart, ulPartLen);
|
||||
const need = switch (encUpdateNeed(op, in.len)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |n| n,
|
||||
};
|
||||
if (pEncryptedPart == null) {
|
||||
pulEncryptedPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulEncryptedPartLen.* < need) {
|
||||
pulEncryptedPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
sess.digest_op.?.update(in);
|
||||
const wrote = switch (encUpdateEmit(inst, op, in, pEncryptedPart.?[0..@intCast(need)])) {
|
||||
.err => |rv| {
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |n| n,
|
||||
};
|
||||
pulEncryptedPartLen.* = @intCast(wrote);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_DecryptDigestUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_DecryptDigestUpdate(hSession: ck.CK_SESSION_HANDLE, pEncryptedPart: [*]ck.CK_BYTE, ulEncryptedPartLen: ck.CK_ULONG, pPart: ?[*]ck.CK_BYTE, pulPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.decrypt_op == null or sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const op = &sess.decrypt_op.?;
|
||||
if (!decryptSideDualOk(op)) return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
const in = part(pEncryptedPart, ulEncryptedPartLen);
|
||||
const need = switch (decUpdateNeed(op, in.len)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |n| n,
|
||||
};
|
||||
if (pPart == null) {
|
||||
pulPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulPartLen.* < need) {
|
||||
pulPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const out = pPart.?[0..@intCast(need)];
|
||||
const wrote = switch (decUpdateEmit(inst, op, in, out)) {
|
||||
.err => |rv| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |n| n,
|
||||
};
|
||||
sess.digest_op.?.update(out[0..wrote]);
|
||||
pulPartLen.* = @intCast(wrote);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_SignEncryptUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_SignEncryptUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG, pEncryptedPart: ?[*]ck.CK_BYTE, pulEncryptedPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_op == null or sess.encrypt_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
switch (sess.sign_op.?) {
|
||||
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
|
||||
else => {},
|
||||
}
|
||||
const op = &sess.encrypt_op.?;
|
||||
const in = part(pPart, ulPartLen);
|
||||
const need = switch (encUpdateNeed(op, in.len)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |n| n,
|
||||
};
|
||||
if (pEncryptedPart == null) {
|
||||
pulEncryptedPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulEncryptedPartLen.* < need) {
|
||||
pulEncryptedPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
sess.sign_op.?.update(in);
|
||||
const wrote = switch (encUpdateEmit(inst, op, in, pEncryptedPart.?[0..@intCast(need)])) {
|
||||
.err => |rv| {
|
||||
sess.endEncrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |n| n,
|
||||
};
|
||||
pulEncryptedPartLen.* = @intCast(wrote);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_DecryptVerifyUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_DecryptVerifyUpdate(hSession: ck.CK_SESSION_HANDLE, pEncryptedPart: [*]ck.CK_BYTE, ulEncryptedPartLen: ck.CK_ULONG, pPart: ?[*]ck.CK_BYTE, pulPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.decrypt_op == null or sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const op = &sess.decrypt_op.?;
|
||||
if (!decryptSideDualOk(op)) return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
switch (sess.verify_op.?) {
|
||||
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
|
||||
else => {},
|
||||
}
|
||||
const in = part(pEncryptedPart, ulEncryptedPartLen);
|
||||
const need = switch (decUpdateNeed(op, in.len)) {
|
||||
.err => |rv| return rv,
|
||||
.ok => |n| n,
|
||||
};
|
||||
if (pPart == null) {
|
||||
pulPartLen.* = need;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulPartLen.* < need) {
|
||||
pulPartLen.* = need;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const out = pPart.?[0..@intCast(need)];
|
||||
const wrote = switch (decUpdateEmit(inst, op, in, out)) {
|
||||
.err => |rv| {
|
||||
sess.endDecrypt(inst.allocator());
|
||||
return rv;
|
||||
},
|
||||
.ok => |n| n,
|
||||
};
|
||||
sess.verify_op.?.update(out[0..wrote]);
|
||||
pulPartLen.* = @intCast(wrote);
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ pub fn C_CancelFunction(_: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
|
|||
return ck.CKR_FUNCTION_NOT_PARALLEL;
|
||||
}
|
||||
|
||||
pub fn C_WaitForSlotEvent(_: ck.CK_FLAGS, _: *ck.CK_SLOT_ID, _: ?*anyopaque) callconv(.c) ck.CK_RV {
|
||||
pub fn C_WaitForSlotEvent(flags: ck.CK_FLAGS, pSlot: *ck.CK_SLOT_ID, pReserved: ?*anyopaque) callconv(.c) ck.CK_RV {
|
||||
_ = pSlot;
|
||||
if (!state.isInitialized()) return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (pReserved != null) return ck.CKR_ARGUMENTS_BAD;
|
||||
if ((flags & ck.CKF_DONT_BLOCK) != 0) return ck.CKR_NO_EVENT;
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ fn ulongFrom(bytes: []const u8) ?ck.CK_ULONG {
|
|||
}
|
||||
|
||||
pub fn C_GenerateKey(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phKey: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (pMechanism.mechanism != ck.CKM_AES_KEY_GEN) return ck.CKR_MECHANISM_INVALID;
|
||||
|
|
@ -168,8 +167,7 @@ fn buildRsaPrivate(obj: *Object, allocator: std.mem.Allocator, template: []const
|
|||
}
|
||||
|
||||
pub fn C_GenerateKeyPair(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, pPublicKeyTemplate: [*]ck.CK_ATTRIBUTE, ulPublicKeyAttributeCount: ck.CK_ULONG, pPrivateKeyTemplate: [*]ck.CK_ATTRIBUTE, ulPrivateKeyAttributeCount: ck.CK_ULONG, phPublicKey: *ck.CK_OBJECT_HANDLE, phPrivateKey: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
||||
|
|
@ -354,8 +352,7 @@ fn oaepParams(pMechanism: *ck.CK_MECHANISM) OaepVal {
|
|||
}
|
||||
|
||||
pub fn C_WrapKey(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hWrappingKey: ck.CK_OBJECT_HANDLE, hKey: ck.CK_OBJECT_HANDLE, pWrappedKey: ?[*]ck.CK_BYTE, pulWrappedKeyLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
_ = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
||||
|
|
@ -411,8 +408,7 @@ pub fn C_WrapKey(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, h
|
|||
}
|
||||
|
||||
pub fn C_UnwrapKey(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hUnwrappingKey: ck.CK_OBJECT_HANDLE, pWrappedKey: [*]ck.CK_BYTE, ulWrappedKeyLen: ck.CK_ULONG, pTemplate: [*]ck.CK_ATTRIBUTE, ulAttributeCount: ck.CK_ULONG, phKey: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
||||
|
|
@ -513,8 +509,7 @@ fn peerPointSec1(curve: ecdsa.Curve, data: []const u8) ?[]const u8 {
|
|||
}
|
||||
|
||||
pub fn C_DeriveKey(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hBaseKey: ck.CK_OBJECT_HANDLE, pTemplate: ?[*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phKey: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (pMechanism.mechanism != ck.CKM_ECDH1_DERIVE) return ck.CKR_MECHANISM_INVALID;
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ fn persistIfToken(inst: *state.Instance, is_token: bool) void {
|
|||
}
|
||||
|
||||
pub fn C_CreateObject(hSession: ck.CK_SESSION_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phObject: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -150,8 +149,7 @@ pub fn C_CreateObject(hSession: ck.CK_SESSION_HANDLE, pTemplate: [*]ck.CK_ATTRIB
|
|||
}
|
||||
|
||||
pub fn C_CopyObject(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phNewObject: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -183,8 +181,7 @@ pub fn C_CopyObject(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE
|
|||
}
|
||||
|
||||
pub fn C_DestroyObject(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -201,8 +198,7 @@ pub fn C_DestroyObject(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HAN
|
|||
}
|
||||
|
||||
pub fn C_GetObjectSize(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pulSize: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
_ = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -214,8 +210,7 @@ pub fn C_GetObjectSize(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HAN
|
|||
}
|
||||
|
||||
pub fn C_GetAttributeValue(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
_ = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -251,8 +246,7 @@ pub fn C_GetAttributeValue(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT
|
|||
}
|
||||
|
||||
pub fn C_SetAttributeValue(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -281,8 +275,7 @@ pub fn C_SetAttributeValue(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT
|
|||
}
|
||||
|
||||
pub fn C_FindObjectsInit(hSession: ck.CK_SESSION_HANDLE, pTemplate: ?[*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -306,8 +299,7 @@ pub fn C_FindObjectsInit(hSession: ck.CK_SESSION_HANDLE, pTemplate: ?[*]ck.CK_AT
|
|||
}
|
||||
|
||||
pub fn C_FindObjects(hSession: ck.CK_SESSION_HANDLE, phObject: [*]ck.CK_OBJECT_HANDLE, ulMaxObjectCount: ck.CK_ULONG, pulObjectCount: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -324,8 +316,7 @@ pub fn C_FindObjects(hSession: ck.CK_SESSION_HANDLE, phObject: [*]ck.CK_OBJECT_H
|
|||
}
|
||||
|
||||
pub fn C_FindObjectsFinal(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
|
|||
|
|
@ -5,16 +5,14 @@ const ck = @import("../ck.zig");
|
|||
const state = @import("../core/state.zig");
|
||||
|
||||
pub fn C_SeedRandom(hSession: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
return ck.CKR_RANDOM_SEED_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
pub fn C_GenerateRandom(hSession: ck.CK_SESSION_HANDLE, pRandomData: [*]ck.CK_BYTE, ulRandomLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (ulRandomLen == 0) return ck.CKR_OK;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const pin = @import("../crypto/pin.zig");
|
|||
const keystore = @import("../crypto/keystore.zig");
|
||||
const token = @import("../core/token.zig");
|
||||
const object_store = @import("../core/object_store.zig");
|
||||
const digest = @import("../crypto/digest.zig");
|
||||
|
||||
fn pinSlice(p: ?[*]ck.CK_UTF8CHAR, len: ck.CK_ULONG) []const u8 {
|
||||
return if (p) |ptr| ptr[0..@intCast(len)] else &.{};
|
||||
|
|
@ -26,12 +27,10 @@ fn sessionState(flags: ck.CK_FLAGS, logged_in: ?ck.CK_USER_TYPE) ck.CK_STATE {
|
|||
pub fn C_OpenSession(slotID: ck.CK_SLOT_ID, flags: ck.CK_FLAGS, pApplication: ?*anyopaque, notify: ck.CK_NOTIFY, phSession: *ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
|
||||
_ = pApplication;
|
||||
_ = notify;
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
|
||||
if ((flags & ck.CKF_SERIAL_SESSION) == 0) return ck.CKR_SESSION_PARALLEL_NOT_SUPPORTED;
|
||||
|
||||
state.mutex.lock();
|
||||
defer state.mutex.unlock();
|
||||
if ((flags & ck.CKF_RW_SESSION) == 0 and inst.logged_in == ck.CKU_SO) {
|
||||
return ck.CKR_SESSION_READ_WRITE_SO_EXISTS;
|
||||
}
|
||||
|
|
@ -41,10 +40,9 @@ pub fn C_OpenSession(slotID: ck.CK_SLOT_ID, flags: ck.CK_FLAGS, pApplication: ?*
|
|||
}
|
||||
|
||||
pub fn C_CloseSession(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
if (!inst.sessions.close(hSession)) return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (!inst.sessions.close(inst.allocator(), hSession)) return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (!inst.sessions.anyOpen()) {
|
||||
inst.relock();
|
||||
inst.logged_in = null;
|
||||
|
|
@ -53,19 +51,17 @@ pub fn C_CloseSession(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
|
|||
}
|
||||
|
||||
pub fn C_CloseAllSessions(slotID: ck.CK_SLOT_ID) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
inst.sessions.closeAll(slotID);
|
||||
if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
|
||||
inst.sessions.closeAll(inst.allocator(), slotID);
|
||||
inst.relock();
|
||||
inst.logged_in = null;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_GetSessionInfo(hSession: ck.CK_SESSION_HANDLE, pInfo: *ck.CK_SESSION_INFO) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const s = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
pInfo.* = .{
|
||||
|
|
@ -77,19 +73,56 @@ pub fn C_GetSessionInfo(hSession: ck.CK_SESSION_HANDLE, pInfo: *ck.CK_SESSION_IN
|
|||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_GetOperationState(_: ck.CK_SESSION_HANDLE, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_GetOperationState(hSession: ck.CK_SESSION_HANDLE, pOperationState: ?[*]ck.CK_BYTE, pulOperationStateLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (sess.sign_op != null or sess.verify_op != null or sess.encrypt_op != null or
|
||||
sess.decrypt_op != null or sess.sign_recover_op != null or sess.verify_recover_op != null)
|
||||
{
|
||||
return ck.CKR_STATE_UNSAVEABLE;
|
||||
}
|
||||
const d = if (sess.digest_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
|
||||
const total: ck.CK_ULONG = @intCast(config.op_state_header_len + d.stateLen());
|
||||
if (pOperationState == null) {
|
||||
pulOperationStateLen.* = total;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
if (pulOperationStateLen.* < total) {
|
||||
pulOperationStateLen.* = total;
|
||||
return ck.CKR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
const out = pOperationState.?[0..@intCast(total)];
|
||||
out[0] = config.op_state_version;
|
||||
out[1] = d.stateTag();
|
||||
d.writeState(out[config.op_state_header_len..]);
|
||||
pulOperationStateLen.* = total;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_SetOperationState(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ck.CK_OBJECT_HANDLE, _: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
return ck.CKR_FUNCTION_NOT_SUPPORTED;
|
||||
pub fn C_SetOperationState(hSession: ck.CK_SESSION_HANDLE, pOperationState: [*]ck.CK_BYTE, ulOperationStateLen: ck.CK_ULONG, hEncryptionKey: ck.CK_OBJECT_HANDLE, hAuthenticationKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (hEncryptionKey != ck.CK_INVALID_HANDLE or hAuthenticationKey != ck.CK_INVALID_HANDLE) {
|
||||
return ck.CKR_KEY_NOT_NEEDED;
|
||||
}
|
||||
const blob = pOperationState[0..@intCast(ulOperationStateLen)];
|
||||
if (blob.len < config.op_state_header_len) return ck.CKR_SAVED_STATE_INVALID;
|
||||
if (blob[0] != config.op_state_version) return ck.CKR_SAVED_STATE_INVALID;
|
||||
const restored = digest.Hasher.fromState(blob[1], blob[config.op_state_header_len..]) orelse return ck.CKR_SAVED_STATE_INVALID;
|
||||
sess.endDigest();
|
||||
sess.digest_op = restored;
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
pub fn C_Login(hSession: ck.CK_SESSION_HANDLE, userType: ck.CK_USER_TYPE, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (userType != ck.CKU_SO and userType != ck.CKU_USER) return ck.CKR_USER_TYPE_INVALID;
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (userType != ck.CKU_SO and userType != ck.CKU_USER) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_USER_TYPE_INVALID;
|
||||
}
|
||||
|
||||
state.mutex.lock();
|
||||
if (inst.sessions.get(hSession) == null) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -182,8 +215,7 @@ pub fn C_Login(hSession: ck.CK_SESSION_HANDLE, userType: ck.CK_USER_TYPE, pPin:
|
|||
}
|
||||
|
||||
pub fn C_Logout(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
if (inst.logged_in == null) return ck.CKR_USER_NOT_LOGGED_IN;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ fn pinSlice(p: ?[*]ck.CK_UTF8CHAR, len: ck.CK_ULONG) []const u8 {
|
|||
return if (p) |ptr| ptr[0..@intCast(len)] else &.{};
|
||||
}
|
||||
|
||||
fn pinLenOk(len: ck.CK_ULONG) bool {
|
||||
return len >= config.min_pin_len and len <= config.max_pin_len;
|
||||
}
|
||||
|
||||
fn labelFrom(p: ?[*]ck.CK_UTF8CHAR) [config.label_len]u8 {
|
||||
var out: [config.label_len]u8 = @splat(' ');
|
||||
if (p) |lp| @memcpy(&out, lp[0..config.label_len]);
|
||||
|
|
@ -63,11 +67,9 @@ pub fn C_GetSlotInfo(slotID: ck.CK_SLOT_ID, pInfo: *ck.CK_SLOT_INFO) callconv(.c
|
|||
}
|
||||
|
||||
pub fn C_GetTokenInfo(slotID: ck.CK_SLOT_ID, pInfo: *ck.CK_TOKEN_INFO) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
|
||||
|
||||
state.mutex.lock();
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
defer state.mutex.unlock();
|
||||
if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
|
||||
|
||||
var flags: ck.CK_FLAGS = ck.CKF_RNG | ck.CKF_LOGIN_REQUIRED;
|
||||
if (inst.token.initialized) flags |= ck.CKF_TOKEN_INITIALIZED;
|
||||
|
|
@ -167,7 +169,7 @@ pub fn C_GetMechanismInfo(slotID: ck.CK_SLOT_ID, mechType: ck.CK_MECHANISM_TYPE,
|
|||
ck.CKM_RSA_PKCS => .{
|
||||
.ulMinKeySize = config.rsa_min_key_bits,
|
||||
.ulMaxKeySize = config.rsa_max_key_bits,
|
||||
.flags = ck.CKF_SIGN | ck.CKF_VERIFY | ck.CKF_ENCRYPT | ck.CKF_DECRYPT,
|
||||
.flags = ck.CKF_SIGN | ck.CKF_VERIFY | ck.CKF_ENCRYPT | ck.CKF_DECRYPT | ck.CKF_SIGN_RECOVER | ck.CKF_VERIFY_RECOVER,
|
||||
},
|
||||
ck.CKM_SHA256_RSA_PKCS, ck.CKM_RSA_PKCS_PSS, ck.CKM_SHA256_RSA_PKCS_PSS => .{
|
||||
.ulMinKeySize = config.rsa_min_key_bits,
|
||||
|
|
@ -185,15 +187,21 @@ pub fn C_GetMechanismInfo(slotID: ck.CK_SLOT_ID, mechType: ck.CK_MECHANISM_TYPE,
|
|||
}
|
||||
|
||||
pub fn C_InitToken(slotID: ck.CK_SLOT_ID, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck.CK_ULONG, pLabel: ?[*]ck.CK_UTF8CHAR) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
if (slotID != config.slot_id) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_SLOT_ID_INVALID;
|
||||
}
|
||||
|
||||
state.mutex.lock();
|
||||
if (inst.sessions.anyOpen()) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_SESSION_EXISTS;
|
||||
}
|
||||
const was_init = inst.token.initialized;
|
||||
if (!was_init and !pinLenOk(ulPinLen)) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_PIN_LEN_RANGE;
|
||||
}
|
||||
if (was_init and inst.token.so_fail >= config.login_max_attempts) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_PIN_LOCKED;
|
||||
|
|
@ -267,9 +275,8 @@ pub fn C_InitToken(slotID: ck.CK_SLOT_ID, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck
|
|||
}
|
||||
|
||||
pub fn C_InitPIN(hSession: ck.CK_SESSION_HANDLE, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
|
||||
state.mutex.lock();
|
||||
if (inst.sessions.get(hSession) == null) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -278,6 +285,10 @@ pub fn C_InitPIN(hSession: ck.CK_SESSION_HANDLE, pPin: ?[*]ck.CK_UTF8CHAR, ulPin
|
|||
state.mutex.unlock();
|
||||
return ck.CKR_USER_NOT_LOGGED_IN;
|
||||
}
|
||||
if (!pinLenOk(ulPinLen)) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_PIN_LEN_RANGE;
|
||||
}
|
||||
_ = state.cryptoBegin();
|
||||
const io = inst.io();
|
||||
const allocator = inst.allocator();
|
||||
|
|
@ -318,9 +329,8 @@ pub fn C_InitPIN(hSession: ck.CK_SESSION_HANDLE, pPin: ?[*]ck.CK_UTF8CHAR, ulPin
|
|||
}
|
||||
|
||||
pub fn C_SetPIN(hSession: ck.CK_SESSION_HANDLE, pOldPin: ?[*]ck.CK_UTF8CHAR, ulOldLen: ck.CK_ULONG, pNewPin: ?[*]ck.CK_UTF8CHAR, ulNewLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
|
||||
const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
const inst = state.acquire() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
|
||||
|
||||
state.mutex.lock();
|
||||
const sess = inst.sessions.get(hSession) orelse {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_SESSION_HANDLE_INVALID;
|
||||
|
|
@ -349,6 +359,10 @@ pub fn C_SetPIN(hSession: ck.CK_SESSION_HANDLE, pOldPin: ?[*]ck.CK_UTF8CHAR, ulO
|
|||
hash = u.hash;
|
||||
wrapped_mk = inst.token.user_mk;
|
||||
}
|
||||
if (!pinLenOk(ulNewLen)) {
|
||||
state.mutex.unlock();
|
||||
return ck.CKR_PIN_LEN_RANGE;
|
||||
}
|
||||
const gen = state.cryptoBegin();
|
||||
const io = inst.io();
|
||||
const allocator = inst.allocator();
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ pub const CKR_FUNCTION_NOT_SUPPORTED: CK_RV = 0x00000054;
|
|||
pub const CKR_KEY_HANDLE_INVALID: CK_RV = 0x00000060;
|
||||
pub const CKR_KEY_SIZE_RANGE: CK_RV = 0x00000062;
|
||||
pub const CKR_KEY_TYPE_INCONSISTENT: CK_RV = 0x00000063;
|
||||
pub const CKR_KEY_NOT_NEEDED: CK_RV = 0x00000064;
|
||||
pub const CKR_KEY_INDIGESTIBLE: CK_RV = 0x00000067;
|
||||
pub const CKR_KEY_FUNCTION_NOT_PERMITTED: CK_RV = 0x00000068;
|
||||
pub const CKR_KEY_NOT_WRAPPABLE: CK_RV = 0x00000069;
|
||||
|
|
@ -253,6 +254,8 @@ pub const CKF_EC_COMPRESS: CK_FLAGS = 0x02000000;
|
|||
pub const CKF_LIBRARY_CANT_CREATE_OS_THREADS: CK_FLAGS = 0x00000001;
|
||||
pub const CKF_OS_LOCKING_OK: CK_FLAGS = 0x00000002;
|
||||
|
||||
pub const CKF_DONT_BLOCK: CK_FLAGS = 0x00000001;
|
||||
|
||||
pub const CKG_MGF1_SHA1: CK_RSA_PKCS_MGF_TYPE = 0x00000001;
|
||||
pub const CKG_MGF1_SHA256: CK_RSA_PKCS_MGF_TYPE = 0x00000002;
|
||||
pub const CKG_MGF1_SHA384: CK_RSA_PKCS_MGF_TYPE = 0x00000003;
|
||||
|
|
|
|||
|
|
@ -63,9 +63,13 @@ pub const gcm_iv_bits: ck.CK_ULONG = 96;
|
|||
pub const gcm_tag_len: usize = 16;
|
||||
pub const gcm_tag_bits: ck.CK_ULONG = 128;
|
||||
pub const max_gcm_aad_len: usize = 256;
|
||||
pub const max_gcm_stream_len: usize = 16 * 1024 * 1024;
|
||||
|
||||
pub const master_key_len: usize = 32;
|
||||
|
||||
pub const op_state_version: u8 = 1;
|
||||
pub const op_state_header_len: usize = 2;
|
||||
|
||||
pub const supported_mechanisms = [_]ck.CK_MECHANISM_TYPE{
|
||||
ck.CKM_SHA256,
|
||||
ck.CKM_SHA384,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ pub const Object = struct {
|
|||
const dup = try allocator.dupe(u8, bytes);
|
||||
secureFree(allocator, a.value);
|
||||
a.value = dup;
|
||||
a.sealed = false;
|
||||
return;
|
||||
}
|
||||
if (self.attrs.items.len >= config.max_attributes_per_object) return error.TooManyAttributes;
|
||||
|
|
@ -385,6 +386,18 @@ test "set replaces an existing attribute and reports size" {
|
|||
try std.testing.expectEqual(@as(ck.CK_ULONG, "second-value".len), obj.sizeBytes());
|
||||
}
|
||||
|
||||
test "set on an existing attribute clears a stale sealed flag" {
|
||||
const a = std.testing.allocator;
|
||||
var obj: Object = .{};
|
||||
defer obj.deinit(a);
|
||||
|
||||
try obj.set(a, ck.CKA_VALUE, "ciphertext-placeholder");
|
||||
obj.findPtr(ck.CKA_VALUE).?.sealed = true;
|
||||
try obj.set(a, ck.CKA_VALUE, "fresh-plaintext");
|
||||
try std.testing.expect(!obj.findPtr(ck.CKA_VALUE).?.sealed);
|
||||
try std.testing.expectEqualSlices(u8, "fresh-plaintext", obj.get(ck.CKA_VALUE).?);
|
||||
}
|
||||
|
||||
test "bool and class helpers read CK_BBOOL semantics" {
|
||||
const a = std.testing.allocator;
|
||||
var obj: Object = .{};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,50 @@ pub const RsaCrypt = struct {
|
|||
out_len: usize,
|
||||
};
|
||||
|
||||
pub const RsaRecover = struct {
|
||||
key: ck.CK_OBJECT_HANDLE,
|
||||
out_len: usize,
|
||||
};
|
||||
|
||||
pub const GcmStream = struct {
|
||||
cipher: cipher.Cipher,
|
||||
buf: ?[]u8 = null,
|
||||
len: usize = 0,
|
||||
|
||||
pub fn append(self: *GcmStream, allocator: std.mem.Allocator, bytes: []const u8) error{ OutOfMemory, TooLarge }!void {
|
||||
if (bytes.len == 0) return;
|
||||
const needed = self.len + bytes.len;
|
||||
if (needed > config.max_gcm_stream_len) return error.TooLarge;
|
||||
if (self.buf == null or self.buf.?.len < needed) {
|
||||
var new_cap: usize = if (self.buf) |b| b.len else 256;
|
||||
while (new_cap < needed) new_cap *|= 2;
|
||||
if (new_cap > config.max_gcm_stream_len) new_cap = config.max_gcm_stream_len;
|
||||
const fresh = try allocator.alloc(u8, new_cap);
|
||||
if (self.buf) |old| {
|
||||
@memcpy(fresh[0..self.len], old[0..self.len]);
|
||||
std.crypto.secureZero(u8, old);
|
||||
allocator.free(old);
|
||||
}
|
||||
self.buf = fresh;
|
||||
}
|
||||
@memcpy(self.buf.?[self.len..][0..bytes.len], bytes);
|
||||
self.len += bytes.len;
|
||||
}
|
||||
|
||||
pub fn data(self: *const GcmStream) []const u8 {
|
||||
return if (self.buf) |b| b[0..self.len] else &.{};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *GcmStream, allocator: std.mem.Allocator) void {
|
||||
if (self.buf) |b| {
|
||||
std.crypto.secureZero(u8, b);
|
||||
allocator.free(b);
|
||||
}
|
||||
self.buf = null;
|
||||
self.len = 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const SignOp = union(enum) {
|
||||
mac: mac.Mac,
|
||||
ec: ecdsa.SignState,
|
||||
|
|
@ -67,18 +111,28 @@ pub const VerifyOp = union(enum) {
|
|||
|
||||
pub const EncryptOp = union(enum) {
|
||||
aes: cipher.Cipher,
|
||||
gcm: GcmStream,
|
||||
rsa: RsaCrypt,
|
||||
|
||||
pub fn zeroize(self: *EncryptOp) void {
|
||||
pub fn deinit(self: *EncryptOp, allocator: std.mem.Allocator) void {
|
||||
switch (self.*) {
|
||||
.gcm => |*g| g.deinit(allocator),
|
||||
else => {},
|
||||
}
|
||||
std.crypto.secureZero(u8, std.mem.asBytes(self));
|
||||
}
|
||||
};
|
||||
|
||||
pub const DecryptOp = union(enum) {
|
||||
aes: cipher.Cipher,
|
||||
gcm: GcmStream,
|
||||
rsa: RsaCrypt,
|
||||
|
||||
pub fn zeroize(self: *DecryptOp) void {
|
||||
pub fn deinit(self: *DecryptOp, allocator: std.mem.Allocator) void {
|
||||
switch (self.*) {
|
||||
.gcm => |*g| g.deinit(allocator),
|
||||
else => {},
|
||||
}
|
||||
std.crypto.secureZero(u8, std.mem.asBytes(self));
|
||||
}
|
||||
};
|
||||
|
|
@ -92,6 +146,8 @@ pub const Session = struct {
|
|||
verify_op: ?VerifyOp = null,
|
||||
encrypt_op: ?EncryptOp = null,
|
||||
decrypt_op: ?DecryptOp = null,
|
||||
sign_recover_op: ?RsaRecover = null,
|
||||
verify_recover_op: ?RsaRecover = null,
|
||||
|
||||
pub fn endDigest(self: *Session) void {
|
||||
if (self.digest_op) |*o| std.crypto.secureZero(u8, std.mem.asBytes(o));
|
||||
|
|
@ -108,15 +164,36 @@ pub const Session = struct {
|
|||
self.verify_op = null;
|
||||
}
|
||||
|
||||
pub fn endEncrypt(self: *Session) void {
|
||||
if (self.encrypt_op) |*o| o.zeroize();
|
||||
pub fn endEncrypt(self: *Session, allocator: std.mem.Allocator) void {
|
||||
if (self.encrypt_op) |*o| o.deinit(allocator);
|
||||
self.encrypt_op = null;
|
||||
}
|
||||
|
||||
pub fn endDecrypt(self: *Session) void {
|
||||
if (self.decrypt_op) |*o| o.zeroize();
|
||||
pub fn endDecrypt(self: *Session, allocator: std.mem.Allocator) void {
|
||||
if (self.decrypt_op) |*o| o.deinit(allocator);
|
||||
self.decrypt_op = null;
|
||||
}
|
||||
|
||||
pub fn endSignRecover(self: *Session) void {
|
||||
if (self.sign_recover_op) |*o| std.crypto.secureZero(u8, std.mem.asBytes(o));
|
||||
self.sign_recover_op = null;
|
||||
}
|
||||
|
||||
pub fn endVerifyRecover(self: *Session) void {
|
||||
if (self.verify_recover_op) |*o| std.crypto.secureZero(u8, std.mem.asBytes(o));
|
||||
self.verify_recover_op = null;
|
||||
}
|
||||
|
||||
pub fn freeHeap(self: *Session, allocator: std.mem.Allocator) void {
|
||||
if (self.encrypt_op) |*o| switch (o.*) {
|
||||
.gcm => |*g| g.deinit(allocator),
|
||||
else => {},
|
||||
};
|
||||
if (self.decrypt_op) |*o| switch (o.*) {
|
||||
.gcm => |*g| g.deinit(allocator),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Table = struct {
|
||||
|
|
@ -139,18 +216,20 @@ pub const Table = struct {
|
|||
return null;
|
||||
}
|
||||
|
||||
pub fn close(self: *Table, h: ck.CK_SESSION_HANDLE) bool {
|
||||
pub fn close(self: *Table, allocator: std.mem.Allocator, h: ck.CK_SESSION_HANDLE) bool {
|
||||
if (h == 0 or h > config.max_sessions) return false;
|
||||
if (self.slots[h - 1] == null) return false;
|
||||
if (self.slots[h - 1]) |*s| s.freeHeap(allocator);
|
||||
std.crypto.secureZero(u8, std.mem.asBytes(&self.slots[h - 1]));
|
||||
self.slots[h - 1] = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn closeAll(self: *Table, slot: ck.CK_SLOT_ID) void {
|
||||
pub fn closeAll(self: *Table, allocator: std.mem.Allocator, slot: ck.CK_SLOT_ID) void {
|
||||
for (&self.slots) |*s| {
|
||||
if (s.*) |*sp| {
|
||||
if (sp.slot == slot) {
|
||||
sp.freeHeap(allocator);
|
||||
std.crypto.secureZero(u8, std.mem.asBytes(s));
|
||||
s.* = null;
|
||||
}
|
||||
|
|
@ -158,7 +237,10 @@ pub const Table = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn wipeAll(self: *Table) void {
|
||||
pub fn wipeAll(self: *Table, allocator: std.mem.Allocator) void {
|
||||
for (&self.slots) |*s| {
|
||||
if (s.*) |*sp| sp.freeHeap(allocator);
|
||||
}
|
||||
std.crypto.secureZero(u8, std.mem.asBytes(&self.slots));
|
||||
}
|
||||
|
||||
|
|
@ -200,13 +282,14 @@ test "open returns nonzero handles and get resolves them" {
|
|||
}
|
||||
|
||||
test "close frees the slot and closeAll empties the table" {
|
||||
const a = std.testing.allocator;
|
||||
var t: Table = .{};
|
||||
const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
|
||||
try std.testing.expect(t.close(h));
|
||||
try std.testing.expect(!t.close(h));
|
||||
try std.testing.expect(t.close(a, h));
|
||||
try std.testing.expect(!t.close(a, h));
|
||||
try std.testing.expect(!t.anyOpen());
|
||||
_ = t.open(0, ck.CKF_SERIAL_SESSION);
|
||||
t.closeAll(0);
|
||||
t.closeAll(a, 0);
|
||||
try std.testing.expect(!t.anyOpen());
|
||||
}
|
||||
|
||||
|
|
@ -214,22 +297,45 @@ fn expectAllZero(bytes: []const u8) !void {
|
|||
for (bytes) |b| try std.testing.expectEqual(@as(u8, 0), b);
|
||||
}
|
||||
|
||||
test "EncryptOp.zeroize zeros the AES key material" {
|
||||
test "EncryptOp.deinit zeros the AES key material" {
|
||||
var op: EncryptOp = .{ .aes = .{ .mode = .cbc, .encrypt = true, .key_len = 32 } };
|
||||
const key: []u8 = &op.aes.key_buf;
|
||||
@memset(key, 0xAA);
|
||||
op.zeroize();
|
||||
op.deinit(std.testing.allocator);
|
||||
try expectAllZero(key);
|
||||
}
|
||||
|
||||
test "DecryptOp.zeroize zeros the AES key material" {
|
||||
test "DecryptOp.deinit zeros the AES key material" {
|
||||
var op: DecryptOp = .{ .aes = .{ .mode = .cbc, .encrypt = false, .key_len = 16 } };
|
||||
const key: []u8 = &op.aes.key_buf;
|
||||
@memset(key, 0xAA);
|
||||
op.zeroize();
|
||||
op.deinit(std.testing.allocator);
|
||||
try expectAllZero(key);
|
||||
}
|
||||
|
||||
test "GcmStream secure-grows across a realloc and deinit clears it" {
|
||||
const a = std.testing.allocator;
|
||||
var g: GcmStream = .{ .cipher = .{ .mode = .gcm, .encrypt = true, .key_len = 32 } };
|
||||
var part: [200]u8 = undefined;
|
||||
for (&part, 0..) |*b, i| b.* = @intCast(i & 0xff);
|
||||
try g.append(a, &part);
|
||||
try g.append(a, &part);
|
||||
try std.testing.expectEqual(@as(usize, 400), g.len);
|
||||
try std.testing.expectEqualSlices(u8, &part, g.data()[0..200]);
|
||||
try std.testing.expectEqualSlices(u8, &part, g.data()[200..400]);
|
||||
g.deinit(a);
|
||||
try std.testing.expect(g.buf == null);
|
||||
try std.testing.expectEqual(@as(usize, 0), g.len);
|
||||
}
|
||||
|
||||
test "GcmStream enforces the DoS bound" {
|
||||
const a = std.testing.allocator;
|
||||
var g: GcmStream = .{ .cipher = .{ .mode = .gcm, .encrypt = true, .key_len = 16 } };
|
||||
defer g.deinit(a);
|
||||
g.len = config.max_gcm_stream_len;
|
||||
try std.testing.expectError(error.TooLarge, g.append(a, "x"));
|
||||
}
|
||||
|
||||
test "SignOp.zeroize zeros the EC private scalar" {
|
||||
const scalar = [_]u8{0xAB} ** 32;
|
||||
var op: SignOp = .{ .ec = ecdsa.SignState.init(.p256, ck.CKM_ECDSA, &scalar).? };
|
||||
|
|
@ -247,35 +353,49 @@ test "VerifyOp.zeroize zeros HMAC key state" {
|
|||
}
|
||||
|
||||
test "endEncrypt clears the op and removes the secret from the slot" {
|
||||
const a = std.testing.allocator;
|
||||
var t: Table = .{};
|
||||
const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
|
||||
const sess = t.get(h).?;
|
||||
sess.encrypt_op = .{ .aes = .{ .mode = .gcm, .encrypt = true, .key_len = 32 } };
|
||||
sess.encrypt_op = .{ .aes = .{ .mode = .cbc, .encrypt = true, .key_len = 32 } };
|
||||
const key: []u8 = &sess.encrypt_op.?.aes.key_buf;
|
||||
@memset(key, 0x5C);
|
||||
sess.endEncrypt();
|
||||
sess.endEncrypt(a);
|
||||
try std.testing.expect(sess.encrypt_op == null);
|
||||
try std.testing.expect(std.mem.indexOfScalar(u8, key, 0x5C) == null);
|
||||
}
|
||||
|
||||
test "close removes an active op's secret from the slot" {
|
||||
const a = std.testing.allocator;
|
||||
var t: Table = .{};
|
||||
const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
|
||||
const sess = t.get(h).?;
|
||||
sess.decrypt_op = .{ .aes = .{ .mode = .cbc, .encrypt = false, .key_len = 32 } };
|
||||
const key: []u8 = &sess.decrypt_op.?.aes.key_buf;
|
||||
@memset(key, 0x5C);
|
||||
try std.testing.expect(t.close(h));
|
||||
try std.testing.expect(t.close(a, h));
|
||||
try std.testing.expect(std.mem.indexOfScalar(u8, key, 0x5C) == null);
|
||||
}
|
||||
|
||||
test "close frees an active GCM stream's heap buffer" {
|
||||
const a = std.testing.allocator;
|
||||
var t: Table = .{};
|
||||
const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
|
||||
const sess = t.get(h).?;
|
||||
sess.encrypt_op = .{ .gcm = .{ .cipher = .{ .mode = .gcm, .encrypt = true, .key_len = 32 } } };
|
||||
try sess.encrypt_op.?.gcm.append(a, "buffered-plaintext-awaiting-final");
|
||||
try std.testing.expect(t.close(a, h));
|
||||
try std.testing.expect(!t.anyOpen());
|
||||
}
|
||||
|
||||
test "wipeAll zeros secret material in every slot" {
|
||||
const a = std.testing.allocator;
|
||||
var t: Table = .{};
|
||||
const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
|
||||
const sess = t.get(h).?;
|
||||
sess.sign_op = .{ .mac = undefined };
|
||||
const st: []u8 = std.mem.asBytes(&sess.sign_op.?.mac);
|
||||
@memset(st, 0xEF);
|
||||
t.wipeAll();
|
||||
t.wipeAll(a);
|
||||
try expectAllZero(st);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,13 @@ var present: bool = false;
|
|||
var inflight: usize = 0;
|
||||
var generation: u64 = 0;
|
||||
|
||||
pub fn current() ?*Instance {
|
||||
return if (@atomicLoad(bool, &present, .acquire)) &storage else null;
|
||||
pub fn acquire() ?*Instance {
|
||||
mutex.lock();
|
||||
if (!@atomicLoad(bool, &present, .acquire)) {
|
||||
mutex.unlock();
|
||||
return null;
|
||||
}
|
||||
return &storage;
|
||||
}
|
||||
|
||||
pub fn isInitialized() bool {
|
||||
|
|
@ -87,11 +92,12 @@ pub fn finalize() ck.CK_RV {
|
|||
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
storage.sessions.wipeAll();
|
||||
storage.sessions.wipeAll(storage.allocator());
|
||||
storage.wipeMasterKey();
|
||||
storage.objects.deinit(storage.allocator());
|
||||
storage.threaded.deinit();
|
||||
_ = storage.debug_alloc.deinit();
|
||||
const leak = storage.debug_alloc.deinit();
|
||||
if (builtin.mode == .Debug and leak == .leak) @panic("hsm: allocator leak detected at C_Finalize");
|
||||
return ck.CKR_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ pub const Cipher = struct {
|
|||
|
||||
fn cbcEncStep(self: *Cipher, in16: *const [block]u8, out16: *[block]u8) void {
|
||||
var x: [block]u8 = undefined;
|
||||
defer std.crypto.secureZero(u8, &x);
|
||||
for (0..block) |j| x[j] = in16[j] ^ self.chain[j];
|
||||
encBlockRaw(self.key(), &x, out16);
|
||||
self.chain = out16.*;
|
||||
|
|
@ -77,6 +78,7 @@ pub const Cipher = struct {
|
|||
|
||||
fn cbcDecStep(self: *Cipher, in16: *const [block]u8, out16: *[block]u8) void {
|
||||
var d: [block]u8 = undefined;
|
||||
defer std.crypto.secureZero(u8, &d);
|
||||
decBlockRaw(self.key(), in16, &d);
|
||||
for (0..block) |j| out16[j] = d[j] ^ self.chain[j];
|
||||
self.chain = in16.*;
|
||||
|
|
@ -142,6 +144,7 @@ pub const Cipher = struct {
|
|||
if (self.mode == .cbc) return 0;
|
||||
if (!self.has_held) return Error.EncryptedDataLenRange;
|
||||
var pt: [block]u8 = undefined;
|
||||
defer std.crypto.secureZero(u8, &pt);
|
||||
self.cbcDecStep(&self.held, &pt);
|
||||
self.has_held = false;
|
||||
const padlen = pt[block - 1];
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ const sha2 = std.crypto.hash.sha2;
|
|||
|
||||
pub const max_digest_len = sha2.Sha512.digest_length;
|
||||
|
||||
pub const state_tag_sha256: u8 = 1;
|
||||
pub const state_tag_sha384: u8 = 2;
|
||||
pub const state_tag_sha512: u8 = 3;
|
||||
|
||||
pub const Hasher = union(enum) {
|
||||
sha256: sha2.Sha256,
|
||||
sha384: sha2.Sha384,
|
||||
|
|
@ -42,6 +46,50 @@ pub const Hasher = union(enum) {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stateTag(self: *const Hasher) u8 {
|
||||
return switch (self.*) {
|
||||
.sha256 => state_tag_sha256,
|
||||
.sha384 => state_tag_sha384,
|
||||
.sha512 => state_tag_sha512,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn stateLen(self: *const Hasher) usize {
|
||||
return switch (self.*) {
|
||||
inline else => |*h| @sizeOf(@TypeOf(h.*)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn writeState(self: *const Hasher, out: []u8) void {
|
||||
switch (self.*) {
|
||||
inline else => |*h| @memcpy(out[0..@sizeOf(@TypeOf(h.*))], std.mem.asBytes(h)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fromState(tag: u8, bytes: []const u8) ?Hasher {
|
||||
switch (tag) {
|
||||
state_tag_sha256 => {
|
||||
if (bytes.len != @sizeOf(sha2.Sha256)) return null;
|
||||
var h: Hasher = .{ .sha256 = undefined };
|
||||
@memcpy(std.mem.asBytes(&h.sha256), bytes);
|
||||
return h;
|
||||
},
|
||||
state_tag_sha384 => {
|
||||
if (bytes.len != @sizeOf(sha2.Sha384)) return null;
|
||||
var h: Hasher = .{ .sha384 = undefined };
|
||||
@memcpy(std.mem.asBytes(&h.sha384), bytes);
|
||||
return h;
|
||||
},
|
||||
state_tag_sha512 => {
|
||||
if (bytes.len != @sizeOf(sha2.Sha512)) return null;
|
||||
var h: Hasher = .{ .sha512 = undefined };
|
||||
@memcpy(std.mem.asBytes(&h.sha512), bytes);
|
||||
return h;
|
||||
},
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn digestLenOf(mech: ck.CK_MECHANISM_TYPE) ?usize {
|
||||
|
|
@ -87,3 +135,26 @@ test "unknown mechanism yields null" {
|
|||
try std.testing.expect(Hasher.init(ck.CKM_AES_CBC) == null);
|
||||
try std.testing.expect(digestLenOf(ck.CKM_SHA384).? == 48);
|
||||
}
|
||||
|
||||
test "op-state serialize then restore continues the same digest" {
|
||||
var a = Hasher.init(ck.CKM_SHA256).?;
|
||||
a.update("first part ");
|
||||
|
||||
var blob: [1 + @sizeOf(sha2.Sha512)]u8 = undefined;
|
||||
const tag = a.stateTag();
|
||||
const len = a.stateLen();
|
||||
a.writeState(blob[0..len]);
|
||||
|
||||
var b = Hasher.fromState(tag, blob[0..len]).?;
|
||||
a.update("second part");
|
||||
b.update("second part");
|
||||
|
||||
var oa: [max_digest_len]u8 = undefined;
|
||||
var ob: [max_digest_len]u8 = undefined;
|
||||
a.finalInto(&oa);
|
||||
b.finalInto(&ob);
|
||||
try std.testing.expectEqualSlices(u8, oa[0..32], ob[0..32]);
|
||||
|
||||
try std.testing.expect(Hasher.fromState(99, blob[0..len]) == null);
|
||||
try std.testing.expect(Hasher.fromState(tag, blob[0 .. len - 1]) == null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ pub extern fn EVP_PKEY_sign_init(ctx: ?*EVP_PKEY_CTX) c_int;
|
|||
pub extern fn EVP_PKEY_sign(ctx: ?*EVP_PKEY_CTX, sig: ?[*]u8, siglen: *usize, tbs: [*]const u8, tbslen: usize) c_int;
|
||||
pub extern fn EVP_PKEY_verify_init(ctx: ?*EVP_PKEY_CTX) c_int;
|
||||
pub extern fn EVP_PKEY_verify(ctx: ?*EVP_PKEY_CTX, sig: [*]const u8, siglen: usize, tbs: [*]const u8, tbslen: usize) c_int;
|
||||
pub extern fn EVP_PKEY_verify_recover_init(ctx: ?*EVP_PKEY_CTX) c_int;
|
||||
pub extern fn EVP_PKEY_verify_recover(ctx: ?*EVP_PKEY_CTX, rout: ?[*]u8, routlen: *usize, sig: [*]const u8, siglen: usize) c_int;
|
||||
|
||||
pub extern fn EVP_PKEY_encrypt_init(ctx: ?*EVP_PKEY_CTX) c_int;
|
||||
pub extern fn EVP_PKEY_encrypt(ctx: ?*EVP_PKEY_CTX, out: ?[*]u8, outlen: *usize, in: [*]const u8, inlen: usize) c_int;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const ossl = @import("openssl.zig");
|
|||
|
||||
pub const max_modulus_bytes: usize = config.rsa_max_key_bits / 8;
|
||||
pub const max_sig_bytes: usize = max_modulus_bytes;
|
||||
pub const pkcs1_v15_min_overhead: usize = 11;
|
||||
const component_count = 8;
|
||||
|
||||
pub const Error = error{Crypto};
|
||||
|
|
@ -235,6 +236,18 @@ pub fn verify(pc: PublicComponents, p: SignParams, data: []const u8, sig: []cons
|
|||
return if (ossl.EVP_PKEY_verify(ctx, sig.ptr, sig.len, data.ptr, data.len) == 1) .ok else .invalid;
|
||||
}
|
||||
|
||||
pub fn recover(pc: PublicComponents, sig: []const u8, out: []u8) Error!usize {
|
||||
const pkey = try buildPublic(pc);
|
||||
defer ossl.EVP_PKEY_free(pkey);
|
||||
const ctx = ossl.EVP_PKEY_CTX_new(pkey, null) orelse return Error.Crypto;
|
||||
defer ossl.EVP_PKEY_CTX_free(ctx);
|
||||
if (ossl.EVP_PKEY_verify_recover_init(ctx) <= 0) return Error.Crypto;
|
||||
if (ossl.EVP_PKEY_CTX_set_rsa_padding(ctx, ossl.pad_pkcs1) <= 0) return Error.Crypto;
|
||||
var outlen: usize = out.len;
|
||||
if (ossl.EVP_PKEY_verify_recover(ctx, out.ptr, &outlen, sig.ptr, sig.len) <= 0) return Error.Crypto;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
fn applyCryptPadding(ctx: ?*ossl.EVP_PKEY_CTX, p: CryptParams) Error!void {
|
||||
if (p.scheme == .oaep) {
|
||||
if (ossl.EVP_PKEY_CTX_set_rsa_padding(ctx, ossl.pad_oaep) <= 0) return Error.Crypto;
|
||||
|
|
@ -341,6 +354,18 @@ test "PKCS#1 v1.5 encrypt/decrypt round-trips" {
|
|||
try std.testing.expectEqualSlices(u8, msg, pt[0..pn]);
|
||||
}
|
||||
|
||||
test "RSA sign-recover then verify-recover returns the original message" {
|
||||
var g = try generate(config.rsa_min_key_bits);
|
||||
defer g.zeroize();
|
||||
const params: SignParams = .{ .scheme = .pkcs1, .digest = .none };
|
||||
const msg = "recoverable enterprise payload";
|
||||
var sig: [max_sig_bytes]u8 = undefined;
|
||||
const n = try sign(testPriv(&g), params, msg, &sig);
|
||||
var rec: [max_modulus_bytes]u8 = undefined;
|
||||
const m = try recover(testPub(&g), sig[0..n], &rec);
|
||||
try std.testing.expectEqualSlices(u8, msg, rec[0..m]);
|
||||
}
|
||||
|
||||
test "OAEP-SHA256 encrypt/decrypt round-trips" {
|
||||
var g = try generate(config.rsa_min_key_bits);
|
||||
defer g.zeroize();
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ test "hand-coded structs match OASIS-translated layout byte-for-byte" {
|
|||
try expectSameLayout(ck.CK_ATTRIBUTE, p11c.CK_ATTRIBUTE);
|
||||
try expectSameLayout(ck.CK_MECHANISM, p11c.CK_MECHANISM);
|
||||
try expectSameLayout(ck.CK_GCM_PARAMS, p11c.CK_GCM_PARAMS);
|
||||
try expectSameLayout(ck.CK_RSA_PKCS_PSS_PARAMS, p11c.CK_RSA_PKCS_PSS_PARAMS);
|
||||
try expectSameLayout(ck.CK_RSA_PKCS_OAEP_PARAMS, p11c.CK_RSA_PKCS_OAEP_PARAMS);
|
||||
try expectSameLayout(ck.CK_ECDH1_DERIVE_PARAMS, p11c.CK_ECDH1_DERIVE_PARAMS);
|
||||
try expectSameLayout(ck.CK_DATE, p11c.CK_DATE);
|
||||
try expectSameLayout(ck.CK_C_INITIALIZE_ARGS, p11c.CK_C_INITIALIZE_ARGS);
|
||||
|
|
|
|||
Loading…
Reference in New Issue