Skip to content

Commit 512e31e

Browse files
authored
Implement GCM Seal and Open in Go (#254)
* implement GCM Seal and Open in Go * minor cleanup
1 parent 8412958 commit 512e31e

File tree

4 files changed

+57
-101
lines changed

4 files changed

+57
-101
lines changed

cgo_go124.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ package openssl
1818
#cgo nocallback go_openssl_RAND_bytes
1919
#cgo noescape go_openssl_EVP_EncryptUpdate
2020
#cgo nocallback go_openssl_EVP_EncryptUpdate
21+
#cgo noescape go_openssl_EVP_EncryptFinal_ex
22+
#cgo nocallback go_openssl_EVP_EncryptFinal_ex
23+
#cgo noescape go_openssl_EVP_DecryptFinal_ex
24+
#cgo nocallback go_openssl_EVP_DecryptFinal_ex
2125
#cgo noescape go_openssl_EVP_DecryptUpdate
2226
#cgo nocallback go_openssl_EVP_DecryptUpdate
2327
#cgo noescape go_openssl_EVP_CipherUpdate

cipher.go

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ func (g *cipherGCM) Overhead() int {
381381
return gcmTagSize
382382
}
383383

384-
func (g *cipherGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
384+
func (g *cipherGCM) Seal(dst, nonce, plaintext, aad []byte) []byte {
385385
if len(nonce) != gcmStandardNonceSize {
386386
panic("cipher: incorrect nonce length given to GCM")
387387
}
@@ -392,9 +392,9 @@ func (g *cipherGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
392392
panic("cipher: message too large for buffer")
393393
}
394394
if g.tls != cipherGCMTLSNone {
395-
if g.tls == cipherGCMTLS12 && len(additionalData) != gcmTls12AddSize {
395+
if g.tls == cipherGCMTLS12 && len(aad) != gcmTls12AddSize {
396396
panic("cipher: incorrect additional data length given to GCM TLS 1.2")
397-
} else if g.tls == cipherGCMTLS13 && len(additionalData) != gcmTls13AddSize {
397+
} else if g.tls == cipherGCMTLS13 && len(aad) != gcmTls13AddSize {
398398
panic("cipher: incorrect additional data length given to GCM TLS 1.3")
399399
}
400400
counter := binary.BigEndian.Uint64(nonce[gcmTlsFixedNonceSize:])
@@ -457,19 +457,30 @@ func (g *cipherGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
457457
// relying in the explicit nonce being securely set externally,
458458
// and it also gives some interesting speed gains.
459459
// Unfortunately we can't use it because Go expects AEAD.Seal to honor the provided nonce.
460-
if C.go_openssl_EVP_CIPHER_CTX_seal_wrapper(ctx, base(out), base(nonce),
461-
base(plaintext), C.int(len(plaintext)),
462-
base(additionalData), C.int(len(additionalData))) != 1 {
463-
464-
panic(fail("EVP_CIPHER_CTX_seal"))
460+
if C.go_openssl_EVP_EncryptInit_ex(ctx, nil, nil, nil, base(nonce)) != 1 {
461+
panic(newOpenSSLError("EVP_EncryptInit_ex"))
462+
}
463+
var outl, discard C.int
464+
if C.go_openssl_EVP_EncryptUpdate(ctx, nil, &discard, baseNeverEmpty(aad), C.int(len(aad))) != 1 ||
465+
C.go_openssl_EVP_EncryptUpdate(ctx, base(out), &outl, baseNeverEmpty(plaintext), C.int(len(plaintext))) != 1 {
466+
panic(newOpenSSLError("EVP_EncryptUpdate"))
467+
}
468+
if len(plaintext) != int(outl) {
469+
panic("cipher: incorrect length returned from GCM EncryptUpdate")
470+
}
471+
if C.go_openssl_EVP_EncryptFinal_ex(ctx, base(out[outl:]), &discard) != 1 {
472+
panic(newOpenSSLError("EVP_EncryptFinal_ex"))
473+
}
474+
if C.go_openssl_EVP_CIPHER_CTX_ctrl(ctx, C.GO_EVP_CTRL_GCM_GET_TAG, 16, unsafe.Pointer(base(out[outl:]))) != 1 {
475+
panic(newOpenSSLError("EVP_CIPHER_CTX_ctrl"))
465476
}
466477
runtime.KeepAlive(g)
467478
return ret
468479
}
469480

470481
var errOpen = errors.New("cipher: message authentication failed")
471482

472-
func (g *cipherGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
483+
func (g *cipherGCM) Open(dst, nonce, ciphertext, aad []byte) (_ []byte, err error) {
473484
if len(nonce) != gcmStandardNonceSize {
474485
panic("cipher: incorrect nonce length given to GCM")
475486
}
@@ -497,18 +508,33 @@ func (g *cipherGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte,
497508
return nil, err
498509
}
499510
defer C.go_openssl_EVP_CIPHER_CTX_free(ctx)
500-
ok := C.go_openssl_EVP_CIPHER_CTX_open_wrapper(
501-
ctx, base(out), base(nonce),
502-
base(ciphertext), C.int(len(ciphertext)),
503-
base(additionalData), C.int(len(additionalData)), base(tag))
504-
runtime.KeepAlive(g)
505-
if ok == 0 {
506-
// Zero output buffer on error.
507-
for i := range out {
508-
out[i] = 0
511+
512+
defer func() {
513+
if err != nil {
514+
// Zero output buffer on error.
515+
for i := range out {
516+
out[i] = 0
517+
}
509518
}
519+
}()
520+
if C.go_openssl_EVP_DecryptInit_ex(ctx, nil, nil, nil, base(nonce)) != 1 {
521+
return nil, errOpen
522+
}
523+
if C.go_openssl_EVP_CIPHER_CTX_ctrl(ctx, C.GO_EVP_CTRL_GCM_SET_TAG, 16, unsafe.Pointer(base(tag))) != 1 {
524+
return nil, errOpen
525+
}
526+
var outl, discard C.int
527+
if C.go_openssl_EVP_DecryptUpdate(ctx, nil, &discard, baseNeverEmpty(aad), C.int(len(aad))) != 1 ||
528+
C.go_openssl_EVP_DecryptUpdate(ctx, base(out), &outl, baseNeverEmpty(ciphertext), C.int(len(ciphertext))) != 1 {
510529
return nil, errOpen
511530
}
531+
if len(ciphertext) != int(outl) {
532+
return nil, errOpen
533+
}
534+
if C.go_openssl_EVP_DecryptFinal_ex(ctx, base(out[outl:]), &discard) != 1 {
535+
return nil, errOpen
536+
}
537+
runtime.KeepAlive(g)
512538
return ret, nil
513539
}
514540

goopenssl.h

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// This header file describes the OpenSSL ABI as built for use in Go.
22

3-
#include <stdlib.h> // size_t
4-
53
#include "shims.h"
64

75
// Suppress warnings about unused parameters.
@@ -73,84 +71,3 @@ go_hash_sum(GO_EVP_MD_CTX_PTR ctx, GO_EVP_MD_CTX_PTR ctx2, unsigned char *out)
7371
return 0;
7472
return go_openssl_EVP_DigestFinal_ex(ctx2, out, NULL);
7573
}
76-
77-
// These wrappers allocate out_len on the C stack, and check that it matches the expected
78-
// value, to avoid having to pass a pointer from Go, which would escape to the heap.
79-
80-
static inline int
81-
go_openssl_EVP_CIPHER_CTX_seal_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
82-
unsigned char *out,
83-
const unsigned char *nonce,
84-
const unsigned char *in, int in_len,
85-
const unsigned char *aad, int aad_len)
86-
{
87-
if (in_len == 0) in = (const unsigned char *)"";
88-
if (aad_len == 0) aad = (const unsigned char *)"";
89-
90-
if (go_openssl_EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1)
91-
return 0;
92-
93-
int discard_len, out_len;
94-
if (go_openssl_EVP_EncryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1
95-
|| go_openssl_EVP_EncryptUpdate(ctx, out, &out_len, in, in_len) != 1
96-
|| go_openssl_EVP_EncryptFinal_ex(ctx, out + out_len, &discard_len) != 1)
97-
{
98-
return 0;
99-
}
100-
101-
if (in_len != out_len)
102-
return 0;
103-
104-
return go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_GET_TAG, 16, out + out_len);
105-
}
106-
107-
static inline int
108-
go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
109-
unsigned char *out,
110-
const unsigned char *nonce,
111-
const unsigned char *in, int in_len,
112-
const unsigned char *aad, int aad_len,
113-
const unsigned char *tag)
114-
{
115-
if (in_len == 0) {
116-
in = (const unsigned char *)"";
117-
// OpenSSL 1.0.2 in FIPS mode contains a bug: it will fail to verify
118-
// unless EVP_DecryptUpdate is called at least once with a non-NULL
119-
// output buffer. OpenSSL will not dereference the output buffer when
120-
// the input length is zero, so set it to an arbitrary non-NULL pointer
121-
// to satisfy OpenSSL when the caller only has authenticated additional
122-
// data (AAD) to verify. While a stack-allocated buffer could be used,
123-
// that would risk a stack-corrupting buffer overflow if OpenSSL
124-
// unexpectedly dereferenced it. Instead pass a value which would
125-
// segfault if dereferenced on any modern platform where a NULL-pointer
126-
// dereference would also segfault.
127-
if (out == NULL) out = (unsigned char *)1;
128-
}
129-
if (aad_len == 0) aad = (const unsigned char *)"";
130-
131-
if (go_openssl_EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce) != 1)
132-
return 0;
133-
134-
// OpenSSL 1.0.x FIPS Object Module 2.0 versions below 2.0.5 require that
135-
// the tag be set before the ciphertext, otherwise EVP_DecryptUpdate returns
136-
// an error. At least one extant commercially-supported, FIPS validated
137-
// build of OpenSSL 1.0.2 uses FIPS module version 2.0.1. Set the tag first
138-
// to maximize compatibility with all OpenSSL version combinations.
139-
if (go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)(tag)) != 1)
140-
return 0;
141-
142-
int discard_len, out_len;
143-
if (go_openssl_EVP_DecryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1
144-
|| go_openssl_EVP_DecryptUpdate(ctx, out, &out_len, in, in_len) != 1)
145-
{
146-
return 0;
147-
}
148-
149-
if (go_openssl_EVP_DecryptFinal_ex(ctx, out + out_len, &discard_len) != 1)
150-
return 0;
151-
152-
if (out_len != in_len)
153-
return 0;
154-
155-
return 1;
156-
}

openssl.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,15 @@ func addr(p []byte) *byte {
258258
return (*byte)(noescape(unsafe.Pointer(&p[0])))
259259
}
260260

261+
// baseNeverEmpty returns the address of the underlying array in b.
262+
// If b has zero length, it returns a pointer to a zero byte.
263+
func baseNeverEmpty(b []byte) *C.uchar {
264+
if len(b) == 0 {
265+
return (*C.uchar)(unsafe.Pointer(&zero))
266+
}
267+
return (*C.uchar)(unsafe.Pointer(&b[0]))
268+
}
269+
261270
// base returns the address of the underlying array in b,
262271
// being careful not to panic when b has zero length.
263272
func base(b []byte) *C.uchar {

0 commit comments

Comments
 (0)