sophuwu.site > goauth
8=====D (:
sophuwu sophie@skisiel.com
Wed, 15 May 2024 22:33:00 +0200
commit

78362f79daf12c90346fd15de225cbe675a84926

parent

e9cd40758658c143331bc8205639d493f58b4024

11 files changed, 166 insertions(+), 52 deletions(-)

jump to
M go.modgo.mod

@@ -2,6 +2,16 @@ module sophuwu.site/goauth

go 1.22.3 -require github.com/pquerna/otp v1.4.0 +require ( + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/hqbobo/text2pic v0.0.0-20180823042751-2479e146d720 + github.com/pquerna/otp v1.4.0 + golang.org/x/image v0.16.0 +) -require github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect +require ( + github.com/disintegration/imaging v1.6.2 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + golang.org/x/text v0.15.0 // indirect +)
M go.sumgo.sum

@@ -2,6 +2,14 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=

github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/hqbobo/text2pic v0.0.0-20180823042751-2479e146d720 h1:ZjeibvTQbGmB4y/k+1QzEbDzR4qbDjzMKTyycLz6aLY= +github.com/hqbobo/text2pic v0.0.0-20180823042751-2479e146d720/go.mod h1:fgapft0wMijV8gctq6YgKjnIwLk8mQfrCDJmhz8n4xI= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=

@@ -9,3 +17,9 @@ github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=

github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw= +golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
M http.gohttp.go

@@ -1,5 +1,6 @@

package goauth +/* import ( "crypto/subtle" "net/http"

@@ -17,3 +18,4 @@ }

next.ServeHTTP(w, r) }) } +*/
M otp.gootp.go

@@ -22,7 +22,7 @@

func validate(key *otp.Key) { // Now Validate that the user's successfully added the passcode. fmt.Println("Validating TOTP...") - passcode := promptForPasscode() + passcode := "123456" valid := totp.Validate(passcode, key.Secret()) if valid { println("Valid passcode!")
M qrcode.goqrcode.go

@@ -9,10 +9,17 @@ * The header will display the string above the qr code.

*/ import ( + "bytes" "fmt" "github.com/boombuler/barcode/qr" + "github.com/disintegration/imaging" + "github.com/golang/freetype" + "github.com/hqbobo/text2pic" "image" "image/color" + "image/png" + "os" + "slices" "strings" )

@@ -35,8 +42,10 @@ var bw = []color.Color{color.Black, color.White, color.White, color.White}

var bwp = color.Palette(bw) func pad(n int, r ...rune) string { - if n <= 1 { + if n == 1 { n += 1 + } else if n < 1 { + n = 1 } if len(r) == 0 { return strings.Repeat(utf8r(whole).String(false), n)

@@ -84,52 +93,100 @@ }

return b } -func wrap(s string, w int) []string { - s = strings.Join(strings.Split(s, " "), "\n") - var b = strings.Split(s, "\n") +func wrap(w int, s ...string) []string { var line = "" - var lines []string - for i, v := range b { - if len(v) > w { - lines = append(lines, v[:w-1], v[w-1:]) - continue - } - if len(line)+len(v) < w { - line += v + " " - } - if len(line)+len(v) >= w { - lines = append(lines, strings.TrimSuffix(line, " ")) - line = "" - } - if i == len(b)-1 { - lines = append(lines, strings.TrimSuffix(line, " ")) + var lines, b []string + for n := range s { + b = strings.Split(s[n], " ") + for i, v := range b { + if len(v) > w { + lines = append(lines, v[:w-1], v[w-1:]) + continue + } + if len(line)+len(v) < w { + line += v + " " + } else { + lines = append(lines, strings.TrimSuffix(line, " ")) + line = "" + continue + } + if i == len(b)-1 { + lines = append(lines, strings.TrimSuffix(line, " ")) + line = "" + } } } - return lines + return slices.Clip(lines) } -func QR(s string, header bool, html bool) (string, error) { - bar, err := qr.Encode(s, qr.L, qr.Auto) +type QRcode struct { + Data string + Headers []string + Code image.Image +} + +func GenQR(data string, header ...string) (QRcode, error) { + code, err := qr.Encode(data, qr.H, qr.Auto) if err != nil { - return "", err + return QRcode{}, err } + return QRcode{ + data, + header, + code, + }, nil +} + +func (q QRcode) String() string { var output = "" - if header && !html { - width := bar.Bounds().Dx() - ar := wrap(s, width) - output += fmt.Sprintln(pad(0, whole) + pad(width+2, upper) + pad(0, whole)) - for i, v := range ar { - v = pad((((width)-len(v)-1*2*(1-width%2))/2)-(width%2), blank) + v + pad((width-len(v)+2+2*(width%2))/2, blank) - v = pad(0) + pad(0, blank) + v + pad(0) - output += fmt.Sprintf("%s", v) - if i < len(ar) { - output += fmt.Sprintln() - } + ar := wrap(q.Code.Bounds().Dx(), q.Headers...) + width := q.Code.Bounds().Dx() + output += fmt.Sprintln(pad(0, whole) + pad(width+2, upper) + pad(0, whole)) + for i, v := range ar { + v = v + pad(width-len(v)-1+2*(width%2), blank) + v = pad(0) + pad(0, blank) + v + pad(0) + output += fmt.Sprintf("%s", v) + if i < len(ar) { + output += fmt.Sprintln() } - output += fmt.Sprintln(pad(0, whole) + pad(width+2, lower) + pad(0, whole)) - } else if header && html { - output += fmt.Sprintf("<p>%s</p>", s) + } + output += fmt.Sprintln(pad(0, whole) + pad(width+2, lower) + pad(0, whole)) + + output += fmt.Sprintln(qrstr(q.Code, false)) + return output +} + +func (q QRcode) HTML() string { + var output = "" + for _, h := range q.Headers { + output += fmt.Sprintln("<p>" + h + "</p>") + } + output += fmt.Sprintln(qrstr(q.Code, true)) + return output +} + +func (q QRcode) Png() []byte { + fontBytes, _ := os.ReadFile("Oxygen.ttf") + ttf, _ := freetype.ParseFont(fontBytes) + // create a new image with white background + var pic = text2pic.NewTextPicture(text2pic.Configure{Width: 1000, BgColor: text2pic.ColorWhite}) + // add the qr code header + for _, h := range q.Headers { + pic.AddTextLine(h, 8, ttf, text2pic.ColorBlack, text2pic.Padding{0, 0, 0, 0, 0}) } - output += fmt.Sprintln(qrstr(bar, html)) - return output, nil + + var buf bytes.Buffer + pic.Draw(&buf, text2pic.TypePng) + img2, _ := png.Decode(&buf) + + // scale q.Code to 1000px + img := imaging.Resize(q.Code, 900, 900, imaging.NearestNeighbor) + img = imaging.Paste(imaging.New(1000, 1000+img2.Bounds().Size().Y, text2pic.ColorWhite), img, image.Pt(50, img2.Bounds().Size().Y+50)) + + img = imaging.Paste(img, img2, image.Pt(0, 0)) + + buf.Reset() + png.Encode(&buf, img) + + return buf.Bytes() }
A test/test.go

@@ -0,0 +1,13 @@

+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) +}
M user.gouser.go

@@ -4,7 +4,10 @@ import (

"crypto/rand" "crypto/sha256" "crypto/subtle" + "fmt" "github.com/pquerna/otp/totp" + "os" + "time" ) // User struct for storing user data

@@ -15,8 +18,8 @@ Salt []byte

OtpS string } -func salt() []byte { - var s = make([]byte, 32) +func salt(n int) []byte { + var s = make([]byte, n) _, err := rand.Read(s) if err != nil { panic(err)

@@ -32,7 +35,7 @@ return h.Sum(nil)

} func (u *User) SetPass(pass string) { - u.Salt = salt() + u.Salt = salt(16) u.Hash = hash([]byte(pass), u.Salt) }

@@ -41,16 +44,31 @@ return subtle.ConstantTimeCompare(u.Hash, hash([]byte(pass), u.Salt)) == 1

} // NewUser creates a new user -func NewUser(name, pass string) *User { +func NewUser(name, pass string) (User, error) { var u User - key, err := totp.Generate(totp.GenerateOpts{AccountName: name, Issuer: "soph.local"}) - if err != nil { - panic(err) - } - key.Secret() 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