-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathopiekey.go
157 lines (121 loc) · 2.74 KB
/
opiekey.go
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package opiekey
import (
"crypto/md5"
"crypto/sha1"
"encoding/binary"
"fmt"
"golang.org/x/crypto/md4"
)
// Algorithm is the hashing algorithm
//type Algorithm int8
const (
algMD5 = iota
algMD4
algSHA1
)
type Algorithm struct {
id int8
text string
}
// String returns the algorithm name
func (a Algorithm) String() string {
return a.text
}
func (a Algorithm) hasher() func(d []byte) []byte {
var f func(d []byte) []byte
switch a.id {
case algMD4:
f = func(d []byte) []byte {
h := md4.New()
h.Write(d)
return h.Sum(nil)
}
case algMD5:
f = func(d []byte) []byte {
h := md5.Sum(d)
return h[:]
}
case algSHA1:
f = func(d []byte) []byte {
h := sha1.Sum(d)
return h[:]
}
}
return f
}
// MD5 is the MD5 algorithm
var MD5 = Algorithm{algMD5, "MD5"}
// MD4 is the MD4 algorithm
var MD4 = Algorithm{algMD4, "MD4"}
// SHA1 is the SHA1 algorithm
var SHA1 = Algorithm{algSHA1, "SHA1"}
func hashLen(seqNum int, seed, passphrase string, alg func([]byte) []byte) []byte {
hash := []byte(seed + passphrase)
r := make([]uint32, 5)
q := make([]byte, 8)
for i := 0; i <= seqNum; i++ {
checksum := alg(hash)
checksumLen := len(checksum)
for j := 0; j < checksumLen/4; j++ {
r[j] = binary.LittleEndian.Uint32(checksum[j*4:])
}
if checksumLen > 16 {
binary.LittleEndian.PutUint32(q[0:], r[0]^r[2]^r[4])
} else {
binary.LittleEndian.PutUint32(q[0:], r[0]^r[2])
}
binary.LittleEndian.PutUint32(q[4:], r[1]^r[3])
hash = q
}
return hash
}
func hashToUint64(hash []byte) uint64 {
var sum uint64
for _, i := range hash {
sum = sum << 8
sum |= uint64(i & 0xff)
}
return sum
}
func binaryToHex(sum uint64) string {
h := fmt.Sprintf("%016X", sum)
s := ""
for i := 0; i < len(h); i += 4 {
if len(s) > 0 {
s = s + " "
}
s = s + h[i:i+4]
}
return s
}
func binaryToWords(sum uint64) string {
var p uint64
workSum := sum
for i := 0; i < 32; i++ {
p = p + workSum&3
workSum = workSum >> 2
}
s := ""
for i := 4; i >= 0; i-- {
s = s + opieWords[(sum>>uint((i*11+9))&0x7ff)] + " "
}
s = s + opieWords[((sum<<2)&0x7fc)|(p&3)]
return s
}
func getOTPNumber(seqNum int, seed, passphrase string, alg Algorithm) uint64 {
hash := hashLen(seqNum, seed, passphrase, alg.hasher())
sum := hashToUint64(hash)
return sum
}
// ComputeHexResponse computes the response to an OTP challenge
func ComputeHexResponse(seqNum int, seed, passphrase string, alg Algorithm) string {
sum := getOTPNumber(seqNum, seed, passphrase, alg)
s := binaryToHex(sum)
return s
}
// ComputeWordResponse computes the response to an OTP challenge
func ComputeWordResponse(seqNum int, seed, passphrase string, alg Algorithm) string {
sum := getOTPNumber(seqNum, seed, passphrase, alg)
s := binaryToWords(sum)
return s
}