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

GCM does not decrypt more than a block on SoftHSM #113

Closed teetjes closed 5 years ago

teetjes commented 5 years ago

If you try to encrypt on SoftHSM and encrypt with AES_GCM more than the block size of data, i.e. more than 16 byte you'll receive a ciphertext as intended.

When you subsequently try do decrypt it, decryption fails, because the buffer that is being allocated in final() (https://github.com/PeculiarVentures/graphene/blob/master/src/crypto/decipher.ts#L43) is not large enough to hold the whole plaintext.

I guess this is due to the fact that for other modes of operation the update() function already processes most of the ciphertext/plaintext, which is not possible for GCM since GCM needs to check the AuthTag on the whole ciphertext, thus shouldn't process ciphertext prior to final() being called

Using latest graphene and SoftHSM 2.4.0

microshine commented 5 years ago

AES GCM encrypt/decrypt

import * as graphene from "graphene-pk11";

const library = "/usr/local/lib/softhsm/libsofthsm2.so";
const pin = "12345";
const slotIndex = 0;
const blockSize = 16;

function main() {
  const softhsm = graphene.Module.load(library);

  softhsm.initialize();

  try {
    const slot = softhsm.getSlots(slotIndex);

    const session = slot.open(graphene.SessionFlag.SERIAL_SESSION);
    session.login(pin);

    //#region Generate AES key
    const key = session.generateKey(graphene.KeyGenMechanism.AES, {
      private: false,
      token: false,
      class: graphene.ObjectClass.SECRET_KEY,
      valueLen: 16,
      encrypt: true,
      decrypt: true,
    }).toType<graphene.SecretKey>();
    console.log("AES key created");
    //#endregion

    const params = new graphene.AesGcm240Params(Buffer.from("123456789012"), Buffer.from("additional"));
    //#region Encrypt
    const cipher = session.createCipher({
      name: "AES_GCM",
      params,
    }, key);
    const cipherBlocks: Buffer[] = [];
    cipherBlocks.push(cipher.update(Buffer.from("123456789012345678901234567890")));
    cipherBlocks.push(cipher.update(Buffer.from("123456789012345678901234567890")));
    cipherBlocks.push(cipher.update(Buffer.from("123456789012345678901234567890")));
    cipherBlocks.push(cipher.final());

    const encryptedMessage = Buffer.concat(cipherBlocks);
    console.log("Encrypted message:", encryptedMessage.toString("hex"));
    //#endregion

    //#region Decrypt
    const decipher = session.createDecipher({
      name: "AES_GCM",
      params,
    }, key, encryptedMessage.length);
    const decipherBlocks: Buffer[] = [];
    let offset = 0;
    while (offset < encryptedMessage.length) {
      const block = encryptedMessage.slice(offset, offset + blockSize);
      decipherBlocks.push(decipher.update(block));
      offset += block.length; 
    }
    decipherBlocks.push(decipher.final());

    console.log("Decrypted message:", Buffer.concat(decipherBlocks).toString());
    //#endregion
  }
  finally {
    softhsm.finalize();
  }
}

main();

Output

AES key created
Encrypted message: 49751226b55143d6712575924f4c4be51fc2403099499fbe4200e0017c8520552931953e1fc894e59da67e41676028fd04ce7ec75b96b527d159a83d04fc306b79d4eaeb0c7bb9943b954a0eb2768fd32bf40d92672bc91a536564136e048e7decf3afedcd13643d3862
Decrypted message: 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

image

AEG GCM mechanism returns decrypted message in Final operation only. It doesn't return decrypted blocks from Update operation as AES CBC does.

createDecipher method allows setting the output buffer size

TS definition

public createDecipher(alg: MechanismType, key: Key, blockSize?: number): Decipher;
teetjes commented 5 years ago

Thank you. That does indeed work.

I did not really look at changing blocksize. Reading blockSize I immediately think of the block cipher block size aka 128, so its a bit of a confusing name I guess.