@@ -2,12 +2,15 @@ package protocol
2
2
3
3
import (
4
4
"crypto/md5"
5
+ "crypto/rand"
6
+ "encoding/base64"
5
7
"encoding/binary"
6
8
"encoding/hex"
7
9
"fmt"
8
10
"postgres-protocol-go/internal/pool"
9
11
"postgres-protocol-go/internal/protocol/messages"
10
12
"postgres-protocol-go/pkg/utils"
13
+ "strings"
11
14
)
12
15
13
16
func ProcessAuth (pgConnection PgConnection ) error {
@@ -25,7 +28,6 @@ func ProcessAuth(pgConnection PgConnection) error {
25
28
26
29
authType := parseAuthType (answer )
27
30
28
- // todo: implement SCRAM-SHA-256
29
31
switch authType {
30
32
case authenticationOk :
31
33
if pgConnection .isVerbose () {
@@ -49,6 +51,49 @@ func ProcessAuth(pgConnection PgConnection) error {
49
51
}
50
52
}
51
53
}
54
+ case authenticationSASL :
55
+ pgConnection .saslMethod = strings .Trim (string (answer [9 :]), "\x00 \n \r " )
56
+ switch pgConnection .saslMethod {
57
+ // https://datatracker.ietf.org/doc/html/rfc7677
58
+ case "SCRAM-SHA-256" :
59
+ initialResponse , err := buildSCRAMInitialResponse (pgConnection .connConfig .User )
60
+ if err != nil {
61
+ return err
62
+ }
63
+
64
+ initialResponseBytes := []byte (initialResponse )
65
+
66
+ lengthBytes := make ([]byte , 4 )
67
+ binary .BigEndian .PutUint32 (lengthBytes , uint32 (len (initialResponseBytes )))
68
+ fmt .Println (lengthBytes )
69
+
70
+ buff := pool .NewWriteBuffer (1024 )
71
+ buff .StartMessage (messages .SASLInitial )
72
+ buff .WriteString (pgConnection .saslMethod )
73
+ buff .WriteInt32 (int32 (len (initialResponseBytes )))
74
+ _ , err = buff .Write (initialResponseBytes )
75
+ if err != nil {
76
+ return err
77
+ }
78
+ buff .FinishMessage ()
79
+
80
+ err = pgConnection .sendMessage (buff )
81
+ if err != nil {
82
+ return err
83
+ }
84
+
85
+ return ProcessAuth (pgConnection )
86
+ default :
87
+ return fmt .Errorf ("SASL authentication method %s is not supported" , pgConnection .saslMethod )
88
+ }
89
+ case authenticationSASLContinue :
90
+ fmt .Println ("SASLContinue" , pgConnection .saslMethod )
91
+ switch pgConnection .saslMethod {
92
+ case "SCRAM-SHA-256" :
93
+ return fmt .Errorf ("not implemented" )
94
+ default :
95
+ return fmt .Errorf ("SASL authentication method %s is not supported" , pgConnection .saslMethod )
96
+ }
52
97
case authenticationMD5Password :
53
98
if pgConnection .connConfig .Password == nil {
54
99
return fmt .Errorf ("password is required for MD5 authentication" )
@@ -119,3 +164,21 @@ func parseAuthType(message []byte) uint32 {
119
164
func parseSalt (message []byte ) string {
120
165
return string (message [9 :13 ])
121
166
}
167
+
168
+ func buildSCRAMInitialResponse (username string ) (string , error ) {
169
+ nonce , err := generateNonce ()
170
+ if err != nil {
171
+ return "" , fmt .Errorf ("failed to generate nonce: %v" , err )
172
+ }
173
+ // Format: "n,,n=<username>,r=<nonce>"
174
+ initialResponse := fmt .Sprintf ("n,,n=%s,r=%s" , username , nonce )
175
+ return initialResponse , nil
176
+ }
177
+
178
+ func generateNonce () (string , error ) {
179
+ nonceBytes := make ([]byte , 16 ) // 16 bytes = 128 bits of randomness
180
+ if _ , err := rand .Read (nonceBytes ); err != nil {
181
+ return "" , err
182
+ }
183
+ return base64 .StdEncoding .EncodeToString (nonceBytes ), nil
184
+ }
0 commit comments