123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- package rfc3961
- import (
- "bytes"
- "github.com/jcmturner/gokrb5/v8/crypto/etype"
- )
- const (
- prfconstant = "prf"
- )
- // DeriveRandom implements the RFC 3961 defined function: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)).
- //
- // key: base key or protocol key. Likely to be a key from a keytab file.
- //
- // usage: a constant.
- //
- // n: block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes.
- //
- // k: key length / key seed length in bits. Eg. for AES256 this value is 256.
- //
- // e: the encryption etype function to use.
- func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) {
- n := e.GetCypherBlockBitLength()
- k := e.GetKeySeedBitLength()
- //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.
- nFoldUsage := Nfold(usage, n)
- //k-truncate implemented by creating a byte array the size of k (k is in bits hence /8)
- out := make([]byte, k/8)
- // Keep feeding the output back into the encryption function until it is no longer short than k.
- _, K, err := e.EncryptData(key, nFoldUsage)
- if err != nil {
- return out, err
- }
- for i := copy(out, K); i < len(out); {
- _, K, _ = e.EncryptData(key, K)
- i = i + copy(out[i:], K)
- }
- return out, nil
- }
- // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods.
- func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
- r, err := e.DeriveRandom(protocolKey, usage)
- if err != nil {
- return nil, err
- }
- return e.RandomToKey(r), nil
- }
- // RandomToKey returns a key from the bytes provided according to the definition in RFC 3961.
- func RandomToKey(b []byte) []byte {
- return b
- }
- // DES3RandomToKey returns a key from the bytes provided according to the definition in RFC 3961 for DES3 etypes.
- func DES3RandomToKey(b []byte) []byte {
- r := fixWeakKey(stretch56Bits(b[:7]))
- r2 := fixWeakKey(stretch56Bits(b[7:14]))
- r = append(r, r2...)
- r3 := fixWeakKey(stretch56Bits(b[14:21]))
- r = append(r, r3...)
- return r
- }
- // DES3StringToKey returns a key derived from the string provided according to the definition in RFC 3961 for DES3 etypes.
- func DES3StringToKey(secret, salt string, e etype.EType) ([]byte, error) {
- s := secret + salt
- tkey := e.RandomToKey(Nfold([]byte(s), e.GetKeySeedBitLength()))
- return e.DeriveKey(tkey, []byte("kerberos"))
- }
- // PseudoRandom function as defined in RFC 3961
- func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) {
- h := e.GetHashFunc()()
- h.Write(b)
- tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()]
- k, err := e.DeriveKey(key, []byte(prfconstant))
- if err != nil {
- return []byte{}, err
- }
- _, prf, err := e.EncryptData(k, tmp)
- if err != nil {
- return []byte{}, err
- }
- return prf, nil
- }
- func stretch56Bits(b []byte) []byte {
- d := make([]byte, len(b), len(b))
- copy(d, b)
- var lb byte
- for i, v := range d {
- bv, nb := calcEvenParity(v)
- d[i] = nb
- if bv != 0 {
- lb = lb | (1 << uint(i+1))
- } else {
- lb = lb &^ (1 << uint(i+1))
- }
- }
- _, lb = calcEvenParity(lb)
- d = append(d, lb)
- return d
- }
- func calcEvenParity(b byte) (uint8, uint8) {
- lowestbit := b & 0x01
- // c counter of 1s in the first 7 bits of the byte
- var c int
- // Iterate over the highest 7 bits (hence p starts at 1 not zero) and count the 1s.
- for p := 1; p < 8; p++ {
- v := b & (1 << uint(p))
- if v != 0 {
- c++
- }
- }
- if c%2 == 0 {
- //Even number of 1s so set parity to 1
- b = b | 1
- } else {
- //Odd number of 1s so set parity to 0
- b = b &^ 1
- }
- return lowestbit, b
- }
- func fixWeakKey(b []byte) []byte {
- if weak(b) {
- b[7] ^= 0xF0
- }
- return b
- }
- func weak(b []byte) bool {
- // weak keys from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-67r1.pdf
- weakKeys := [4][]byte{
- {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
- {0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE},
- {0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1},
- {0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
- }
- semiWeakKeys := [12][]byte{
- {0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E},
- {0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01},
- {0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1},
- {0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01},
- {0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE},
- {0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01},
- {0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1},
- {0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E},
- {0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE},
- {0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E},
- {0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
- {0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1},
- }
- for _, k := range weakKeys {
- if bytes.Equal(b, k) {
- return true
- }
- }
- for _, k := range semiWeakKeys {
- if bytes.Equal(b, k) {
- return true
- }
- }
- return false
- }
|