NAME

Crypt::PKCS11::Easy - Wrapper around Crypt::PKCS11 to make using a HSM not suck

VERSION

version 0.180080

SYNOPSIS

  use Crypt::PKCS11::Easy;
  use IO::Prompter;

  my $file = '/file/to/sign';

  my $hsm = Crypt::PKCS11::Easy->new(
      module => 'libCryptoki2_64',
      key    => 'MySigningKey',
      slot   => '0',
      pin    => sub { prompt 'Enter PIN: ', -echo=>'*' },
  );

  my $base64_signature = $hsm->sign_and_encode(file => $file);
  my $binary_signature = $hsm->decode_signature(data => $base64_signature);

  $hsm->verify(file => $data_file, sig => $binary_signature)
    or die "VERIFICATION FAILED\n";

DESCRIPTION

This module is an OO wrapper around Crypt::PKCS11, designed primarily to make using a HSM as simple as possible.

Signing a file with Crypt::PKCS11

    use IO::Prompter;
    use Crypt::PKCS11;
    use Crypt::PKCS11::Attributes;

    my $pkcs11 = Crypt::PKCS11->new;
    $pkcs11->load('/usr/safenet/lunaclient/lib/libCryptoki2_64.so');
    $pkcs11->Initialize;
    # assuming there is only one slot
    my @slot_ids = $pkcs11->GetSlotList(1);
    my $slot_id = shift @slot_ids;

    my $session = $pkcs11->OpenSession($slot_id, CKF_SERIAL_SESSION)
        or die "Error" . $pkcs11->errstr;

    $session->Login(CKU_USER, sub { prompt 'Enter PIN: ', -echo=>'*' } )
        or die "Failed to login: " . $session->errstr;

    my $object_template = Crypt::PKCS11::Attributes->new->push(
        Crypt::PKCS11::Attribute::Label->new->set('MySigningKey'),
        Crypt::PKCS11::Attribute::Sign->new->set(1),
    );
    $session->FindObjectsInit($object_template);
    my $objects = $session->FindObjects(1);
    my $key = shift @$objects;

    my $sign_mech = Crypt::PKCS11::CK_MECHANISM->new;
    $sign_mech->set_mechanism(CKM_SHA1_RSA_PKCS);

    $session->SignInit($sign_mech, $key)
        or die "Failed to set init signing: " . $session->errstr;

    my $sig = $session->Sign('SIGN ME')
        or die "Failed to sign: " . $session->errstr;

Signing a file with Crypt::PKCS11::Easy

    use Crypt::PKCS11::Easy;
    use IO::Prompter;

    my $hsm = Crypt::PKCS11::Easy->new(
        module => 'libCryptoki2_64',
        key    => 'MySigningKey',
        slot   => '0',
        pin    => sub { prompt 'Enter PIN: ', -echo=>'*' },
    );

    my $sig = $hsm->sign(data => 'SIGN ME');

To make that conciseness possible a Crypt::PKCS11::Object can only be used for one function, e.g. signing OR verifying, and cannot be set to use a different key or a different token after instantiation. A new object should be created for each function.

ATTRIBUTES

module

String. Required.

The name of the PKCS#11 module to use. Either pass the full path to the module, or just pass the base name of the library and the rest will be handled automagically. e.g.

  libsofthsm2          => /usr/lib64/pkcs11/libsofthsm2.so
  libCryptoki2_64      => /usr/lib64/pkcs11/libCryptoki2_64.so
  gnome-keyring-pkcs11 => /usr/lib64/pkcs11/gnome-keyring-pkcs11.so

rw

Boolean. Controls whether a session will be opened in Read/Write mode or not. Defaults to off. Writing is only needed to make modifications to a token or the objects on it.

key

String. The label of the you want to use.

function

String. The function that will be performed with this object. Can be 'sign' or 'verify'. Defaults to 'sign'. It affects how the key can be used. If function is sign and you try to verify a signature, the underlying library will return an error.

slot

Integer. The id number of the slot to use.

token

String. Instead of specifying the "slot", find and use the slot that contains a token with this label.

pin

String, Coderef or Path::Tiny object. This is either the PIN/password required to access a token, a coderef that returns it, or a file that contains it.

 use IO::Prompter;
 $pin = sub { prompt 'Enter PIN: ', -echo=>'*' };

 use Path::Tiny;
 $pin = path '/secure/file/with/password'

 $pin = '1234';

module_dirs

Array of paths to check for PKCS#11 modules.

METHODS

get_info

Returns a hashref containing basic info about the PKCS#11 implementation, currently the manufacturer, library description and Cryptoki version that is implemented.

get_token_info(Int $slot_id)

Returns a hashref containing details on the token in slot identified by $slot_id.

get_slot(id = $int | token => $string)>

Returns a hashref containing details on the slot identified by $id OR the slot which contains a token with the label $string. If a token is present in the slot, its details will also be retrieved.

  my $slot = $pkcs11->get_slot(id => 1);

  my $slot = $pkcs11->get_slot(token => 'Build Signer');
  say $slot->{token}->{serialNumber};

get_slots(Bool $with_token?)

Returns an arrayref of all visible slots. Each element in the array will be a hashref returned by "get_slot".

If $with_token is true then only slots that contain a token will be returned.

login

Attempts to login to the HSM. In most use cases, this will be handled automatically when needed.

get_signing_key(Str $label)

Will look for a key matching with a label matching $label which can be used for signing.

The returned key is a Crypt::PKCS11::Object.

get_verification_key(Str $label)

Will look for a key matching with a label matching $label which can be used for signature verification.

The returned key is a Crypt::PKCS11::Object.

get_encryption_key(Str $label)

Will look for a key matching with a label matching $label which can be used for encryption.

The returned key is a Crypt::PKCS11::Object.

sign((data = 'some data' | file => '/path'), mech => 'RSA_PKCS'?)>

Returns a binary signature. The data to be signed is either passed as a scalar in data, or in file which can be a string path or a Path::Tiny object.

A PKCS#11 mechanism can optionally be specified as a string and without the leading 'CKM_'.

  my $sig = $hsm->sign(file => $file, mech => 'RSA_PKCS');
  my $sig = $hsm->sign(data => 'SIGN ME');

sign_and_encode(...)

Wrapper around "sign" which will return the signature data as base64 PEM, e.g.

  -----BEGIN SIGNATURE-----
  YHXMbvdWyUXeNvgfMzQA+9FjytOWPZCik/H3GS6t72xtk1gvHNfQpKdURKvgBeJM
  QdUJ7ceujzGX5v/UJRJ4oSpLLiptn2BYaeAn/gUg7yKDFg4YuVN7RU7MbrN2jjlw
  RfKHq6h6G4FP8LJz5jQWlKKIPoiJ2g3a9M7dq0+hG/kPOv4pBLm7G30uaiSpi/3O
  hhV+aw87HB7H7i09NSIHoWRxXqw8BeFse7jWTjbj5X1j9uNxD+W6+sxyERawfqFP
  3WuzDIcD8kgMA7cM7a6z+h1bEgUt2FUKGytcTX4ymAz9+aS+u24V81mg0Ia3pZQd
  Pth2532FY0z+Ajn3GojNVw==
  -----END SIGNATURE-----

verify((data = 'some data' | file => '/path'), sig => $sig, mech => 'RSA_PKCS'?)>

Verifies a signature. Parameters are the same as "sign", and also requires a binary signature. Returns true or false.

  $hsm->verify(file => $file_to_check, sig => $binary_sig, mech => 'RSA_PKCS')
      or die "Signature verification failed!\n";

digest((data = 'some data' | file => '/path'), mech => 'SHA_1'?)>

Returns a binary digest. Parameters are the same as "sign".

  $hsm->digest(file => $file_to_check, mech => 'RSA_PKCS')

decode_signature((data = 'some data' | file => '/path'))>

Verifies a signature. Parameters are the same as "sign", and also requires a binary signature. Returns true or false.

  $hsm->verify(file => $file_to_check, sig => $binary_sig, mech => 'RSA_PKCS')
      or die "Signature verification failed!\n";

get_mechanism_info($mech, $slot_id?)

Will return a details of a mechanism as a hashref. If a slot id is specifed, the mechanisms for that slot will be retrieved. Otherwise, the slot id in "slot" will be used if there is one.

get_mechanisms($slot_id?)

Will return a hashref of available mechanisms. If a slot id is specifed, the mechanisms for that slot will be retrieved. Otherwise, the slot id in "slot" will be used if there is one.

encrypt((data = 'some data' | file => '/path'), mech => 'RSA_PKCS'?)>

Returns encrypted data. The data to be encrypted is either passed as a scalar in data, or in file which can be a string path or a Path::Tiny object.

A PKCS#11 mechanism can optionally be specified as a string and without the leading 'CKM_'.

  my $encrypted_data = $hsm->sign(file => $file, mech => 'RSA_PKCS');
  my $encrypted_data = $hsm->sign(data => 'SIGN ME');

DIAGNOSTICS

Crypt::PKCS11::Easy uses Log::Any for logging. To see debug output on STDOUT, for example, in your application use:

    use Log::Any::Adapter 'Stdout', log_level => 'debug';

ERRORS

Unless stated otherwise, methods will die when encountering an error.

PKCS#11 MECHANISMS

The default mechanisms are:

SEE ALSO

PKCS#11 v2.40 Mechanisms Crypt::PKCS11 SoftHSM2

AUTHOR

Ioan Rogers <ioan.rogers@sophos.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2018 by Sophos Ltd.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.