Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
HOTP
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
7 / 7
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getInstance
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getParams
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createOTP
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 generateQr
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
4
 generateBackupCodes
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 reverseMnemonic
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php declare(strict_types=1);
2
3/**
4 * TOTP dan HOTP Library with backup codes, compatible with google authenticator
5 * 
6 * @author Eko Junaidi Salam <[email protected]>
7 */
8
9namespace Ekojs\Otp;
10
11use OTPHP\HOTP as LabHOTP;
12use OTPHP\OTP as LabOTP;
13use \FurqanSiddiqui\BIP39\BIP39;
14use \FurqanSiddiqui\BIP39\WordList;
15use ParagonIE\ConstantTime\Base32;
16
17use Endroid\QrCode\QrCode;
18use Endroid\QrCode\Encoding\Encoding;
19use Endroid\QrCode\ErrorCorrectionLevel;
20use Endroid\QrCode\Label\LabelAlignment;
21use Endroid\QrCode\Label\Font\OpenSans;
22use Endroid\QrCode\RoundBlockSizeMode;
23use Endroid\QrCode\Writer\PngWriter;
24use Endroid\QrCode\Logo\Logo;
25use Endroid\QrCode\Label\Label;
26use Endroid\QrCode\Color\Color;
27use Endroid\QrCode\Writer\Result\ResultInterface;
28
29class HOTP implements OTPInterface {
30    public static $instance;
31    public $otp;
32    protected $writer;
33    protected $bip39;
34    public $parameters = [];
35
36    public function __construct() {
37        $this->writer = new PngWriter();
38        self::$instance = $this;
39    }
40
41    public static function getInstance() {
42        if (self::$instance === null) {
43            self::$instance = new self();
44        }
45        return self::$instance;
46    }
47
48    public function getParams(): ?array {
49        return $this->parameters ?? null;
50    }
51
52    public function createOTP(?array $params=null): OTPInterface {
53        $this->parameters = $params;
54        $this->otp = LabHOTP::create($params["secret"] ?? Base32::encodeUpper(random_bytes(64)), $params["counter"] ?? 0, $params["digest"] ?? 'sha1', $params["digits"] ?? 6);
55        return $this;
56    }
57
58    public function generateQr(?string $logo=null, bool $setLabel=false, int $size=300): ResultInterface {
59        $qlogo = null;
60        $qlabel = null;
61
62        $qrCode = new QrCode(
63            data: $this->otp->getProvisioningUri(),
64            encoding: new Encoding('UTF-8'),
65            errorCorrectionLevel: ErrorCorrectionLevel::Low,
66            size: $size,
67            margin: 10,
68            roundBlockSizeMode: RoundBlockSizeMode::Margin,
69            foregroundColor: new Color(0, 0, 0),
70            backgroundColor: new Color(255, 255, 255)
71        );
72
73        if(!empty($logo)) {
74            $qlogo = new Logo(
75                path: $logo,
76                resizeToWidth: 50,
77                punchoutBackground: true
78            );
79        }
80
81        if(!empty($this->otp->getLabel()) && $setLabel) {
82            $qlabel = new Label(
83                text: $this->otp->getLabel(),
84                textColor: new Color(0, 0, 255)
85            );
86        }
87
88        return $this->writer->write($qrCode, $qlogo, $qlabel);
89    }
90
91    public function generateBackupCodes(string $entropy, ?WordList $wordList=null): array {
92        $wordList = $wordList ?? WordList::English();
93        return BIP39::Entropy(hash("sha256",$entropy))->words;
94    }
95
96    public function reverseMnemonic($words): string {
97        return BIP39::Words($words)->entropy;
98    }
99}
100