$Id$ package main; use strict; use warnings; ## apt-get install libconvert-base32-perl libauthen-oath-perl libcrypt-urandom-perl use Convert::Base32; use Authen::OATH; use URI::Escape; use Crypt::URandom qw( urandom ); sub GoogleAuth_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "GoogleAuth_Define"; $hash->{UndefFn} = "GoogleAuth_Undefine"; $hash->{DeleteFn} = "GoogleAuth_Delete"; $hash->{SetFn} = "GoogleAuth_Set"; $hash->{GetFn} = "GoogleAuth_Get"; # $hash->{AttrFn} = "GoogleAuth_Attr"; $hash->{AttrList} = "ga_qrsize ". "$readingFnAttributes"; } sub GoogleAuth_Define($$) { my ($hash, $def) = @_; my $name = $hash->{NAME}; my @a = split("[ \t][ \t]*", $def); return "Usage: Use Google Authenticator" if(@a != 2); ## ich würde keine Prüfung auf bereits vorhandene GA machen, ## es kann durchaus Fälle geben, in denen man mehr als 1 Schlüssel definieren möchte # my $d = $modules{$hash->{TYPE}}{defptr}; # return "$hash->{TYPE} device already defined as $d->{NAME}." if( defined($d) ); Log3($hash,4,"googleAuth $name: defined"); readingsSingleUpdate($hash,'state','defined',1); return undef; } sub GoogleAuth_Undefine($$) { my ($hash, $arg) = @_; delete $modules{$hash->{TYPE}}{defptr}; return undef; } sub GoogleAuth_Delete() { my ($hash, $arg) = @_; my $name = $hash->{NAME}; setKeyValue("googleAuth$name",undef); } sub GoogleAuth_Set($$@) { my ($hash, $name, $cmd, @args) = @_; my $usage = "Unknown argument, choose one of new:noArg"; if($cmd eq "new") { #SOURCE: https://blog.darkpan.com/article/6/Perl-and-Google-Authenticator.html my $secret_bytes = urandom(50); my $secret_base32 = encode_base32( $secret_bytes ); Log3($hash,5,"googleAuth $name: secret_bytes=$secret_bytes"); Log3($hash,5,"googleAuth $name: set secret_base32=$secret_base32"); setKeyValue("googleAuth$name",$secret_base32); # write to fhem keystore my $label = "FHEM%20Authentication%20$name"; my $qrsize = AttrVal("$name",'ga_qrsize','200x200'); my $url = "otpauth://totp/$label?secret=$secret_base32"; my $qr_url = "https://chart.googleapis.com/chart?cht=qr&chs=$qrsize"."&chl=".uri_escape($url); readingsSingleUpdate($hash,'qr_url',$qr_url,1); my $ret = "<\/a>"; return $ret; ; } return $usage; } sub GoogleAuth_Get($$@) { my ($hash, $name, $cmd, @args) = @_; my $usage = "Unknown argument, choose one of check"; if ($cmd eq "check") { my $given_token = shift @args; return "Token missing!" unless (defined($given_token) && $given_token); $given_token = _ga_make_token_6($given_token); Log3($hash,4,"googleAuth $name: given: $given_token"); my $secret_base32 = getKeyValue("googleAuth$name"); # read from fhem keystore Log3($hash,5,"googleAuth $name: get secret_base32=$secret_base32"); $secret_base32 = decode_base32($secret_base32); Log3($hash,5,"googleAuth $name: secret_bytes=$secret_base32"); my $oath = Authen::OATH->new; my @possible = map { _ga_make_token_6($oath->totp($secret_base32, $_)) } time-30, time, time+30; Log3($hash,4,"googleAuth $name: possible: ".join ' ',@possible); my $result = (grep /^$given_token$/, @possible) ? 1 : -1; Log3($hash,4,"googleAuth $name: result: $result"); return $result; } return $usage; } sub _ga_make_token_6($) { my $token = shift; while (length $token < 6) { $token = "0$token"; } return $token; } 1; =pod =item helper =item summary Module to use GoogleAuthnticator =item summary_DE Modul zur Nutzung von GoogleAuthenticator =begin html