1package s3crypto
2
3import (
4	"io"
5)
6
7const (
8	cbcKeySize   = 32
9	cbcNonceSize = 16
10)
11
12type cbcContentCipherBuilder struct {
13	generator CipherDataGenerator
14	padder    Padder
15}
16
17// AESCBCContentCipherBuilder returns a new encryption only AES/CBC mode structure using the provided padder. The provided cipher data generator
18// will be used to provide keys for content encryption.
19//
20// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
21func AESCBCContentCipherBuilder(generator CipherDataGenerator, padder Padder) ContentCipherBuilder {
22	return cbcContentCipherBuilder{generator: generator, padder: padder}
23}
24
25// RegisterAESCBCContentCipher registers the AES/CBC cipher and padder with the provided CryptoRegistry.
26//
27// Example:
28//	cr := s3crypto.NewCryptoRegistry()
29//	if err := s3crypto.RegisterAESCBCContentCipher(cr, s3crypto.AESCBCPadder); err != nil {
30//		panic(err) // handle error
31//	}
32//
33// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
34func RegisterAESCBCContentCipher(registry *CryptoRegistry, padder Padder) error {
35	if registry == nil {
36		return errNilCryptoRegistry
37	}
38	name := AESCBC + "/" + padder.Name()
39	err := registry.AddCEK(name, newAESCBCContentCipher)
40	if err != nil {
41		return err
42	}
43	if err := registry.AddPadder(name, padder); err != nil {
44		return err
45	}
46	return nil
47}
48
49func (builder cbcContentCipherBuilder) ContentCipher() (ContentCipher, error) {
50	cd, err := builder.generator.GenerateCipherData(cbcKeySize, cbcNonceSize)
51	if err != nil {
52		return nil, err
53	}
54
55	cd.Padder = builder.padder
56	return newAESCBCContentCipher(cd)
57}
58
59func (builder cbcContentCipherBuilder) isAWSFixture() bool {
60	return true
61}
62
63func (cbcContentCipherBuilder) isEncryptionVersionCompatible(version clientVersion) error {
64	if version != v1ClientVersion {
65		return errDeprecatedIncompatibleCipherBuilder
66	}
67	return nil
68}
69
70// newAESCBCContentCipher will create a new aes cbc content cipher. If the cipher data's
71// will set the cek algorithm if it hasn't been set.
72func newAESCBCContentCipher(cd CipherData) (ContentCipher, error) {
73	if len(cd.CEKAlgorithm) == 0 {
74		cd.CEKAlgorithm = AESCBC + "/" + cd.Padder.Name()
75	}
76	cipher, err := newAESCBC(cd, cd.Padder)
77	if err != nil {
78		return nil, err
79	}
80
81	return &aesCBCContentCipher{
82		CipherData: cd,
83		Cipher:     cipher,
84	}, nil
85}
86
87// aesCBCContentCipher will use AES CBC for the main cipher.
88type aesCBCContentCipher struct {
89	CipherData CipherData
90	Cipher     Cipher
91}
92
93// EncryptContents will generate a random key and iv and encrypt the data using cbc
94func (cc *aesCBCContentCipher) EncryptContents(src io.Reader) (io.Reader, error) {
95	return cc.Cipher.Encrypt(src), nil
96}
97
98// DecryptContents will use the symmetric key provider to instantiate a new CBC cipher.
99// We grab a decrypt reader from CBC and wrap it in a CryptoReadCloser. The only error
100// expected here is when the key or iv is of invalid length.
101func (cc *aesCBCContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser, error) {
102	reader := cc.Cipher.Decrypt(src)
103	return &CryptoReadCloser{Body: src, Decrypter: reader}, nil
104}
105
106// GetCipherData returns cipher data
107func (cc aesCBCContentCipher) GetCipherData() CipherData {
108	return cc.CipherData
109}
110
111var (
112	_ ContentCipherBuilder        = (*cbcContentCipherBuilder)(nil)
113	_ compatibleEncryptionFixture = (*cbcContentCipherBuilder)(nil)
114	_ awsFixture                  = (*cbcContentCipherBuilder)(nil)
115
116	_ ContentCipher = (*aesCBCContentCipher)(nil)
117)
118