Coverage report: /home/ellis/comp/core/lib/cry/hotp.lisp

KindCoveredAll%
expression6063 95.2
branch00nil
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; crypto/hotp.lisp --- HMAC-Based One-Time Passwords
2
 
3
 ;; see https://github.com/bhyde/cl-one-time-passwords/hotp.lisp
4
 
5
 ;; RFC 4226
6
 
7
 ;;; Code:
8
 (in-package :cry/hotp)
9
 
10
 (defvar *digits* 6)
11
 
12
 (defvar *hmac-sha-mode* :sha3)
13
 
14
 (defun hmac-sha-n (key-string counter)
15
   (loop
16
     with counter-bytes = (make-array 8 :element-type '(unsigned-byte 8))
17
     with hmac = (ironclad:make-hmac
18
                  (ironclad:hex-string-to-byte-array key-string)
19
                  *hmac-sha-mode*)
20
     finally
21
        (ironclad:update-hmac hmac counter-bytes)
22
        (return (ironclad:hmac-digest hmac))
23
     for i from 7 downto 0
24
     for offset from 0 by 8
25
     do (setf (aref counter-bytes i) (ldb (byte 8 offset) counter))))
26
 
27
 (defun hotp-truncate (20-bytes)
28
   (flet ((dt (ht)
29
            (let* ((byte19 (aref ht 19))
30
                   (byte-offset (ldb (byte 4 0) byte19))
31
                   (result 0))
32
              (setf (ldb (byte 7 24) result) (aref ht byte-offset))
33
              (setf (ldb (byte 8 16) result) (aref ht (+ 1 byte-offset)))
34
              (setf (ldb (byte 8  8) result) (aref ht (+ 2 byte-offset)))
35
              (setf (ldb (byte 8  0) result) (aref ht (+ 3 byte-offset)))
36
              result)))
37
     (let ((sbits (dt 20-bytes)))
38
       (mod sbits
39
            (svref #(1 10 100 1000 10000 100000 1000000 10000000 100000000)
40
                   *digits*)))))
41
 
42
 (defun hotp (key-string counter)
43
   (hotp-truncate (hmac-sha-n key-string counter)))