| package rfc3961 |
| |
| import ( |
| "bytes" |
| |
| "gopkg.in/jcmturner/gokrb5.v7/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) |
| |
| /*If the output of E is shorter than k bits, it is fed back into the encryption as many times as necessary. |
| The construct is as follows (where | indicates concatenation): |
| |
| K1 = E(Key, n-fold(Constant), initial-cipher-state) |
| K2 = E(Key, K1, initial-cipher-state) |
| K3 = E(Key, K2, initial-cipher-state) |
| K4 = ... |
| |
| DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)*/ |
| _, 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 |
| } |