package moffas import ( "encoding/json" "errors" "moffas_go/db" "moffas_go/helper" "moffas_go/logger" "strconv" "strings" "time" ) type GeneratedChallenge struct { Full_nonce string User_id int64 Salt string Iterations int } type VerificationResult struct { Server_hash string Final_nonce string Session_id string Privileges map[string]interface{} Tier string Full_name string Organization_data map[string]interface{} } func Generate_challenge(reference_id, username string, half_nonce string) (GeneratedChallenge, error) { logger.Debug(reference_id, " -- start generate_challenge") startTime := time.Now() defer func() { dur := time.Since(startTime) logger.Debug(reference_id, " -- generate_challenge done in ", dur) }() conn, err := db.GetConnection() if err != nil { return GeneratedChallenge{}, err } defer db.ReleaseConnection() current_time := time.Now().Unix() nonce, err := helper.GenerateRandomString(8) if err != nil { return GeneratedChallenge{}, err } full_nonce := half_nonce + nonce logger.Info(reference_id, "HALF NONCE : ", half_nonce) logger.Info(reference_id, "USERNAME : ", username) logger.Info(reference_id, "FULL NONCE : ", full_nonce) type DBResult struct { User_id int64 `db:"id"` Salt string `db:"salt"` Iterations int `db:"iterations"` Timestamp int64 `db:"tstamp"` SaltedPasword string `db:"saltedpassword"` } query := ` SELECT a.id,a.salt,a.saltedpassword,a.iterations, COALESCE(b.tstamp,0) as tstamp FROM servouser.user a LEFT JOIN servouser.challenge_response b ON b.user_id = a.id WHERE a.username = $1 AND a.st=1 LIMIT 1` sql := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " "), " ", " ") logger.Debug(reference_id, "SQL : ", sql) dbresult := DBResult{} err = conn.Get(&dbresult, sql, username) if err != nil { return GeneratedChallenge{}, err } logger.Info(reference_id, "USER ID : ", dbresult.User_id) waittime, err := strconv.Atoi(helper.GetEnv("AUTH_WAIT_TIME", "10")) if err != nil { waittime = 10 } wait_until := dbresult.Timestamp + int64(waittime) logger.Debug(reference_id, "LAST AUTH REQUEST : ", dbresult.Timestamp) logger.Debug(reference_id, "WAIT TIME : ", waittime) logger.Debug(reference_id, "WAIT UNTIL : ", wait_until) logger.Debug(reference_id, "CURRENT TIME : ", current_time) if wait_until > current_time { logger.Info(reference_id, "!!! WAIT TIME FOR THE USER ID HAS NOT ELAPSED") err = errors.New("wait time has not elapsed") return GeneratedChallenge{}, err } //----- CONTEKAN DOANG ----- sharedSecret := helper.GetEnv("SHARED_SECRET", "Winter is coming") logger.Info(reference_id, "SHARED SECRET : ", sharedSecret) logger.Info(reference_id, "SALTED PASSWORD : ", dbresult.SaltedPasword) hash1, err := helper.HMAC_SHA256(sharedSecret, dbresult.SaltedPasword) if err != nil { return GeneratedChallenge{}, err } logger.Info(reference_id, "HASH1 : ", hash1) calculated_client_hash, err := helper.HMAC_SHA256(full_nonce, hash1) if err != nil { return GeneratedChallenge{}, err } logger.Info(reference_id, "CALCULATED CLIENT HASH : ", calculated_client_hash) //----- CONTEKAN DOANG ----- err = upsert_challenge(reference_id, full_nonce, dbresult.User_id) if err != nil { return GeneratedChallenge{}, err } return GeneratedChallenge{ User_id: dbresult.User_id, Salt: dbresult.Salt, Iterations: dbresult.Iterations, Full_nonce: full_nonce, }, nil } func Verify_challenge(reference_id string, full_nonce string, client_hash string, next_nonce string) (VerificationResult, error) { logger.Debug(reference_id, " start verify_challenge") startTime := time.Now() defer func() { dur := time.Since(startTime) logger.Debug(reference_id, " -- verify_challenge done in ", dur) }() conn, err := db.GetConnection() if err != nil { return VerificationResult{}, err } defer db.ReleaseConnection() current_time := time.Now().Unix() nonce, err := helper.GenerateRandomString(8) if err != nil { return VerificationResult{}, err } logger.Info(reference_id, "FULL NONCE : ", full_nonce) type DBResult struct { User_id int64 `db:"user_id"` Timestamp int64 `db:"tstamp"` Fullname string `db:"full_name"` Privileges string `db:"privileges"` Tier string `db:"tier"` SaltedPasword string `db:"saltedpassword"` Organization_data string `db:"organization_data"` } dbresult := DBResult{} query := ` SELECT a.user_id, a.tstamp, b.full_name, b.saltedpassword, c.privileges, d.tier, d.data organization_data FROM servouser.challenge_response a LEFT JOIN servouser.user b ON b.id = a.user_id LEFT JOIN servouser.role c ON c.name = b.role LEFT JOIN servouser.organization d ON d.id = b.organization_id WHERE a.full_nonce = $1 AND b.id IS NOT NULL AND c.privileges IS NOT NULL AND b.st = 1 LIMIT 1` sql := strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ") logger.Debug(reference_id, "SQL : ", sql) err = conn.Get(&dbresult, sql, full_nonce) if err != nil { return VerificationResult{}, err } rep_nonce := full_nonce[0:15] logger.Debug(reference_id, "replacement nonce : ", rep_nonce) query = "UPDATE servouser.challenge_response SET full_nonce = $1 WHERE full_nonce = $2" logger.Debug(reference_id, "SQL : ", query) _, err = conn.Exec(query, rep_nonce, full_nonce) if err != nil { return VerificationResult{}, err } timeout, err := strconv.Atoi(helper.GetEnv("AUTH_TIMEOUT", "300")) if err != nil { timeout = 300 } logger.Info(reference_id, "AUTH TIMEOUT : ", timeout) expire_limit := dbresult.Timestamp + int64(timeout) logger.Info(reference_id, "AUTH EXPIRATION TIME : ", expire_limit) logger.Info(reference_id, "CURRENT TIME : ", current_time) if current_time > expire_limit { logger.Error(reference_id, "!!! CHALLENGE EXPIRED") err = errors.New("client expired") return VerificationResult{}, err } sharedSecret := helper.GetEnv("SHARED_SECRET", "Winter is coming") logger.Info(reference_id, "SHARED SECRET : ", sharedSecret) logger.Info(reference_id, "SALTED PASSWORD : ", dbresult.SaltedPasword) hash1, err := helper.HMAC_SHA256(sharedSecret, dbresult.SaltedPasword) if err != nil { return VerificationResult{}, err } logger.Info(reference_id, "HASH1 : ", hash1) calculated_client_hash, err := helper.HMAC_SHA256(full_nonce, hash1) if err != nil { return VerificationResult{}, err } logger.Info(reference_id, "CALCULATED CLIENT HASH : ", calculated_client_hash) logger.Info(reference_id, "CLIENT HASH : ", client_hash) if calculated_client_hash != client_hash { logger.Warning(reference_id, "!!! CLIENT HASH DOES NOT MATCH STORED CHALLENGE RESPONSE") err = errors.New("client hash does not match stored challenge response") return VerificationResult{}, err } logger.Info(reference_id, "NEXT NONCE : ", next_nonce) final_nonce := next_nonce + nonce logger.Info(reference_id, "FINAL NONCE : ", final_nonce) server_hash, err := helper.HMAC_SHA256(final_nonce, hash1) if err != nil { return VerificationResult{}, err } logger.Info(reference_id, "SERVER HASH : ", server_hash) session_secret, err := helper.HMAC_SHA256(full_nonce+final_nonce, hash1) if err != nil { return VerificationResult{}, err } logger.Info(reference_id, "SESSION SECRET : ", session_secret) session_id, _ := helper.GenerateRandomString(16) logger.Info(reference_id, "SESSION ID : ", session_id) err = upsert_session(reference_id, session_id, dbresult.User_id, session_secret) if err != nil { return VerificationResult{}, err } privileges := make(map[string]interface{}) organization_data := make(map[string]interface{}) err = json.Unmarshal([]byte(dbresult.Privileges), &privileges) if err != nil { return VerificationResult{}, err } err = json.Unmarshal([]byte(dbresult.Organization_data), &organization_data) if err != nil { return VerificationResult{}, err } return VerificationResult{ Final_nonce: final_nonce, Server_hash: server_hash, Session_id: session_id, Privileges: privileges, Tier: dbresult.Tier, Full_name: dbresult.Fullname, Organization_data: organization_data, }, nil } func upsert_challenge(reference_id, full_nonce string, user_id int64) error { logger.Debug(reference_id, " start upsert_challenge") startTime := time.Now() defer func() { dur := time.Since(startTime) logger.Debug(reference_id, " -- upsert_challenge done in ", dur) }() conn, err := db.GetConnection() if err != nil { return err } defer db.ReleaseConnection() query := "DELETE FROM servouser.challenge_response WHERE full_nonce = $1 OR user_id = $2" sql := strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ") logger.Debug(reference_id, "SQL : ", sql) _, err = conn.Exec(query, full_nonce, user_id) if err != nil { return err } query = ` INSERT INTO servouser.challenge_response (full_nonce,user_id,challenge_response,tstamp) VALUES ($1,$2,'0123456789abcdefghijklmnopqrstuv',$3)` sql = strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ") logger.Debug(reference_id, "SQL : ", sql) _, err = conn.Exec(sql, full_nonce, user_id, time.Now().Unix()) return err } func upsert_session(reference_id, session_id string, user_id int64, session_secret string) error { logger.Debug(reference_id, " start upsert_challenge") startTime := time.Now() defer func() { dur := time.Since(startTime) logger.Debug(reference_id, " -- upsert_session done in ", dur) }() conn, err := db.GetConnection() if err != nil { return err } defer db.ReleaseConnection() query := "DELETE FROM servouser.session WHERE session_id = $1 OR user_id = $2" sql := strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ") logger.Debug(reference_id, "SQL : ", sql) _, err = conn.Exec(query, session_id, user_id) if err != nil { return err } query = ` INSERT INTO servouser.session (session_id,user_id,session_secret,tstamp,st,last_ms_tstamp,last_sequence) VALUES ($1,$2,$3,$4,1,0,0)` sql = strings.ReplaceAll(strings.ReplaceAll(query, "\t", " "), "\n", " ") logger.Debug(reference_id, "SQL : ", sql) _, err = conn.Exec(query, session_id, user_id, session_secret, time.Now().Unix()) return err }