Scott Baker | 8487c5d | 2019-10-18 12:49:46 -0700 | [diff] [blame] | 1 | // Package common provides encryption methods common across encryption types |
| 2 | package common |
| 3 | |
| 4 | import ( |
| 5 | "bytes" |
| 6 | "crypto/hmac" |
| 7 | "encoding/binary" |
| 8 | "encoding/hex" |
| 9 | "errors" |
| 10 | "fmt" |
| 11 | |
| 12 | "gopkg.in/jcmturner/gokrb5.v7/crypto/etype" |
| 13 | ) |
| 14 | |
| 15 | // ZeroPad pads bytes with zeros to nearest multiple of message size m. |
| 16 | func ZeroPad(b []byte, m int) ([]byte, error) { |
| 17 | if m <= 0 { |
| 18 | return nil, errors.New("Invalid message block size when padding") |
| 19 | } |
| 20 | if b == nil || len(b) == 0 { |
| 21 | return nil, errors.New("Data not valid to pad: Zero size") |
| 22 | } |
| 23 | if l := len(b) % m; l != 0 { |
| 24 | n := m - l |
| 25 | z := make([]byte, n) |
| 26 | b = append(b, z...) |
| 27 | } |
| 28 | return b, nil |
| 29 | } |
| 30 | |
| 31 | // PKCS7Pad pads bytes according to RFC 2315 to nearest multiple of message size m. |
| 32 | func PKCS7Pad(b []byte, m int) ([]byte, error) { |
| 33 | if m <= 0 { |
| 34 | return nil, errors.New("Invalid message block size when padding") |
| 35 | } |
| 36 | if b == nil || len(b) == 0 { |
| 37 | return nil, errors.New("Data not valid to pad: Zero size") |
| 38 | } |
| 39 | n := m - (len(b) % m) |
| 40 | pb := make([]byte, len(b)+n) |
| 41 | copy(pb, b) |
| 42 | copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n)) |
| 43 | return pb, nil |
| 44 | } |
| 45 | |
| 46 | // PKCS7Unpad removes RFC 2315 padding from byes where message size is m. |
| 47 | func PKCS7Unpad(b []byte, m int) ([]byte, error) { |
| 48 | if m <= 0 { |
| 49 | return nil, errors.New("invalid message block size when unpadding") |
| 50 | } |
| 51 | if b == nil || len(b) == 0 { |
| 52 | return nil, errors.New("padded data not valid: Zero size") |
| 53 | } |
| 54 | if len(b)%m != 0 { |
| 55 | return nil, errors.New("padded data not valid: Not multiple of message block size") |
| 56 | } |
| 57 | c := b[len(b)-1] |
| 58 | n := int(c) |
| 59 | if n == 0 || n > len(b) { |
| 60 | return nil, errors.New("padded data not valid: Data may not have been padded") |
| 61 | } |
| 62 | for i := 0; i < n; i++ { |
| 63 | if b[len(b)-n+i] != c { |
| 64 | return nil, errors.New("padded data not valid") |
| 65 | } |
| 66 | } |
| 67 | return b[:len(b)-n], nil |
| 68 | } |
| 69 | |
| 70 | // GetHash generates the keyed hash value according to the etype's hash function. |
| 71 | func GetHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) { |
| 72 | k, err := etype.DeriveKey(key, usage) |
| 73 | if err != nil { |
| 74 | return nil, fmt.Errorf("unable to derive key for checksum: %v", err) |
| 75 | } |
| 76 | mac := hmac.New(etype.GetHashFunc(), k) |
| 77 | p := make([]byte, len(pt)) |
| 78 | copy(p, pt) |
| 79 | mac.Write(p) |
| 80 | return mac.Sum(nil)[:etype.GetHMACBitLength()/8], nil |
| 81 | } |
| 82 | |
| 83 | // GetChecksumHash returns a keyed checksum hash of the bytes provided. |
| 84 | func GetChecksumHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) { |
| 85 | return GetHash(b, key, GetUsageKc(usage), etype) |
| 86 | } |
| 87 | |
| 88 | // GetIntegrityHash returns a keyed integrity hash of the bytes provided. |
| 89 | func GetIntegrityHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) { |
| 90 | return GetHash(b, key, GetUsageKi(usage), etype) |
| 91 | } |
| 92 | |
| 93 | // VerifyChecksum compares the checksum of the msg bytes is the same as the checksum provided. |
| 94 | func VerifyChecksum(key, chksum, msg []byte, usage uint32, etype etype.EType) bool { |
| 95 | //The ciphertext output is the concatenation of the output of the basic |
| 96 | //encryption function E and a (possibly truncated) HMAC using the |
| 97 | //specified hash function H, both applied to the plaintext with a |
| 98 | //random confounder prefix and sufficient padding to bring it to a |
| 99 | //multiple of the message block size. When the HMAC is computed, the |
| 100 | //key is used in the protocol key form. |
| 101 | expectedMAC, _ := GetChecksumHash(msg, key, usage, etype) |
| 102 | return hmac.Equal(chksum, expectedMAC) |
| 103 | } |
| 104 | |
| 105 | // GetUsageKc returns the checksum key usage value for the usage number un. |
| 106 | // |
| 107 | // RFC 3961: The "well-known constant" used for the DK function is the key usage number, expressed as four octets in big-endian order, followed by one octet indicated below. |
| 108 | // |
| 109 | // Kc = DK(base-key, usage | 0x99); |
| 110 | func GetUsageKc(un uint32) []byte { |
| 111 | return getUsage(un, 0x99) |
| 112 | } |
| 113 | |
| 114 | // GetUsageKe returns the encryption key usage value for the usage number un |
| 115 | // |
| 116 | // RFC 3961: The "well-known constant" used for the DK function is the key usage number, expressed as four octets in big-endian order, followed by one octet indicated below. |
| 117 | // |
| 118 | // Ke = DK(base-key, usage | 0xAA); |
| 119 | func GetUsageKe(un uint32) []byte { |
| 120 | return getUsage(un, 0xAA) |
| 121 | } |
| 122 | |
| 123 | // GetUsageKi returns the integrity key usage value for the usage number un |
| 124 | // |
| 125 | // RFC 3961: The "well-known constant" used for the DK function is the key usage number, expressed as four octets in big-endian order, followed by one octet indicated below. |
| 126 | // |
| 127 | // Ki = DK(base-key, usage | 0x55); |
| 128 | func GetUsageKi(un uint32) []byte { |
| 129 | return getUsage(un, 0x55) |
| 130 | } |
| 131 | |
| 132 | func getUsage(un uint32, o byte) []byte { |
| 133 | var buf bytes.Buffer |
| 134 | binary.Write(&buf, binary.BigEndian, un) |
| 135 | return append(buf.Bytes(), o) |
| 136 | } |
| 137 | |
| 138 | // IterationsToS2Kparams converts the number of iterations as an integer to a string representation. |
| 139 | func IterationsToS2Kparams(i uint32) string { |
| 140 | b := make([]byte, 4, 4) |
| 141 | binary.BigEndian.PutUint32(b, i) |
| 142 | return hex.EncodeToString(b) |
| 143 | } |