1// Package common provides encryption methods common across encryption types
2package common
3
4import (
5	"bytes"
6	"crypto/hmac"
7	"encoding/binary"
8	"encoding/hex"
9	"errors"
10	"fmt"
11
12	"github.com/jcmturner/gokrb5/v8/crypto/etype"
13)
14
15// ZeroPad pads bytes with zeros to nearest multiple of message size m.
16func 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.
32func 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.
47func 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.
71func 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.
84func 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.
89func 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.
94func 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);
110func 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);
119func 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);
128func GetUsageKi(un uint32) []byte {
129	return getUsage(un, 0x55)
130}
131
132func 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.
139func IterationsToS2Kparams(i uint32) string {
140	b := make([]byte, 4, 4)
141	binary.BigEndian.PutUint32(b, i)
142	return hex.EncodeToString(b)
143}
144