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
15package openssl
16
17// #include <openssl/evp.h>
18import "C"
19
20import (
21	"errors"
22	"fmt"
23)
24
25type AuthenticatedEncryptionCipherCtx interface {
26	EncryptionCipherCtx
27
28	// data passed in to ExtraData() is part of the final output; it is
29	// not encrypted itself, but is part of the authenticated data. when
30	// decrypting or authenticating, pass back with the decryption
31	// context's ExtraData()
32	ExtraData([]byte) error
33
34	// use after finalizing encryption to get the authenticating tag
35	GetTag() ([]byte, error)
36}
37
38type AuthenticatedDecryptionCipherCtx interface {
39	DecryptionCipherCtx
40
41	// pass in any extra data that was added during encryption with the
42	// encryption context's ExtraData()
43	ExtraData([]byte) error
44
45	// use before finalizing decryption to tell the library what the
46	// tag is expected to be
47	SetTag([]byte) error
48}
49
50type authEncryptionCipherCtx struct {
51	*encryptionCipherCtx
52}
53
54type authDecryptionCipherCtx struct {
55	*decryptionCipherCtx
56}
57
58func getGCMCipher(blocksize int) (*Cipher, error) {
59	var cipherptr *C.EVP_CIPHER
60	switch blocksize {
61	case 256:
62		cipherptr = C.EVP_aes_256_gcm()
63	case 192:
64		cipherptr = C.EVP_aes_192_gcm()
65	case 128:
66		cipherptr = C.EVP_aes_128_gcm()
67	default:
68		return nil, fmt.Errorf("unknown block size %d", blocksize)
69	}
70	return &Cipher{ptr: cipherptr}, nil
71}
72
73func NewGCMEncryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) (
74	AuthenticatedEncryptionCipherCtx, error) {
75	cipher, err := getGCMCipher(blocksize)
76	if err != nil {
77		return nil, err
78	}
79	ctx, err := newEncryptionCipherCtx(cipher, e, key, nil)
80	if err != nil {
81		return nil, err
82	}
83	if len(iv) > 0 {
84		err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv))
85		if err != nil {
86			return nil, fmt.Errorf("could not set IV len to %d: %s",
87				len(iv), err)
88		}
89		if 1 != C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, nil,
90			(*C.uchar)(&iv[0])) {
91			return nil, errors.New("failed to apply IV")
92		}
93	}
94	return &authEncryptionCipherCtx{encryptionCipherCtx: ctx}, nil
95}
96
97func NewGCMDecryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) (
98	AuthenticatedDecryptionCipherCtx, error) {
99	cipher, err := getGCMCipher(blocksize)
100	if err != nil {
101		return nil, err
102	}
103	ctx, err := newDecryptionCipherCtx(cipher, e, key, nil)
104	if err != nil {
105		return nil, err
106	}
107	if len(iv) > 0 {
108		err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv))
109		if err != nil {
110			return nil, fmt.Errorf("could not set IV len to %d: %s",
111				len(iv), err)
112		}
113		if 1 != C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, nil,
114			(*C.uchar)(&iv[0])) {
115			return nil, errors.New("failed to apply IV")
116		}
117	}
118	return &authDecryptionCipherCtx{decryptionCipherCtx: ctx}, nil
119}
120
121func (ctx *authEncryptionCipherCtx) ExtraData(aad []byte) error {
122	if aad == nil {
123		return nil
124	}
125	var outlen C.int
126	if 1 != C.EVP_EncryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]),
127		C.int(len(aad))) {
128		return errors.New("failed to add additional authenticated data")
129	}
130	return nil
131}
132
133func (ctx *authDecryptionCipherCtx) ExtraData(aad []byte) error {
134	if aad == nil {
135		return nil
136	}
137	var outlen C.int
138	if 1 != C.EVP_DecryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]),
139		C.int(len(aad))) {
140		return errors.New("failed to add additional authenticated data")
141	}
142	return nil
143}
144
145func (ctx *authEncryptionCipherCtx) GetTag() ([]byte, error) {
146	return ctx.getCtrlBytes(C.EVP_CTRL_GCM_GET_TAG, GCM_TAG_MAXLEN,
147		GCM_TAG_MAXLEN)
148}
149
150func (ctx *authDecryptionCipherCtx) SetTag(tag []byte) error {
151	return ctx.setCtrlBytes(C.EVP_CTRL_GCM_SET_TAG, len(tag), tag)
152}
153