Authen-U2F-Tester
view release on metacpan or search on metacpan
lib/Authen/U2F/Tester.pm view on Meta::CPAN
my $COUNTER = 0;
has key => (
is => 'ro',
isa => 'Crypt::PK::ECC',
required => 1);
has keystore => (
is => 'ro',
does => 'Authen::U2F::Tester::Role::Keystore',
required => 1);
has certificate => (
is => 'ro',
isa => 'Crypt::OpenSSL::X509',
required => 1);
around BUILDARGS => sub {
my ($orig, $self) = splice @_, 0, 2;
if (@_ > 1) {
my %args = @_;
if (my $keyfile = delete $args{key_file}) {
$args{key} = Crypt::PK::ECC->new($keyfile);
}
if (my $certfile = delete $args{cert_file}) {
$args{certificate} = Crypt::OpenSSL::X509->new_from_file($certfile);
}
# if no keystore was given, use the wrapped keystore
unless (defined $args{keystore}) {
require Authen::U2F::Tester::Keystore::Wrapped;
$args{keystore} = Authen::U2F::Tester::Keystore::Wrapped->new(key => $args{key});
}
return $self->$orig(%args);
}
else {
return $self->$orig(@_);
}
};
sub register {
my ($self, $app_id, $challenge, @registered_handles) = @_;
# check if this device has already been registered
for my $registered (@registered_handles) {
if ($self->keystore->exists($registered)) {
return Authen::U2F::Tester::Error->new(DEVICE_INELIGIBLE);
}
}
# generate a new keypair for this application
my $keypair = Authen::U2F::Tester::Keypair->new;
my $handle = $self->keystore->put($keypair->private_key);
my $cert = $self->certificate->as_string(Crypt::OpenSSL::X509::FORMAT_ASN1);
my %client_data = (
typ => 'navigator.id.finishEnrollment',
challenge => $challenge,
origin => $app_id,
cid_pubkey => 'unused');
my $client_data = encode_json(\%client_data);
my $sign_data = pack 'x a32 a32 a* a65',
sha256($app_id),
sha256($client_data),
$handle,
$keypair->public_key;
my $signature = $self->key->sign_hash(sha256($sign_data));
my $response = pack 'a a65 C/a* a* a*',
chr(0x05), $keypair->public_key, $handle, $cert, $signature;
return Authen::U2F::Tester::RegisterResponse->new(
error_code => OK,
response => $response,
client_data => encode_base64url($client_data));
}
sub sign {
my ($self, $app_id, $challenge, @handles) = @_;
my $handle = first { $self->keystore->exists($_) } @handles;
unless (defined $handle) {
return Authen::U2F::Tester::Error->new(DEVICE_INELIGIBLE);
}
my %client_data = (
typ => 'navigator.id.getAssertion',
challenge => $challenge,
origin => $app_id,
cid_pubkey => 'unused');
my $client_data = encode_json(\%client_data);
my $pkec = $self->keystore->get($handle);
my $counter = ++$COUNTER;
# generate the signature
my $sign_data = pack 'a32 a N a32',
sha256($app_id), # 32 byte SHA256 application parameter
chr(0x01), # 1 byte user presence
$counter, # 4 byte counter
sha256($client_data); # 32 byte SHA256 of client data JSON
my $signature = $pkec->sign_hash(sha256($sign_data));
my $response = pack 'a N a*',
chr(0x01),
( run in 1.044 second using v1.01-cache-2.11-cpan-13bb782fe5a )