penguin
sophuwu sophie@skisiel.com
Wed, 04 Dec 2024 23:13:54 +0100
A
const.go
@@ -0,0 +1,64 @@
+package goauth + +// SecurityLevelAccessible - Lower security level but easier to use +// +// This will give 60 seconds for the user to write 6 digits. Its aim is to help +// people with difficulties using a computer by allowing them more time to type. +// The actual security implications are minimal. +const SecurityLevelAccessible = 0 + +// SecurityLevelDefault - Default security level used everywhere +// +// This will give 30 seconds for the user to write 6 digits. This is the same as +// the default for Google Authenticator. +const SecurityLevelDefault = 1 + +// SecurityLevelCozyHigh - Higher security but more time for the user +// +// This will give 60 seconds for the user to write 10 digits. This offers a +// slight security improvement. While remaining easier to use than other levels. +const SecurityLevelCozyHigh = 2 + +// SecurityLevelHigh - Same as default but with double the digits +// +// With this you will get 30 seconds to write 12 digits. This is significantly +// harder to brute force than the default level. This is the highest level +// recommended for general use. +const SecurityLevelHigh = 3 + +// SecurityLevelRealTime - Extremely long password with very little time +// +// Gives 5 seconds to write 15 digits. Intended for computers and application +// interface use for automated task. May be useful for authentication of +// unattended systems with less risk of being intercepted. +const SecurityLevelRealTime = 4 + +// securityLevel is a bitfield of the security levels +// each level is 8 bits long and contains another bitfield for the length +// of the password and the time allowed to enter it, allowing customisation +// ease of use and security +const securityLevel = uint64(uint64((15<<2)|0b00)<<(8*4) | uint64((12<<2)|0b01)<<(8*3) | uint64((10<<2)|0b10)<<(8*2) | uint64((6<<2)|0b01)<<8 | uint64((6<<2)|0b10)) + +// getSecurityLevel returns the number of digits and seconds allowed for a security level +func getSecurityLevel(i int) (int, int, error) { + + if i > 4 || i < 0 { + return 0, 0, Error("invalid security level") + } + u := (securityLevel & (255 << (8 * uint64(i)))) >> (8 * uint64(i)) + x := int(u & 0b11) + return int(u >> 2), (x*x*25)/10 + (225*x)/10 + 5, nil +} + +func Error(text string) error { + return &errorString{text} +} + +// errorString is a trivial implementation of error. +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +}
M
http.go
→
http.go
@@ -1,21 +1,20 @@
package goauth -/* -import ( - "crypto/subtle" - "net/http" -) +import "net/http" -func HttpAuth(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - inputUser, inputPass, authOK := r.BasicAuth() - CheckPassword(inputUser, inputPass) - if !authOK || !lookupOK || { - w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) - http.Error(w, "Unauthorized.", http.StatusUnauthorized) +func HttpGen(r *http.Request, w http.ResponseWriter) { + if r.Method == "POST" { + r.ParseForm() + user, qr, err := NewUser(r.Form.Get("username")) + if err != nil { + http.Error(w, "error: "+err.Error(), 500) return } - next.ServeHTTP(w, r) - }) + w.Header().Set("Content-Type", "text/html") + w.Write([]byte("<h1>Success!</h1>")) + w.Write([]byte("<p>QR Code:</p>")) + w.Write([]byte(qr.HTML())) + w.Write([]byte("<a href='/validate'>validate</a>")) + } + } -*/
M
otp.go
→
otp.go
@@ -1,34 +1,80 @@
package goauth import ( + "fmt" "github.com/pquerna/otp" "github.com/pquerna/otp/totp" - - "fmt" "os" ) -func generate(user string) { - key, err := totp.Generate(totp.GenerateOpts{ - Issuer: "soph.local", - AccountName: user, +// ServiceHost is the host of the service +var ServiceHost string + +// init here gets the hostname used for issuing keys +func init() { + s, e := os.Hostname() + if e != nil { + ServiceHost = "localhost" + } else { + ServiceHost = s + } +} + +// OTP holds the information for the otp +type OTP struct { + Secret string + Vals totp.ValidateOpts +} + +// Users is a map of users to their otp keys for validation +var Users map[string]OTP + +// NewUser creates a new user +func NewUser(name string, SecurityLevel ...int) (QRcode, error) { + if _, ok := Users[name]; ok { + return QRcode{}, fmt.Errorf("user %s already exists", name) + } + seclvl := SecurityLevelDefault + if len(SecurityLevel) > 0 { + seclvl = SecurityLevel[0] + } + userOtp, e := totp.Generate(totp.GenerateOpts{ + Issuer: ServiceHost, + AccountName: name, + Digits: otp.Digits(seclvl >> 8), }) - if err != nil { - panic(err) + + if e != nil { + return QRcode{}, e + } + Users[name] = OTP{ + Secret: userOtp.Secret(), + URL: userOtp.URL(), + } + + q, e := GenQR(userOtp.URL(), + "OTP for "+name+" at "+ServiceHost, + "Copy the secret below into your OTP app", + "Secret: "+userOtp.Secret(), + "Or scan the QR code below", + ) + return q, e +} + +// DelUser deletes a user +func DelUser(name string) error { + if _, ok := Users[name]; !ok { + return fmt.Errorf("user %s does not exist", name) } - fmt.Println(key.URL()) + delete(Users, name) + return nil } -func validate(key *otp.Key) { - // Now Validate that the user's successfully added the passcode. - fmt.Println("Validating TOTP...") - passcode := "123456" - valid := totp.Validate(passcode, key.Secret()) - if valid { - println("Valid passcode!") - os.Exit(0) - } else { - println("Invalid passcode!") - os.Exit(1) +// ValidateOtp validates the otp +func ValidateOtp(user, pass string) bool { + if _, ok := Users[user]; !ok { + return false } + + return totp.Validate(pass, Users[user].Secret) }
D
test/test.go
@@ -1,13 +0,0 @@
-package main - -import "fmt" -import "sophuwu.site/goauth" - -func main() { - u, e := goauth.NewUser("sophie", "password") - if e != nil { - fmt.Println(e) - return - } - fmt.Println(u) -}
D
user.go
@@ -1,79 +0,0 @@
-package goauth - -import ( - "crypto/rand" - "crypto/sha256" - "crypto/subtle" - "fmt" - "github.com/pquerna/otp/totp" - "os" - "time" -) - -// User struct for storing user data -type User struct { - Name string - Hash []byte - Salt []byte - OtpS string -} - -func salt(n int) []byte { - var s = make([]byte, n) - _, err := rand.Read(s) - if err != nil { - panic(err) - } - return s -} -func hash(b ...[]byte) []byte { - h := sha256.New() - for _, v := range b { - h.Write(v) - } - return h.Sum(nil) -} - -func (u *User) SetPass(pass string) { - u.Salt = salt(16) - u.Hash = hash([]byte(pass), u.Salt) -} - -func (u *User) CheckPass(pass string) bool { - return subtle.ConstantTimeCompare(u.Hash, hash([]byte(pass), u.Salt)) == 1 -} - -// NewUser creates a new user -func NewUser(name, pass string) (User, error) { - var u User - u.Name = name - u.SetPass(pass) - otp, e := totp.Generate(totp.GenerateOpts{Issuer: "soph.local", AccountName: u.Name}) - if e != nil { - return u, e - } - u.OtpS = otp.Secret() - q, e := GenQR(otp.URL(), - "One Time Password:", - " User : "+u.Name, - " Issuer: "+"local", - " Secret: "+otp.Secret(), - " Period: "+fmt.Sprint(time.Duration(otp.Period()*10e8).String()), - " Digits: "+fmt.Sprintf("%d", otp.Digits()), - " Algo : "+fmt.Sprintf("%s", otp.Algorithm()), - "Scan the QR code with your authenticator app.", - ) - if e != nil { - return u, e - } - fmt.Println(q.String()) - os.WriteFile(u.Name+".png", q.Png(), 0644) - return u, nil -} - -// CheckPassword checks if the password is correct -func CheckPassword(name, pass string) bool { - return false -} - -// subtle.ConstantTimeCompare([]byte(expectedPass), []byte(pass)) == 1