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("this is a token for cerrado")
27
28func NewAuthService(repostiory authRepository) *AuthService {
29 return &AuthService{
30 authRepository: repostiory,
31 }
32}
33
34func (a *AuthService) CheckAuth(username, password string) bool {
35 passphrase := a.authRepository.GetPassphrase()
36 pass := []byte(fmt.Sprintf("%s:%s", username, password))
37
38 err := bcrypt.CompareHashAndPassword(passphrase, pass)
39
40 return err == nil
41}
42
43func (a *AuthService) IssueToken() ([]byte, error) {
44 // TODO: do this block only once
45 base := a.authRepository.GetBase64AesKey()
46
47 dbuf, err := base64.StdEncoding.DecodeString(string(base))
48 if err != nil {
49 return nil, err
50 }
51
52 block, err := aes.NewCipher(dbuf)
53 if err != nil {
54 return nil, err
55 }
56
57 gcm, err := cipher.NewGCM(block)
58 if err != nil {
59 return nil, err
60 }
61
62 nonce := make([]byte, gcm.NonceSize())
63 if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
64 return nil, err
65 }
66
67 ciphertext := gcm.Seal(nonce, nonce, tokenSeed, nil)
68
69 return ciphertext, nil
70}
71
72func (a *AuthService) ValidateToken(token []byte) (bool, error) {
73 base := a.authRepository.GetBase64AesKey()
74
75 dbuf, err := base64.StdEncoding.DecodeString(string(base))
76 if err != nil {
77 return false, err
78 }
79
80 block, err := aes.NewCipher(dbuf)
81 if err != nil {
82 return false, err
83 }
84
85 gcm, err := cipher.NewGCM(block)
86 if err != nil {
87 return false, err
88 }
89
90 nonceSize := gcm.NonceSize()
91 if len(token) < nonceSize {
92 return false, fmt.Errorf("ciphertext too short")
93 }
94
95 nonce, ciphertext := token[:nonceSize], token[nonceSize:]
96 plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
97 if err != nil {
98 return false, err
99 }
100
101 return bytes.Equal(tokenSeed, plaintext), nil
102}
103
104func GenerateHash(username, password string) (string, error) {
105 passphrase := fmt.Sprintf("%s:%s", username, password)
106 bytes, err := bcrypt.GenerateFromPassword([]byte(passphrase), 14)
107 if err != nil {
108 return "", err
109 }
110
111 return string(bytes), nil
112}
113
114func GenerateAesKey() (string, error) {
115 key := make([]byte, 32)
116
117 _, err := rand.Read(key)
118 if err != nil {
119 return "", err
120 }
121
122 return base64.StdEncoding.EncodeToString(key), nil
123}