2017-02-04 08:55:47 +01:00
|
|
|
# Self U2F registration
|
2018-02-19 14:15:29 +01:00
|
|
|
package Lemonldap::NG::Portal::2F::Register::U2F;
|
2017-02-02 22:48:32 +01:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Mouse;
|
|
|
|
|
|
|
|
our $VERSION = '2.0.0';
|
|
|
|
|
2017-02-04 08:55:47 +01:00
|
|
|
extends 'Lemonldap::NG::Portal::Lib::U2F';
|
2017-02-02 22:48:32 +01:00
|
|
|
|
2017-02-04 08:55:47 +01:00
|
|
|
# INITIALIZATION
|
2017-02-03 18:14:13 +01:00
|
|
|
|
2018-03-08 20:36:32 +01:00
|
|
|
has prefix => ( is => 'rw', default => 'u' );
|
|
|
|
|
2017-02-02 22:48:32 +01:00
|
|
|
sub init {
|
|
|
|
my ($self) = @_;
|
2017-02-04 08:55:47 +01:00
|
|
|
return 0 unless $self->SUPER::init;
|
2017-02-08 14:01:02 +01:00
|
|
|
$self->addAuthRoute( u2fregister => { ':action' => 'run' }, ['POST'] );
|
|
|
|
$self->addAuthRoute( 'u2fregister.html' => undef, ['GET'] );
|
2017-02-02 22:48:32 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-02-04 08:55:47 +01:00
|
|
|
# RUNNING METHODS
|
|
|
|
|
|
|
|
# Main method
|
2017-02-02 22:48:32 +01:00
|
|
|
sub run {
|
|
|
|
my ( $self, $req ) = @_;
|
2017-02-08 14:01:02 +01:00
|
|
|
my $action = $req->param('action');
|
2017-02-02 22:48:32 +01:00
|
|
|
|
2017-02-08 14:01:02 +01:00
|
|
|
if ( $action eq 'register' ) {
|
|
|
|
my $challenge = $self->crypter->registrationChallenge;
|
|
|
|
return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
|
|
|
|
}
|
|
|
|
if ( $action eq 'registration' ) {
|
2018-03-06 22:50:40 +01:00
|
|
|
my ( $resp, $challenge );
|
|
|
|
unless ($resp = $req->param('registration')
|
|
|
|
and $challenge = $req->param('challenge') )
|
|
|
|
{
|
2017-02-08 14:01:02 +01:00
|
|
|
return $self->p->sendError( $req, 'Missing registration parameter',
|
|
|
|
400 );
|
|
|
|
}
|
2017-02-15 07:41:50 +01:00
|
|
|
$self->logger->debug("Get registration data $resp");
|
2018-03-06 22:50:40 +01:00
|
|
|
$self->logger->debug("Get challenge $challenge");
|
|
|
|
eval { $challenge = JSON::from_json($challenge)->{challenge} };
|
|
|
|
if ($@) {
|
|
|
|
$self->userLogger->error("Bad challenge: $@");
|
|
|
|
return $self->p->sendError( $req, 'Bad challenge', 400 );
|
|
|
|
}
|
|
|
|
my $c = $self->crypter;
|
|
|
|
if ( $c->setChallenge($challenge) ) {
|
|
|
|
my ( $keyHandle, $userKey ) = $c->registrationVerify($resp);
|
|
|
|
if ( $keyHandle and $userKey ) {
|
|
|
|
$self->p->updatePersistentSession(
|
|
|
|
$req,
|
|
|
|
{
|
|
|
|
_u2fKeyHandle =>
|
|
|
|
$self->encode_base64url( $keyHandle, '' ),
|
|
|
|
_u2fUserKey => $self->encode_base64url( $userKey, '' )
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return [
|
|
|
|
200, [ 'Content-Type' => 'application/json' ],
|
|
|
|
['{"result":1}']
|
|
|
|
];
|
|
|
|
}
|
2017-02-03 07:23:39 +01:00
|
|
|
}
|
2017-02-08 14:01:02 +01:00
|
|
|
my $err = Crypt::U2F::Server::Simple::lastError();
|
2017-02-15 15:16:59 +01:00
|
|
|
$self->userLogger->warn("U2F Registration failed: $err");
|
2017-02-08 14:01:02 +01:00
|
|
|
return $self->p->sendError( $req, $err, 200 );
|
2017-02-02 22:48:32 +01:00
|
|
|
}
|
2018-02-21 09:23:41 +01:00
|
|
|
|
|
|
|
if ( $action eq 'unregister' ) {
|
|
|
|
my $challenge = $self->crypter->registrationChallenge;
|
|
|
|
return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
|
|
|
|
}
|
|
|
|
if ( $action eq 'unregistration' ) {
|
2018-03-09 22:32:42 +01:00
|
|
|
#my ( $resp, $challenge );
|
|
|
|
#unless ($resp = $req->param('registration')
|
|
|
|
#and $challenge = $req->param('challenge') )
|
|
|
|
#{
|
|
|
|
#return $self->p->sendError( $req, 'Missing registration parameter',
|
|
|
|
#400 );
|
|
|
|
#}
|
|
|
|
#$self->logger->debug("Get unregistration data $resp");
|
|
|
|
#$self->logger->debug("Get challenge $challenge");
|
|
|
|
#eval { $challenge = JSON::from_json($challenge)->{challenge} };
|
|
|
|
#if ($@) {
|
|
|
|
#$self->userLogger->error("Bad challenge: $@");
|
|
|
|
#return $self->p->sendError( $req, 'Bad challenge', 400 );
|
|
|
|
#}
|
|
|
|
#my $c = $self->crypter;
|
|
|
|
#if ( $c->setChallenge($challenge) ) {
|
|
|
|
#my ( $keyHandle, $userKey ) = $c->registrationVerify($resp);
|
|
|
|
#if ( $keyHandle and $userKey ) {
|
2018-03-06 22:56:16 +01:00
|
|
|
$self->p->updatePersistentSession(
|
|
|
|
$req,
|
|
|
|
{
|
|
|
|
_u2fKeyHandle => '',
|
|
|
|
_u2fUserKey => ''
|
|
|
|
}
|
|
|
|
);
|
2018-03-09 22:32:42 +01:00
|
|
|
$self->userLogger->notice('U2F key unregistration succeed');
|
2018-03-06 22:56:16 +01:00
|
|
|
return [
|
|
|
|
200, [ 'Content-Type' => 'application/json' ],
|
|
|
|
['{"result":1}']
|
|
|
|
];
|
2018-03-09 22:32:42 +01:00
|
|
|
#}
|
|
|
|
#}
|
2018-02-21 09:23:41 +01:00
|
|
|
my $err = Crypt::U2F::Server::Simple::lastError();
|
|
|
|
$self->userLogger->warn("U2F Unregistration failed: $err");
|
|
|
|
return $self->p->sendError( $req, $err, 200 );
|
|
|
|
}
|
|
|
|
|
2017-02-08 14:01:02 +01:00
|
|
|
if ( $action eq 'verify' ) {
|
|
|
|
my ( $err, $error ) = $self->loadUser($req);
|
|
|
|
if ( $err == -1 ) {
|
|
|
|
return $self->p->sendError( $req, "U2F error: $error", 200 );
|
|
|
|
}
|
|
|
|
elsif ( $err == 0 ) {
|
|
|
|
return $self->p->sendError( $req, "noU2FKeyFound" );
|
|
|
|
}
|
2018-03-07 23:29:42 +01:00
|
|
|
my $challenge = $req->datas->{crypter}->authenticationChallenge;
|
2017-02-08 14:01:02 +01:00
|
|
|
return [ 200, [ 'Content-Type' => 'application/json' ], [$challenge] ];
|
|
|
|
}
|
|
|
|
if ( $action eq 'signature' ) {
|
|
|
|
my $resp;
|
|
|
|
unless ( $resp = $req->param('signature') ) {
|
|
|
|
return $self->p->sendError( $req, 'Missing signature parameter',
|
|
|
|
400 );
|
|
|
|
}
|
|
|
|
my ( $err, $error ) = $self->loadUser($req);
|
|
|
|
if ( $err == -1 ) {
|
|
|
|
return $self->p->sendError( $req, "U2F error: $error", 200 );
|
2017-02-07 23:04:49 +01:00
|
|
|
}
|
2017-02-08 14:01:02 +01:00
|
|
|
elsif ( $err == 0 ) {
|
|
|
|
return $self->p->sendError( $req, "noU2FKeyFound" );
|
|
|
|
}
|
2018-03-06 22:50:40 +01:00
|
|
|
my $res =
|
2018-03-07 23:29:42 +01:00
|
|
|
( $req->datas->{crypter}->authenticationVerify($resp) ? 1 : 0 );
|
2017-02-08 14:01:02 +01:00
|
|
|
return [
|
|
|
|
200, [ 'Content-Type' => 'application/json' ],
|
|
|
|
[qq'{"result":$res}']
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub loadUser {
|
|
|
|
my ( $self, $req ) = @_;
|
|
|
|
my $uid = $req->userData->{ $self->conf->{whatToTrace} };
|
2017-02-08 19:10:06 +01:00
|
|
|
my $session = $self->p->getPersistentSession($uid);
|
2017-02-08 14:01:02 +01:00
|
|
|
my $kh = $session->data->{_u2fKeyHandle};
|
|
|
|
my $uk = $session->data->{_u2fUserKey};
|
|
|
|
unless ( $kh and $uk ) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-03-07 23:29:42 +01:00
|
|
|
$req->datas->{crypter} = $self->crypter(
|
2018-03-06 07:03:42 +01:00
|
|
|
keyHandle => $self->decode_base64url($kh),
|
|
|
|
publicKey => $self->decode_base64url($uk)
|
|
|
|
);
|
2018-03-07 23:29:42 +01:00
|
|
|
unless ( $req->datas->{crypter} ) {
|
2017-02-08 14:01:02 +01:00
|
|
|
my $error = Crypt::U2F::Server::Simple::lastError();
|
|
|
|
return ( -1, $error );
|
|
|
|
}
|
|
|
|
return 1;
|
2017-02-02 22:48:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
1;
|