github.com/jcmturner/gokrb5/v8@v8.4.4/crypto/rfc3961/keyDerivation.go (about)

     1  package rfc3961
     2  
     3  import (
     4  	"bytes"
     5  
     6  	"github.com/jcmturner/gokrb5/v8/crypto/etype"
     7  )
     8  
     9  const (
    10  	prfconstant = "prf"
    11  )
    12  
    13  // DeriveRandom implements the RFC 3961 defined function: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)).
    14  //
    15  // key: base key or protocol key. Likely to be a key from a keytab file.
    16  //
    17  // usage: a constant.
    18  //
    19  // n: block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes.
    20  //
    21  // k: key length / key seed length in bits. Eg. for AES256 this value is 256.
    22  //
    23  // e: the encryption etype function to use.
    24  func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) {
    25  	n := e.GetCypherBlockBitLength()
    26  	k := e.GetKeySeedBitLength()
    27  	//Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be.
    28  	nFoldUsage := Nfold(usage, n)
    29  	//k-truncate implemented by creating a byte array the size of k (k is in bits hence /8)
    30  	out := make([]byte, k/8)
    31  	// Keep feeding the output back into the encryption function until it is no longer short than k.
    32  	_, K, err := e.EncryptData(key, nFoldUsage)
    33  	if err != nil {
    34  		return out, err
    35  	}
    36  	for i := copy(out, K); i < len(out); {
    37  		_, K, _ = e.EncryptData(key, K)
    38  		i = i + copy(out[i:], K)
    39  	}
    40  	return out, nil
    41  }
    42  
    43  // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods.
    44  func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
    45  	r, err := e.DeriveRandom(protocolKey, usage)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return e.RandomToKey(r), nil
    50  }
    51  
    52  // RandomToKey returns a key from the bytes provided according to the definition in RFC 3961.
    53  func RandomToKey(b []byte) []byte {
    54  	return b
    55  }
    56  
    57  // DES3RandomToKey returns a key from the bytes provided according to the definition in RFC 3961 for DES3 etypes.
    58  func DES3RandomToKey(b []byte) []byte {
    59  	r := fixWeakKey(stretch56Bits(b[:7]))
    60  	r2 := fixWeakKey(stretch56Bits(b[7:14]))
    61  	r = append(r, r2...)
    62  	r3 := fixWeakKey(stretch56Bits(b[14:21]))
    63  	r = append(r, r3...)
    64  	return r
    65  }
    66  
    67  // DES3StringToKey returns a key derived from the string provided according to the definition in RFC 3961 for DES3 etypes.
    68  func DES3StringToKey(secret, salt string, e etype.EType) ([]byte, error) {
    69  	s := secret + salt
    70  	tkey := e.RandomToKey(Nfold([]byte(s), e.GetKeySeedBitLength()))
    71  	return e.DeriveKey(tkey, []byte("kerberos"))
    72  }
    73  
    74  // PseudoRandom function as defined in RFC 3961
    75  func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) {
    76  	h := e.GetHashFunc()()
    77  	h.Write(b)
    78  	tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()]
    79  	k, err := e.DeriveKey(key, []byte(prfconstant))
    80  	if err != nil {
    81  		return []byte{}, err
    82  	}
    83  	_, prf, err := e.EncryptData(k, tmp)
    84  	if err != nil {
    85  		return []byte{}, err
    86  	}
    87  	return prf, nil
    88  }
    89  
    90  func stretch56Bits(b []byte) []byte {
    91  	d := make([]byte, len(b), len(b))
    92  	copy(d, b)
    93  	var lb byte
    94  	for i, v := range d {
    95  		bv, nb := calcEvenParity(v)
    96  		d[i] = nb
    97  		if bv != 0 {
    98  			lb = lb | (1 << uint(i+1))
    99  		} else {
   100  			lb = lb &^ (1 << uint(i+1))
   101  		}
   102  	}
   103  	_, lb = calcEvenParity(lb)
   104  	d = append(d, lb)
   105  	return d
   106  }
   107  
   108  func calcEvenParity(b byte) (uint8, uint8) {
   109  	lowestbit := b & 0x01
   110  	// c counter of 1s in the first 7 bits of the byte
   111  	var c int
   112  	// Iterate over the highest 7 bits (hence p starts at 1 not zero) and count the 1s.
   113  	for p := 1; p < 8; p++ {
   114  		v := b & (1 << uint(p))
   115  		if v != 0 {
   116  			c++
   117  		}
   118  	}
   119  	if c%2 == 0 {
   120  		//Even number of 1s so set parity to 1
   121  		b = b | 1
   122  	} else {
   123  		//Odd number of 1s so set parity to 0
   124  		b = b &^ 1
   125  	}
   126  	return lowestbit, b
   127  }
   128  
   129  func fixWeakKey(b []byte) []byte {
   130  	if weak(b) {
   131  		b[7] ^= 0xF0
   132  	}
   133  	return b
   134  }
   135  
   136  func weak(b []byte) bool {
   137  	// weak keys from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-67r1.pdf
   138  	weakKeys := [4][]byte{
   139  		{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
   140  		{0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE},
   141  		{0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1},
   142  		{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
   143  	}
   144  	semiWeakKeys := [12][]byte{
   145  		{0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E},
   146  		{0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01},
   147  		{0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1},
   148  		{0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01},
   149  		{0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE},
   150  		{0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01},
   151  		{0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1},
   152  		{0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E},
   153  		{0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE},
   154  		{0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E},
   155  		{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
   156  		{0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1},
   157  	}
   158  	for _, k := range weakKeys {
   159  		if bytes.Equal(b, k) {
   160  			return true
   161  		}
   162  	}
   163  	for _, k := range semiWeakKeys {
   164  		if bytes.Equal(b, k) {
   165  			return true
   166  		}
   167  	}
   168  	return false
   169  }