PeculiarVentures / graphene

A simple layer for interacting with PKCS #11 / PKCS11 / CryptoKI for Node in TypeScript. (Keywords: Javascript, PKCS#11, Crypto, Smart Card, HSM)
MIT License
169 stars 34 forks source link

ECDSA Derive Key Example Error #110

Closed mfaisaltariq closed 5 years ago

mfaisaltariq commented 5 years ago

The Derive Key example gives the following error and needs to be fixed. Error: CKR_MECHANISM_PARAM_INVALID:113

microshine commented 5 years ago

Can you share code how you derive key?

Here is a test example of key derivation https://github.com/PeculiarVentures/graphene/blob/master/test/ec.ts#L145-L162

mfaisaltariq commented 5 years ago

I'm using the code example in the Readme.md file for the repo. Please find the link below. The test code pattern you shared above and the one given below on the link is some what same.

Derive Key

The error occurs on the following line. var dKey = session.deriveKey(alg, keys.privateKey, template);

microshine commented 5 years ago

Are you using similar params? Error code says that your mechanism params are invalid

var alg = {
  name: "ECDH1_DERIVE",
  params: new graphene.EcdhParams(
    graphene.EcKdf.SHA1,
    null,
    keys.publicKey.getAttribute({pointEC: null}).pointEC)
};
mfaisaltariq commented 5 years ago

Yes, using exactly the same code. Just changed the PIN for my SoftHSM slot.

Can you suggest any other way I can generate the Public Key for the ECDSA and then verify the signature using that key.

I'm not fully aware of the working of this algorithm but if there is some other way to work around it like get the EC Point and generate public key from the x-y coordinate please do share it.

Thank you.

microshine commented 5 years ago

You can try node-webcrypto-p11. It's based on graphene-pk11 and implements WebCrypto interface.

You can use these examples for generating/signing/verifing

node-webcrypto-p11 extends WebCrypto and allows to put/get objects to/from tokens. See KeyStorage and CertStorage

And tests for examples

mfaisaltariq commented 5 years ago

Using this package we have done the following things on RSA

  1. Generate RSA private-public keypair on softHSM2
  2. Get the modulus and exponent of the RSA public key
  3. Generate a pem file using the modulus and exponent for the public key to be distributed among the users for verification of signature.

Now we want to do the above following things using the ECDSA Challenges currently we are facing are given below.

  1. We can generate keypair ECDSA key pair
  2. What is required for ECDSA public key pem generation (like we can generate Public key(pem file) from modulus and exponent for RSA)?

You can try node-webcrypto-p11. It's based on graphene-pk11 and implements WebCrypto interface.

You can use these examples for generating/signing/verifing

node-webcrypto-p11 extends WebCrypto and allows to put/get objects to/from tokens. See KeyStorage and CertStorage

And tests for examples

The above library that you have recommended has the same issue, the examples are broken. Broken Example Tried the above example returned error with 'unexpected token'

rmhrisk commented 5 years ago

The samples work, they have all been tested and used regularly.

Their ability to function is dependent on the underlying P11. If they do not work with your P11
then there is something with your P11 that: A) is needed and not supported B) behaves inconsistently with our understanding of the P11 standard

Instead of working with you to reimplement it is better to debug the issue with the higher level library.

Please file a bug in that repro with details (code, environment, etc) to reproduce the problem along with logs and errors.

mfaisaltariq commented 5 years ago

@rmhrisk I'm relatively new to cryptography so I'm not sure how can I test my p11, I'm using softHSM v2.5.0.

Thank you for the suggestion and I'll definitely open up an issue with all the details on the repo.

I also need to understand that once I have generated a keypair using this example, I suppose it gets stored in the softHSM. This example always generates an keypair on the fly whenever we call it. But lets say that I want to generate the key once and then when I need to sign something, how can I pull up the Private Key from softHSM to sign it. It there some kind of attribute that serves as a unique identifier and can be used to pull it up. Like can we set Label for the keys and then pull them out for signing using their Label.

microshine commented 5 years ago

ECDSA generate key/sign/verify/export public key

const { Crypto } = require("node-webcrypto-p11");

async function main() {
  const crypto = new Crypto({
    library: "/usr/local/lib/softhsm/libsofthsm2.so",
    name: "SoftHSMv2",
    slot: 0,
    pin: "12345",
  });
  const data = Buffer.from("Message to be signed");

  const keys = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, false, ["sign", "verify"]);

  // Sign
  const signature = await crypto.subtle.sign({name: "ECDSA", hash: "SHA-256"}, keys.privateKey, data);
  console.log("Signature:", Buffer.from(signature).toString("hex"));

  // Verify
  const ok = await crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, keys.publicKey, signature, data);
  console.log("Status:", ok);

  // SPKI
  const spki = await crypto.subtle.exportKey("spki", keys.publicKey);
  console.log("SPKI:", Buffer.from(spki).toString("base64"));
}

main().catch(err => console.error(err));

Output

Signature: c62915b263f7e8e8daa28ae6b5fb4637d127c6cce28a2d5076e761c97d433efa686dea787e93ab7a187605b2143da8b364782b66e17c16e11238f5e3c4ff4163
Status: true
SPKI: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER1G/uHT5IqM1sOJZB8mjd2cK4k8f45MR+lsF61yVPhSSgGA6x764+IvEfXRzBLQ/AtHOyVXlphjoO62lFgD61Q==

You can use PKIjs to create a public key PEM

microshine commented 5 years ago

Set/get keys

const { Crypto } = require("node-webcrypto-p11");

const crypto = new Crypto({
  library: "/usr/local/lib/softhsm/libsofthsm2.so",
  name: "SoftHSMv2",
  slot: 0,
  readWrite: true,
  pin: "12345",
});

async function generateKeysAndPushToSlot() {
  // generate key
  const keys = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign", "verify"]);

  // Put private key to slot
  const prvIndex = await crypto.keyStorage.setItem(keys.privateKey);
  console.log("Private key was added to slot:", prvIndex);

  // Put public key to slot
  const pubIndex = await crypto.keyStorage.setItem(keys.publicKey);
  console.log("Public key was added to slot:", pubIndex);

  return prvIndex.split("-")[2]; // CKA_ID from the PKCS#11
}

async function getKey(type, id) {
  const indexes = await crypto.keyStorage.keys();

  for (const index of indexes) {
    const indexParts = index.split("-");
    if (indexParts[0] === type && indexParts[2] === id) {
      return crypto.keyStorage.getItem(index);
    }
  }

  return null;
}

async function printAllKeysInSlot() {
  console.log("Keys from slot:");
  const indexes = await crypto.keyStorage.keys();
  indexes.forEach((index) => {
    console.log(" ", index);
  })
}

async function main() {
  await printAllKeysInSlot();

  const id = await generateKeysAndPushToSlot();

  const key = await getKey("public", id);
  console.log("Key from storage:", key);
}

main().catch(err => console.error(err));

Output

Keys from slot:
  private-0200000000000000-fe9a8e35bef4f03cedeaf28b08590bf5186a6449
  public-0300000000000000-fe9a8e35bef4f03cedeaf28b08590bf5186a6449
Private key was added to slot: private-0600000000000000-75cfc3cda0043fa3f505dfd3ae9b650b7b452569
Public key was added to slot: public-0700000000000000-75cfc3cda0043fa3f505dfd3ae9b650b7b452569
Key from storage: EcCryptoKey {
  usages: [ 'verify' ],
  p11Object:
   PublicKey {
     handle: <Buffer 07 00 00 00 00 00 00 00>,
     session:
      Session {
        handle: <Buffer 01 00 00 00 00 00 00 00>,
        slot: [Slot],
        state: 2,
        flags: 6,
        deviceError: 0 } },
  type: 'public',
  extractable: true,
  algorithm: { name: 'ECDSA', namedCurve: 'P-256' },
  id:
   'public-0700000000000000-75cfc3cda0043fa3f505dfd3ae9b650b7b452569' }

Key uses a composite id <type>-<hex(handle)>-<hex(CKA_ID)>. CKA_ID propertis for assymetric private key and public key are equal

mfaisaltariq commented 5 years ago

@microshine thank you very much for your input on this. Thank you for all your help and time.

1 thing that I'm curious about is that, do we have a quicker way to fetch the key using index from the HSM and reduce O(n) complexity.

microshine commented 5 years ago

node-webcrypto-p11 doesn't implement a mechanism to get PKCS#11 by CKA_ID only. You can implement own method by using crypto.session object

Example

const { ObjectClass } = require("graphene-pk11");
const { Crypto } = require("node-webcrypto-p11");

const crypto = new Crypto({
  library: "/usr/local/lib/softhsm/libsofthsm2.so",
  name: "SoftHSMv2",
  slot: 0,
  readWrite: true,
  pin: "12345",
});

async function main() {
  const id = "fe9a8e35bef4f03cedeaf28b08590bf5186a6449";
  const type = "public";

  const keys = crypto.session.find({
    class: type === "public"
      ? ObjectClass.PUBLIC_KEY
      : ObjectClass.PRIVATE_KEY,
    id: Buffer.from(id, "hex"),
  });

  if (keys.length > 0) {
    console.log(keys.items(0).toType());
  } else {
    console.log("Key not found");
  }
}

main().catch(err => console.error(err));

Output

PublicKey {
  handle: <Buffer 02 00 00 00 00 00 00 00>,
  session:
   Session {
     handle: <Buffer 01 00 00 00 00 00 00 00>,
     slot:
      Slot {
        handle: <Buffer 80 84 0a 69 00 00 00 00>,
        module: [Module],
        slotDescription: 'SoftHSM slot ID 0x690a8480',
        manufacturerID: 'SoftHSM project',
        flags: 1,
        hardwareVersion: [Object],
        firmwareVersion: [Object] },
     state: 2,
     flags: 6,
     deviceError: 0 } }

You just need to wrap PKCS#11 key to CryptoKey after that

mfaisaltariq commented 5 years ago

@microshine when we sign a message like below

  const signature = await crypto.subtle.sign({name: "ECDSA", hash: "SHA-256"}, keys.privateKey, data);

the hash: 'SHA-256' part means that our message will first be hashed using SHA-256 and then signed using the private key or is it something else?

Thank you

microshine commented 5 years ago

@mfaisaltariq You are right. First computes hash then signs using a private key

mfaisaltariq commented 5 years ago

Hi @microshine ,

I hope you are well. Recently came across an error while retrieving a key from softHSM. Its actually a CKR_GENERAL_ERROR:5 for the function C_GetAttributeValue. I have searched for this error and didn't find any references for it. Please let me know the cause if you have any idea regarding this or how can I avoid it.

The logs can be found on the below link. I'm running it in a docker container on ubuntu:16.04

https://pasteboard.co/IA6gNx8.png

Thank you

mfaisaltariq commented 5 years ago

On further investigation I found out that it corrupts the SLOT. When I initialised a new slot and changed the slot number in the code it started working fine. Now I'm trying to find out if I can move the keys in slot 0 to slot 1 or not.