Cybersecurity-Projects/PROJECTS/advanced/hsm-emulator/src/api/crypto_ops.zig

1241 lines
53 KiB
Zig

// ©AngelaMos | 2026
// crypto_ops.zig
const std = @import("std");
const ck = @import("../ck.zig");
const config = @import("../config.zig");
const state = @import("../core/state.zig");
const session = @import("../core/session.zig");
const object_store = @import("../core/object_store.zig");
const digest = @import("../crypto/digest.zig");
const mac = @import("../crypto/mac.zig");
const cipher = @import("../crypto/cipher.zig");
const ecdsa = @import("../crypto/ecdsa.zig");
const rsa = @import("../crypto/rsa.zig");
fn part(p: [*]ck.CK_BYTE, len: ck.CK_ULONG) []const u8 {
return p[0..@intCast(len)];
}
fn ctEql(a: []const u8, b: []const u8) bool {
if (a.len != b.len) return false;
var diff: u8 = 0;
for (a, b) |x, y| diff |= x ^ y;
return diff == 0;
}
fn objectClass(obj: *const object_store.Object) ?ck.CK_OBJECT_CLASS {
const v = obj.get(ck.CKA_CLASS) orelse return null;
if (v.len != @sizeOf(ck.CK_OBJECT_CLASS)) return null;
return std.mem.bytesToValue(ck.CK_OBJECT_CLASS, v[0..@sizeOf(ck.CK_OBJECT_CLASS)]);
}
fn keyType(obj: *const object_store.Object) ?ck.CK_KEY_TYPE {
const v = obj.get(ck.CKA_KEY_TYPE) orelse return null;
if (v.len != @sizeOf(ck.CK_KEY_TYPE)) return null;
return std.mem.bytesToValue(ck.CK_KEY_TYPE, v[0..@sizeOf(ck.CK_KEY_TYPE)]);
}
const KeyVal = union(enum) {
ok: []const u8,
err: ck.CK_RV,
};
fn secretKeyValue(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, usage: ck.CK_ATTRIBUTE_TYPE) KeyVal {
const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (objectClass(obj) != ck.CKO_SECRET_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (obj.has(usage) and !obj.getBool(usage)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
const a = obj.findPtr(ck.CKA_VALUE) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (a.sealed) return .{ .err = ck.CKR_USER_NOT_LOGGED_IN };
return .{ .ok = a.value };
}
const EcKey = union(enum) {
ok: struct { curve: ecdsa.Curve, material: []const u8 },
err: ck.CK_RV,
};
fn ecPrivateKey(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE) EcKey {
const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (objectClass(obj) != ck.CKO_PRIVATE_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (keyType(obj) != ck.CKK_EC) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (obj.has(ck.CKA_SIGN) and !obj.getBool(ck.CKA_SIGN)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
const params = obj.get(ck.CKA_EC_PARAMS) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
const curve = ecdsa.curveFromParams(params) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
const sa = obj.findPtr(ck.CKA_VALUE) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (sa.sealed) return .{ .err = ck.CKR_USER_NOT_LOGGED_IN };
if (sa.value.len != curve.scalarLen()) return .{ .err = ck.CKR_FUNCTION_FAILED };
return .{ .ok = .{ .curve = curve, .material = sa.value } };
}
fn ecPublicKey(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE) EcKey {
const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (objectClass(obj) != ck.CKO_PUBLIC_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (keyType(obj) != ck.CKK_EC) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (obj.has(ck.CKA_VERIFY) and !obj.getBool(ck.CKA_VERIFY)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
const params = obj.get(ck.CKA_EC_PARAMS) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
const curve = ecdsa.curveFromParams(params) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
const der = obj.get(ck.CKA_EC_POINT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
const point = ecdsa.unwrapEcPoint(der) orelse return .{ .err = ck.CKR_FUNCTION_FAILED };
if (point.len != curve.pointLen()) return .{ .err = ck.CKR_FUNCTION_FAILED };
return .{ .ok = .{ .curve = curve, .material = point } };
}
const RsaPriv = union(enum) {
ok: rsa.PrivateComponents,
err: ck.CK_RV,
};
const RsaPub = union(enum) {
ok: rsa.PublicComponents,
err: ck.CK_RV,
};
fn rsaPrivateComponents(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, usage: ck.CK_ATTRIBUTE_TYPE) RsaPriv {
const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (objectClass(obj) != ck.CKO_PRIVATE_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (keyType(obj) != ck.CKK_RSA) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (obj.has(usage) and !obj.getBool(usage)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
if (obj.findPtr(ck.CKA_PRIVATE_EXPONENT)) |da| {
if (da.sealed) return .{ .err = ck.CKR_USER_NOT_LOGGED_IN };
}
return .{ .ok = .{
.n = obj.get(ck.CKA_MODULUS) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.e = obj.get(ck.CKA_PUBLIC_EXPONENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.d = obj.get(ck.CKA_PRIVATE_EXPONENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.p = obj.get(ck.CKA_PRIME_1) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.q = obj.get(ck.CKA_PRIME_2) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.dmp1 = obj.get(ck.CKA_EXPONENT_1) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.dmq1 = obj.get(ck.CKA_EXPONENT_2) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.iqmp = obj.get(ck.CKA_COEFFICIENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
} };
}
fn rsaPublicComponents(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, usage: ck.CK_ATTRIBUTE_TYPE) RsaPub {
const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
if (objectClass(obj) != ck.CKO_PUBLIC_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (keyType(obj) != ck.CKK_RSA) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
if (obj.has(usage) and !obj.getBool(usage)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
return .{ .ok = .{
.n = obj.get(ck.CKA_MODULUS) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
.e = obj.get(ck.CKA_PUBLIC_EXPONENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
} };
}
fn isRsaSignMech(mech: ck.CK_MECHANISM_TYPE) bool {
return switch (mech) {
ck.CKM_RSA_PKCS, ck.CKM_SHA256_RSA_PKCS, ck.CKM_RSA_PKCS_PSS, ck.CKM_SHA256_RSA_PKCS_PSS => true,
else => false,
};
}
fn mgfHash(mgf: ck.CK_RSA_PKCS_MGF_TYPE) ?rsa.Hash {
return switch (mgf) {
ck.CKG_MGF1_SHA256 => .sha256,
ck.CKG_MGF1_SHA384 => .sha384,
ck.CKG_MGF1_SHA512 => .sha512,
else => null,
};
}
const SignParamsResult = union(enum) {
ok: rsa.SignParams,
err: ck.CK_RV,
};
fn parsePss(pMechanism: *ck.CK_MECHANISM, digest_hash: rsa.Hash) SignParamsResult {
var params: rsa.SignParams = .{
.scheme = .pss,
.digest = digest_hash,
.pss_hash = if (digest_hash == .none) .sha256 else digest_hash,
};
const p = pMechanism.pParameter orelse {
if (digest_hash == .none) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
return .{ .ok = params };
};
if (pMechanism.ulParameterLen != @sizeOf(ck.CK_RSA_PKCS_PSS_PARAMS)) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
const pp: *const ck.CK_RSA_PKCS_PSS_PARAMS = @ptrCast(@alignCast(p));
const h = rsa.Hash.fromMech(pp.hashAlg) orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (digest_hash != .none and h != digest_hash) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (mgfHash(pp.mgf) != h) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (pp.sLen > rsa.max_modulus_bytes) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
params.pss_hash = h;
params.salt_len = @intCast(pp.sLen);
return .{ .ok = params };
}
fn rsaSignParams(pMechanism: *ck.CK_MECHANISM) SignParamsResult {
return switch (pMechanism.mechanism) {
ck.CKM_RSA_PKCS => .{ .ok = .{ .scheme = .pkcs1, .digest = .none } },
ck.CKM_SHA256_RSA_PKCS => .{ .ok = .{ .scheme = .pkcs1, .digest = .sha256 } },
ck.CKM_RSA_PKCS_PSS => parsePss(pMechanism, .none),
ck.CKM_SHA256_RSA_PKCS_PSS => parsePss(pMechanism, .sha256),
else => .{ .err = ck.CKR_MECHANISM_INVALID },
};
}
const CryptParamsResult = union(enum) {
ok: rsa.CryptParams,
err: ck.CK_RV,
};
fn rsaCryptParams(pMechanism: *ck.CK_MECHANISM) CryptParamsResult {
return switch (pMechanism.mechanism) {
ck.CKM_RSA_PKCS => .{ .ok = .{ .scheme = .pkcs1 } },
ck.CKM_RSA_PKCS_OAEP => blk: {
const p = pMechanism.pParameter orelse break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (pMechanism.ulParameterLen != @sizeOf(ck.CK_RSA_PKCS_OAEP_PARAMS)) break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
const op: *const ck.CK_RSA_PKCS_OAEP_PARAMS = @ptrCast(@alignCast(p));
const h = rsa.Hash.fromMech(op.hashAlg) orelse break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (mgfHash(op.mgf) != h) break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (op.ulSourceDataLen != 0) break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
break :blk .{ .ok = .{ .scheme = .oaep, .oaep_hash = h } };
},
else => .{ .err = ck.CKR_MECHANISM_INVALID },
};
}
fn signLen(op: *const session.SignOp) ck.CK_ULONG {
return switch (op.*) {
.mac => |*m| @intCast(m.macLen()),
.ec => |*e| @intCast(e.sigLen()),
.rsa => |*r| @intCast(r.sig_len),
};
}
fn emitSign(inst: *state.Instance, sess: *session.Session, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) ck.CK_RV {
const op = &sess.sign_op.?;
const slen = signLen(op);
if (pSignature == null) {
pulSignatureLen.* = slen;
return ck.CKR_OK;
}
if (pulSignatureLen.* < slen) {
pulSignatureLen.* = slen;
return ck.CKR_BUFFER_TOO_SMALL;
}
const out = pSignature.?[0..@intCast(slen)];
switch (op.*) {
.mac => |*m| m.finalInto(out),
.ec => |*e| _ = e.finalInto(inst.io(), out) catch {
sess.endSign();
return ck.CKR_FUNCTION_FAILED;
},
.rsa => return ck.CKR_FUNCTION_FAILED,
}
pulSignatureLen.* = slen;
sess.endSign();
return ck.CKR_OK;
}
fn finalizeVerify(sess: *session.Session, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG) ck.CK_RV {
const sig = pSignature[0..@intCast(ulSignatureLen)];
const rv = switch (sess.verify_op.?) {
.mac => |*m| blk: {
const mlen: ck.CK_ULONG = @intCast(m.macLen());
var computed: [mac.max_mac_len]u8 = undefined;
m.finalInto(computed[0..@intCast(mlen)]);
if (ulSignatureLen != mlen) break :blk ck.CKR_SIGNATURE_LEN_RANGE;
if (!ctEql(computed[0..@intCast(mlen)], sig)) break :blk ck.CKR_SIGNATURE_INVALID;
break :blk ck.CKR_OK;
},
.ec => |*e| switch (e.finalVerify(sig)) {
.ok => ck.CKR_OK,
.invalid => ck.CKR_SIGNATURE_INVALID,
.len_range => ck.CKR_SIGNATURE_LEN_RANGE,
},
.rsa => ck.CKR_FUNCTION_FAILED,
};
sess.endVerify();
return rv;
}
fn signInitOp(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, pMechanism: *ck.CK_MECHANISM) union(enum) { ok: session.SignOp, err: ck.CK_RV } {
const mech = pMechanism.mechanism;
if (mac.macLenOf(mech) != null) {
const val = switch (secretKeyValue(inst, hKey, ck.CKA_SIGN)) {
.err => |rv| return .{ .err = rv },
.ok => |v| v,
};
return .{ .ok = .{ .mac = mac.Mac.init(mech, val) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
}
if (ecdsa.hashModeOf(mech) != null) {
const k = switch (ecPrivateKey(inst, hKey)) {
.err => |rv| return .{ .err = rv },
.ok => |v| v,
};
return .{ .ok = .{ .ec = ecdsa.SignState.init(k.curve, mech, k.material) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
}
if (isRsaSignMech(mech)) {
const params = switch (rsaSignParams(pMechanism)) {
.err => |rv| return .{ .err = rv },
.ok => |p| p,
};
const pc = switch (rsaPrivateComponents(inst, hKey, ck.CKA_SIGN)) {
.err => |rv| return .{ .err = rv },
.ok => |c| c,
};
return .{ .ok = .{ .rsa = .{ .key = hKey, .params = params, .sig_len = pc.n.len } } };
}
return .{ .err = ck.CKR_MECHANISM_INVALID };
}
fn verifyInitOp(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, pMechanism: *ck.CK_MECHANISM) union(enum) { ok: session.VerifyOp, err: ck.CK_RV } {
const mech = pMechanism.mechanism;
if (mac.macLenOf(mech) != null) {
const val = switch (secretKeyValue(inst, hKey, ck.CKA_VERIFY)) {
.err => |rv| return .{ .err = rv },
.ok => |v| v,
};
return .{ .ok = .{ .mac = mac.Mac.init(mech, val) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
}
if (ecdsa.hashModeOf(mech) != null) {
const k = switch (ecPublicKey(inst, hKey)) {
.err => |rv| return .{ .err = rv },
.ok => |v| v,
};
return .{ .ok = .{ .ec = ecdsa.VerifyState.init(k.curve, mech, k.material) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
}
if (isRsaSignMech(mech)) {
const params = switch (rsaSignParams(pMechanism)) {
.err => |rv| return .{ .err = rv },
.ok => |p| p,
};
const pc = switch (rsaPublicComponents(inst, hKey, ck.CKA_VERIFY)) {
.err => |rv| return .{ .err = rv },
.ok => |c| c,
};
return .{ .ok = .{ .rsa = .{ .key = hKey, .params = params, .sig_len = pc.n.len } } };
}
return .{ .err = ck.CKR_MECHANISM_INVALID };
}
fn mapCipherErr(e: cipher.Error) ck.CK_RV {
return switch (e) {
cipher.Error.DataLenRange => ck.CKR_DATA_LEN_RANGE,
cipher.Error.EncryptedDataLenRange => ck.CKR_ENCRYPTED_DATA_LEN_RANGE,
cipher.Error.EncryptedDataInvalid => ck.CKR_ENCRYPTED_DATA_INVALID,
cipher.Error.KeySize => ck.CKR_KEY_SIZE_RANGE,
cipher.Error.AadTooLarge => ck.CKR_ARGUMENTS_BAD,
cipher.Error.IvInvalid => ck.CKR_MECHANISM_PARAM_INVALID,
};
}
const CipherInit = union(enum) {
ok: cipher.Cipher,
err: ck.CK_RV,
};
fn buildCipher(inst: *state.Instance, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE, encrypt: bool, usage: ck.CK_ATTRIBUTE_TYPE) CipherInit {
const mode = cipher.modeOf(pMechanism.mechanism) orelse return .{ .err = ck.CKR_MECHANISM_INVALID };
const val = switch (secretKeyValue(inst, hKey, usage)) {
.err => |rv| return .{ .err = rv },
.ok => |v| v,
};
if (!cipher.validKeyLen(val.len)) return .{ .err = ck.CKR_KEY_SIZE_RANGE };
var c: cipher.Cipher = .{ .mode = mode, .encrypt = encrypt, .key_len = @intCast(val.len) };
@memcpy(c.key_buf[0..val.len], val);
switch (mode) {
.cbc, .cbc_pad => {
const p = pMechanism.pParameter orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (pMechanism.ulParameterLen != config.aes_block_len) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
@memcpy(&c.chain, @as([*]const u8, @ptrCast(p))[0..config.aes_block_len]);
},
.gcm => {
const p = pMechanism.pParameter orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (pMechanism.ulParameterLen != @sizeOf(ck.CK_GCM_PARAMS)) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
const gp: *const ck.CK_GCM_PARAMS = @ptrCast(@alignCast(p));
if (gp.ulIvLen != config.gcm_iv_len) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (gp.ulIvBits != 0 and gp.ulIvBits != config.gcm_iv_bits) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
if (gp.ulTagBits != config.gcm_tag_bits) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
const ivp = gp.pIv orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
@memcpy(&c.iv, ivp[0..config.gcm_iv_len]);
const aad_len: usize = @intCast(gp.ulAADLen);
if (aad_len > config.max_gcm_aad_len) return .{ .err = ck.CKR_ARGUMENTS_BAD };
if (aad_len > 0) {
const ap = gp.pAAD orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
@memcpy(c.aad_buf[0..aad_len], ap[0..aad_len]);
}
c.aad_len = aad_len;
},
}
return .{ .ok = c };
}
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());
if (pDigest == null) {
pulDigestLen.* = dlen;
return ck.CKR_OK;
}
if (pulDigestLen.* < dlen) {
pulDigestLen.* = dlen;
return ck.CKR_BUFFER_TOO_SMALL;
}
op.finalInto(pDigest.?[0..@intCast(dlen)]);
pulDigestLen.* = dlen;
sess.endDigest();
return ck.CKR_OK;
}
fn isRsaCryptMech(mech: ck.CK_MECHANISM_TYPE) bool {
return mech == ck.CKM_RSA_PKCS or mech == ck.CKM_RSA_PKCS_OAEP;
}
const RsaCryptResult = union(enum) {
ok: session.RsaCrypt,
err: ck.CK_RV,
};
fn rsaCryptInit(inst: *state.Instance, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE, private: bool, usage: ck.CK_ATTRIBUTE_TYPE) RsaCryptResult {
const params = switch (rsaCryptParams(pMechanism)) {
.err => |rv| return .{ .err = rv },
.ok => |p| p,
};
const mod_len = if (private) switch (rsaPrivateComponents(inst, hKey, usage)) {
.err => |rv| return .{ .err = rv },
.ok => |c| c.n.len,
} else switch (rsaPublicComponents(inst, hKey, usage)) {
.err => |rv| return .{ .err = rv },
.ok => |c| c.n.len,
};
return .{ .ok = .{ .key = hKey, .params = params, .out_len = mod_len } };
}
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.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) {
const c = switch (buildCipher(inst, pMechanism, hKey, true, ck.CKA_ENCRYPT)) {
.err => |rv| return rv,
.ok => |built| built,
};
sess.encrypt_op = if (c.mode == .gcm) .{ .gcm = .{ .cipher = c } } else .{ .aes = c };
return ck.CKR_OK;
}
if (isRsaCryptMech(pMechanism.mechanism)) {
sess.encrypt_op = .{ .rsa = switch (rsaCryptInit(inst, pMechanism, hKey, false, ck.CKA_ENCRYPT)) {
.err => |rv| return rv,
.ok => |o| o,
} };
return ck.CKR_OK;
}
return ck.CKR_MECHANISM_INVALID;
}
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.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 in = part(pData, ulDataLen);
switch (op.*) {
.aes => |*c| {
const need: ck.CK_ULONG = @intCast(cipher.encryptOutLen(c.mode, in.len));
if (pEncryptedData == null) {
pulEncryptedDataLen.* = need;
return ck.CKR_OK;
}
if (pulEncryptedDataLen.* < need) {
pulEncryptedDataLen.* = need;
return ck.CKR_BUFFER_TOO_SMALL;
}
const out = pEncryptedData.?[0..@intCast(need)];
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(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| {
const need: ck.CK_ULONG = @intCast(r.out_len);
if (pEncryptedData == null) {
pulEncryptedDataLen.* = need;
return ck.CKR_OK;
}
if (pulEncryptedDataLen.* < need) {
pulEncryptedDataLen.* = need;
return ck.CKR_BUFFER_TOO_SMALL;
}
const pc = switch (rsaPublicComponents(inst, r.key, ck.CKA_ENCRYPT)) {
.err => |rv| {
sess.endEncrypt(inst.allocator());
return rv;
},
.ok => |c| c,
};
const n = rsa.encrypt(pc, r.params, in, pEncryptedData.?[0..@intCast(need)]) catch {
sess.endEncrypt(inst.allocator());
return ck.CKR_DATA_LEN_RANGE;
};
pulEncryptedDataLen.* = @intCast(n);
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.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 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;
}
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.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;
switch (op.*) {
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
.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;
},
}
}
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.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) {
const c = switch (buildCipher(inst, pMechanism, hKey, false, ck.CKA_DECRYPT)) {
.err => |rv| return rv,
.ok => |built| built,
};
sess.decrypt_op = if (c.mode == .gcm) .{ .gcm = .{ .cipher = c } } else .{ .aes = c };
return ck.CKR_OK;
}
if (isRsaCryptMech(pMechanism.mechanism)) {
sess.decrypt_op = .{ .rsa = switch (rsaCryptInit(inst, pMechanism, hKey, true, ck.CKA_DECRYPT)) {
.err => |rv| return rv,
.ok => |o| o,
} };
return ck.CKR_OK;
}
return ck.CKR_MECHANISM_INVALID;
}
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.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 in = part(pEncryptedData, ulEncryptedDataLen);
switch (op.*) {
.aes => |*c| {
const need: ck.CK_ULONG = @intCast(cipher.decryptOutLen(c.mode, in.len));
if (pData == null) {
pulDataLen.* = need;
return ck.CKR_OK;
}
if (pulDataLen.* < need) {
pulDataLen.* = need;
return ck.CKR_BUFFER_TOO_SMALL;
}
const out = pData.?[0..@intCast(need)];
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(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| {
const need: ck.CK_ULONG = @intCast(r.out_len);
if (pData == null) {
pulDataLen.* = need;
return ck.CKR_OK;
}
if (pulDataLen.* < need) {
pulDataLen.* = need;
return ck.CKR_BUFFER_TOO_SMALL;
}
const sc = switch (rsaPrivateComponents(inst, r.key, ck.CKA_DECRYPT)) {
.err => |rv| {
sess.endDecrypt(inst.allocator());
return rv;
},
.ok => |c| c,
};
const n = rsa.decrypt(sc, r.params, in, pData.?[0..@intCast(need)]) catch {
sess.endDecrypt(inst.allocator());
return ck.CKR_ENCRYPTED_DATA_INVALID;
};
pulDataLen.* = @intCast(n);
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.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 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 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.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;
switch (op.*) {
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
.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;
},
}
}
pub fn C_DigestInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM) 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) return ck.CKR_OPERATION_ACTIVE;
sess.digest_op = digest.Hasher.init(pMechanism.mechanism) orelse return ck.CKR_MECHANISM_INVALID;
return ck.CKR_OK;
}
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.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;
const dlen: ck.CK_ULONG = @intCast(sess.digest_op.?.digestLen());
if (pDigest == null) {
pulDigestLen.* = dlen;
return ck.CKR_OK;
}
if (pulDigestLen.* < dlen) {
pulDigestLen.* = dlen;
return ck.CKR_BUFFER_TOO_SMALL;
}
sess.digest_op.?.update(part(pData, ulDataLen));
return emitDigest(sess, pDigest, pulDigestLen);
}
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.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;
sess.digest_op.?.update(part(pPart, ulPartLen));
return ck.CKR_OK;
}
pub fn C_DigestKey(hSession: ck.CK_SESSION_HANDLE, 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.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
const obj = inst.objects.getPtr(hKey) orelse return ck.CKR_KEY_HANDLE_INVALID;
if (!object_store.visible(obj, inst.logged_in)) return ck.CKR_KEY_HANDLE_INVALID;
if (objectClass(obj) != ck.CKO_SECRET_KEY) return ck.CKR_KEY_INDIGESTIBLE;
const sa = obj.findPtr(ck.CKA_VALUE) orelse return ck.CKR_KEY_INDIGESTIBLE;
if (sa.sealed) return ck.CKR_USER_NOT_LOGGED_IN;
sess.digest_op.?.update(sa.value);
return ck.CKR_OK;
}
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.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;
return emitDigest(sess, pDigest, pulDigestLen);
}
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.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;
sess.sign_op = switch (signInitOp(inst, hKey, pMechanism)) {
.err => |rv| return rv,
.ok => |op| op,
};
return ck.CKR_OK;
}
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.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;
const slen = signLen(&sess.sign_op.?);
if (pSignature == null) {
pulSignatureLen.* = slen;
return ck.CKR_OK;
}
if (pulSignatureLen.* < slen) {
pulSignatureLen.* = slen;
return ck.CKR_BUFFER_TOO_SMALL;
}
switch (sess.sign_op.?) {
.rsa => |op| {
const out = pSignature.?[0..@intCast(slen)];
const sc = switch (rsaPrivateComponents(inst, op.key, ck.CKA_SIGN)) {
.err => |rv| {
sess.endSign();
return rv;
},
.ok => |c| c,
};
const n = rsa.sign(sc, op.params, part(pData, ulDataLen), out) catch {
sess.endSign();
return ck.CKR_FUNCTION_FAILED;
};
pulSignatureLen.* = @intCast(n);
sess.endSign();
return ck.CKR_OK;
},
else => {
sess.sign_op.?.update(part(pData, ulDataLen));
return emitSign(inst, sess, pSignature, pulSignatureLen);
},
}
}
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.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;
switch (sess.sign_op.?) {
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
else => {},
}
sess.sign_op.?.update(part(pPart, ulPartLen));
return ck.CKR_OK;
}
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.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;
switch (sess.sign_op.?) {
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
else => {},
}
return emitSign(inst, sess, pSignature, pulSignatureLen);
}
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(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.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;
sess.verify_op = switch (verifyInitOp(inst, hKey, pMechanism)) {
.err => |rv| return rv,
.ok => |op| op,
};
return ck.CKR_OK;
}
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.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;
switch (sess.verify_op.?) {
.rsa => |op| {
const data = part(pData, ulDataLen);
sess.endVerify();
if (ulSignatureLen != op.sig_len) return ck.CKR_SIGNATURE_LEN_RANGE;
const pc = switch (rsaPublicComponents(inst, op.key, ck.CKA_VERIFY)) {
.err => |rv| return rv,
.ok => |c| c,
};
const r = rsa.verify(pc, op.params, data, pSignature[0..@intCast(ulSignatureLen)]) catch return ck.CKR_FUNCTION_FAILED;
return switch (r) {
.ok => ck.CKR_OK,
.invalid => ck.CKR_SIGNATURE_INVALID,
};
},
else => {
sess.verify_op.?.update(part(pData, ulDataLen));
return finalizeVerify(sess, pSignature, ulSignatureLen);
},
}
}
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.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;
switch (sess.verify_op.?) {
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
else => {},
}
sess.verify_op.?.update(part(pPart, ulPartLen));
return ck.CKR_OK;
}
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.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;
switch (sess.verify_op.?) {
.rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
else => {},
}
return finalizeVerify(sess, pSignature, ulSignatureLen);
}
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(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(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(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(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(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;
}