Skip to content
This repository has been archived by the owner on Feb 5, 2024. It is now read-only.

Commit

Permalink
Drop bcrypt in favor of salted SHA512
Browse files Browse the repository at this point in the history
With bcrypt, due to its own per-generation salt, we could not select from MySQL for mlchkid, i.e for check.cgi, as we don't know the mlid.
  • Loading branch information
spotlightishere committed Apr 18, 2018
1 parent fa6cc06 commit 93ded9a
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 59 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@
.glide/

.idea/
config.json

config/
!config/config.json.example
24 changes: 9 additions & 15 deletions account.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import (
"log"
"net/http"
_ "github.com/go-sql-driver/mysql"
"golang.org/x/crypto/bcrypt"
"crypto/sha512"
"encoding/hex"
)

func Account(w http.ResponseWriter, r *http.Request) {
Expand All @@ -16,29 +17,22 @@ func Account(w http.ResponseWriter, r *http.Request) {

w.Header().Add("Content-Type", "text/plain;charset=utf-8")

stmt, err := db.Prepare("INSERT IGNORE INTO `accounts` (`mlid`,`mlchkid`, `passwd` ) VALUES (?, ?, ?)")
stmt, err := db.Prepare("INSERT IGNORE INTO `accounts` (`mlid`,`passwd`, `mlchkid` ) VALUES (?, ?, ?)")
if err != nil {
fmt.Fprint(w, GenNormalErrorCode(450, "Database error."))
log.Fatal(err)
return
}

mlchkid := RandStringBytesMaskImprSrc(32)
passwd := RandStringBytesMaskImprSrc(16)
passwdByte := sha512.Sum512(append(salt, []byte(passwd)...))
passwdHash := hex.EncodeToString(passwdByte[:])

mlchkidByte, err := bcrypt.GenerateFromPassword([]byte(mlchkid), bcrypt.DefaultCost)
if err != nil {
log.Printf("Bcrypt error: %v", err)
return
}

passwdByte, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
if err != nil {
log.Printf("Bcrypt error: %v", err)
return
}
mlchkid := RandStringBytesMaskImprSrc(32)
mlchkidByte := sha512.Sum512(append(salt, []byte(mlchkid)...))
mlchkidHash := hex.EncodeToString(mlchkidByte[:])

_, err = stmt.Exec(wiiID, mlchkidByte, passwdByte)
_, err = stmt.Exec(wiiID, passwdHash, mlchkidHash)
if err != nil {
fmt.Fprint(w, GenNormalErrorCode(450, "Database error."))
log.Println(err)
Expand Down
34 changes: 12 additions & 22 deletions auth.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package main

import (
"golang.org/x/crypto/bcrypt"
"errors"
"net/url"
"database/sql"
"log"
"crypto/sha512"
"encoding/hex"
)

// Auth is a function designed to parse potential information from
Expand All @@ -18,41 +18,31 @@ func Auth(form url.Values) (bool, error) {
}

// Now we need to double check the given auth type is even used.
formGivenType := form.Get("passwd")
if formGivenType == "" {
passwd := form.Get("passwd")
if passwd == "" {
return false, errors.New("invalid authentication type")
}

if global.Debug {
bytes, err := bcrypt.GenerateFromPassword([]byte(formGivenType), bcrypt.DefaultCost)
if err != nil {
return false, err
}
log.Println("Generated:", string(bytes))
}

// If we're using passwd, we want to select passwd and mlid for security.
// Since we only have mlkchkid for check, it's the best we can do.
stmt, err := db.Prepare("SELECT `passwd` FROM `accounts` WHERE `mlid` = ?")
// Grab salt + passwd sha512
hashByte := sha512.Sum512(append(salt, []byte(passwd)...))
hash := hex.EncodeToString(hashByte[:])

stmt, err := db.Prepare("SELECT `passwd` FROM `accounts` WHERE `mlid` = ? AND `passwd` = ?")
if err != nil {
return false, err
}

var passwdResult string
err = stmt.QueryRow(mlid).Scan(&passwdResult)
err = stmt.QueryRow(mlid, hash).Scan(&passwdResult)

if err == sql.ErrNoRows {
// Not found.
return false, nil
} else if err != nil {
// Some type of SQL error... pass it on.
return false, err
} else {
// Found.
if global.Debug {
log.Println("Stored:", passwdResult)
}

// We now need to double check what was given.
return bcrypt.CompareHashAndPassword([]byte(passwdResult), []byte(formGivenType)) == nil, nil
return true, nil
}
}
8 changes: 7 additions & 1 deletion check.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"log"
"net/http"
"strconv"
"crypto/sha512"
"encoding/hex"
)

// Check handles adding the proper interval for check.cgi along with future
Expand Down Expand Up @@ -45,8 +47,12 @@ func Check(w http.ResponseWriter, r *http.Request, db *sql.DB, inter int) {
return
}

// Grab salt + mlchkid sha512
hashByte := sha512.Sum512(append(salt, []byte(mlchkid)...))
hash := hex.EncodeToString(hashByte[:])

// Check mlchkid
result, err := stmt.Query(mlchkid)
result, err := stmt.Query(hash)
if err != nil {
fmt.Fprintf(w, GenNormalErrorCode(320, "Unable to parse parameters."))
log.Fatal(err)
Expand Down
27 changes: 25 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (
"io/ioutil"
"html/template"
"github.com/RiiConnect24/Mail-Go/patch"
"crypto/rand"
)

var global patch.Config
var db *sql.DB
var templates *template.Template
var salt []byte

func logRequest(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -71,7 +73,7 @@ func configHandle(w http.ResponseWriter, r *http.Request) {
return
}

patched, err := patch.ModifyNwcConfig(file, db, global)
patched, err := patch.ModifyNwcConfig(file, db, global, salt)
if err != nil {
log.Printf("unable to patch: %v", err)
fmt.Fprintf(w, "It seems your patching went awry. Contact our support email: [email protected].\nError: %v", err)
Expand All @@ -88,6 +90,27 @@ func configHandle(w http.ResponseWriter, r *http.Request) {
}

func main() {
// Get salt for passwords
saltLocation := "config/salt.bin"
salt, err := ioutil.ReadFile(saltLocation)
if os.IsNotExist(err) {
log.Printf("No salt found. Creating....")
salt = make([]byte, 128)

_, err := rand.Read(salt)
if err != nil {
panic(err)
}

err = ioutil.WriteFile("config/salt.bin", salt, os.ModePerm)
if err != nil {
panic(err)
}
} else if err != nil {
panic(err)
}


file, err := os.Open("config/config.json")
if err != nil {
panic(err)
Expand Down Expand Up @@ -150,7 +173,7 @@ func main() {
s3.Execute(w, nil)
})
http.HandleFunc("/patch", configHandle)

log.Println("Running...")

// We do this to log all access to the page.
Expand Down
26 changes: 10 additions & 16 deletions patch/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import (
"strconv"
"database/sql"
"log"
"golang.org/x/crypto/bcrypt"
"crypto/sha512"
"encoding/hex"
)

// ModifyNwcConfig takes an original config, applies needed patches to the URL and such,
// updates the checksum and returns either nil, error or a patched config w/o error.
func ModifyNwcConfig(originalConfig []byte, db *sql.DB, global Config) ([]byte, error) {
func ModifyNwcConfig(originalConfig []byte, db *sql.DB, global Config, salt []byte) ([]byte, error) {
if len(originalConfig) == 0 {
return nil, errors.New("config seems to be empty. double check you uploaded a file")
}
Expand Down Expand Up @@ -41,29 +42,22 @@ func ModifyNwcConfig(originalConfig []byte, db *sql.DB, global Config) ([]byte,
}
mlid = "w" + mlid

// Go ahead and push read data.
// Go ahead and push generated data.
mlchkid := RandStringBytesMaskImprSrc(32)
passwd := RandStringBytesMaskImprSrc(16)

mlchkidByte, err := bcrypt.GenerateFromPassword([]byte(mlchkid), bcrypt.DefaultCost)
if err != nil {
log.Printf("Bcrypt error: %v", err)
return nil, err
}
mlchkidByte := sha512.Sum512(append(salt, []byte(mlchkid)...))
mlchkidHash := hex.EncodeToString(mlchkidByte[:])

passwdByte, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
if err != nil {
log.Printf("Bcrypt error: %v", err)
return nil, err
}
passwd := RandStringBytesMaskImprSrc(16)
passwdByte := sha512.Sum512(append(salt, []byte(passwd)...))
passwdHash := hex.EncodeToString(passwdByte[:])

stmt, err := db.Prepare("INSERT IGNORE INTO `accounts` (`mlid`,`mlchkid`, `passwd` ) VALUES (?, ?, ?)")
if err != nil {
log.Printf("Database error: %v", err)
return nil, err
}

_, err = stmt.Exec(mlid, mlchkidByte, passwdByte)
_, err = stmt.Exec(mlid, mlchkidHash, passwdHash)
if err != nil {
log.Printf("Database error: %v", err)
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions schema/1-create-mail-tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ DROP TABLE IF EXISTS `accounts`;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `accounts` (
`mlid` varchar(17) DEFAULT NULL COMMENT 'Mail ID',
`passwd` varchar(60) DEFAULT NULL COMMENT 'Password',
`mlchkid` varchar(60) DEFAULT NULL COMMENT 'Mail Check ID',
`passwd` varchar(128) DEFAULT NULL COMMENT 'Password',
`mlchkid` varchar(128) DEFAULT NULL COMMENT 'Mail Check ID',
UNIQUE KEY `mlid` (`mlid`),
UNIQUE KEY `mlchkid` (`mlchkid`),
UNIQUE KEY `passwd` (`passwd`)
Expand Down

0 comments on commit 93ded9a

Please sign in to comment.