You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
342 lines
10 KiB
342 lines
10 KiB
11 months ago
|
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
|
||
|
}
|