view release on metacpan or search on metacpan
Credential ID <https://www.w3.org/TR/webauthn-2/#credential-id>. If
this is not supplied, one will be generated.
* is_resident - contains a boolean that if set to true, a client-side
discoverable credential
<https://w3c.github.io/webauthn/#client-side-discoverable-credential>
is created. If set to false, a server-side credential
<https://w3c.github.io/webauthn/#server-side-credential> is created
instead.
* private_key - either a RFC5958
<https://www.rfc-editor.org/rfc/rfc5958> encoded private key encoded
using encode_base64url or a hash containing the following keys;
* name - contains the name of the private key algorithm, such as
"RSA-PSS" (the default), "RSASSA-PKCS1-v1_5", "ECDSA" or "ECDH".
* size - contains the modulus length of the private key. This is
only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.
* hash - contains the name of the hash algorithm, such as "SHA-512"
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
foreach my $credential ($firefox->webauthn_credentials()) {
$firefox->delete_webauthn_credential($credential);
# ... time passes ...
$firefox->add_webauthn_credential(
id => $credential->id(),
host => $credential->host(),
user => $credential->user(),
private_key => $credential->private_key(),
is_resident => $credential->is_resident(),
sign_count => $credential->sign_count(),
);
}
$firefox->go('about:blank');
$firefox->clear_cache(Firefox::Marionette::Cache::CLEAR_COOKIES());
$firefox->go('https://webauthn.io');
$firefox->find_id('input-email')->type($user_name);
$firefox->find_id('login-button')->click();
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
foreach my $credential ($firefox->webauthn_credentials()) {
$firefox->delete_webauthn_credential($credential);
\# ... time passes ...
$firefox->add_webauthn_credential(
id => $credential->id(),
host => $credential->host(),
user => $credential->user(),
private_key => $credential->private_key(),
is_resident => $credential->is_resident(),
sign_count => $credential->sign_count(),
);
}
$firefox->go('about:blank');
$firefox->clear_cache(Firefox::Marionette::Cache::CLEAR_COOKIES());
$firefox->go('https://webauthn.io');
$firefox->find_id('input-email')->type($user_name);
$firefox->find_id('login-button')->click();
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
lib/Firefox/Marionette.pm view on Meta::CPAN
}
if ( !defined $parameters{id} ) {
my $credential_id = MIME::Base64::encode_base64url(
Crypt::URandom::urandom( _CREDENTIAL_ID_LENGTH() ) );
$parameters{id} = $credential_id;
}
if ( defined $parameters{user} ) {
$parameters{user} =
MIME::Base64::encode_base64url( $parameters{user} );
}
if ( !defined $parameters{private_key} ) {
$parameters{private_key} = {};
}
if ( ref $parameters{private_key} eq 'HASH' ) {
my $script = <<'_JS_';
let privateKeyArguments = {};
if (arguments[0]["name"]) {
privateKeyArguments["name"] = arguments[0]["name"];
} else {
privateKeyArguments["name"] = "RSA-PSS";
}
if ((privateKeyArguments["name"] == "RSA-PSS") || (privateKeyArguments["name"] == "RSASSA-PKCS1-v1_5")) {
privateKeyArguments["modulusLength"] = arguments[0]["size"] || 8192;
privateKeyArguments["publicExponent"] = new Uint8Array([1, 0, 1]);
lib/Firefox/Marionette.pm view on Meta::CPAN
for (let i = 0; i < array.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, array.subarray(i, i + CHUNK_SZ)));
}
let b64Key = window.btoa(c.join(""));
let urlSafeKey = b64Key.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
return urlSafeKey;
})();
return privateKey;
_JS_
my $old = $self->_context('chrome');
$parameters{private_key} = $self->script(
$self->_compress_script($script),
args => [ $parameters{private_key} ]
);
$self->_context($old);
}
if ( !defined $parameters{sign_count} ) {
$parameters{sign_count} = 0;
}
my $authenticator = $self->_get_webauthn_authenticator(%parameters);
my %credential_parameters = (
authenticatorId => $authenticator->id(),
credentialId => $parameters{id},
isResidentCredential => $parameters{is_resident},
rpId => $parameters{host},
privateKey => $parameters{private_key},
signCount => $parameters{sign_count},
userHandle => $parameters{user},
);
my $message_id = $self->_new_message_id();
$self->_send_request(
[
_COMMAND(), $message_id, $self->_command('WebAuthn:AddCredential'),
\%credential_parameters,
]
);
lib/Firefox/Marionette.pm view on Meta::CPAN
=over 4
=item * authenticator - contains the L<authenticator|Firefox::Marionette::WebAuthn::Authenticator> that the credential will be added to. If this parameter is not supplied, the credential will be added to the default authenticator, if one exists.
=item * host - contains the domain that this credential is to be used for. In the language of L<WebAuthn|https://www.w3.org/TR/webauthn-2>, this field is referred to as the L<relying party identifier|https://www.w3.org/TR/webauthn-2/#relying-party-i...
=item * id - contains the unique id for this credential, also known as the L<Credential ID|https://www.w3.org/TR/webauthn-2/#credential-id>. If this is not supplied, one will be generated.
=item * is_resident - contains a boolean that if set to true, a L<client-side discoverable credential|https://w3c.github.io/webauthn/#client-side-discoverable-credential> is created. If set to false, a L<server-side credential|https://w3c.github.io/w...
=item * private_key - either a L<RFC5958|https://www.rfc-editor.org/rfc/rfc5958> encoded private key encoded using L<encode_base64url|MIME::Base64::encode_base64url> or a hash containing the following keys;
=over 8
=item * name - contains the name of the private key algorithm, such as "RSA-PSS" (the default), "RSASSA-PKCS1-v1_5", "ECDSA" or "ECDH".
=item * size - contains the modulus length of the private key. This is only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.
=item * hash - contains the name of the hash algorithm, such as "SHA-512" (the default). This is only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.
=item * curve - contains the name of the curve for the private key, such as "P-384" (the default). This is only valid for "ECDSA" or "ECDH" private keys.
lib/Firefox/Marionette.pm view on Meta::CPAN
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
foreach my $credential ($firefox->webauthn_credentials()) {
$firefox->delete_webauthn_credential($credential);
# ... time passes ...
$firefox->add_webauthn_credential(
id => $credential->id(),
host => $credential->host(),
user => $credential->user(),
private_key => $credential->private_key(),
is_resident => $credential->is_resident(),
sign_count => $credential->sign_count(),
);
}
$firefox->go('about:blank');
$firefox->clear_cache(Firefox::Marionette::Cache::CLEAR_COOKIES());
$firefox->go('https://webauthn.io');
$firefox->find_id('input-email')->type($user_name);
$firefox->find_id('login-button')->click();
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
lib/Firefox/Marionette/WebAuthn/Credential.pm view on Meta::CPAN
use strict;
use warnings;
our $VERSION = '1.67';
my %key_mapping = (
isResidentCredential => 'is_resident',
rpId => 'host',
signCount => 'sign_count',
userHandle => 'user',
privateKey => 'private_key',
credentialId => 'id',
);
sub new {
my ( $class, %parameters ) = @_;
my $self = bless {}, $class;
foreach my $key_name ( sort { $a cmp $b } keys %key_mapping ) {
if ( exists $parameters{ $key_mapping{$key_name} } ) {
$self->{ $key_mapping{$key_name} } =
$parameters{ $key_mapping{$key_name} };
lib/Firefox/Marionette/WebAuthn/Credential.pm view on Meta::CPAN
sub host {
my ($self) = @_;
return $self->{host};
}
sub is_resident {
my ($self) = @_;
return $self->{is_resident};
}
sub private_key {
my ($self) = @_;
return $self->{private_key};
}
sub sign_count {
my ($self) = @_;
return $self->{sign_count};
}
sub user {
my ($self) = @_;
return $self->{user};
lib/Firefox/Marionette/WebAuthn/Credential.pm view on Meta::CPAN
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
foreach my $credential ($firefox->webauthn_credentials()) {
$firefox->delete_webauthn_credential($credential);
# ... time passes ...
$firefox->add_webauthn_credential(
id => $credential->id(),
host => $credential->host(),
user => $credential->user(),
private_key => $credential->private_key(),
is_resident => $credential->is_resident(),
sign_count => $credential->sign_count(),
);
}
$firefox->go('about:blank');
$firefox->clear_cache(Firefox::Marionette::Cache::CLEAR_COOKIES());
$firefox->go('https://webauthn.io');
$firefox->find_id('input-email')->type($user_name);
$firefox->find_id('login-button')->click();
$firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
lib/Firefox/Marionette/WebAuthn/Credential.pm view on Meta::CPAN
accepts a hash as a parameter. Allowed keys are below;
=over 4
=item * host - contains the domain that this credential is to be used for. In the language of L<WebAuthn|https://www.w3.org/TR/webauthn-2>, this field is referred to as the L<relying party identifier|https://www.w3.org/TR/webauthn-2/#relying-party-i...
=item * id - contains the unique id for this credential, also known as the L<Credential ID|https://www.w3.org/TR/webauthn-2/#credential-id>.
=item * is_resident - contains a boolean that if set to true, a L<client-side discoverable credential|https://w3c.github.io/webauthn/#client-side-discoverable-credential> is to be created. If set to false, a L<server-side credential|https://w3c.githu...
=item * private_key - either a L<RFC5958|https://www.rfc-editor.org/rfc/rfc5958> encoded private key encoded using L<encode_base64url|MIME::Base64#encode_base64url(-$bytes-)> or a hash containing the following keys;
=over 8
=item * name - contains the name of the private key algorithm, such as "RSA-PSS" (the default), "RSASSA-PKCS1-v1_5", "ECDSA" or "ECDH".
=item * size - contains the modulus length of the private key. This is only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.
=item * hash - contains the name of the hash algorithm, such as "SHA-512" (the default). This is only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.
=item * curve - contains the name of the curve for the private key, such as "P-384" (the default). This is only valid for "ECDSA" or "ECDH" private keys.
lib/Firefox/Marionette/WebAuthn/Credential.pm view on Meta::CPAN
This method returns a new L<Firefox::Marionette::WebAuthn::Credential|Firefox::Marionette::WebAuthn::Credential> object.
=head2 host
returns the domain that this credential is to be used for. In the language of L<WebAuthn|https://www.w3.org/TR/webauthn-2>, this field is referred to as the L<relying party identifier|https://www.w3.org/TR/webauthn-2/#relying-party-identifier> or L<...
=head2 is_resident
returns a boolean that if true, a L<client-side discoverable credential|https://w3c.github.io/webauthn/#client-side-discoverable-credential> is to be created. If false, a L<server-side credential|https://w3c.github.io/webauthn/#server-side-credential...
=head2 private_key
returns a L<RFC5958|https://www.rfc-editor.org/rfc/rfc5958> encoded private key encoded using L<encode_base64url|MIME::Base64#encode_base64url(-$bytes-)>.
=head2 sign_count
returns the L<signature counter|https://w3c.github.io/webauthn/#signature-counter> associated to the L<public key credential source|https://w3c.github.io/webauthn/#public-key-credential-source>.
=head2 user
returns the L<userHandle|https://w3c.github.io/webauthn/#public-key-credential-source-userhandle> associated to the credential encoded using L<encode_base64url|MIME::Base64#encode_base64url(-$bytes-)>.
t/01-marionette.t view on Meta::CPAN
return;
} elsif (time - $^T > $test_time_limit) {
return 1;
} else {
return;
}
}
my $launches = 0;
my $ca_cert_handle;
my $ca_private_key_handle;
my $metacpan_ca_cert_handle;
my $guid_regex = qr/[a-f\d]{8}\-[a-f\d]{4}\-[a-f\d]{4}\-[a-f\d]{4}\-[a-f\d]{12}/smx;
my @old_binary_keys = (qw(firefox_binary firefox marionette));;
my ($major_version, $minor_version, $patch_version);
sub start_firefox {
my ($require_visible, %parameters) = @_;
if ($terminated) {
die "Caught a signal";
}
t/01-marionette.t view on Meta::CPAN
$firefox = undef;
ok(!process_alive($firefox_pid), "Cannot contact firefox process ($firefox_pid)");
}
}
if ($^O eq 'MSWin32') {
} elsif ($ENV{RELEASE_TESTING}) {
eval {
$ca_cert_handle = File::Temp->new( TEMPLATE => File::Spec->catfile( File::Spec->tmpdir(), 'firefox_test_ca_cert_XXXXXXXXXXX')) or Firefox::Marionette::Exception->throw( "Failed to open temporary file for writing:$!");
fcntl $ca_cert_handle, Fcntl::F_SETFD(), 0 or Carp::croak("Can't clear close-on-exec flag on temporary file:$!");
$ca_private_key_handle = File::Temp->new( TEMPLATE => File::Spec->catfile( File::Spec->tmpdir(), 'firefox_test_ca_private_XXXXXXXXXXX')) or Firefox::Marionette::Exception->throw( "Failed to open temporary file for writing:$!");
system {'openssl'} 'openssl', 'genrsa', '-out' => $ca_private_key_handle->filename(), 4096 and Carp::croak("Failed to generate a private key:$!");
my $ca_config_handle = File::Temp->new( TEMPLATE => File::Spec->catfile( File::Spec->tmpdir(), 'firefox_test_ca_config_XXXXXXXXXXX')) or Firefox::Marionette::Exception->throw( "Failed to open temporary file for writing:$!");
$ca_config_handle->print(<<"_CONFIG_");
[ req ]
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
[ req_distinguished_name ]
C = AU
ST = Victoria
t/01-marionette.t view on Meta::CPAN
emailAddress = ddick\@cpan.org
[ req_attributes ]
_CONFIG_
seek $ca_config_handle, 0, 0 or Carp::croak("Failed to seek to start of temporary file:$!");
fcntl $ca_config_handle, Fcntl::F_SETFD(), 0 or Carp::croak("Can't clear close-on-exec flag on temporary file:$!");
system {'openssl'} 'openssl', 'req', '-new', '-x509',
'-set_serial' => '1',
'-config' => $ca_config_handle->filename(),
'-days' => 10,
'-key' => $ca_private_key_handle->filename(),
'-out' => $ca_cert_handle->filename()
and Carp::croak("Failed to generate a CA root certificate:$!");
1;
} or do {
chomp $@;
diag("Did not generate a CA root certificate:$@");
};
}
SKIP: {
t/01-marionette.t view on Meta::CPAN
diag("insecure cert test is not supported for remote hosts");
} elsif (($ENV{FIREFOX_HOST}) && ($ENV{FIREFOX_HOST} eq 'localhost') && ($ENV{FIREFOX_PORT})) {
diag("insecure cert test is not supported for remote hosts");
} elsif ((exists $Config::Config{'d_fork'}) && (defined $Config::Config{'d_fork'}) && ($Config::Config{'d_fork'} eq 'define')) {
my $ip_address = '127.0.0.1';
my $daemon = IO::Socket::SSL->new(
LocalAddr => $ip_address,
LocalPort => 0,
Listen => 20,
SSL_cert_file => $ca_cert_handle->filename(),
SSL_key_file => $ca_private_key_handle->filename(),
);
my $url = "https://$ip_address:" . $daemon->sockport();
if (my $pid = fork) {
wait_for_server_on($daemon, $url, $pid);
eval { $firefox->go(URI->new($url)) };
my $exception = "$@";
chomp $exception;
ok(ref $@ eq 'Firefox::Marionette::Exception::InsecureCertificate', $url . " threw an exception:$exception");
while(kill 0, $pid) {
kill $signals_by_name{TERM}, $pid;
t/04-webauthn.t view on Meta::CPAN
ok($credential->id(), "Credential id is " . $credential->id());
ok($credential->host() eq $host_name, "Hostname is $host_name:" . $credential->host());
ok($credential->user(), "Username is " . $credential->user());
$sign_count_after_one_login = $credential->sign_count();
ok($credential->sign_count() >= 1, "Sign Count is >= 1:" . $credential->sign_count());
$firefox->delete_webauthn_credential($credential);
$firefox->add_webauthn_credential(
id => $credential->id(),
host => $credential->host(),
user => $credential->user(),
private_key => $credential->private_key(),
is_resident => $credential->is_resident(),
sign_count => $credential->sign_count(),
);
}
ok($firefox->go('about:blank'), "Loading about:blank");
ok($firefox->clear_cache(Firefox::Marionette::Cache::CLEAR_COOKIES()), "Deleting all cookies");
ok($firefox->go('https://' . $host_name), "Loading https://$host_name");
ok($firefox->find_id('input-email')->type($user_name), "Entering $user_name for username");
ok($firefox->find_id('login-button')->click(), "Clicking login button for $host_name");
ok($firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); }), "Successfully authenticated to $host_name");
t/04-webauthn.t view on Meta::CPAN
};
foreach my $credential ($firefox->webauthn_credentials($authenticator)) {
ok($credential->id() eq $cred_id, "Credential id is '$cred_id':" . $credential->id());
ok($credential->host() eq $host_name, "Hostname is '$host_name':" . $credential->host());
ok(!$credential->user(), "Username is empty");
ok($credential->is_resident() == 0, "is_resident is 0:" . $credential->is_resident());
ok($credential->sign_count() == 0, "Sign Count is 0:" . $credential->sign_count());
$firefox->delete_webauthn_credential($credential, $authenticator);
}
eval {
$credential = $firefox->add_webauthn_credential( private_key => { name => 'RSA-PSS', size => 1024 }, host => $host_name, user => $user_name);
ok($credential->id(), "Credential id is " . $credential->id());
ok($credential->host() eq $host_name, "Hostname is '$host_name':" . $credential->host());
ok($credential->user() eq $user_name, "Username is '$user_name':" . $credential->user());
};
ok($firefox->delete_webauthn_authenticator($authenticator), "Deleted virtual authenticator");
my $default_authenticator = $firefox->webauthn_authenticator();
ok($default_authenticator, "Default virtual authenticator still exists");
ok($firefox->delete_webauthn_authenticator(), "Deleted default virtual authenticator");
ok(!$firefox->webauthn_authenticator(), "Default virtual authenticator no longer exists");
$authenticator = $firefox->add_webauthn_authenticator( transport => Firefox::Marionette::WebAuthn::Authenticator::BLE(), protocol => Firefox::Marionette::WebAuthn::Authenticator::CTAP1_U2F() );
t/test_daemons.pm view on Meta::CPAN
$self->{ca_directory} = $class->tmp_directory('ca');
$self->{ca_cert_path} =
File::Spec->catfile( $self->{ca_directory}->dirname(), 'ca.crt' );
$self->{ca_cert_handle} = FileHandle->new(
$self->{ca_cert_path},
Fcntl::O_EXCL() | Fcntl::O_RDWR() | Fcntl::O_CREAT(),
Fcntl::S_IRUSR() | Fcntl::S_IWUSR()
)
or
Carp::croak("Failed to create $self->{ca_cert_path}:$EXTENDED_OS_ERROR");
$self->{ca_private_key_path} =
File::Spec->catfile( $self->{ca_directory}->dirname(), 'ca.key' );
$self->{ca_private_key_handle} =
$class->new_key( $key_size, $self->{ca_private_key_path} );
$self->{ca_serial_path} =
File::Spec->catfile( $self->{ca_directory}->dirname(), 'ca.serial' );
$self->{ca_serial_handle} = FileHandle->new(
$self->{ca_serial_path},
Fcntl::O_EXCL() | Fcntl::O_RDWR() | Fcntl::O_CREAT(),
Fcntl::S_IRUSR() | Fcntl::S_IWUSR()
)
or Carp::croak(
"Failed to create $self->{ca_serial_path}:$EXTENDED_OS_ERROR");
print { $self->{ca_serial_handle} } '01'
t/test_daemons.pm view on Meta::CPAN
basicConstraints=critical,CA:TRUE,pathlen:1
extendedKeyUsage=serverAuth
_CONFIG_
seek $self->{ca_config_handle}, 0, 0
or Carp::croak(
"Failed to seek to start of temporary file:$EXTENDED_OS_ERROR");
system {$openssl_binary} $openssl_binary, 'req', '-new', '-x509',
'-config' => $self->{ca_config_path},
'-days' => 10,
'-key' => $self->{ca_private_key_path},
'-out' => $self->{ca_cert_path}
and Carp::croak(
"Failed to generate a CA root certificate:$EXTENDED_OS_ERROR");
return $self;
}
sub config {
my ($self) = @_;
return $self->{ca_config_path};
}
t/test_daemons.pm view on Meta::CPAN
return $self->{ca_serial_path};
}
sub cert {
my ($self) = @_;
return $self->{ca_cert_path};
}
sub key {
my ($self) = @_;
return $self->{ca_private_key_path};
}
sub new_cert {
my ( $self, $key_path, $host_name, $path ) = @_;
my $csr = $self->tmp_handle('csr');
my $cert_handle;
my $cert_path;
if ($path) {
$cert_handle = FileHandle->new(
$path,
t/test_daemons.pm view on Meta::CPAN
Carp::croak("Failed to write to temporary file:$EXTENDED_OS_ERROR");
}
seek $cert_handle, 0, Fcntl::SEEK_SET()
or Carp::croak(
"Failed to seek to start of temporary file:$EXTENDED_OS_ERROR");
return $cert_handle;
}
sub new_key {
my ( $class, $size, $path ) = @_;
my $private_key_handle;
my $private_key_path;
if ($path) {
$private_key_handle = FileHandle->new(
$path,
Fcntl::O_EXCL() | Fcntl::O_RDWR() | Fcntl::O_CREAT(),
Fcntl::S_IRUSR() | Fcntl::S_IWUSR()
) or Carp::croak("Failed to create $path:$EXTENDED_OS_ERROR");
$private_key_path = $path;
}
else {
$private_key_handle = $class->tmp_handle('private_key');
$private_key_path = $private_key_handle->filename();
}
system {$openssl_binary} $openssl_binary, 'genrsa',
'-out' => $private_key_path,
$size
and Carp::croak("Failed to generate a private key:$EXTENDED_OS_ERROR");
return $private_key_handle;
}
package Test::Daemon;
use strict;
use warnings;
use Carp();
use Config;
use Socket();
use English qw( -no_match_vars );