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.
317 lines
9.9 KiB
317 lines
9.9 KiB
package moffas |
|
|
|
import ( |
|
"encoding/json" |
|
"errors" |
|
"moffas_go/helper" |
|
"moffas_go/logger" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"github.com/jmoiron/sqlx" |
|
) |
|
|
|
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 string, conn *sqlx.DB, 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) |
|
}() |
|
|
|
current_time := startTime.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, conn, 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, conn *sqlx.DB, 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) |
|
}() |
|
|
|
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, conn, 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 string, conn *sqlx.DB, 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) |
|
}() |
|
|
|
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 string, conn *sqlx.DB, session_id string, user_id int64, session_secret string) error { |
|
logger.Debug(reference_id, " -- start upsert_session.") |
|
startTime := time.Now() |
|
defer func() { |
|
dur := time.Since(startTime) |
|
logger.Debug(reference_id, " -- upsert_session done in ", dur) |
|
}() |
|
|
|
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 |
|
}
|
|
|