Skip to content

Commit 24d67d8

Browse files
committed
feat: add support for Date attributes in PKCS11 class
1 parent 8275db1 commit 24d67d8

File tree

4 files changed

+275
-2
lines changed

4 files changed

+275
-2
lines changed

index.d.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ declare module "pkcs11js" {
229229
/**
230230
* The value of the attribute
231231
*/
232-
value?: number | boolean | string | Buffer;
232+
value?: number | boolean | string | Buffer | Date;
233233
}
234234

235235
/**
@@ -472,6 +472,12 @@ declare module "pkcs11js" {
472472
*/
473473
public libPath: string;
474474

475+
/**
476+
* Creates an instance of PKCS11
477+
* @param libPath The path to PKCS#11 library
478+
*/
479+
constructor(libPath?: string);
480+
475481
/**
476482
* Loads dynamic library with PKCS#11 interface
477483
* @param path The path to PKCS#11 library

index.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// @ts-check
21
const pkcs11 = require("./build/Release/pkcs11.node");
32
const util = require("node:util");
43

@@ -249,6 +248,18 @@ function modify(obj) {
249248
}
250249
}
251250

251+
function processAttributes(attrs) {
252+
if (attrs && Array.isArray(attrs)) {
253+
for (const attr of attrs) {
254+
if (attr.type === pkcs11.CKA_START_DATE || attr.type === pkcs11.CKA_END_DATE) {
255+
if (attr.value instanceof Date) {
256+
attr.value = Buffer.from(attr.value.toISOString().slice(0, 10).replace(/-/g, ""));
257+
}
258+
}
259+
}
260+
}
261+
}
262+
252263
class PKCS11 extends pkcs11.PKCS11 {
253264
constructor(library) {
254265
super();
@@ -264,6 +275,21 @@ class PKCS11 extends pkcs11.PKCS11 {
264275
super.load(library);
265276
this.libPath = library;
266277
}
278+
279+
C_SetAttributeValue(session, object, attrs) {
280+
processAttributes(attrs);
281+
return super.C_SetAttributeValue(session, object, attrs);
282+
}
283+
284+
C_CreateObject(session, attrs) {
285+
processAttributes(attrs);
286+
return super.C_CreateObject(session, attrs);
287+
}
288+
289+
C_CopyObject(session, object, attrs) {
290+
processAttributes(attrs);
291+
return super.C_CopyObject(session, object, attrs);
292+
}
267293
}
268294

269295
module.exports = { ...pkcs11, PKCS11 };

src/pkcs11.cpp

+90
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,34 @@ bool get_args_mechanism(napi_env env, napi_value *arg, size_t argc, size_t index
587587
return nullptr; \
588588
}
589589

590+
/**
591+
* @brief Get the attributes object from the argument list at a specified index.
592+
*
593+
* @param env The N-API environment.
594+
* @param attr The CK_ATTRIBUTE to store the retrieved attributes.
595+
* @param data The data to store the retrieved attributes.
596+
* @param length The length of the data.
597+
*/
598+
bool processDate(napi_env env, CK_ATTRIBUTE *attr, const char *data, size_t length)
599+
{
600+
if (length < 8)
601+
{
602+
THROW_TYPE_ERRORF(false, "Attribute with type 0x%08lX is not convertible to CK_DATE. The length of the data should be at least 8 bytes.", attr->type);
603+
}
604+
605+
CK_DATE *datePtr = (CK_DATE *)attr->pValue;
606+
char year[5], month[3], day[3];
607+
strncpy(year, data, 4);
608+
strncpy(month, data + 4, 2);
609+
strncpy(day, data + 6, 2);
610+
year[4] = month[2] = day[2] = '\0';
611+
strncpy((char *)datePtr->year, year, 4);
612+
strncpy((char *)datePtr->month, month, 2);
613+
strncpy((char *)datePtr->day, day, 2);
614+
615+
return true;
616+
}
617+
590618
/**
591619
* @brief Get the attributes object from the argument list at a specified index.
592620
*
@@ -670,6 +698,39 @@ bool get_args_attributes(napi_env env, napi_value *arg, size_t argc, size_t inde
670698
napi_get_value_uint32(env, typeValue, &type);
671699
attr->type = (CK_ATTRIBUTE_TYPE)type;
672700

701+
if (attr->type == CKA_START_DATE || attr->type == CKA_END_DATE)
702+
{
703+
if (valueValueType == napi_string)
704+
{
705+
size_t length;
706+
napi_get_value_string_utf8(env, valueValue, nullptr, 0, &length);
707+
attrs->allocValue(i, sizeof(CK_DATE));
708+
if (processDate(env, attr, (char *)valueValue, length) == false)
709+
{
710+
return false;
711+
}
712+
}
713+
else if (valueIsBuffer)
714+
{
715+
void *data;
716+
size_t length;
717+
napi_get_buffer_info(env, valueValue, &data, &length);
718+
attrs->allocValue(i, sizeof(CK_DATE));
719+
if (processDate(env, attr, (char *)data, length) == false)
720+
{
721+
return false;
722+
}
723+
}
724+
else if (valueValueType == napi_undefined || valueValueType == napi_null)
725+
{
726+
// do nothing
727+
}
728+
else
729+
{
730+
THROW_TYPE_ERRORF(false, "Attribute with type 0x%08lX is not convertible to CK_DATE. Should be a String, Buffer or Date", attr->type);
731+
}
732+
}
733+
673734
if (valueValueType == napi_undefined || valueValueType == napi_null)
674735
{
675736
attrs->allocValue(i, 0);
@@ -1709,6 +1770,35 @@ class Pkcs11
17091770
// create Buffer for value
17101771
napi_value value;
17111772
CK_ATTRIBUTE_PTR attr = &attrs.attributes[i];
1773+
1774+
if (attr->ulValueLen == CK_UNAVAILABLE_INFORMATION)
1775+
{
1776+
napi_get_undefined(env, &value);
1777+
napi_set_named_property(env, element, "value", value);
1778+
continue;
1779+
}
1780+
1781+
if (attr->type == CKA_START_DATE || attr->type == CKA_END_DATE)
1782+
{
1783+
if (attr->ulValueLen != sizeof(CK_DATE))
1784+
{
1785+
THROW_TYPE_ERRORF(nullptr, "Attribute 0x%08lX has wrong length. Should be %lu, but is %lu", attr->type, sizeof(CK_DATE), attr->ulValueLen);
1786+
}
1787+
1788+
char *dateStr = (char *)malloc(9);
1789+
CK_DATE *datePtr = (CK_DATE *)attr->pValue;
1790+
snprintf(dateStr, 9, "%04d%02d%02d",
1791+
atoi((char *)datePtr->year), // year
1792+
atoi((char *)datePtr->month), // month
1793+
atoi((char *)datePtr->day)); // day
1794+
1795+
napi_create_buffer_copy(env, 8, dateStr, nullptr, &value);
1796+
free(dateStr);
1797+
napi_set_named_property(env, element, "value", value);
1798+
1799+
continue;
1800+
}
1801+
17121802
napi_create_buffer_copy(env, attr->ulValueLen, attr->pValue, nullptr, &value);
17131803

17141804
// set value property on element

test/test.js

+151
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ const pin = "12345";
2020

2121
context("PKCS11", () => {
2222
context("load", () => {
23+
it("load via constructor", () => {
24+
const token = new pkcs11.PKCS11(softHsmLib);
25+
assert.strictEqual(token.libPath, softHsmLib);
26+
});
2327
it("correct", () => {
2428
const token = new pkcs11.PKCS11();
2529
token.load(softHsmLib);
30+
assert.strictEqual(token.libPath, softHsmLib);
2631
});
2732
it("throw exception if file does not exist", () => {
2833
const token = new pkcs11.PKCS11();
@@ -384,6 +389,152 @@ context("PKCS11", () => {
384389
assert.strictEqual(attrs[0].value.toString("hex"), Buffer.from("new label").toString("hex"));
385390
});
386391
});
392+
context("CK_DATE attributes", () => {
393+
const certRaw = Buffer.from(
394+
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/" +
395+
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT" +
396+
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow" +
397+
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT" +
398+
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC" +
399+
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF" +
400+
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8" +
401+
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0" +
402+
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA" +
403+
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj" +
404+
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T" +
405+
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG" +
406+
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv" +
407+
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k" +
408+
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw" +
409+
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC" +
410+
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz" +
411+
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu" +
412+
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF" +
413+
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo" +
414+
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/" +
415+
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu" +
416+
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG" +
417+
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6" +
418+
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==", "base64");
419+
420+
const subjectRaw = Buffer.from(
421+
"4A310B300906035504061302555331163014060355040A130D4C65742773204" +
422+
"56E6372797074312330210603550403131A4C6574277320456E637279707420" +
423+
"417574686F7269747920583", "hex");
424+
425+
it("String", () => {
426+
const obj = token.C_CreateObject(session, [
427+
{ type: pkcs11.CKA_CLASS, value: pkcs11.CKO_CERTIFICATE },
428+
{ type: pkcs11.CKA_CERTIFICATE_TYPE, value: pkcs11.CKC_X_509 },
429+
{ type: pkcs11.CKA_CERTIFICATE_CATEGORY, value: 0 },
430+
{ type: pkcs11.CKA_ID, value: Buffer.from("1234") },
431+
{ type: pkcs11.CKA_LABEL, value: Buffer.from("CKA_DATE") },
432+
{ type: pkcs11.CKA_SUBJECT, value: subjectRaw },
433+
{ type: pkcs11.CKA_VALUE, value: certRaw },
434+
{ type: pkcs11.CKA_TOKEN, value: false },
435+
{ type: pkcs11.CKA_START_DATE, value: "20200102" },
436+
{ type: pkcs11.CKA_END_DATE, value: "20200103" },
437+
]);
438+
439+
const attrs = token.C_GetAttributeValue(session, obj, [
440+
{ type: pkcs11.CKA_START_DATE },
441+
{ type: pkcs11.CKA_END_DATE },
442+
]);
443+
assert.strictEqual(attrs.length, 2);
444+
assert.strictEqual(attrs[0].type, pkcs11.CKA_START_DATE);
445+
assert.strictEqual(attrs[0].value.toString(), "20200102");
446+
assert.strictEqual(attrs[1].type, pkcs11.CKA_END_DATE);
447+
assert.strictEqual(attrs[1].value.toString(), "20200103");
448+
});
449+
it("Buffer", () => {
450+
const obj = token.C_CreateObject(session, [
451+
{ type: pkcs11.CKA_CLASS, value: pkcs11.CKO_CERTIFICATE },
452+
{ type: pkcs11.CKA_CERTIFICATE_TYPE, value: pkcs11.CKC_X_509 },
453+
{ type: pkcs11.CKA_CERTIFICATE_CATEGORY, value: 0 },
454+
{ type: pkcs11.CKA_ID, value: Buffer.from("1234") },
455+
{ type: pkcs11.CKA_LABEL, value: Buffer.from("CKA_DATE") },
456+
{ type: pkcs11.CKA_SUBJECT, value: subjectRaw },
457+
{ type: pkcs11.CKA_VALUE, value: certRaw },
458+
{ type: pkcs11.CKA_TOKEN, value: false },
459+
{ type: pkcs11.CKA_START_DATE, value: Buffer.from("20200102") },
460+
{ type: pkcs11.CKA_END_DATE, value: Buffer.from("20200103") },
461+
]);
462+
463+
const attrs = token.C_GetAttributeValue(session, obj, [
464+
{ type: pkcs11.CKA_START_DATE },
465+
{ type: pkcs11.CKA_END_DATE },
466+
]);
467+
assert.strictEqual(attrs.length, 2);
468+
assert.strictEqual(attrs[0].type, pkcs11.CKA_START_DATE);
469+
assert.strictEqual(attrs[0].value.toString(), "20200102");
470+
assert.strictEqual(attrs[1].type, pkcs11.CKA_END_DATE);
471+
assert.strictEqual(attrs[1].value.toString(), "20200103");
472+
});
473+
it("Date", () => {
474+
const obj = token.C_CreateObject(session, [
475+
{ type: pkcs11.CKA_CLASS, value: pkcs11.CKO_CERTIFICATE },
476+
{ type: pkcs11.CKA_CERTIFICATE_TYPE, value: pkcs11.CKC_X_509 },
477+
{ type: pkcs11.CKA_CERTIFICATE_CATEGORY, value: 0 },
478+
{ type: pkcs11.CKA_ID, value: Buffer.from("1234") },
479+
{ type: pkcs11.CKA_LABEL, value: Buffer.from("CKA_DATE") },
480+
{ type: pkcs11.CKA_SUBJECT, value: subjectRaw },
481+
{ type: pkcs11.CKA_VALUE, value: certRaw },
482+
{ type: pkcs11.CKA_TOKEN, value: false },
483+
{ type: pkcs11.CKA_START_DATE, value: new Date("2020-01-02") },
484+
{ type: pkcs11.CKA_END_DATE, value: new Date("2020-01-03") },
485+
]);
486+
487+
const attrs = token.C_GetAttributeValue(session, obj, [
488+
{ type: pkcs11.CKA_START_DATE },
489+
{ type: pkcs11.CKA_END_DATE },
490+
]);
491+
assert.strictEqual(attrs.length, 2);
492+
assert.strictEqual(attrs[0].type, pkcs11.CKA_START_DATE);
493+
assert.strictEqual(attrs[0].value.toString(), "20200102");
494+
assert.strictEqual(attrs[1].type, pkcs11.CKA_END_DATE);
495+
assert.strictEqual(attrs[1].value.toString(), "20200103");
496+
});
497+
it("should throw error if argument is not a String, Buffer or Date", () => {
498+
assert.throws(() => {
499+
token.C_CreateObject(session, [
500+
{ type: pkcs11.CKA_CLASS, value: pkcs11.CKO_CERTIFICATE },
501+
{ type: pkcs11.CKA_CERTIFICATE_TYPE, value: pkcs11.CKC_X_509 },
502+
{ type: pkcs11.CKA_CERTIFICATE_CATEGORY, value: 0 },
503+
{ type: pkcs11.CKA_ID, value: Buffer.from("1234") },
504+
{ type: pkcs11.CKA_LABEL, value: Buffer.from("CKA_DATE") },
505+
{ type: pkcs11.CKA_SUBJECT, value: subjectRaw },
506+
{ type: pkcs11.CKA_VALUE, value: certRaw },
507+
{ type: pkcs11.CKA_TOKEN, value: false },
508+
{ type: pkcs11.CKA_START_DATE, value: 123 },
509+
{ type: pkcs11.CKA_END_DATE, value: 123 },
510+
]);
511+
}, (e) => {
512+
assert.strictEqual(e instanceof TypeError, true);
513+
assert.strictEqual(e.message, "Attribute with type 0x00000110 is not convertible to CK_DATE. Should be a String, Buffer or Date");
514+
return true;
515+
});
516+
});
517+
it("should throw error if argument is less than 8 bytes", () => {
518+
assert.throws(() => {
519+
token.C_CreateObject(session, [
520+
{ type: pkcs11.CKA_CLASS, value: pkcs11.CKO_CERTIFICATE },
521+
{ type: pkcs11.CKA_CERTIFICATE_TYPE, value: pkcs11.CKC_X_509 },
522+
{ type: pkcs11.CKA_CERTIFICATE_CATEGORY, value: 0 },
523+
{ type: pkcs11.CKA_ID, value: Buffer.from("1234") },
524+
{ type: pkcs11.CKA_LABEL, value: Buffer.from("CKA_DATE") },
525+
{ type: pkcs11.CKA_SUBJECT, value: subjectRaw },
526+
{ type: pkcs11.CKA_VALUE, value: certRaw },
527+
{ type: pkcs11.CKA_TOKEN, value: false },
528+
{ type: pkcs11.CKA_START_DATE, value: "123" },
529+
{ type: pkcs11.CKA_END_DATE, value: "123" },
530+
]);
531+
}, (e) => {
532+
assert.strictEqual(e instanceof TypeError, true);
533+
assert.strictEqual(e.message, "Attribute with type 0x00000110 is not convertible to CK_DATE. The length of the data should be at least 8 bytes.");
534+
return true;
535+
});
536+
});
537+
});
387538
});
388539
[
389540
"C_DigestEncryptUpdate",

0 commit comments

Comments
 (0)