passwd.go 3.2 KB
Newer Older
martianzhang's avatar
martianzhang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
package native

import (
	"crypto/sha1"
	"crypto/sha256"
	"math"
)

// Borrowed from GoMySQL
// SHA1(SHA1(SHA1(password)), scramble) XOR SHA1(password)
func encryptedPasswd(password string, scramble []byte) (out []byte) {
	if len(password) == 0 {
		return
	}
	// stage1_hash = SHA1(password)
	// SHA1 encode
	crypt := sha1.New()
	crypt.Write([]byte(password))
	stg1Hash := crypt.Sum(nil)
	// token = SHA1(SHA1(stage1_hash), scramble) XOR stage1_hash
	// SHA1 encode again
	crypt.Reset()
	crypt.Write(stg1Hash)
	stg2Hash := crypt.Sum(nil)
	// SHA1 2nd hash and scramble
	crypt.Reset()
	crypt.Write(scramble)
	crypt.Write(stg2Hash)
	stg3Hash := crypt.Sum(nil)
	// XOR with first hash
	out = make([]byte, len(scramble))
	for ii := range scramble {
		out[ii] = stg3Hash[ii] ^ stg1Hash[ii]
	}
	return
}

// Hash password using MySQL 8+ method (SHA256)
func encryptedSHA256Passwd(password string, scramble []byte) []byte {
	if len(password) == 0 {
		return nil
	}

	// XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))

	crypt := sha256.New()
	crypt.Write([]byte(password))
	message1 := crypt.Sum(nil)

	crypt.Reset()
	crypt.Write(message1)
	message1Hash := crypt.Sum(nil)

	crypt.Reset()
	crypt.Write(message1Hash)
	crypt.Write(scramble)
	message2 := crypt.Sum(nil)

	for i := range message1 {
		message1[i] ^= message2[i]
	}

	return message1
}

// Old password handling based on translating to Go some functions from
// libmysql

// The main idea is that no password are sent between client & server on
// connection and that no password are saved in mysql in a decodable form.
//
// On connection a random string is generated and sent to the client.
// The client generates a new string with a random generator inited with
// the hash values from the password and the sent string.
// This 'check' string is sent to the server where it is compared with
// a string generated from the stored hash_value of the password and the
// random string.

// libmysql/my_rnd.c
type myRnd struct {
	seed1, seed2 uint32
}

const myRndMaxVal = 0x3FFFFFFF

func newMyRnd(seed1, seed2 uint32) *myRnd {
	r := new(myRnd)
	r.seed1 = seed1 % myRndMaxVal
	r.seed2 = seed2 % myRndMaxVal
	return r
}

func (r *myRnd) Float64() float64 {
	r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal
	r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal
	return float64(r.seed1) / myRndMaxVal
}

// libmysql/password.c
func pwHash(password []byte) (result [2]uint32) {
	var nr, add, nr2, tmp uint32
	nr, add, nr2 = 1345345333, 7, 0x12345671

	for _, c := range password {
		if c == ' ' || c == '\t' {
			continue // skip space in password
		}

		tmp = uint32(c)
		nr ^= (((nr & 63) + add) * tmp) + (nr << 8)
		nr2 += (nr2 << 8) ^ nr
		add += tmp
	}

	result[0] = nr & ((1 << 31) - 1) // Don't use sign bit (str2int)
	result[1] = nr2 & ((1 << 31) - 1)
	return
}

func encryptedOldPassword(password string, scramble []byte) []byte {
	if len(password) == 0 {
		return nil
	}
	scramble = scramble[:8]
	hashPw := pwHash([]byte(password))
	hashSc := pwHash(scramble)
	r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
	var out [8]byte
	for i := range out {
		out[i] = byte(math.Floor(r.Float64()*31) + 64)
	}
	extra := byte(math.Floor(r.Float64() * 31))
	for i := range out {
		out[i] ^= extra
	}
	return out[:]
}