1// Copyright 2020 Google LLC 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//////////////////////////////////////////////////////////////////////////////// 16 17package subtle 18 19import ( 20 "errors" 21 "fmt" 22 23 "golang.org/x/crypto/chacha20poly1305" 24 "github.com/google/tink/go/subtle/random" 25 "github.com/google/tink/go/tink" 26) 27 28const ( 29 poly1305TagSize = 16 30) 31 32// ChaCha20Poly1305 is an implementation of AEAD interface. 33type ChaCha20Poly1305 struct { 34 Key []byte 35} 36 37// Assert that ChaCha20Poly1305 implements the AEAD interface. 38var _ tink.AEAD = (*ChaCha20Poly1305)(nil) 39 40// NewChaCha20Poly1305 returns an ChaCha20Poly1305 instance. 41// The key argument should be a 32-bytes key. 42func NewChaCha20Poly1305(key []byte) (*ChaCha20Poly1305, error) { 43 if len(key) != chacha20poly1305.KeySize { 44 return nil, errors.New("chacha20poly1305: bad key length") 45 } 46 47 return &ChaCha20Poly1305{Key: key}, nil 48} 49 50// Encrypt encrypts plaintext with associatedData. 51// 52// The resulting ciphertext consists of two parts: 53// 1. the nonce used for encryption 54// 2. the actual ciphertext 55func (ca *ChaCha20Poly1305) Encrypt(plaintext []byte, associatedData []byte) ([]byte, error) { 56 if len(plaintext) > maxInt-chacha20poly1305.NonceSize-poly1305TagSize { 57 return nil, fmt.Errorf("chacha20poly1305: plaintext too long") 58 } 59 c, err := chacha20poly1305.New(ca.Key) 60 if err != nil { 61 return nil, err 62 } 63 64 nonce := random.GetRandomBytes(chacha20poly1305.NonceSize) 65 // Set dst's capacity to fit the nonce and ciphertext. 66 dst := make([]byte, 0, chacha20poly1305.NonceSize+len(plaintext)+c.Overhead()) 67 dst = append(dst, nonce...) 68 // Seal appends the ciphertext to dst. So the final output is: nonce || ciphertext. 69 return c.Seal(dst, nonce, plaintext, associatedData), nil 70} 71 72// Decrypt decrypts ciphertext with associatedData. 73// 74// ciphertext consists of two parts: 75// 1. the nonce used for encryption 76// 2. the actual ciphertext 77func (ca *ChaCha20Poly1305) Decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) { 78 if len(ciphertext) < chacha20poly1305.NonceSize+poly1305TagSize { 79 return nil, fmt.Errorf("chacha20poly1305: ciphertext too short") 80 } 81 82 c, err := chacha20poly1305.New(ca.Key) 83 if err != nil { 84 return nil, err 85 } 86 87 nonce := ciphertext[:chacha20poly1305.NonceSize] 88 pt, err := c.Open(nil, nonce, ciphertext[chacha20poly1305.NonceSize:] /*=ciphertext*/, associatedData) 89 if err != nil { 90 return nil, fmt.Errorf("ChaCha20Poly1305.Decrypt: %v", err) 91 } 92 return pt, nil 93} 94