# pkcs11js-modified

> A Node.js implementation of the PKCS#11 2.3 interface

Latest version **1.0.0** (published 2023-08-01) · MIT license · 0 weekly downloads

## Install

```sh
npm install pkcs11js-modified
pnpm add pkcs11js-modified
yarn add pkcs11js-modified
bun add pkcs11js-modified
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.0 |
| Published | 2023-08-01 |
| First published | 2023-08-01 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 1 |
| Unpacked size | 372.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | yes |
| Author | PeculiarVentures |
| Maintainers | ghadifoo |
| Keywords | pkcs11, rsa, ecdsa, aes, crypto, smartcard, token, nss, softhsm2 |

## Links

- npm: https://www.npmjs.com/package/pkcs11js-modified
- Repository: https://github.com/PeculiarVentures/pkcs11js
- Homepage: https://github.com/PeculiarVentures/pkcs11js#readme
- Issues: https://github.com/PeculiarVentures/pkcs11js/issues
- npm.io page: https://npm.io/package/pkcs11js-modified

## Dependencies (1)

- [nan](https://npm.io/package/nan.md) ^2.10.0

## Alternatives

- [@gemini-wallet/core](https://npm.io/package/@gemini-wallet/core.md) — 515.6K weekly downloads
- [utility](https://npm.io/package/utility.md) — 416.6K weekly downloads
- [@primno/dpapi](https://npm.io/package/@primno/dpapi.md) — 7.2K weekly downloads
- [pi-readseek](https://npm.io/package/pi-readseek.md) — 3.7K weekly downloads
- [@emilia-protocol/verify](https://npm.io/package/@emilia-protocol/verify.md) — 1.1K weekly downloads

## Recent versions

- 1.0.0 (latest) — 2023-08-01

## README

# LUNA - PKCS11-JS
NOTE:  npm run build, the project and add it to npm repo to be used by PKCS11 HSM Clients.
This requires a SafeNet Luna HSM with BIP32 support.

## Init LUNA HSM Client
#### LUNACM
```bash
> source setenv
> lunacm
> > slot set -slot 3
> > partition init -label default
> > role login -name partition so
> > slot set -slot 3
> > role init -name crypto officer
> > role logout

> > slot set -slot 3
> > role login -name crypto officer
> > slot set -slot 3
> > role changepw -name crypto officer
```

#### Graphene
```bash
> graphene
> > module load -l /home/opc/lunacloudhsmclient/libs/64/libCryptoki2.so -n SoftHSMv2.0
> > slot open --slot 0 -p 1234567890
> > test sign -it 1 --slot 0x3
```

### BIP32 Master and Child Key Pair Derivation
Performing BIP32 master and child key pair derivations.

```javascript
var lib = "/home/opc/lunacloudhsmclient/libs/64/libCryptoki2.so";
var pin = "12345678";
var pkcs11js = require("pkcs11js");
var sha256 = require("sha256");
var BN = require('bn.js');

var pkcs11 = new pkcs11js.PKCS11();
var slot = 0;

var secp256k1_N = new BN("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);

function dbg(...args) {
    let vargs = ['⏰', new Date().toLocaleString(), '→ '];
    vargs.push.apply(vargs, args);
    console.log.apply(null, vargs);
}

function generateSeed(session) {
    var seedTemplate = [
        {type: pkcs11js.CKA_KEY_TYPE, value: pkcs11js.CKK_GENERIC_SECRET},
        {type: pkcs11js.CKA_TOKEN, value: false},
        {type: pkcs11js.CKA_DERIVE, value: true},
        {type: pkcs11js.CKA_PRIVATE, value: true},
        {type: pkcs11js.CKA_EXTRACTABLE, value: false},
        {type: pkcs11js.CKA_MODIFIABLE, value: false},
        {type: pkcs11js.CKA_VALUE_LEN, value: 32}
    ];

    return pkcs11.C_GenerateKey(session, { mechanism: pkcs11js.CKM_GENERIC_SECRET_KEY_GEN }, seedTemplate);
}

function deriveMaster(session, seed) {
    var publicKeyTemplate = [
                        { type: pkcs11js.CKA_TOKEN, value: false },
                        { type: pkcs11js.CKA_PRIVATE, value: true },
                        { type: pkcs11js.CKA_VERIFY, value: true },
                        { type: pkcs11js.CKA_DERIVE, value: true },
                        { type: pkcs11js.CKA_MODIFIABLE, value: false },
    ];
    var privateKeyTemplate = [
                        { type: pkcs11js.CKA_TOKEN, value: false },
                        { type: pkcs11js.CKA_PRIVATE, value: true },
                        { type: pkcs11js.CKA_SIGN, value: true },
                        { type: pkcs11js.CKA_DERIVE, value: true },
                        { type: pkcs11js.CKA_MODIFIABLE, value: false },
                        { type: pkcs11js.CKA_EXTRACTABLE, value: false },
    ];

    return pkcs11.DeriveBIP32Master(session, seed, publicKeyTemplate, privateKeyTemplate);
}

function deriveChild(session, masterPrivate, path) {
    var publicKeyTemplate = [
                        { type: pkcs11js.CKA_TOKEN, value: false },
                        { type: pkcs11js.CKA_PRIVATE, value: true },
                        { type: pkcs11js.CKA_VERIFY, value: true },
                        { type: pkcs11js.CKA_DERIVE, value: false },
                        { type: pkcs11js.CKA_MODIFIABLE, value: false },
    ];
    var privateKeyTemplate = [
                        { type: pkcs11js.CKA_TOKEN, value: false },
                        { type: pkcs11js.CKA_PRIVATE, value: true },
                        { type: pkcs11js.CKA_SIGN, value: true },
                        { type: pkcs11js.CKA_DERIVE, value: false },
                        { type: pkcs11js.CKA_MODIFIABLE, value: false },
                        { type: pkcs11js.CKA_EXTRACTABLE, value: false },
    ];

    return pkcs11.DeriveBIP32Child(session, masterPrivate, publicKeyTemplate, privateKeyTemplate, path);
}

function sign(session, privateKey, data) {
    var mech = {
        mechanism: pkcs11js.CKM_ECDSA,
    };
    pkcs11.C_SignInit(session, mech, privateKey)
    return pkcs11.C_Sign(session, data, Buffer.alloc(64));
}

function verify(session, publicKey, data, signature) {
    var mech = {
        mechanism: pkcs11js.CKM_ECDSA,
    };
    pkcs11.C_VerifyInit(session, mech, publicKey)
    return pkcs11.C_Verify(session, data, signature)
}

function signatureLowS(sig) {
    var r = new BN(sig.slice(0, sig.length / 2).toString('hex'), 16);
    var s = new BN(sig.slice(sig.length / 2).toString('hex'), 16);
    var halfOrder =  secp256k1_N.shrn(1);
    if (s.cmp(halfOrder) == 1) {
        s = secp256k1_N.sub(s);
    }
    return Buffer.concat([r.toBuffer("be", 32), s.toBuffer("be", 32)]);
}


pkcs11.load(lib);
pkcs11.C_Initialize();
dbg('C_Initialize ✅');
var session;

try {

    var slots = pkcs11.C_GetSlotList(true);
    slot = slots[slot];
    dbg('C_GetSlotList ✅');

    session = pkcs11.C_OpenSession(slot, pkcs11js.CKF_RW_SESSION | pkcs11js.CKF_SERIAL_SESSION);
    dbg('C_OpenSession ✅');

    pkcs11.C_Login(session, 1, pin);
    dbg('C_Login ✅');

    var seed = generateSeed(session);
    dbg('generateSeed ✅');

    var master = deriveMaster(session, seed);
    dbg('deriveMaster ✅');

    /* 
        https://thalesdocs.com/gphsm/luna/7/docs/network/Content/sdk/extensions/BIP32.htm#
    
        The path generates a key pair that follows the BIP44 convention and can be used to receive BTC.
        This mechanism can be used to generate keys that are several levels deep in the key hierarchy.
        The path of the key is specified with pulPath and ulPathLen.
        The path is an array of 32-bit unsigned integers (key indices).
        The highest bit (0x80000000) is used to indicate a hardened key.
        So a path value for a normal key must be <= 0x7FFFFFFF (decimal 2147483647).
        For a hardened key, 0x80000000 <= path value <= 0xFFFFFFFF.
        The path is relative to the input key. 
        
        BIP32: If the key index is greater than or equal to 2^31 (0x80000000 in hexadecimal), the derived key is considered hardened.

        For example, if the path is [5, 1, 4] and the path of the input key is m/0 then the resulting path is m/0/5/1/4.    

        CK_ULONG path[] = { 
                CKF_BIP32_HARDENED | CKG_BIP44_PURPOSE, 
                CKF_BIP32_HARDENED | CKG_BIP44_COIN_TYPE_BTC, 
                CKF_BIP32_HARDENED | 1, 
                CKG_BIP32_EXTERNAL_CHAIN,
                0 
        };

        BIP32 Serialization Format
            Extended public and private keys are serialized as follows:
            >4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, 0x04358394 private)
            >1 byte: depth: 0x00 for master nodes, 0x01 for level-1 derived keys, ....
            >4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
            >4 bytes: child number (index) – 32-bit unsigned integer with most significant byte first (0x00000000 if master key)
            >32 bytes: the chain code
            >33 bytes: the public key or private key data

        This 78 byte structure is encoded like other Bitcoin data in Base58, 
        by first adding 32 checksum bits (derived from the double SHA-256 checksum), 
        and then converting to the Base58 representation. 
        This results in a Base58-encoded string of up to CKG_BIP32_MAX_SERIALIZED_LEN characters. 
        Because of the choice of the version bytes:
        The Base58 representation will start with "xprv" or "xpub" on mainnet, "tprv" or "tpub" on testnet.

        CKF_BIP32_HARDENED: 0x80000000
        CKG_BIP44_PURPOSE:  0x0000002C (44)
        
        CKG_BIP44_COIN_TYPE_BTC: 0x00000000 (0)
        CKG_BIP44_COIN_TYPE_ETC: 0x0000003C (60)
    */

    // m / purpose' / coin_type' / account' / change / address_index
    // Bitcoin has a 0' coin type, Ethereum has a 60 
    var path = [0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0];

    var child = deriveChild(session, master['privateKey'], path);
    dbg('deriveChild ✅');

    var hash = sha256("BIP32 Message")
    var data = new Buffer(hash, "hex");
    dbg({hash});

    var signature = sign(session, master['privateKey'], data);
    signature = signatureLowS(signature);
    dbg({signature});

    verify(session, master['publicKey'], data, signature);
    dbg('verification done ✅');
}
catch(e){
    console.error(' ❌ ERROR ❌ \n', e);
}
finally {
    if(session) {
        pkcs11.C_Logout(session);
        dbg('C_Logout ✅');
        pkcs11.C_CloseSession(session);
        dbg('C_CloseSession ✅');
    }
    pkcs11.C_Finalize();
    dbg('C_Finalize ✅');
}
```

### Outputs:

<center>![image.png](./image.png)</center>

---
_Source: https://npm.io/package/pkcs11js-modified · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
