github.com/webx-top/com@v1.2.12/password.go (about)

     1  /*
     2  
     3     Copyright 2016 Wenhui Shen <www.webx.top>
     4  
     5     Licensed under the Apache License, Version 2.0 (the "License");
     6     you may not use this file except in compliance with the License.
     7     You may obtain a copy of the License at
     8  
     9         http://www.apache.org/licenses/LICENSE-2.0
    10  
    11     Unless required by applicable law or agreed to in writing, software
    12     distributed under the License is distributed on an "AS IS" BASIS,
    13     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14     See the License for the specific language governing permissions and
    15     limitations under the License.
    16  
    17  */
    18  
    19  package com
    20  
    21  import (
    22  	"crypto/hmac"
    23  	"crypto/sha1"
    24  	"hash"
    25  )
    26  
    27  // Hash 生成哈希值
    28  func Hash(str string) string {
    29  	return Sha256(str)
    30  }
    31  
    32  // Salt 盐值加密(生成64个字符)
    33  func Salt() string {
    34  	return Hash(RandStr(64))
    35  }
    36  
    37  // MakePassword 创建密码(生成64个字符)
    38  // 可以指定positions用来在hash处理后的密码的不同位置插入salt片段(数量取决于positions的数量),然后再次hash
    39  func MakePassword(password string, salt string, positions ...uint) string {
    40  	length := len(positions)
    41  	if length < 1 {
    42  		return Hash(salt + password)
    43  	}
    44  	saltLength := len(salt)
    45  	if saltLength < length {
    46  		return Hash(salt + password)
    47  	}
    48  	saltChars := saltLength / length
    49  	hashedPassword := Hash(password)
    50  	maxIndex := len(hashedPassword) - 1
    51  	saltMaxIndex := saltLength - 1
    52  	var result string
    53  	var lastPos int
    54  	for k, pos := range positions {
    55  		end := int(pos)
    56  		start := lastPos
    57  		if start > end {
    58  			start, end = end, start
    59  		}
    60  		if start > maxIndex {
    61  			continue
    62  		}
    63  		lastPos = end
    64  		saltStart := k * saltChars
    65  		saltEnd := saltStart + saltChars
    66  		if end > maxIndex {
    67  			result += hashedPassword[start:] + salt[saltStart:saltEnd]
    68  			continue
    69  		}
    70  		result += hashedPassword[start:end] + salt[saltStart:saltEnd]
    71  		if k == length-1 {
    72  			if end <= maxIndex {
    73  				result += hashedPassword[end:]
    74  			}
    75  			if saltEnd <= saltMaxIndex {
    76  				result += salt[saltEnd:]
    77  			}
    78  		}
    79  	}
    80  	return Hash(result)
    81  }
    82  
    83  // CheckPassword 检查密码(密码原文,数据库中保存的哈希过后的密码,数据库中保存的盐值)
    84  func CheckPassword(rawPassword string, hashedPassword string, salt string, positions ...uint) bool {
    85  	return MakePassword(rawPassword, salt, positions...) == hashedPassword
    86  }
    87  
    88  // PBKDF2Key derives a key from the password, salt and iteration count, returning a
    89  // []byte of length keylen that can be used as cryptographic key. The key is
    90  // derived based on the method described as PBKDF2 with the HMAC variant using
    91  // the supplied hash function.
    92  //
    93  // For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
    94  // can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
    95  // doing:
    96  //
    97  // 	dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
    98  //
    99  // Remember to get a good random salt. At least 8 bytes is recommended by the
   100  // RFC.
   101  //
   102  // Using a higher iteration count will increase the cost of an exhaustive
   103  // search but will also make derivation proportionally slower.
   104  func PBKDF2Key(password, salt []byte, iter, keyLen int, hFunc ...func() hash.Hash) string {
   105  	var h func() hash.Hash
   106  	if len(hFunc) > 0 {
   107  		h = hFunc[0]
   108  	}
   109  	if h == nil {
   110  		h = sha1.New
   111  	}
   112  	prf := hmac.New(h, password)
   113  	hashLen := prf.Size()
   114  	numBlocks := (keyLen + hashLen - 1) / hashLen
   115  
   116  	var buf [4]byte
   117  	dk := make([]byte, 0, numBlocks*hashLen)
   118  	U := make([]byte, hashLen)
   119  	for block := 1; block <= numBlocks; block++ {
   120  		// N.B.: || means concatenation, ^ means XOR
   121  		// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
   122  		// U_1 = PRF(password, salt || uint(i))
   123  		prf.Reset()
   124  		prf.Write(salt)
   125  		buf[0] = byte(block >> 24)
   126  		buf[1] = byte(block >> 16)
   127  		buf[2] = byte(block >> 8)
   128  		buf[3] = byte(block)
   129  		prf.Write(buf[:4])
   130  		dk = prf.Sum(dk)
   131  		T := dk[len(dk)-hashLen:]
   132  		copy(U, T)
   133  
   134  		// U_n = PRF(password, U_(n-1))
   135  		for n := 2; n <= iter; n++ {
   136  			prf.Reset()
   137  			prf.Write(U)
   138  			U = U[:0]
   139  			U = prf.Sum(U)
   140  			for x := range U {
   141  				T[x] ^= U[x]
   142  			}
   143  		}
   144  	}
   145  	return Base64Encode(string(dk[:keyLen]))
   146  }