Scott Baker | 8487c5d | 2019-10-18 12:49:46 -0700 | [diff] [blame] | 1 | // Package rfc3961 provides encryption and checksum methods as specified in RFC 3961 |
| 2 | package rfc3961 |
| 3 | |
| 4 | import ( |
| 5 | "crypto/cipher" |
| 6 | "crypto/des" |
| 7 | "crypto/hmac" |
| 8 | "crypto/rand" |
| 9 | "errors" |
| 10 | "fmt" |
| 11 | |
| 12 | "gopkg.in/jcmturner/gokrb5.v7/crypto/common" |
| 13 | "gopkg.in/jcmturner/gokrb5.v7/crypto/etype" |
| 14 | ) |
| 15 | |
| 16 | // DES3EncryptData encrypts the data provided using DES3 and methods specific to the etype provided. |
| 17 | func DES3EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) { |
| 18 | if len(key) != e.GetKeyByteSize() { |
| 19 | return nil, nil, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) |
| 20 | } |
| 21 | data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize()) |
| 22 | |
| 23 | block, err := des.NewTripleDESCipher(key) |
| 24 | if err != nil { |
| 25 | return nil, nil, fmt.Errorf("error creating cipher: %v", err) |
| 26 | } |
| 27 | |
| 28 | //RFC 3961: initial cipher state All bits zero |
| 29 | ivz := make([]byte, des.BlockSize) |
| 30 | |
| 31 | ct := make([]byte, len(data)) |
| 32 | mode := cipher.NewCBCEncrypter(block, ivz) |
| 33 | mode.CryptBlocks(ct, data) |
| 34 | return ct[len(ct)-e.GetMessageBlockByteSize():], ct, nil |
| 35 | } |
| 36 | |
| 37 | // DES3EncryptMessage encrypts the message provided using DES3 and methods specific to the etype provided. |
| 38 | // The encrypted data is concatenated with its integrity hash to create an encrypted message. |
| 39 | func DES3EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) { |
| 40 | //confounder |
| 41 | c := make([]byte, e.GetConfounderByteSize()) |
| 42 | _, err := rand.Read(c) |
| 43 | if err != nil { |
| 44 | return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err) |
| 45 | } |
| 46 | plainBytes := append(c, message...) |
| 47 | plainBytes, _ = common.ZeroPad(plainBytes, e.GetMessageBlockByteSize()) |
| 48 | |
| 49 | // Derive key for encryption from usage |
| 50 | var k []byte |
| 51 | if usage != 0 { |
| 52 | k, err = e.DeriveKey(key, common.GetUsageKe(usage)) |
| 53 | if err != nil { |
| 54 | return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err) |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | iv, b, err := e.EncryptData(k, plainBytes) |
| 59 | if err != nil { |
| 60 | return iv, b, fmt.Errorf("error encrypting data: %v", err) |
| 61 | } |
| 62 | |
| 63 | // Generate and append integrity hash |
| 64 | ih, err := common.GetIntegrityHash(plainBytes, key, usage, e) |
| 65 | if err != nil { |
| 66 | return iv, b, fmt.Errorf("error encrypting data: %v", err) |
| 67 | } |
| 68 | b = append(b, ih...) |
| 69 | return iv, b, nil |
| 70 | } |
| 71 | |
| 72 | // DES3DecryptData decrypts the data provided using DES3 and methods specific to the etype provided. |
| 73 | func DES3DecryptData(key, data []byte, e etype.EType) ([]byte, error) { |
| 74 | if len(key) != e.GetKeyByteSize() { |
| 75 | return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key)) |
| 76 | } |
| 77 | |
| 78 | if len(data) < des.BlockSize || len(data)%des.BlockSize != 0 { |
| 79 | return []byte{}, errors.New("ciphertext is not a multiple of the block size") |
| 80 | } |
| 81 | block, err := des.NewTripleDESCipher(key) |
| 82 | if err != nil { |
| 83 | return []byte{}, fmt.Errorf("error creating cipher: %v", err) |
| 84 | } |
| 85 | pt := make([]byte, len(data)) |
| 86 | ivz := make([]byte, des.BlockSize) |
| 87 | mode := cipher.NewCBCDecrypter(block, ivz) |
| 88 | mode.CryptBlocks(pt, data) |
| 89 | return pt, nil |
| 90 | } |
| 91 | |
| 92 | // DES3DecryptMessage decrypts the message provided using DES3 and methods specific to the etype provided. |
| 93 | // The integrity of the message is also verified. |
| 94 | func DES3DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) { |
| 95 | //Derive the key |
| 96 | k, err := e.DeriveKey(key, common.GetUsageKe(usage)) |
| 97 | if err != nil { |
| 98 | return nil, fmt.Errorf("error deriving key: %v", err) |
| 99 | } |
| 100 | // Strip off the checksum from the end |
| 101 | b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8]) |
| 102 | if err != nil { |
| 103 | return nil, fmt.Errorf("error decrypting: %v", err) |
| 104 | } |
| 105 | //Verify checksum |
| 106 | if !e.VerifyIntegrity(key, ciphertext, b, usage) { |
| 107 | return nil, errors.New("error decrypting: integrity verification failed") |
| 108 | } |
| 109 | //Remove the confounder bytes |
| 110 | return b[e.GetConfounderByteSize():], nil |
| 111 | } |
| 112 | |
| 113 | // VerifyIntegrity verifies the integrity of cipertext bytes ct. |
| 114 | func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype etype.EType) bool { |
| 115 | //The ciphertext output is the concatenation of the output of the basic |
| 116 | //encryption function E and a (possibly truncated) HMAC using the |
| 117 | //specified hash function H, both applied to the plaintext with a |
| 118 | //random confounder prefix and sufficient padding to bring it to a |
| 119 | //multiple of the message block size. When the HMAC is computed, the |
| 120 | //key is used in the protocol key form. |
| 121 | h := make([]byte, etype.GetHMACBitLength()/8) |
| 122 | copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:]) |
| 123 | expectedMAC, _ := common.GetIntegrityHash(pt, key, usage, etype) |
| 124 | return hmac.Equal(h, expectedMAC) |
| 125 | } |