#1386 - Add to list2FDevice name & epoch hash keys

This commit is contained in:
Christophe Maudoux 2018-03-29 23:11:46 +02:00
parent 961aea971a
commit 869166cb31
12 changed files with 102 additions and 75 deletions

View File

@ -63,9 +63,11 @@ sub run {
# ($token->{_totp2fSecret})
# Now check TOTP code to verify that user has a valid TOTP app
my $code = $req->param('code');
unless ($code) {
$self->logger->userInfo('TOTP registration: empty validation form');
my $code = $req->param('code');
my $TOTPName = $req->param('TOTPName');
unless ( $code and $TOTPName ) {
$self->logger->userInfo(
'TOTP registration: empty code or name in validation form');
return $self->p->sendError( $req, 'missingCode', 200 );
}
my $r = $self->verifyCode(
@ -90,17 +92,26 @@ sub run {
# Now code is verified, let's store the master key in persistent data
$self->p->updatePersistentSession( $req,
{ _totp2fSecret => $token->{_totp2fSecret} } );
my $list2FDevices = eval { from_json($req->userData->{list2FDevices}, { allow_nonref => 1 } ) };
unless ($list2FDevices) {
$self->logger->debug("No 2F Device found");
$list2FDevices = [];
};
push $list2FDevices, { type => 'totp', name => 'TOTP_1', _secret => $token->{_totp2fSecret}, epoch => time() };
$self->logger->debug("Append 2F Device : { type => 'totp', name => 'TOTP1' }");
my $list2FDevices = eval {
from_json( $req->userData->{list2FDevices}, { allow_nonref => 1 } );
};
unless ($list2FDevices) {
$self->logger->debug("No 2F Device found");
$list2FDevices = [];
}
push $list2FDevices,
{
type => 'totp',
name => $TOTPName,
_secret => $token->{_totp2fSecret},
epoch => time()
};
$self->logger->debug(
"Append 2F Device : { type => 'totp', name => $TOTPName }");
$self->p->updatePersistentSession( $req,
{ list2FDevices => to_json($list2FDevices) } );
{ list2FDevices => to_json($list2FDevices) } );
$self->userLogger->notice('TOTP registration succeed');
return [ 200, [ 'Content-Type' => 'application/json' ],
['{"result":1}'] ];

View File

@ -5,7 +5,6 @@ use strict;
use Mouse;
use JSON qw(from_json to_json);
our $VERSION = '2.0.0';
extends 'Lemonldap::NG::Portal::Lib::U2F';
@ -47,14 +46,15 @@ sub run {
my ( $resp, $challenge );
$self->logger->debug('Regististration response');
unless ($resp = $req->param('registration')
and $challenge = $req->param('challenge') )
and $challenge = $req->param('challenge')
and $req->param('keyName') )
{
return $self->p->sendError( $req, 'Missing registration parameter',
400 );
}
$self->logger->debug("Get registration data $resp");
$self->logger->debug("Get challenge $challenge");
eval { $challenge = JSON::from_json($challenge)->{challenge} };
eval { $challenge = from_json($challenge)->{challenge} };
if ($@) {
$self->userLogger->error("Bad challenge: $@");
return $self->p->sendError( $req, 'Bad challenge', 400 );
@ -63,34 +63,29 @@ sub run {
if ( $c->setChallenge($challenge) ) {
my ( $keyHandle, $userKey ) = $c->registrationVerify($resp);
if ( $keyHandle and $userKey ) {
my $list2FDevices = eval { from_json($req->userData->{list2FDevices}, { allow_nonref => 1 } ) };
unless ($list2FDevices) {
$self->logger->debug("No 2F Device found");
$list2FDevices = [];
};
push $list2FDevices, { type => 'U2F', name => 'U2F_1', _userKey => $self->encode_base64url($userKey, ''), _keyHandle => $keyHandle, epoch => time() };
$self->logger->debug("Append 2F Device : { type => 'U2F', name => 'U2F_1' }" );
$self->p->updatePersistentSession( $req,
{ list2FDevices => to_json($list2FDevices) } );
my $list2FDevices = eval {
from_json( $req->userData->{list2FDevices},
{ allow_nonref => 1 } );
};
unless ($list2FDevices) {
$self->logger->debug("No 2F Device found");
$list2FDevices = [];
}
my $keyName = $req->param('keyName');
push $list2FDevices,
{
type => 'U2F',
name => $keyName,
_userKey => $self->encode_base64url( $userKey, '' ),
_keyHandle => $keyHandle,
epoch => time()
};
$self->logger->debug(
"Append 2F Device : { type => 'U2F', name => $keyName }");
$self->p->updatePersistentSession( $req,
{ list2FDevices => to_json($list2FDevices) } );
$self->p->updatePersistentSession(
$req,
{

View File

@ -32,41 +32,48 @@ sub init {
sub run {
my ( $self, $req, $action ) = @_;
if ( $action eq 'register' ) {
my $otp = $req->param('otp');
if ( $otp and length($otp) > $self->conf->{yubikey2fPublicIDSize} ) {
my $otp = $req->param('otp');
my $UBKName = $req->param('UBKName');
if ( $UBKName
and $otp
and length($otp) > $self->conf->{yubikey2fPublicIDSize} )
{
my $keys = $req->userData->{_yubikeys} || '';
$keys .= ( $keys ? ', ' : '' )
. substr( $otp, 0, $self->conf->{yubikey2fPublicIDSize} );
my $key = substr( $otp, 0, $self->conf->{yubikey2fPublicIDSize} );
my $list2FDevices = eval { from_json($req->userData->{list2FDevices}, { allow_nonref => 1 } ) };
unless ($list2FDevices) {
$self->logger->debug("No 2F Device found");
$list2FDevices = [];
};
push $list2FDevices, { type => 'UBK', name => 'UBK_1', _yubikey => $key, epoch => time() };
$self->logger->debug("Append 2F Device : { type => 'UBK', name => 'UBK_1' }" );
$self->p->updatePersistentSession( $req,
{ list2FDevices => to_json($list2FDevices) } );
my $list2FDevices = eval {
from_json( $req->userData->{list2FDevices},
{ allow_nonref => 1 } );
};
unless ($list2FDevices) {
$self->logger->debug("No 2F Device found");
$list2FDevices = [];
}
push $list2FDevices,
{
type => 'UBK',
name => $UBKName,
_yubikey => $key,
epoch => time()
};
$self->logger->debug(
"Append 2F Device : { type => 'UBK', name => $UBKName }");
$self->p->updatePersistentSession( $req,
{ list2FDevices => to_json($list2FDevices) } );
$self->p->updatePersistentSession( $req, { _yubikeys => $keys } );
return $self->p->sendHtml(
$req, 'error',
params => {
RAW_ERROR => 'yourKeyIsRegistered',
RAW_ERROR => 'yourKeyIsRegistered',
AUTH_ERROR_TYPE => 'positive',
}
);
}
else {
$self->userLogger->error('Yubikey 2F: no code');
$self->userLogger->error('Yubikey 2F: no code or name');
return $self->p->sendHtml(
$req, 'error',
params => {

View File

@ -66,6 +66,7 @@ verify = ->
data:
token: token
code: val
TOTPName: $('#TOTPName').val()
error: displayError
success: (data) ->
if data.error

View File

@ -44,9 +44,10 @@ register = ->
$.ajax
type: "POST"
url: "#{portal}2fregisters/u/registration"
data:
data:
registration: JSON.stringify data
challenge: JSON.stringify ch
keyName: $('#keyName').val()
dataType: 'json'
success: (resp) ->
if resp.error

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.9.3
/*
LemonLDAP::NG TOTP registration script
@ -82,7 +82,8 @@ LemonLDAP::NG TOTP registration script
dataType: 'json',
data: {
token: token,
code: val
code: val,
TOTPName: $('#TOTPName').val()
},
error: displayError,
success: function(data) {

View File

@ -1 +1 @@
(function(){var a,b,e,d,c,f;e=function(g,h){$("#msg").html(window.translate(g));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+h);if(h==="positive"){h="success"}return $("#color").addClass("alert-"+h)};a=function(h,g,k){var i;console.log("Error",k);i=JSON.parse(h.responseText);if(i&&i.error){i=i.error.replace(/.* /,"");console.log("Returned error",i);return e(i,"warning")}};d="";b=function(g){return $.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",data:{newkey:g},error:a,success:function(j){var h,i;if(j.error){return e(j.error,"warning")}if(!(j.portal&&j.user&&j.secret)){return e("PE24","danger")}i="otpauth://totp/"+(escape(j.portal))+":"+(escape(j.user))+"?secret="+j.secret+"&issuer="+(escape(j.portal));if(j.digits!==6){i+="&digits="+j.digits}if(j.interval!==30){i+="&period="+j.interval}h=new QRious({element:document.getElementById("qr"),value:i,size:150});$("#serialized").text(i);if(j.newkey){e("yourNewTotpKey","warning")}else{e("yourTotpKey","success")}return d=j.token}})};f=function(){var g;g=$("#code").val();if(!g){return e("fillTheForm","warning")}else{return $.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:d,code:g},error:a,success:function(h){if(h.error){if(h.error.match(/badCode/)){return e("badCode","warning")}else{return e(h.error,"danger")}}else{return e("yourKeyIsRegistered","success")}}})}};c=function(){return $.ajax({type:"POST",url:portal+"/totpregister/unregister",data:{},dataType:"json",error:a,success:function(g){}},e("yourKeyIsUnregistered","success"))};$(document).ready(function(){b(0);$("#changekey").on("click",function(){return b(1)});$("#verify").on("click",function(){return f()});return $("#unregister").on("click",function(){return c()})})}).call(this);
(function(){var a,b,e,d,c,f;e=function(g,h){$("#msg").html(window.translate(g));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+h);if(h==="positive"){h="success"}return $("#color").addClass("alert-"+h)};a=function(h,g,k){var i;console.log("Error",k);i=JSON.parse(h.responseText);if(i&&i.error){i=i.error.replace(/.* /,"");console.log("Returned error",i);return e(i,"warning")}};d="";b=function(g){return $.ajax({type:"POST",url:portal+"/2fregisters/totp/getkey",dataType:"json",data:{newkey:g},error:a,success:function(j){var h,i;if(j.error){return e(j.error,"warning")}if(!(j.portal&&j.user&&j.secret)){return e("PE24","danger")}i="otpauth://totp/"+(escape(j.portal))+":"+(escape(j.user))+"?secret="+j.secret+"&issuer="+(escape(j.portal));if(j.digits!==6){i+="&digits="+j.digits}if(j.interval!==30){i+="&period="+j.interval}h=new QRious({element:document.getElementById("qr"),value:i,size:150});$("#serialized").text(i);if(j.newkey){e("yourNewTotpKey","warning")}else{e("yourTotpKey","success")}return d=j.token}})};f=function(){var g;g=$("#code").val();if(!g){return e("fillTheForm","warning")}else{return $.ajax({type:"POST",url:portal+"/2fregisters/totp/verify",dataType:"json",data:{token:d,code:g,TOTPName:$("#TOTPName").val()},error:a,success:function(h){if(h.error){if(h.error.match(/badCode/)){return e("badCode","warning")}else{return e(h.error,"danger")}}else{return e("yourKeyIsRegistered","success")}}})}};c=function(){return $.ajax({type:"POST",url:portal+"/totpregister/unregister",data:{},dataType:"json",error:a,success:function(g){}},e("yourKeyIsUnregistered","success"))};$(document).ready(function(){b(0);$("#changekey").on("click",function(){return b(1)});$("#verify").on("click",function(){return f()});return $("#unregister").on("click",function(){return c()})})}).call(this);

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7
// Generated by CoffeeScript 1.9.3
/*
LemonLDAP::NG U2F registration script
@ -55,7 +55,8 @@ LemonLDAP::NG U2F registration script
url: portal + "2fregisters/u/registration",
data: {
registration: JSON.stringify(data),
challenge: JSON.stringify(ch)
challenge: JSON.stringify(ch),
keyName: $('#keyName').val()
},
dataType: 'json',
success: function(resp) {

View File

@ -1 +1 @@
(function(){var a,c,d,b,e;d=function(f,g){$("#msg").html(window.translate(f));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+g);if(g==="positive"){g="success"}return $("#color").addClass("alert-"+g)};a=function(g,f,i){var h;console.log("Error",i);h=JSON.parse(g.responseText);if(h&&h.error){h=h.error.replace(/.* /,"");console.log("Returned error",h);return d(h,"warning")}};c=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/register",data:{},dataType:"json",error:a,success:function(f){var g;g=[{challenge:f.challenge,version:f.version}];d("touchU2fDevice","positive");$("#u2fPermission").show();return u2f.register(f.appId,g,[],function(h){$("#u2fPermission").hide();if(h.errorCode){return d("unableToGetKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/registration",data:{registration:JSON.stringify(h),challenge:JSON.stringify(f)},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsRegistered","positive")}}},error:a})}})}})};b=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/unregistration",data:{},dataType:"json",error:a,success:function(f){if(f.error){return d("u2fFailed","warning")}else{if(f.result){return d("yourKeyIsUnregistered","positive")}}},error:a})};e=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/verify",data:{},dataType:"json",error:a,success:function(f){var g;g=[{keyHandle:f.keyHandle,version:f.version}];d("touchU2fDevice","positive");return u2f.sign(f.appId,f.challenge,g,function(h){if(h.errorCode){return d("unableToGetU2FKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/signature",data:{signature:JSON.stringify(h),challenge:f.challenge},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsVerified","positive")}}},error:function(k,i,l){return console.log("error",l)}})}})}})};$(document).ready(function(){$("#u2fPermission").hide();$("#register").on("click",c);$("#unregister").on("click",b);$("#verify").on("click",e);return $("#goback").attr("href",portal)})}).call(this);
(function(){var a,c,d,b,e;d=function(f,g){$("#msg").html(window.translate(f));$("#color").removeClass("message-positive message-warning alert-success alert-warning");$("#color").addClass("message-"+g);if(g==="positive"){g="success"}return $("#color").addClass("alert-"+g)};a=function(g,f,i){var h;console.log("Error",i);h=JSON.parse(g.responseText);if(h&&h.error){h=h.error.replace(/.* /,"");console.log("Returned error",h);return d(h,"warning")}};c=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/register",data:{},dataType:"json",error:a,success:function(f){var g;g=[{challenge:f.challenge,version:f.version}];d("touchU2fDevice","positive");$("#u2fPermission").show();return u2f.register(f.appId,g,[],function(h){$("#u2fPermission").hide();if(h.errorCode){return d("unableToGetKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/registration",data:{registration:JSON.stringify(h),challenge:JSON.stringify(f),keyName:$("#keyName").val()},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsRegistered","positive")}}},error:a})}})}})};b=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/unregistration",data:{},dataType:"json",error:a,success:function(f){if(f.error){return d("u2fFailed","warning")}else{if(f.result){return d("yourKeyIsUnregistered","positive")}}},error:a})};e=function(){return $.ajax({type:"POST",url:portal+"2fregisters/u/verify",data:{},dataType:"json",error:a,success:function(f){var g;g=[{keyHandle:f.keyHandle,version:f.version}];d("touchU2fDevice","positive");return u2f.sign(f.appId,f.challenge,g,function(h){if(h.errorCode){return d("unableToGetU2FKey","warning")}else{return $.ajax({type:"POST",url:portal+"2fregisters/u/signature",data:{signature:JSON.stringify(h),challenge:f.challenge},dataType:"json",success:function(i){if(i.error){return d("u2fFailed","warning")}else{if(i.result){return d("yourKeyIsVerified","positive")}}},error:function(k,i,l){return console.log("error",l)}})}})}})};$(document).ready(function(){$("#u2fPermission").hide();$("#register").on("click",c);$("#unregister").on("click",b);$("#verify").on("click",e);return $("#goback").attr("href",portal)})}).call(this);

View File

@ -13,7 +13,10 @@
</pre>
</div>
<div class="form-group">
<input id="code" name="code" type="number" />
<b>Code&nbsp;: </b>&nbsp;<input id="code" name="code" type="number" />
<br>
<br>
<b>Nom : </b>&nbsp;<input type="text" id="TOTPName" name="TOTPName" value="MyTOTP"/>
</div>
<div class="buttons">
<span id="changekey" class="btn btn-warning" role="button">

View File

@ -10,6 +10,9 @@
<div id="u2fPermission" trspan="u2fPermission" class="alert alert-info">You may be prompted to allow the site permission to access your security keys. After granting permission, the device will start to blink.
</div>
<div class="buttons">
<span class="form-group">
<b>Nom : </b>&nbsp;<input type="text" id="keyName" name="keyName" value="MyU2FKey"/>
</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span id="register" class="btn btn-warning" role="button">
<span class="glyphicon glyphicon-plus-sign"></span>&nbsp;
<span trspan="register">Register</span>

View File

@ -8,7 +8,11 @@
<div class="panel-body">
<form action="/2fregisters/yubikey/register" method="post">
<div class="form-group">
<input id="otp" name="otp" />
<b>Nom : </b><input type="text" id="UBKName" name="UBKName" value="MyYubikey"/>
<br>
<br>
<b>Id : </b><input type="text" id="otp" name="otp" />
<input type="submit" value="Submit">
</div>
</form>
</div>