func benchmarkAEAD(b *testing.B, c cipher.AEAD) { b.SetBytes(benchSize) input := make([]byte, benchSize) output := make([]byte, benchSize) nonce := make([]byte, c.NonceSize()) counter := 0 t := time.Now() b.ResetTimer() for i := 0; i < b.N; i++ { c.Seal(output[:0], nonce, input, nil) counter += len(output) } ts := time.Since(t) b.Logf("aes-gcm speed: %.2f Mbit/s", float64(counter)*8/ts.Seconds()/1024/1024) }
func (alg *AesGcm) Decrypt(aad, cek, iv, cipherText, authTag []byte) (plainText []byte, err error) { cekSizeBits := len(cek) << 3 if cekSizeBits != alg.keySizeBits { return nil, errors.New(fmt.Sprintf("AesGcm.Decrypt(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, cekSizeBits)) } var block cipher.Block if block, err = aes.NewCipher(cek); err != nil { return nil, err } var aesgcm cipher.AEAD if aesgcm, err = cipher.NewGCM(block); err != nil { return nil, err } cipherWithTag := append(cipherText, authTag...) if plainText, err = aesgcm.Open(nil, iv, cipherWithTag, aad); err != nil { return nil, err } return plainText, nil }
// Decrypts a proto using an AEAD. Unmarshals the result into dst. The result // should only be considered written if this function returns true. func DecryptProto(aead cipher.AEAD, msg string, additionalData []byte, dst proto.Message) bool { msgBytes, err := base64.RawURLEncoding.DecodeString(msg) if err != nil { glog.V(2).Infof("Tried to decrypt proto with invalid base64: %v", err) return false } var msgProto pb.EncryptedMessage err = proto.Unmarshal(msgBytes, &msgProto) if err != nil { glog.V(2).Infof("Tried to decrypt proto with invalid pb.EncryptedMessage: %v", err) return false } // Decrypt in-place. plaintext := msgProto.Ciphertext plaintext, err = aead.Open(plaintext[:0], msgProto.Nonce, msgProto.Ciphertext, additionalData) if err != nil { glog.V(2).Infof("Failed to decrypt data: %v", err) return false } err = proto.Unmarshal(plaintext, dst) if err != nil { glog.V(2).Infof("Failed to decrypt proto: %v", err) return false } return true }
func (c AesContentCipher) encrypt(cek, plaintext, aad []byte) (iv, ciphertext, tag []byte, err error) { var aead cipher.AEAD aead, err = c.AeadFetch(cek) if err != nil { if debug.Enabled { debug.Printf("AeadFetch failed: %s", err) } err = fmt.Errorf("failed to fetch AEAD: %s", err) return } // Seal may panic (argh!), so protect ourselves from that defer func() { if e := recover(); e != nil { switch e.(type) { case error: err = e.(error) case string: err = errors.New(e.(string)) default: err = fmt.Errorf("%s", e) } return } }() var bs ByteSource if c.NonceGenerator == nil { bs, err = NewRandomKeyGenerate(aead.NonceSize()).KeyGenerate() } else { bs, err = c.NonceGenerator.KeyGenerate() } if err != nil { return } iv = bs.Bytes() combined := aead.Seal(nil, iv, plaintext, aad) tagoffset := len(combined) - c.TagSize() if debug.Enabled { debug.Printf("tagsize = %d", c.TagSize()) } tag = combined[tagoffset:] ciphertext = make([]byte, tagoffset) copy(ciphertext, combined[:tagoffset]) if debug.Enabled { debug.Printf("encrypt: combined = %x (%d)\n", combined, len(combined)) debug.Printf("encrypt: ciphertext = %x (%d)\n", ciphertext, len(ciphertext)) debug.Printf("encrypt: tag = %x (%d)\n", tag, len(tag)) debug.Printf("finally ciphertext = %x\n", ciphertext) } return }
func (alg *AesGcmKW) Unwrap(encryptedCek []byte, key interface{}, cekSizeBits int, header map[string]interface{}) (cek []byte, err error) { if kek, ok := key.([]byte); ok { kekSizeBits := len(kek) << 3 if kekSizeBits != alg.keySizeBits { return nil, errors.New(fmt.Sprintf("AesGcmKW.Unwrap(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, kekSizeBits)) } var iv, tag string if iv, ok = header["iv"].(string); !ok { return nil, errors.New("AesGcmKW.Unwrap(): expected 'iv' param in JWT header, but was not found.") } if tag, ok = header["tag"].(string); !ok { return nil, errors.New("AesGcmKW.Unwrap(): expected 'tag' param in JWT header, but was not found.") } var ivBytes, tagBytes []byte if ivBytes, err = base64url.Decode(iv); err != nil { return nil, err } if tagBytes, err = base64url.Decode(tag); err != nil { return nil, err } var block cipher.Block if block, err = aes.NewCipher(kek); err != nil { return nil, err } var aesgcm cipher.AEAD if aesgcm, err = cipher.NewGCM(block); err != nil { return nil, err } cipherAndTag := append(encryptedCek, tagBytes...) if cek, err = aesgcm.Open(nil, ivBytes, cipherAndTag, nil); err != nil { fmt.Printf("err = %v\n", err) return nil, err } return cek, nil } return nil, errors.New("AesGcmKW.Unwrap(): expected key to be '[]byte' array") }
// Encrypt a proto using an AEAD. func EncryptProto(aead cipher.AEAD, msg proto.Message, additionalData []byte) string { plaintext := MarshalProtoOrPanic(msg) nonce := getNonce(aead.NonceSize()) // Encrypt in-place. ciphertext := plaintext ciphertext = aead.Seal(ciphertext[:0], nonce, plaintext, additionalData) outBytes := MarshalProtoOrPanic(&pb.EncryptedMessage{ Nonce: nonce, Ciphertext: ciphertext, }) // Return base64'd, so that the output is ASCII-safe. return base64.RawURLEncoding.EncodeToString(outBytes) }
func (alg *AesGcm) Encrypt(aad, plainText, cek []byte) (iv, cipherText, authTag []byte, err error) { cekSizeBits := len(cek) << 3 if cekSizeBits != alg.keySizeBits { return nil, nil, nil, errors.New(fmt.Sprintf("AesGcm.Encrypt(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, cekSizeBits)) } if iv, err = arrays.Random(12); err != nil { return nil, nil, nil, err } var block cipher.Block if block, err = aes.NewCipher(cek); err != nil { return nil, nil, nil, err } var aesgcm cipher.AEAD if aesgcm, err = cipher.NewGCM(block); err != nil { return nil, nil, nil, err } cipherWithTag := aesgcm.Seal(nil, iv, plainText, aad) cipherText = cipherWithTag[:len(cipherWithTag)-aesgcm.Overhead()] authTag = cipherWithTag[len(cipherWithTag)-aesgcm.Overhead():] return iv, cipherText, authTag, nil }
// encrypt is used to encrypt a value func (b *AESGCMBarrier) encrypt(term uint32, gcm cipher.AEAD, plain []byte) []byte { // Allocate the output buffer with room for tern, version byte, // nonce, GCM tag and the plaintext capacity := termSize + 1 + gcm.NonceSize() + gcm.Overhead() + len(plain) size := termSize + 1 + gcm.NonceSize() out := make([]byte, size, capacity) // Set the key term binary.BigEndian.PutUint32(out[:4], term) // Set the version byte out[4] = aesgcmVersionByte // Generate a random nonce nonce := out[5 : 5+gcm.NonceSize()] rand.Read(nonce) // Seal the output out = gcm.Seal(out, nonce, plain, nil) return out }
// encrypt is used to encrypt a value func (b *AESGCMBarrier) encrypt(gcm cipher.AEAD, plain []byte) []byte { // Allocate the output buffer with room for epoch, version byte, // nonce, GCM tag and the plaintext capacity := epochSize + 1 + gcm.NonceSize() + gcm.Overhead() + len(plain) size := epochSize + 1 + gcm.NonceSize() out := make([]byte, size, capacity) // Set the epoch to 1 out[3] = keyEpoch // Set the version byte out[4] = aesgcmVersionByte // Generate a random nonce nonce := out[5 : 5+gcm.NonceSize()] rand.Read(nonce) // Seal the output out = gcm.Seal(out, nonce, plain, nil) return out }
func getErrData(aead cipher.AEAD, stream io.Reader) (string, ErrData) { // Decode encrypted error from STDIN (json base64 gob) var api APIErr err := json.NewDecoder(stream).Decode(&api) if err != nil { panic(err) } rawdata, err := base64.StdEncoding.DecodeString(api.Data) if err != nil { panic(err) } rawdatabuf := bytes.NewBuffer(rawdata) encdecoder := gob.NewDecoder(rawdatabuf) var encerr EncryptedError err = encdecoder.Decode(&encerr) if err != nil { panic(err) } errbytes, err := aead.Open(nil, encerr.Nonce, encerr.Ciphertext, nil) if err != nil { panic(err) } // Decode error struct from decrypted bytes errbuf := bytes.NewBuffer(errbytes) errdecoder := gob.NewDecoder(errbuf) var errdata ErrData err = errdecoder.Decode(&errdata) if err != nil { panic(err) } return api.Status, errdata }
// decrypt is used to decrypt a value func (b *AESGCMBarrier) decrypt(path string, gcm cipher.AEAD, cipher []byte) ([]byte, error) { // Verify the term is always just one term := binary.BigEndian.Uint32(cipher[:4]) if term != initialKeyTerm { return nil, fmt.Errorf("term mis-match") } // Capture the parts nonce := cipher[5 : 5+gcm.NonceSize()] raw := cipher[5+gcm.NonceSize():] out := make([]byte, 0, len(raw)-gcm.NonceSize()) // Verify the cipher byte and attempt to open switch cipher[4] { case AESGCMVersion1: return gcm.Open(out, nonce, raw, nil) case AESGCMVersion2: return gcm.Open(out, nonce, raw, []byte(path)) default: return nil, fmt.Errorf("version bytes mis-match") } }
/* Encrypt generates a literal of the form <b64URLmetadata>.<b64URLciphertext>.<b64URLnonce> given an AEAD cipher, a metadata string and a data string. Only the data is encrypted - the metadata must be appropriate to expose in the clear. Each call generates a random nonce of the length required by the cipher. */ func Encrypt(aeadCipher cipher.AEAD, metadata, data string) (string, error) { var ( nonce = make([]byte, aeadCipher.NonceSize()) ciphertext []byte b64metadata []byte b64ciphertext []byte b64nonce []byte buf bytes.Buffer err error ) //A nonce of the length required by the AEAD is generated _, err = rand.Read(nonce) if err != nil { return "", err } //Seal encrypts the data using the aeadCipher's key and the nonce and appends an authentication code for the metadata ciphertext = aeadCipher.Seal(ciphertext, nonce, []byte(data), []byte(metadata)) //Base64 Encode metadata, ciphertext and nonce b64metadata = make([]byte, base64.URLEncoding.EncodedLen(len([]byte(metadata)))) base64.URLEncoding.Encode(b64metadata, []byte(metadata)) b64ciphertext = make([]byte, base64.URLEncoding.EncodedLen(len(ciphertext))) base64.URLEncoding.Encode(b64ciphertext, ciphertext) b64nonce = make([]byte, base64.URLEncoding.EncodedLen(len(nonce))) base64.URLEncoding.Encode(b64nonce, nonce) //Compose a <b64URLmetadata>.<b64URLciphertext>.<b64URLnonce> literal buf.Write(b64metadata) buf.Write([]byte(".")) buf.Write(b64ciphertext) buf.Write([]byte(".")) buf.Write(b64nonce) //Return the AEAD literal return string(buf.Bytes()), nil }
/* Decrypt decrypts a literal of the form <b64URLmetadata>.<b64URLciphertext>.<b64URLnonce> given an AEAD cipher and produces a metadata and data string. */ func Decrypt(aeadCipher cipher.AEAD, literal string) (string, string, error) { var ( literalSubStrings []string metadata []byte ciphertext []byte nonce []byte data []byte err error ) //Split the literal into its base64 encoded metadata, ciphertext and nonce components literalSubStrings = strings.Split(literal, ".") if len(literalSubStrings) != 3 { return "", "", fmt.Errorf("Bad AEAD Literal: %v\n", literal) } //Decode the metadata, ciphertext and nonce metadata, err = base64.URLEncoding.DecodeString(literalSubStrings[0]) if err != nil { return "", "", fmt.Errorf("Decode metadata failed: %v\n", literal) } ciphertext, err = base64.URLEncoding.DecodeString(literalSubStrings[1]) if err != nil { return "", "", fmt.Errorf("Decode ciphertext failed: %v\n", literal) } nonce, err = base64.URLEncoding.DecodeString(literalSubStrings[2]) if err != nil { return "", "", fmt.Errorf("Decode nonce failed: %v\n", literal) } //Open validates the integrity of the metadata using the authentication code in the ciphertext //and, if valid, decrypts the ciphertext data, err = aeadCipher.Open(data, nonce, ciphertext, metadata) if err != nil { return "", "", err } return string(metadata), string(data), nil }
// decrypt is used to decrypt a value func (b *AESGCMBarrier) decrypt(gcm cipher.AEAD, cipher []byte) ([]byte, error) { // Verify the epoch if cipher[0] != 0 || cipher[1] != 0 || cipher[2] != 0 || cipher[3] != keyEpoch { return nil, fmt.Errorf("epoch mis-match") } // Verify the version byte if cipher[4] != aesgcmVersionByte { return nil, fmt.Errorf("version bytes mis-match") } // Capture the parts nonce := cipher[5 : 5+gcm.NonceSize()] raw := cipher[5+gcm.NonceSize():] out := make([]byte, 0, len(raw)-gcm.NonceSize()) // Attempt to open return gcm.Open(out, nonce, raw, nil) }
// decrypt is used to decrypt a value func (b *AESGCMBarrier) decrypt(gcm cipher.AEAD, cipher []byte) ([]byte, error) { // Verify the term is always just one term := binary.BigEndian.Uint32(cipher[:4]) if term != initialKeyTerm { return nil, fmt.Errorf("term mis-match") } // Verify the version byte if cipher[4] != aesgcmVersionByte { return nil, fmt.Errorf("version bytes mis-match") } // Capture the parts nonce := cipher[5 : 5+gcm.NonceSize()] raw := cipher[5+gcm.NonceSize():] out := make([]byte, 0, len(raw)-gcm.NonceSize()) // Attempt to open return gcm.Open(out, nonce, raw, nil) }
func (alg *AesGcmKW) WrapNewKey(cekSizeBits int, key interface{}, header map[string]interface{}) (cek []byte, encryptedCek []byte, err error) { if kek, ok := key.([]byte); ok { kekSizeBits := len(kek) << 3 if kekSizeBits != alg.keySizeBits { return nil, nil, errors.New(fmt.Sprintf("AesGcmKW.WrapNewKey(): expected key of size %v bits, but was given %v bits.", alg.keySizeBits, kekSizeBits)) } if cek, err = arrays.Random(cekSizeBits >> 3); err != nil { return nil, nil, err } var iv []byte if iv, err = arrays.Random(12); err != nil { return nil, nil, err } var block cipher.Block if block, err = aes.NewCipher(kek); err != nil { return nil, nil, err } var aesgcm cipher.AEAD if aesgcm, err = cipher.NewGCM(block); err != nil { return nil, nil, err } cipherWithTag := aesgcm.Seal(nil, iv, cek, nil) cipherText := cipherWithTag[:len(cipherWithTag)-aesgcm.Overhead()] authTag := cipherWithTag[len(cipherWithTag)-aesgcm.Overhead():] header["iv"] = base64url.Encode(iv) header["tag"] = base64url.Encode(authTag) return cek, cipherText, nil } return nil, nil, errors.New("AesGcmKW.WrapNewKey(): expected key to be '[]byte' array") }
func fixNonce(gcm cipher.AEAD, nonce []byte) []byte { realNonce := make([]byte, gcm.NonceSize()) copy(realNonce, nonce) return realNonce }
// encrypt is used to encrypt a value func (b *AESGCMBarrier) encrypt(path string, term uint32, gcm cipher.AEAD, plain []byte) []byte { // Allocate the output buffer with room for tern, version byte, // nonce, GCM tag and the plaintext capacity := termSize + 1 + gcm.NonceSize() + gcm.Overhead() + len(plain) size := termSize + 1 + gcm.NonceSize() out := make([]byte, size, capacity) // Set the key term binary.BigEndian.PutUint32(out[:4], term) // Set the version byte out[4] = b.currentAESGCMVersionByte // Generate a random nonce nonce := out[5 : 5+gcm.NonceSize()] rand.Read(nonce) // Seal the output switch b.currentAESGCMVersionByte { case AESGCMVersion1: out = gcm.Seal(out, nonce, plain, nil) case AESGCMVersion2: out = gcm.Seal(out, nonce, plain, []byte(path)) default: panic("Unknown AESGCM version") } return out }