Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Commit

Permalink
Merge pull request #5 from sophos/feature/gh-4-encryption
Browse files Browse the repository at this point in the history
Feature/gh 4 encryption
  • Loading branch information
ioanrogers committed Aug 2, 2016
2 parents e3a5ec9 + 1f0af1f commit a29a3f8
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 108 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
Crypt-*
.tidyall.d
Changes
*.der
author/tokens
39 changes: 39 additions & 0 deletions author/keys.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

# This generates the softhsm2 tokens. It requires softhsm2-util, pkcs11-tool from OpenSC and openssl commands
# WARNING: It is destructive!
set -e

export SOFTHSM2_CONF="author/softhsm2.conf"

TOKEN_DIR="tokens";
PKCS11TOOL_CMD="pkcs11-tool --module /usr/lib64/pkcs11/libsofthsm2.so --login --pin 1234 -v"

rm -rfv $TOKEN_DIR && mkdir $TOKEN_DIR

softhsm2-util --init-token --pin 1234 --so-pin 123456 --slot 0 --label test_keys_1

# add an RSA 1024 private key just for signing and the public key for verifying
openssl rsa -in t/keys/1024_sign.pem -outform DER -out t/keys/1024_sign.der
$PKCS11TOOL_CMD --usage-sign --write-object t/keys/1024_sign.der -y privkey --label 'signing_key' --id 0001

openssl rsa -pubin -in t/keys/1024_sign_pub.pem -outform DER -out t/keys/1024_sign_pub.der
$PKCS11TOOL_CMD --usage-sign --write-object t/keys/1024_sign_pub.der -y pubkey --label 'signing_key' --id 0001



# add a RSA 1024 private and public keys for encryption/decryption
openssl rsa -in t/keys/1024_enc.pem -outform DER -out t/keys/1024_enc.der
$PKCS11TOOL_CMD --usage-decrypt --write-object t/keys/1024_enc.der -y privkey --label 'encryption_key' --id 0002

openssl rsa -pubin -in t/keys/1024_enc_pub.pem -outform DER -out t/keys/1024_enc_pub.der
$PKCS11TOOL_CMD --usage-decrypt --write-object t/keys/1024_enc_pub.der -y pubkey --label 'encryption_key' --id 0002

echo -e "\n ****** Listing objects ******\n"

$PKCS11TOOL_CMD --list-slots
$PKCS11TOOL_CMD --list-objects

# $PKCS11TOOL_CMD -t

cd author; tar -cvzf ../t/data/tokens.tar.gz tokens
2 changes: 2 additions & 0 deletions author/softhsm2.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
directories.tokendir = author/tokens
objectstore.backend = file
117 changes: 112 additions & 5 deletions lib/Crypt/PKCS11/Easy.pm
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,11 @@ has _default_mech => (
is => 'ro',
default => sub {
{
sign => CKM_SHA1_RSA_PKCS,
verify => CKM_SHA1_RSA_PKCS,
digest => CKM_SHA_1,
digest => CKM_SHA_1,
encrypt => CKM_RSA_PKCS,
sign => CKM_SHA1_RSA_PKCS,
verify => CKM_SHA1_RSA_PKCS,

};
},
);
Expand Down Expand Up @@ -281,6 +283,7 @@ sub _build__key {
given ($self->function) {
return $self->get_signing_key($self->key) when 'sign';
return $self->get_verification_key($self->key) when 'verify';
return $self->get_encryption_key($self->key) when 'encrypt';
default {
die "Unknown key type: " . $self->function;
}
Expand Down Expand Up @@ -605,6 +608,28 @@ sub get_verification_key {
return $self->_get_key($label, $tmpl);
}

=method C<get_encryption_key(Str $label)>
Will look for a key matching with a label matching C<$label> which can be used
for encryption.
The returned key is a L<Crypt::PKCS11::Object>.
=cut

sub get_encryption_key {
my ($self, $label) = @_;

$log->debug('Looking for an encryption key');

my $tmpl =
Crypt::PKCS11::Attributes->new->push(
Crypt::PKCS11::Attribute::Encrypt->new->set(1),
);

return $self->_get_key($label, $tmpl);
}

sub _get_pss_params {
my ($self, $hash, $hash_number) = @_;

Expand Down Expand Up @@ -653,6 +678,46 @@ sub _get_pss_params {
return $pss_param;
}

sub _get_oaep_params {
my ($self) = @_;

# SHA1 is the only one supported for now as it is the only one supported by
# openssl and softhsm2
# https://github.com/openssl/openssl/blob/master/crypto/rsa/rsa_oaep.c
# https://github.com/pspacek/SoftHSMv2/blob/master/src/lib/SoftHSM.cpp#L10173
my $hash = 'SHA1';

$log->debug('Finding params for an RSA OAEP encryption');

my $oaep_param = Crypt::PKCS11::CK_RSA_PKCS_OAEP_PARAMS->new;

no strict 'refs'; ## no critic
my $hash_const = 'Crypt::PKCS11::CKM_';

# SHA1 is a special case
$hash_const .= $hash eq 'SHA1' ? 'SHA_1' : $hash;
$log->debug("Hash constant: $hash_const");

my $r = $oaep_param->set_hashAlg($hash_const->());
if ($r != CKR_OK) {
die 'Failed to set hash algorithm for OAEP params: '
. Crypt::PKCS11::XS::rv2str($r);
}

my $mgf_const = "Crypt::PKCS11::CKG_MGF1_$hash";
$log->debug("MGF constant: $mgf_const");

$r = $oaep_param->set_mgf($mgf_const->());
if ($r != CKR_OK) {
die 'Failed to set MGF on OAEP params: '
. Crypt::PKCS11::XS::rv2str($r);
}

$oaep_param->set_source(CKZ_DATA_SPECIFIED);

return $oaep_param;
}

sub _handle_common_args {
my ($self, $args) = @_;

Expand Down Expand Up @@ -686,12 +751,19 @@ sub _handle_common_args {

# does this mechanism need parameters?
my $params;
if ($args->{mech} =~ /(^SHA(\d+))_RSA_PKCS_PSS$/) {
$params = $self->_get_pss_params($1, $2);
given ($args->{mech}) {
when (/^(SHA(\d+))_RSA_PKCS_PSS$/) {
$params = $self->_get_pss_params($1, $2);
}
when (/^RSA_PKCS_OAEP$/) {
$params = $self->_get_oaep_params;
}
default { $log->debug('No extra params required for this mech') }
}

if ($params) {
my $r = $mech->set_pParameter($params->toBytes);

if ($r != CKR_OK) {
die 'Failed to set params for mechanism: '
. Crypt::PKCS11::XS::rv2str($r);
Expand Down Expand Up @@ -901,6 +973,41 @@ sub get_mechanisms {
return \%mech;
}

=method C<encrypt((data => 'some data' | file => '/path'), mech => 'RSA_PKCS'?)>
Returns encrypted data. The data to be encrypted is either passed as a scalar
in C<data>, or in C<file> which can be a string path or a L<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');
=cut

sub encrypt {
my ($self, %args) = @_;

$self->_handle_common_args(\%args);

if (!$args{mech}) {
$args{mech} = Crypt::PKCS11::CK_MECHANISM->new;
$args{mech}->set_mechanism($self->_default_mech->{encrypt});
}

# TODO check key size and size of data to be encrypted and look up max sizes for mechanism
# XXX trying to encrypt data that is too big returns a CKR_GENERAL_ERROR, which is super-unhelpful

$self->_session->EncryptInit($args{mech}, $self->_key)
or die "Failed to init encryption: " . $self->_session->errstr;

my $encrypted_data = $self->_session->Encrypt($args{data})
or die "Failed to encrypt: " . $self->_session->errstr;

return $encrypted_data;
}

1;

__END__
Expand Down
60 changes: 41 additions & 19 deletions t/02-basic.t
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ test slots => sub {

ok my $slots = $self->pkcs11->get_slots;
isa_ok $slots, 'ARRAY';
is scalar @$slots, 1, 'softhsm always has at least one token';
is scalar @$slots, 2, 'softhsm always has at least one token';

# this is done by softhsm
$self->init_token(0, 'token_0');
$self->import_key(0, '1024', 'test_key_1024');
$self->clear_pkcs11;

ok $slots = $self->pkcs11->get_slots;
Expand All @@ -44,7 +41,7 @@ test slots => sub {
is scalar @$slots, 2, 'There are now two tokens';

like(
exception { $self->pkcs11->get_slot(label => 'token_0') },
exception { $self->pkcs11->get_slot(label => 'test_keys_1') },
qr/Missing id or token/,
'Failed to find slot using invalid args',
);
Expand All @@ -57,8 +54,9 @@ test slots => sub {

my $slot;
is(
exception { $slot = $self->pkcs11->get_slot(token => 'token_0') },
undef, 'Found token by label',
exception { $slot = $self->pkcs11->get_slot(token => 'test_keys_1') },
undef,
'Found token by label',
);

my $slot2;
Expand All @@ -78,40 +76,64 @@ test get_mechs => sub {

};

my $sig;
test sign => sub {
test signing_and_verifying => sub {
my $self = shift;

my $data_file = path 't/data/10K.file';
my $key_file = path 't/keys/1024.pem';
my $key_file = path 't/keys/1024_sign.pem';

my $pkcs11 = $self->_new_pkcs11('test_key_1024', 0);
my $pkcs11 = $self->_new_pkcs11(key => 'signing_key', slot => 0);

ok $sig = $pkcs11->sign(file => $data_file);
ok my $sig = $pkcs11->sign(file => $data_file);
my $ossl_sig = $self->openssl_sign($key_file, $data_file);

is $sig, $ossl_sig, 'Signing produced same sig as openssl';

# save the sig to verify with openssl later
my $sig_file = $self->workdir->child($data_file->basename . '.sig');
$sig_file->spew_raw($sig);

ok my $enc_sig = $pkcs11->sign_and_encode(file => $data_file);
my $expectec_sig = q{-----BEGIN SIGNATURE-----
my $expected_sig = q{-----BEGIN SIGNATURE-----
mjNMN4+Xf7PNsDGXjzyentTLSs1JI8G55Bbr+rBvHvDl9sOgFZTh9ZjTM1ekVcTN
mUwq3aC/GjFW+pOLRYevQ2UwJiZmcVtP4nDD9Vt/exZS/ggM4HnaoGm8QyGnhlk3
77J68o6bq2ilVIUxhTn2WzwZN/Se+5PuCCIomcy2OEY=
-----END SIGNATURE-----
};

is $enc_sig, $expectec_sig, 'Encoded sigs are good';
is $enc_sig, $expected_sig, 'Encoded sigs are good';

$pkcs11 = undef;
$pkcs11 =
$self->_new_pkcs11(key => 'signing_key', slot => 0, func => 'verify');
ok $pkcs11->verify(sig => $sig, file => $data_file), 'verified signature';

$key_file = path 't/keys/1024_sign_pub.pem';
ok $self->openssl_verify($key_file, $sig_file, $data_file);
};

test verify => sub {
test encryption => sub {
my $self = shift;

my $data_file = path 't/data/10K.file';
my $key_file = path 't/keys/1024.pem';
my $data_file = path 't/data/64B.file';

for my $mech (qw/RSA_PKCS RSA_PKCS_OAEP/) {
my $pkcs11 = $self->_new_pkcs11(
key => 'encryption_key',
slot => 0,
func => 'encrypt'
);
ok my $encrypted_data =
$pkcs11->encrypt(file => $data_file, mech => $mech),
"Encrypted with mech $mech";

my $private_key_file = path 't/keys/1024_enc.pem';
ok my $decrypted_data =
$self->openssl_decrypt($private_key_file, $encrypted_data, $mech),
'Decrypted using openssl';
is $decrypted_data, $data_file->slurp_raw, 'Successful round-trip';
}

my $pkcs11 = $self->_new_pkcs11('test_key_1024', 0, 'verify');
ok $pkcs11->verify(sig => $sig, file => $data_file);
};

run_me;
Expand Down
1 change: 1 addition & 0 deletions t/data/64B.file
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
P�AbxX�ޢ�}�Df�P�v���+r��l���$���d�«0"�80�\�'���v���O��a3�
Binary file added t/data/tokens.tar.gz
Binary file not shown.
16 changes: 0 additions & 16 deletions t/keys/1024.pkcs8

This file was deleted.

15 changes: 15 additions & 0 deletions t/keys/1024_enc.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCsvMNNhewFhhGfD1Dtzf1Y399LDelqyi9BcUTeEeZd7Gtj/IpT
4d1SVLCWHuI9z6uB18pbtnVnehllSEEcuIudjPE/tkTzlIt+r8d5nPuAbFNEd1bf
GAgaez/4HbUC7qM5/zpZSr20vzXo8xUhvWF1LARKiWMPhJnwE1JHc1k20wIDAQAB
AoGBAKE9hnT72KgDULmP+3QvfxiRyQZvUv4rAtdsmuNMBCSSRmBL4PeR8x0iDr/i
zj7ydd67Bpp7g7X2pHQFcToVDcSWalklIdJglRZzxYBtLAouHkgHJknwEzew65EM
gbkECxD2XnEPRSM6Ow2Swc04wMDQqIQH/aQVyEVb+Z6o+HaRAkEA4+oU6tw4tlwX
8hllaCm5szm2AHTB6moZd89brS4BrZR9mDWObhzgMPirR1XatvRCCGddY02HJbEk
rDKNLThVSwJBAMIGCPJSqn7gVc1Ch+kw+Q9yAQkyQ3M4jx4Q76Gj4JOgN7L2FNPw
oGZkJJFMVxI4HUBW9Y7UNGxu0ExEOm69l5kCQQDU3BKdJwBwkKJsoftrxUyCbyvk
USeq31zZ4PgmTgmTLFQAjERx4tYf5p9RCIY0yPvDDYbS792A/mjZyny/9xTxAkAX
hvnQwJtyFSvyXu3yJgEdkVp0zgaF00bvg2g7do7F9/ipF9QAuz4IDH6uq84nJGzP
n3vZXPwaZfmWNfPL9+uJAkB5ou9YpX4gqdu7HCq8llHRetDWUSkVurfAtVoRFrbw
Aop4kutRFgCuxmreYVPeHbnvIZMk1vptIDzJY4fJbdAa
-----END RSA PRIVATE KEY-----
6 changes: 6 additions & 0 deletions t/keys/1024_enc_pub.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsvMNNhewFhhGfD1Dtzf1Y399L
Delqyi9BcUTeEeZd7Gtj/IpT4d1SVLCWHuI9z6uB18pbtnVnehllSEEcuIudjPE/
tkTzlIt+r8d5nPuAbFNEd1bfGAgaez/4HbUC7qM5/zpZSr20vzXo8xUhvWF1LARK
iWMPhJnwE1JHc1k20wIDAQAB
-----END PUBLIC KEY-----
File renamed without changes.
6 changes: 6 additions & 0 deletions t/keys/1024_sign_pub.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDO8asskS/X4TRPdQCH3b9aUT0Y
SYMrIu2EKH92YA90OZQUzc2Hcu8WQ/KRK/9UMSTAtaVBKKfiO7MkSwh0wuG2LWWS
mZH0EhWKONoCYuKWGMrUFsLFTyNQWRC1+R4Wbfk53uKEI3TxYzylKwLLNBB4R7Hz
fu1YNf0MgNEpHP8KVwIDAQAB
-----END PUBLIC KEY-----

0 comments on commit a29a3f8

Please sign in to comment.