1package service
2
3import (
4 "bytes"
5 "crypto/aes"
6 "crypto/cipher"
7 "crypto/rand"
8 "encoding/base64"
9 "fmt"
10 "io"
11
12 "golang.org/x/crypto/bcrypt"
13)
14
15type (
16 AuthService struct {
17 authRepository authRepository
18 }
19
20 authRepository interface {
21 GetPassphrase() []byte
22 GetBase64AesKey() []byte
23 }
24)
25
26var tokenSeed = []byte("cerrado")
27
28func (a *AuthService) CheckAuth(username, password string) bool {
29 passphrase := a.authRepository.GetPassphrase()
30 pass := []byte(fmt.Sprintf("%s:%s", username, password))
31
32 err := bcrypt.CompareHashAndPassword(passphrase, pass)
33
34 return err == nil
35}
36
37func (a *AuthService) IssueToken() ([]byte, error) {
38 // TODO: do this block only once
39 base := a.authRepository.GetBase64AesKey()
40
41 dbuf, err := base64.StdEncoding.DecodeString(string(base))
42 if err != nil {
43 return nil, err
44 }
45
46 block, err := aes.NewCipher(dbuf)
47 if err != nil {
48 return nil, err
49 }
50
51 gcm, err := cipher.NewGCM(block)
52 if err != nil {
53 return nil, err
54 }
55
56 nonce := make([]byte, gcm.NonceSize())
57 if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
58 return nil, err
59 }
60
61 ciphertext := gcm.Seal(nonce, nonce, tokenSeed, nil)
62
63 return ciphertext, nil
64}
65
66func (a *AuthService) ValidateToken(token []byte) (bool, error) {
67 base := a.authRepository.GetBase64AesKey()
68
69 dbuf, err := base64.StdEncoding.DecodeString(string(base))
70 if err != nil {
71 return false, err
72 }
73
74 block, err := aes.NewCipher(dbuf)
75 if err != nil {
76 return false, err
77 }
78
79 gcm, err := cipher.NewGCM(block)
80 if err != nil {
81 return false, err
82 }
83
84 nonceSize := gcm.NonceSize()
85 if len(token) < nonceSize {
86 return false, fmt.Errorf("ciphertext too short")
87 }
88
89 nonce, ciphertext := token[:nonceSize], token[nonceSize:]
90 plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
91 if err != nil {
92 return false, err
93 }
94
95 return bytes.Equal(tokenSeed, plaintext), nil
96}
97
98func GenerateHash(username, password string) (string, error) {
99 passphrase := fmt.Sprintf("%s:%s", username, password)
100 bytes, err := bcrypt.GenerateFromPassword([]byte(passphrase), 14)
101 if err != nil {
102 return "", err
103 }
104
105 return string(bytes), nil
106}
107
108func GenerateAesKey() (string, error) {
109 key := make([]byte, 32)
110
111 _, err := rand.Read(key)
112 if err != nil {
113 return "", err
114 }
115
116 return base64.StdEncoding.EncodeToString(key), nil
117}