...

Source file src/edge-infra.dev/pkg/lib/crypto/osutilcrypt/sha512/sha512crypt.go

Documentation: edge-infra.dev/pkg/lib/crypto/osutilcrypt/sha512

     1  // Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>
     2  // Copyright 2013, Jonas mg
     3  // All rights reserved.
     4  //
     5  // Use of this source code is governed by a BSD-style license
     6  // that can be found in the LICENSE file.
     7  
     8  // Package sha512_crypt implements Ulrich Drepper's SHA512-crypt password
     9  // hashing algorithm.
    10  //
    11  // The specification for this algorithm can be found here:
    12  // http://www.akkadia.org/drepper/SHA-crypt.txt
    13  package sha512crypt
    14  
    15  import (
    16  	"bytes"
    17  	"crypto/sha512"
    18  	"strconv"
    19  
    20  	crypt "edge-infra.dev/pkg/lib/crypto/osutilcrypt"
    21  	"edge-infra.dev/pkg/lib/crypto/osutilcrypt/common"
    22  )
    23  
    24  func init() {
    25  	crypt.RegisterCrypt(crypt.SHA512, New, MagicPrefix)
    26  }
    27  
    28  const (
    29  	MagicPrefix   = "$6$"
    30  	SaltLenMin    = 1
    31  	SaltLenMax    = 16
    32  	RoundsMin     = 1000
    33  	RoundsMax     = 999999999
    34  	RoundsDefault = 5000
    35  )
    36  
    37  var _rounds = []byte("rounds=")
    38  
    39  type crypter struct{ Salt common.Salt }
    40  
    41  // New returns a new crypt.Crypter computing the SHA512-crypt password hashing.
    42  func New() crypt.Crypter {
    43  	return &crypter{GetSalt()}
    44  }
    45  
    46  func (c *crypter) Generate(key, salt []byte) (string, error) {
    47  	var rounds int
    48  	var isRoundsDef bool
    49  
    50  	if len(salt) == 0 {
    51  		salt = c.Salt.GenerateWRounds(SaltLenMax, RoundsDefault)
    52  	}
    53  	if !bytes.HasPrefix(salt, c.Salt.MagicPrefix) {
    54  		return "", common.ErrSaltPrefix
    55  	}
    56  
    57  	saltToks := bytes.Split(salt, []byte{'$'})
    58  	if len(saltToks) < 3 {
    59  		return "", common.ErrSaltFormat
    60  	}
    61  
    62  	if bytes.HasPrefix(saltToks[2], _rounds) {
    63  		isRoundsDef = true
    64  		pr, err := strconv.ParseInt(string(saltToks[2][7:]), 10, 32)
    65  		if err != nil {
    66  			return "", common.ErrSaltRounds
    67  		}
    68  		rounds = int(pr)
    69  		if rounds < RoundsMin {
    70  			rounds = RoundsMin
    71  		} else if rounds > RoundsMax {
    72  			rounds = RoundsMax
    73  		}
    74  		salt = saltToks[3]
    75  	} else {
    76  		rounds = RoundsDefault
    77  		salt = saltToks[2]
    78  	}
    79  
    80  	if len(salt) > SaltLenMax {
    81  		salt = salt[0:SaltLenMax]
    82  	}
    83  
    84  	// Compute alternate SHA512 sum with input KEY, SALT, and KEY.
    85  	Alternate := sha512.New()
    86  	Alternate.Write(key)
    87  	Alternate.Write(salt)
    88  	Alternate.Write(key)
    89  	AlternateSum := Alternate.Sum(nil) // 64 bytes
    90  
    91  	A := sha512.New()
    92  	A.Write(key)
    93  	A.Write(salt)
    94  	// Add for any character in the key one byte of the alternate sum.
    95  	i := len(key)
    96  	for ; i > 64; i -= 64 {
    97  		A.Write(AlternateSum)
    98  	}
    99  	A.Write(AlternateSum[0:i])
   100  
   101  	// Take the binary representation of the length of the key and for every add
   102  	// the alternate sum, for every 0 the key.
   103  	for i = len(key); i > 0; i >>= 1 {
   104  		if (i & 1) != 0 {
   105  			A.Write(AlternateSum)
   106  		} else {
   107  			A.Write(key)
   108  		}
   109  	}
   110  	Asum := A.Sum(nil)
   111  
   112  	// Start computation of P byte sequence.
   113  	P := sha512.New()
   114  	// For every character in the password add the entire password.
   115  	for i = 0; i < len(key); i++ {
   116  		P.Write(key)
   117  	}
   118  	Psum := P.Sum(nil)
   119  	// Create byte sequence P.
   120  	Pseq := make([]byte, 0, len(key))
   121  	for i = len(key); i > 64; i -= 64 {
   122  		Pseq = append(Pseq, Psum...)
   123  	}
   124  	Pseq = append(Pseq, Psum[0:i]...)
   125  
   126  	// Start computation of S byte sequence.
   127  	S := sha512.New()
   128  	for i = 0; i < (16 + int(Asum[0])); i++ {
   129  		S.Write(salt)
   130  	}
   131  	Ssum := S.Sum(nil)
   132  	// Create byte sequence S.
   133  	Sseq := make([]byte, 0, len(salt))
   134  	for i = len(salt); i > 64; i -= 64 {
   135  		Sseq = append(Sseq, Ssum...)
   136  	}
   137  	Sseq = append(Sseq, Ssum[0:i]...)
   138  
   139  	Csum := Asum
   140  
   141  	// Repeatedly run the collected hash value through SHA512 to burn CPU cycles.
   142  	for i = 0; i < rounds; i++ {
   143  		C := sha512.New()
   144  
   145  		// Add key or last result.
   146  		if (i & 1) != 0 {
   147  			C.Write(Pseq)
   148  		} else {
   149  			C.Write(Csum)
   150  		}
   151  		// Add salt for numbers not divisible by 3.
   152  		if (i % 3) != 0 {
   153  			C.Write(Sseq)
   154  		}
   155  		// Add key for numbers not divisible by 7.
   156  		if (i % 7) != 0 {
   157  			C.Write(Pseq)
   158  		}
   159  		// Add key or last result.
   160  		if (i & 1) != 0 {
   161  			C.Write(Csum)
   162  		} else {
   163  			C.Write(Pseq)
   164  		}
   165  
   166  		Csum = C.Sum(nil)
   167  	}
   168  
   169  	out := make([]byte, 0, 123)
   170  	out = append(out, c.Salt.MagicPrefix...)
   171  	if isRoundsDef {
   172  		out = append(out, []byte("rounds="+strconv.Itoa(rounds)+"$")...)
   173  	}
   174  	out = append(out, salt...)
   175  	out = append(out, '$')
   176  	out = append(out, common.Base64_24Bit([]byte{
   177  		Csum[42], Csum[21], Csum[0],
   178  		Csum[1], Csum[43], Csum[22],
   179  		Csum[23], Csum[2], Csum[44],
   180  		Csum[45], Csum[24], Csum[3],
   181  		Csum[4], Csum[46], Csum[25],
   182  		Csum[26], Csum[5], Csum[47],
   183  		Csum[48], Csum[27], Csum[6],
   184  		Csum[7], Csum[49], Csum[28],
   185  		Csum[29], Csum[8], Csum[50],
   186  		Csum[51], Csum[30], Csum[9],
   187  		Csum[10], Csum[52], Csum[31],
   188  		Csum[32], Csum[11], Csum[53],
   189  		Csum[54], Csum[33], Csum[12],
   190  		Csum[13], Csum[55], Csum[34],
   191  		Csum[35], Csum[14], Csum[56],
   192  		Csum[57], Csum[36], Csum[15],
   193  		Csum[16], Csum[58], Csum[37],
   194  		Csum[38], Csum[17], Csum[59],
   195  		Csum[60], Csum[39], Csum[18],
   196  		Csum[19], Csum[61], Csum[40],
   197  		Csum[41], Csum[20], Csum[62],
   198  		Csum[63],
   199  	})...)
   200  
   201  	// Clean sensitive data.
   202  	A.Reset()
   203  	Alternate.Reset()
   204  	P.Reset()
   205  	for i = 0; i < len(Asum); i++ {
   206  		Asum[i] = 0
   207  	}
   208  	for i = 0; i < len(AlternateSum); i++ {
   209  		AlternateSum[i] = 0
   210  	}
   211  	for i = 0; i < len(Pseq); i++ {
   212  		Pseq[i] = 0
   213  	}
   214  
   215  	return string(out), nil
   216  }
   217  
   218  func (c *crypter) Verify(hashedKey string, key []byte) error {
   219  	newHash, err := c.Generate(key, []byte(hashedKey))
   220  	if err != nil {
   221  		return err
   222  	}
   223  	if newHash != hashedKey {
   224  		return crypt.ErrKeyMismatch
   225  	}
   226  	return nil
   227  }
   228  
   229  func (c *crypter) Cost(hashedKey string) (int, error) {
   230  	saltToks := bytes.Split([]byte(hashedKey), []byte{'$'})
   231  	if len(saltToks) < 3 {
   232  		return 0, common.ErrSaltFormat
   233  	}
   234  
   235  	if !bytes.HasPrefix(saltToks[2], _rounds) {
   236  		return RoundsDefault, nil
   237  	}
   238  	roundToks := bytes.Split(saltToks[2], []byte{'='})
   239  	cost, err := strconv.ParseInt(string(roundToks[1]), 10, 0)
   240  	return int(cost), err
   241  }
   242  
   243  func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }
   244  
   245  func GetSalt() common.Salt {
   246  	return common.Salt{
   247  		MagicPrefix:   []byte(MagicPrefix),
   248  		SaltLenMin:    SaltLenMin,
   249  		SaltLenMax:    SaltLenMax,
   250  		RoundsDefault: RoundsDefault,
   251  		RoundsMin:     RoundsMin,
   252  		RoundsMax:     RoundsMax,
   253  	}
   254  }
   255  

View as plain text