OTP routines

This commit is contained in:
Xavier Henner 2019-07-09 12:34:45 +02:00
parent 55ae63dc1d
commit f975a19f65
4 changed files with 138 additions and 17 deletions

57
crypto.go Normal file
View File

@ -0,0 +1,57 @@
package main
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base32"
"encoding/binary"
"errors"
"fmt"
"hash"
"math"
"strings"
"time"
)
func ComputeHmac256(message string, secret string) []byte {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(message))
return h.Sum(nil)
}
func encodeSecret(secret []byte) string {
return strings.TrimRight(base32.StdEncoding.EncodeToString(secret), "=")
}
func GenericTotpCode(secret string, t time.Time, algo string, digits int, period int) (string, error) {
var mac hash.Hash
var format string
now := [8]byte{}
binary.BigEndian.PutUint64(now[:], uint64(math.Floor(float64(t.Unix())/float64(period))))
binsecret, _ := base32.StdEncoding.DecodeString(secret)
switch algo {
case "sha1":
mac = hmac.New(sha1.New, binsecret)
case "sha256":
mac = hmac.New(sha256.New, binsecret)
default:
return "", errors.New("unsupported algorithm")
}
switch digits {
case 6:
format = "%06d"
case 8:
format = "%08d"
default:
return "", errors.New("unsupported code size")
}
mac.Write(now[:])
sum := mac.Sum(nil)
return fmt.Sprintf(format, (binary.BigEndian.Uint32(sum[sum[len(sum)-1]&0xf:])&0x7fffffff)%1000000), nil
}

View File

@ -35,6 +35,10 @@ func main() {
server.slackTemplate2 = config.GetString("config.slackTemplate2", "")
server.cacheDir = config.GetString("config.cacheDir", "")
server.authCa = config.GetString("config.authCa", "")
server.otpMasterSecrets = parseConfigArray(config, "config.masterSecrets")
if len(server.otpMasterSecrets) == 0 {
server.otpMasterSecrets = append(server.otpMasterSecrets, "*******************")
}
server.syslog = false
if *logToSyslog {

42
otp.go Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"log"
"time"
)
func (s *OpenVpnMgt) GenerateOTP(user string) ([]string, error) {
return s.GenerateOTPGeneric(user, 30, "sha1", 10, 6)
}
// alternative OTP generator, not used at the moment
func (s *OpenVpnMgt) GenerateSlackOTP(user string) ([]string, error) {
return s.GenerateOTPGeneric(user, 60, "sha256", 30, 8)
}
func (s *OpenVpnMgt) GenerateOTPGeneric(user string, period int, algo string, secretLen int, digits int) ([]string, error) {
codes := []string{}
now := time.Now()
secret := encodeSecret(ComputeHmac256(user, s.otpMasterSecrets[0])[:secretLen])
code, err := GenericTotpCode(secret, now, algo, digits, period)
if err != nil {
return codes, err
}
// the first code is the generic one
codes = append(codes, code)
for i := 1; i < 3; i++ {
code, _ = GenericTotpCode(secret, now.Add(-1*time.Second*time.Duration(period*i)), algo, digits, period)
codes = append(codes, code)
}
for j := 1; j < len(s.otpMasterSecrets); j++ {
secret = encodeSecret(ComputeHmac256(user, s.otpMasterSecrets[j])[:secretLen])
for i := 0; i < 3; i++ {
code, _ = GenericTotpCode(secret, now.Add(-1*time.Second*time.Duration(period*i)), algo, digits, period)
codes = append(codes, code)
}
}
return codes, nil
}

View File

@ -30,6 +30,7 @@ type OpenVpnMgt struct {
slackTemplate2 string
cacheDir string
syslog bool
otpMasterSecrets []string
}
// NewServer returns a pointer to a new server
@ -86,6 +87,23 @@ func (s *OpenVpnMgt) Auth(c *vpnSession) (error, bool) {
c.password = ""
}
// if the otp is indicated, we check it against the valid codes as soon as
// possible
otpvalidated := false
if c.otpCode != "" {
codes, err := s.GenerateOTP(c.Login)
if err != nil {
return err, false
}
for _, possible := range codes {
if possible == c.otpCode {
otpvalidated = true
}
}
}
log.Println(otpvalidated)
c.Profile = ""
login := []string{c.Login}
pass := c.password