-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
task: adding totp based on the rfc 6238
- Loading branch information
1 parent
15b1d5e
commit 27a0ef1
Showing
3 changed files
with
104 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,84 @@ | ||
package maildoor | ||
|
||
import ( | ||
"math/rand" | ||
"crypto/hmac" | ||
"crypto/rand" | ||
"crypto/sha1" | ||
"encoding/base32" | ||
"encoding/binary" | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
var ( | ||
tux sync.Mutex | ||
codes = map[string]string{} | ||
letters = []rune("1234567890") | ||
secrets = map[string]string{} | ||
) | ||
|
||
// newCodeFor generates a new code for the email and stores it in the codes map. | ||
// tokens are always 6 characters long. | ||
func newCodeFor(email string) string { | ||
// Generating a new token | ||
b := make([]rune, 6) | ||
for i := range b { | ||
b[i] = letters[rand.Intn(len(letters))] | ||
const ( | ||
// expiresTime is the time in seconds that the code expires | ||
// 2 minutes | ||
expiresTime = 120 | ||
) | ||
|
||
// generateSecret generates a 16 byte base32 encoded secret | ||
// The secret is then returned | ||
func generateSecret() (string, error) { | ||
secret := make([]byte, 10) | ||
_, err := rand.Read(secret) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(secret), nil | ||
} | ||
|
||
// generateCode calls the gen function to generate a 6-digit code | ||
// using the secret and the time step | ||
func generateCode(secret string) string { | ||
timeStep := time.Now().Unix() / expiresTime | ||
return gen(secret, timeStep) | ||
} | ||
|
||
// validateCode validates the codeToValid with the secret | ||
// by generating the code for the current time step and the previous and next time steps | ||
// If the code matches any of the generated codes, it returns true otherwise false | ||
func validateCode(codeToValid, secret string) bool { | ||
timeStep := time.Now().Unix() / expiresTime | ||
// Check the current time step, the previous and the next time steps | ||
for i := -1; i <= 1; i++ { | ||
code := gen(secret, timeStep+int64(i)) | ||
if code == codeToValid { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// gen generates a 6-digit code using the secret and the time step | ||
// The code is then returned | ||
func gen(secret string, timeStep int64) string { | ||
timeBytes := make([]byte, 8) | ||
binary.BigEndian.PutUint64(timeBytes, uint64(timeStep)) | ||
|
||
key := []byte(secret) | ||
hmacSha1 := hmac.New(sha1.New, key) | ||
hmacSha1.Write(timeBytes) | ||
hash := hmacSha1.Sum(nil) | ||
|
||
offset := hash[len(hash)-1] & 0x0f | ||
codeBytes := binary.BigEndian.Uint32(hash[offset : offset+4]) | ||
|
||
code := codeBytes % 1_000_000 | ||
codeStr := fmt.Sprintf("%06d", code) | ||
|
||
return codeStr | ||
} | ||
|
||
func saveSecret(email, secret string) { | ||
tux.Lock() | ||
defer tux.Unlock() | ||
codes[email] = string(b) | ||
|
||
return string(b) | ||
secrets[email] = secret | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters