Skip to content

Instantly share code, notes, and snippets.

@DanGe42
Created September 20, 2016 05:59
Show Gist options
  • Save DanGe42/f27d924d9d4b850164372065e5a6fb4b to your computer and use it in GitHub Desktop.
Save DanGe42/f27d924d9d4b850164372065e5a6fb4b to your computer and use it in GitHub Desktop.
A go/crypto + protobuf sample
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
playground "playground/gcmcrypto/proto"
proto "github.com/golang/protobuf/proto"
)
var (
ErrEncrypt = errors.New("secret: encryption failed")
ErrDecrypt = errors.New("secret: decryption failed")
)
// GenerateNonce creates a new random nonce.
func GenerateNonce(nonceSize int) (*[]byte, error) {
nonce := make([]byte, nonceSize)
_, err := io.ReadFull(rand.Reader, nonce[:])
if err != nil {
return nil, err
}
return &nonce, nil
}
// Encrypt secures a message using AES-GCM.
func Encrypt(key, message []byte) ([]byte, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, ErrEncrypt
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, ErrEncrypt
}
nonce, err := GenerateNonce(gcm.NonceSize())
if err != nil {
return nil, ErrEncrypt
}
// Seal will append the output to the first argument; the usage
// here appends the ciphertext to the nonce. The final parameter
// is any additional data to be authenticated.
out := gcm.Seal(*nonce, *nonce, message, nil)
return out, nil
}
func Decrypt(key, message []byte) ([]byte, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, ErrDecrypt
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, ErrDecrypt
}
nonce := make([]byte, gcm.NonceSize())
copy(nonce, message)
out, err := gcm.Open(message[:0], nonce, message[gcm.NonceSize():], nil)
if err != nil {
return nil, ErrDecrypt
}
return out, nil
}
func generateToken(n int) (string, error) {
buf := make([]byte, n)
if _, err := rand.Read(buf); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(buf), nil
}
func mustGenerateToken(n int) string {
if encoded, err := generateToken(n); err != nil {
panic(err)
} else {
return encoded
}
}
func main() {
key := []byte("00000000000000000000000000000000")
message := &playground.Context{
Token1: proto.String("prefix1-prefix-" + mustGenerateToken(16)),
Token2: proto.String(mustGenerateToken(8)),
}
fmt.Println(message)
messageBytes, err := proto.Marshal(message)
if err != nil {
panic(err)
}
fmt.Println("len(message) = ", len(messageBytes))
fmt.Println("len(base64(message) = ", len(base64.URLEncoding.EncodeToString(messageBytes)))
encrypted, err := Encrypt(key, messageBytes)
if err != nil {
panic(err)
}
b64message := base64.URLEncoding.EncodeToString(encrypted)
fmt.Printf("%s [len=%d]\n", b64message, len(b64message))
unb64message, err := base64.URLEncoding.DecodeString(b64message)
if err != nil {
panic(err)
}
decrypted, err := Decrypt(key, unb64message)
origMessage := &playground.Context{}
if err := proto.Unmarshal(decrypted, origMessage); err != nil {
panic(err)
}
fmt.Println(origMessage)
}
# go run main.go
token1:"prefix1-prefix-Nra3S_677EweBAolqEm49w==" token2:"9j01k3uv8ns="
len(message) = 55
len(base64(message) = 76
eQ4zNLYAKpi_ImNDZEvycf-rkU7E6frJh4SuB_LJkwtrO1MCrWony5CMWHqrv1NyKTVyAJF4LAexRc8apY0DDj9fMCqOOZAOtqXdcuODvfaFMxw= [len=112]
token1:"prefix1-prefix-Nra3S_677EweBAolqEm49w==" token2:"9j01k3uv8ns="
// Code generated by protoc-gen-go.
// source: test.proto
// DO NOT EDIT!
/*
Package proto is a generated protocol buffer package.
It is generated from these files:
test.proto
It has these top-level messages:
Context
*/
package proto
import proto1 "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto1.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package
type Context struct {
Token1 *string `protobuf:"bytes,1,opt,name=token1" json:"token1,omitempty"`
Token2 *string `protobuf:"bytes,2,opt,name=token2" json:"token2,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Context) Reset() { *m = Context{} }
func (m *Context) String() string { return proto1.CompactTextString(m) }
func (*Context) ProtoMessage() {}
func (*Context) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Context) GetToken1() string {
if m != nil && m.Token1 != nil {
return *m.Token1
}
return ""
}
func (m *Context) GetToken2() string {
if m != nil && m.Token2 != nil {
return *m.Token2
}
return ""
}
func init() {
proto1.RegisterType((*Context)(nil), "gemcrypto.proto.Context")
}
func init() { proto1.RegisterFile("test.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 109 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0x2d, 0x2e,
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0x4f, 0xcd, 0x4d, 0x2e, 0xaa, 0x2c, 0x28,
0xc9, 0x87, 0x08, 0x28, 0x59, 0x72, 0xb1, 0x3b, 0xe7, 0xe7, 0x95, 0xa4, 0x56, 0x94, 0x08, 0x89,
0x71, 0xb1, 0x95, 0xe4, 0x67, 0xa7, 0xe6, 0x19, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x41,
0x79, 0x70, 0x71, 0x23, 0x09, 0x26, 0x24, 0x71, 0x23, 0x27, 0x99, 0x28, 0xa9, 0x82, 0x9c, 0xc4,
0xca, 0xf4, 0xa2, 0xfc, 0xd2, 0xbc, 0x14, 0xfd, 0xf4, 0x64, 0xa8, 0xc1, 0xfa, 0x60, 0x83, 0x01,
0x01, 0x00, 0x00, 0xff, 0xff, 0x4f, 0xba, 0x63, 0x1b, 0x76, 0x00, 0x00, 0x00,
}
syntax = "proto2";
package gemcrypto.proto;
option go_package = "playground/gcmcrypto/proto";
message Context {
optional string token1 = 1;
optional string token2 = 2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment