1// Package rfc3962 provides encryption and checksum methods as specified in RFC 3962
2package rfc3962
3
4import (
5	"crypto/rand"
6	"errors"
7	"fmt"
8
9	"gopkg.in/jcmturner/aescts.v1"
10	"gopkg.in/jcmturner/gokrb5.v7/crypto/common"
11	"gopkg.in/jcmturner/gokrb5.v7/crypto/etype"
12)
13
14// EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 3962.
15func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
16	if len(key) != e.GetKeyByteSize() {
17		return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
18	}
19	ivz := make([]byte, e.GetCypherBlockBitLength()/8)
20	return aescts.Encrypt(key, ivz, data)
21}
22
23// EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 3962.
24// The encrypted data is concatenated with its integrity hash to create an encrypted message.
25func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
26	if len(key) != e.GetKeyByteSize() {
27		return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
28	}
29	//confounder
30	c := make([]byte, e.GetConfounderByteSize())
31	_, err := rand.Read(c)
32	if err != nil {
33		return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err)
34	}
35	plainBytes := append(c, message...)
36
37	// Derive key for encryption from usage
38	var k []byte
39	if usage != 0 {
40		k, err = e.DeriveKey(key, common.GetUsageKe(usage))
41		if err != nil {
42			return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err)
43		}
44	}
45
46	// Encrypt the data
47	iv, b, err := e.EncryptData(k, plainBytes)
48	if err != nil {
49		return iv, b, fmt.Errorf("error encrypting data: %v", err)
50	}
51
52	// Generate and append integrity hash
53	ih, err := common.GetIntegrityHash(plainBytes, key, usage, e)
54	if err != nil {
55		return iv, b, fmt.Errorf("error encrypting data: %v", err)
56	}
57	b = append(b, ih...)
58	return iv, b, nil
59}
60
61// DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 3962.
62func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
63	if len(key) != e.GetKeyByteSize() {
64		return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
65	}
66	ivz := make([]byte, e.GetCypherBlockBitLength()/8)
67	return aescts.Decrypt(key, ivz, data)
68}
69
70// DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 3962.
71// The integrity of the message is also verified.
72func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
73	//Derive the key
74	k, err := e.DeriveKey(key, common.GetUsageKe(usage))
75	if err != nil {
76		return nil, fmt.Errorf("error deriving key: %v", err)
77	}
78	// Strip off the checksum from the end
79	b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
80	if err != nil {
81		return nil, err
82	}
83	//Verify checksum
84	if !e.VerifyIntegrity(key, ciphertext, b, usage) {
85		return nil, errors.New("integrity verification failed")
86	}
87	//Remove the confounder bytes
88	return b[e.GetConfounderByteSize():], nil
89}
90