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

Question: Encrypting/Decrypting across restarts #145

Open santosh-shaastry opened 3 years ago

santosh-shaastry commented 3 years ago

Hi,

I am a newbie to SoftHSM/PKCS 11. I am trying to:

Step 1

  1. Initialize the SoftHSM module
  2. Login to access the token/slot
  3. Obtain the session
  4. Encrypt a message using the session's cipher methods as described in your README documentation
  5. Persist the encrypted value in a file/database

Step 2

Step 3

  1. Initialize the SoftHSM module
  2. Read the encrypted value from the file/database
  3. Login to access the token/slot
  4. Obtain the session
  5. Attempt to decrypt the encrypted message using the session's cipher methods as described in your README documentation

Though I am able to login successfully using the same token and seem to obtain the same session, I run into the following error when performing Buffer.concat([dec, decipher.final()]).toString();

[Pkcs11Error: CKR_GENERAL_ERROR] { relayer_1 | method: 'crypto_final', relayer_1 | nativeStack: ' at Error (native) crypto_final:710', relayer_1 | code: 5 relayer_1 | }

From what I understand about the PKCS 11 Session as described in the spec, unless a session is closed/logged out the application should be able to access the same session and hence the token/slot.

Appreciate any direction, inputs and help on this one. Thank you.

microshine commented 3 years ago

Here is my example

Key generation

const label = "test_key";
const softHsm = graphene.Module.load("/usr/local/lib/softhsm/libsofthsm2.so");
softHsm.initialize();

try {
  const slot = softHsm.getSlots(0);
  const session = slot.open(graphene.SessionFlag.RW_SESSION | graphene.SessionFlag.SERIAL_SESSION);
  session.login("12345", graphene.UserType.USER);

  const keys = session.generateKeyPair(graphene.KeyGenMechanism.RSA, {
    keyType: graphene.KeyType.RSA,
    token: true,
    label,
    publicExponent: Buffer.from([1, 0, 1]),
    modulusBits: 2048,
    encrypt: true,
  }, {
    keyType: graphene.KeyType.RSA,
    private: true,
    sensitive: true,
    token: true,
    label,
    decrypt: true,
  });
  console.log(keys);
} catch (e) {
  console.error(e);

  softHsm.finalize();
}

Encryption

const label = "test_key";
const softHsm = graphene.Module.load("/usr/local/lib/softhsm/libsofthsm2.so");
softHsm.initialize();

try {
  const slot = softHsm.getSlots(0);
  const session = slot.open(graphene.SessionFlag.SERIAL_SESSION);

  const publicKey = session.find({
    class: graphene.ObjectClass.PUBLIC_KEY,
    label,
  }).items(0).toType();

  const cipher = session.createCipher({
    name: "RSA_PKCS_OAEP",
    params: new graphene.RsaOaepParams(graphene.MechanismEnum.SHA1, graphene.RsaMgf.MGF1_SHA1),
  }, publicKey);
  encrypted = cipher.once(data, Buffer.alloc(2048));
  console.log(encrypted);
} catch (e) {
  console.error(e);

  softHsm.finalize();
}

Decryption

const label = "test_key";
const softHsm = graphene.Module.load("/usr/local/lib/softhsm/libsofthsm2.so");
softHsm.initialize();

try {
  const slot = softHsm.getSlots(0);
  const session = slot.open(graphene.SessionFlag.SERIAL_SESSION);
  session.login("12345", graphene.UserType.USER);

  const privateKey = session.find({
    class: graphene.ObjectClass.PRIVATE_KEY,
    label,
  }).items(0).toType();

  const decipher = session.createDecipher({
    name: "RSA_PKCS_OAEP",
    params: new graphene.RsaOaepParams(graphene.MechanismEnum.SHA1, graphene.RsaMgf.MGF1_SHA1),
  }, privateKey);
  const decrypted = decipher.once(encrypted, Buffer.alloc(2048));
  console.log(decrypted);
} catch (e) {
  console.error(e);

  softHsm.finalize();
}
santosh-shaastry commented 3 years ago

@microshine Thank you for the detailed example. I can confirm what you have shared works as expected, however without a an application restart between encryption and decryption functions. So the question I still have is, what could be a recommended way to store the Public/Private key pair in order to ensure that the data encrypted using a set of keys can be decrypted in a deterministic way even after the application restarts.

An interesting side note on something that I observed:

  1. I was hoping to obtain the session by storing the handle Buffer and using it to fetch the SessionObject (via the getObject method) after application restart
  2. However the handle Buffer's value seem to be different every time I opened the slot. This means that I get different instances of the session but all of them pointing to the same slot (0, in my case) in which the data is encrypted/stored.

Thank you for the clarification and further help.

KawtharAlakri commented 6 months ago

Have you managed to solve this issue by any chance and decrypt data separately from the encryption ? @santosh-shaastry i'm trying to do that and getting the same error.