1// Copyright (C) 2017. See AUTHORS.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build !openssl_pre_1.0
16
17package openssl
18
19// #include <openssl/evp.h>
20import "C"
21
22import (
23	"errors"
24	"fmt"
25)
26
27type AuthenticatedEncryptionCipherCtx interface {
28	EncryptionCipherCtx
29
30	// data passed in to ExtraData() is part of the final output; it is
31	// not encrypted itself, but is part of the authenticated data. when
32	// decrypting or authenticating, pass back with the decryption
33	// context's ExtraData()
34	ExtraData([]byte) error
35
36	// use after finalizing encryption to get the authenticating tag
37	GetTag() ([]byte, error)
38}
39
40type AuthenticatedDecryptionCipherCtx interface {
41	DecryptionCipherCtx
42
43	// pass in any extra data that was added during encryption with the
44	// encryption context's ExtraData()
45	ExtraData([]byte) error
46
47	// use before finalizing decryption to tell the library what the
48	// tag is expected to be
49	SetTag([]byte) error
50}
51
52type authEncryptionCipherCtx struct {
53	*encryptionCipherCtx
54}
55
56type authDecryptionCipherCtx struct {
57	*decryptionCipherCtx
58}
59
60func getGCMCipher(blocksize int) (*Cipher, error) {
61	var cipherptr *C.EVP_CIPHER
62	switch blocksize {
63	case 256:
64		cipherptr = C.EVP_aes_256_gcm()
65	case 192:
66		cipherptr = C.EVP_aes_192_gcm()
67	case 128:
68		cipherptr = C.EVP_aes_128_gcm()
69	default:
70		return nil, fmt.Errorf("unknown block size %d", blocksize)
71	}
72	return &Cipher{ptr: cipherptr}, nil
73}
74
75func NewGCMEncryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) (
76	AuthenticatedEncryptionCipherCtx, error) {
77	cipher, err := getGCMCipher(blocksize)
78	if err != nil {
79		return nil, err
80	}
81	ctx, err := newEncryptionCipherCtx(cipher, e, key, nil)
82	if err != nil {
83		return nil, err
84	}
85	if len(iv) > 0 {
86		err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv))
87		if err != nil {
88			return nil, fmt.Errorf("could not set IV len to %d: %s",
89				len(iv), err)
90		}
91		if 1 != C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, nil,
92			(*C.uchar)(&iv[0])) {
93			return nil, errors.New("failed to apply IV")
94		}
95	}
96	return &authEncryptionCipherCtx{encryptionCipherCtx: ctx}, nil
97}
98
99func NewGCMDecryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) (
100	AuthenticatedDecryptionCipherCtx, error) {
101	cipher, err := getGCMCipher(blocksize)
102	if err != nil {
103		return nil, err
104	}
105	ctx, err := newDecryptionCipherCtx(cipher, e, key, nil)
106	if err != nil {
107		return nil, err
108	}
109	if len(iv) > 0 {
110		err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv))
111		if err != nil {
112			return nil, fmt.Errorf("could not set IV len to %d: %s",
113				len(iv), err)
114		}
115		if 1 != C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, nil,
116			(*C.uchar)(&iv[0])) {
117			return nil, errors.New("failed to apply IV")
118		}
119	}
120	return &authDecryptionCipherCtx{decryptionCipherCtx: ctx}, nil
121}
122
123func (ctx *authEncryptionCipherCtx) ExtraData(aad []byte) error {
124	if aad == nil {
125		return nil
126	}
127	var outlen C.int
128	if 1 != C.EVP_EncryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]),
129		C.int(len(aad))) {
130		return errors.New("failed to add additional authenticated data")
131	}
132	return nil
133}
134
135func (ctx *authDecryptionCipherCtx) ExtraData(aad []byte) error {
136	if aad == nil {
137		return nil
138	}
139	var outlen C.int
140	if 1 != C.EVP_DecryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]),
141		C.int(len(aad))) {
142		return errors.New("failed to add additional authenticated data")
143	}
144	return nil
145}
146
147func (ctx *authEncryptionCipherCtx) GetTag() ([]byte, error) {
148	return ctx.getCtrlBytes(C.EVP_CTRL_GCM_GET_TAG, GCM_TAG_MAXLEN,
149		GCM_TAG_MAXLEN)
150}
151
152func (ctx *authDecryptionCipherCtx) SetTag(tag []byte) error {
153	return ctx.setCtrlBytes(C.EVP_CTRL_GCM_SET_TAG, len(tag), tag)
154}
155