| // Package rfc4757 provides encryption and checksum methods as specified in RFC 4757 |
| package rfc4757 |
| |
| import ( |
| "crypto/hmac" |
| "crypto/rand" |
| "crypto/rc4" |
| "errors" |
| "fmt" |
| |
| "gopkg.in/jcmturner/gokrb5.v7/crypto/etype" |
| ) |
| |
| // EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 4757. |
| func EncryptData(key, data []byte, e etype.EType) ([]byte, error) { |
| if len(key) != e.GetKeyByteSize() { |
| return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) |
| } |
| rc4Cipher, err := rc4.NewCipher(key) |
| if err != nil { |
| return []byte{}, fmt.Errorf("error creating RC4 cipher: %v", err) |
| } |
| ed := make([]byte, len(data)) |
| copy(ed, data) |
| rc4Cipher.XORKeyStream(ed, ed) |
| rc4Cipher.Reset() |
| return ed, nil |
| } |
| |
| // DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 4757. |
| func DecryptData(key, data []byte, e etype.EType) ([]byte, error) { |
| return EncryptData(key, data, e) |
| } |
| |
| // EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 4757. |
| // The encrypted data is concatenated with its RC4 header containing integrity checksum and confounder to create an encrypted message. |
| func EncryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) { |
| confounder := make([]byte, e.GetConfounderByteSize()) // size = 8 |
| _, err := rand.Read(confounder) |
| if err != nil { |
| return []byte{}, fmt.Errorf("error generating confounder: %v", err) |
| } |
| k1 := key |
| k2 := HMAC(k1, UsageToMSMsgType(usage)) |
| toenc := append(confounder, data...) |
| chksum := HMAC(k2, toenc) |
| k3 := HMAC(k2, chksum) |
| |
| ed, err := EncryptData(k3, toenc, e) |
| if err != nil { |
| return []byte{}, fmt.Errorf("error encrypting data: %v", err) |
| } |
| |
| msg := append(chksum, ed...) |
| return msg, nil |
| } |
| |
| // DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 4757. |
| // The integrity of the message is also verified. |
| func DecryptMessage(key, data []byte, usage uint32, export bool, e etype.EType) ([]byte, error) { |
| checksum := data[:e.GetHMACBitLength()/8] |
| ct := data[e.GetHMACBitLength()/8:] |
| _, k2, k3 := deriveKeys(key, checksum, usage, export) |
| |
| pt, err := DecryptData(k3, ct, e) |
| if err != nil { |
| return []byte{}, fmt.Errorf("error decrypting data: %v", err) |
| } |
| |
| if !VerifyIntegrity(k2, pt, data, e) { |
| return []byte{}, errors.New("integrity checksum incorrect") |
| } |
| return pt[e.GetConfounderByteSize():], nil |
| } |
| |
| // VerifyIntegrity checks the integrity checksum of the data matches that calculated from the decrypted data. |
| func VerifyIntegrity(key, pt, data []byte, e etype.EType) bool { |
| chksum := HMAC(key, pt) |
| return hmac.Equal(chksum, data[:e.GetHMACBitLength()/8]) |
| } |