summaryrefslogtreecommitdiff
path: root/totp.pl
blob: fbc892bff47ad5750bc7d7332e0d09d91da54047 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use MIME::Base32;
use POSIX ;
use Digest::SHA;
use v5.14;

# take a string, make it a hex and run HMAC on it
sub  hmac_sha1_hex_string {
	my ($data, $key) = map pack('H*', $_), @_;
	return Digest::SHA::hmac_sha1_hex($data, $key);
}

# make a usable number out of that HMAC result
sub hotp_truncate {
	my ($hex) = @_;
	my $lastbyte= substr $hex, -2;
	my $offset = (hex $lastbyte) & 0xf;
	my $offset = $offset * 2;

	my $truncate = (
		(hex substr($hex, $offset    , 2) & 0x7f) << 24 | 
		(hex substr($hex, $offset + 2, 2) & 0xff) << 16 | 
		(hex substr($hex, $offset + 4, 2) & 0xff) << 8 | 
		(hex substr($hex, $offset + 6, 2) & 0xff) 
	);

	# 6 letter;
	$truncate = substr($truncate, -6);

	return $truncate;
}

# TOTP is HOTP(K,c) where counter c is time based and changed every x seconds (30 here)
sub totp {
	my ($time, $secret) = @_;

	# definition for otp(K,c) where c is the counter -> with totp, we use time / 30
	$time = floor($time / 30);

	# do the work
	my $otp = hotp($time, $secret);

	return $otp;
}

# basically HMAC(key, counter)
sub hotp {
	my ($time, $secret) = @_;

	my $secret_hex = unpack "H*", decode_base32($secret);
	my $time_hex= sprintf("%016s", sprintf("%x", $time));

	# SHA-1 Hex your secret 
	my $hash = hmac_sha1_hex_string($time_hex, $secret_hex);

	# run trunc to generate a user useable number
	my $otp = hotp_truncate($hash);

	return $otp;
}

my $secret = "";
if($#ARGV != 0)  {
	$secret = <STDIN>;
	chomp $secret;
} else {
	$secret = $ARGV[0] ;
}

my $starttime = time;

my $otp = totp($starttime, $secret);

printf("%ds left: %06d, in your clipboard\n", 30 - $starttime % 30, $otp);
system("echo $otp | xclip /dev/stdin");