package pkcs12 import ( "crypto/sha1" "math/big" ) var ( deriveKeyByAlg = map[string]func(salt, password []byte, iterations int) []byte{ pbeWithSHAAnd3KeyTripleDESCBC: func(salt, password []byte, iterations int) []byte { return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) }, pbewithSHAAnd40BitRC2CBC: func(salt, password []byte, iterations int) []byte { return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) }, } deriveIVByAlg = map[string]func(salt, password []byte, iterations int) []byte{ pbeWithSHAAnd3KeyTripleDESCBC: func(salt, password []byte, iterations int) []byte { return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) }, pbewithSHAAnd40BitRC2CBC: func(salt, password []byte, iterations int) []byte { return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) }, } ) func sha1Sum(in []byte) []byte { sum := sha1.Sum(in) return sum[:] } func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments // Let H be a hash function built around a compression function f: // Z_2^u x Z_2^v -> Z_2^u // (that is, H has a chaining variable and output of length u bits, and // the message input to the compression function of H is v bits). The // values for u and v are as follows: // HASH FUNCTION VALUE u VALUE v // MD2, MD5 128 512 // SHA-1 160 512 // SHA-224 224 512 // SHA-256 256 512 // SHA-384 384 1024 // SHA-512 512 1024 // SHA-512/224 224 1024 // SHA-512/256 256 1024 // Furthermore, let r be the iteration count. // We assume here that u and v are both multiples of 8, as are the // lengths of the password and salt strings (which we denote by p and s, // respectively) and the number n of pseudorandom bits required. In // addition, u and v are of course non-zero. // For information on security considerations for MD5 [19], see [25] and // [1], and on those for MD2, see [18]. // The following procedure can be used to produce pseudorandom bits for // a particular "purpose" that is identified by a byte called "ID". // This standard specifies 3 different values for the ID byte: // 1. If ID=1, then the pseudorandom bits being produced are to be used // as key material for performing encryption or decryption. // 2. If ID=2, then the pseudorandom bits being produced are to be used // as an IV (Initial Value) for encryption or decryption. // 3. If ID=3, then the pseudorandom bits being produced are to be used // as an integrity key for MACing. // 1. Construct a string, D (the "diversifier"), by concatenating v/8 // copies of ID. D := []byte{} for i := 0; i < v; i++ { D = append(D, ID) } // 2. Concatenate copies of the salt together to create a string S of // length v(ceiling(s/v)) bits (the final copy of the salt may be // truncated to create S). Note that if the salt is the empty // string, then so is S. S := []byte{} { s := len(salt) times := s / v if s%v > 0 { times++ } for len(S) < times*v { S = append(S, salt...) } S = S[:times*v] } // 3. Concatenate copies of the password together to create a string P // of length v(ceiling(p/v)) bits (the final copy of the password // may be truncated to create P). Note that if the password is the // empty string, then so is P. P := []byte{} { s := len(password) times := s / v if s%v > 0 { times++ } for len(P) < times*v { P = append(P, password...) } password = nil P = P[:times*v] } // 4. Set I=S||P to be the concatenation of S and P. I := append(S, P...) // 5. Set c=ceiling(n/u). c := size / u if size%u > 0 { c++ } // 6. For i=1, 2, ..., c, do the following: A := make([]byte, c*20) for i := 0; i < c; i++ { // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, // H(H(H(... H(D||I)))) Ai := hash(append(D, I...)) for j := 1; j < r; j++ { Ai = hash(Ai[:]) } copy(A[i*20:], Ai[:]) if i < c-1 { // skip on last iteration // B. Concatenate copies of Ai to create a string B of length v // bits (the final copy of Ai may be truncated to create B). B := []byte{} for len(B) < v { B = append(B, Ai[:]...) } B = B[:v] // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by // setting I_j=(I_j+B+1) mod 2^v for each j. { Bbi := new(big.Int) Bbi.SetBytes(B) one := big.NewInt(1) for j := 0; j < len(I)/v; j++ { Ij := new(big.Int) Ij.SetBytes(I[j*v : (j+1)*v]) Ij.Add(Ij, Bbi) Ij.Add(Ij, one) Ijb := Ij.Bytes() if len(Ijb) > v { Ijb = Ijb[len(Ijb)-v:] } copy(I[j*v:(j+1)*v], Ijb) } } } } // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom // bit string, A. // 8. Use the first n bits of A as the output of this entire process. A = A[:size] return A // If the above process is being used to generate a DES key, the process // should be used to create 64 random bits, and the key's parity bits // should be set after the 64 bits have been produced. Similar concerns // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any // similar keys with parity bits "built into them". }