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 28// XChaCha20Poly1305 is an implementation of AEAD interface. 29type XChaCha20Poly1305 struct { 30 Key []byte 31} 32 33// Assert that XChaCha20Poly1305 implements the AEAD interface. 34var _ tink.AEAD = (*XChaCha20Poly1305)(nil) 35 36// NewXChaCha20Poly1305 returns an XChaCha20Poly1305 instance. 37// The key argument should be a 32-bytes key. 38func NewXChaCha20Poly1305(key []byte) (*XChaCha20Poly1305, error) { 39 if len(key) != chacha20poly1305.KeySize { 40 return nil, errors.New("xchacha20poly1305: bad key length") 41 } 42 43 return &XChaCha20Poly1305{Key: key}, nil 44} 45 46// Encrypt encrypts plaintext with associatedData. 47// 48// The resulting ciphertext consists of two parts: 49// 1. the nonce used for encryption 50// 2. the actual ciphertext 51func (x *XChaCha20Poly1305) Encrypt(plaintext []byte, associatedData []byte) ([]byte, error) { 52 if len(plaintext) > maxInt-chacha20poly1305.NonceSizeX-poly1305TagSize { 53 return nil, fmt.Errorf("xchacha20poly1305: plaintext too long") 54 } 55 c, err := chacha20poly1305.NewX(x.Key) 56 if err != nil { 57 return nil, err 58 } 59 60 nounce := random.GetRandomBytes(chacha20poly1305.NonceSizeX) 61 // Make the capacity of dst large enough so that both the nounce and the ciphertext fit inside. 62 dst := make([]byte, 0, chacha20poly1305.NonceSizeX+len(plaintext)+c.Overhead()) 63 dst = append(dst, nounce...) 64 // Seal appends the ciphertext to dst. So the final output is: nounce || ciphertext. 65 return c.Seal(dst, nounce, plaintext, associatedData), nil 66} 67 68// Decrypt decrypts ciphertext with associatedData. 69// 70// ciphertext consists of two parts: 71// 1. the nonce used for encryption 72// 2. the actual ciphertext 73func (x *XChaCha20Poly1305) Decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) { 74 if len(ciphertext) < chacha20poly1305.NonceSizeX+poly1305TagSize { 75 return nil, fmt.Errorf("xchacha20poly1305: ciphertext too short") 76 } 77 78 c, err := chacha20poly1305.NewX(x.Key) 79 if err != nil { 80 return nil, err 81 } 82 83 n := ciphertext[:chacha20poly1305.NonceSizeX] 84 pt, err := c.Open(nil, n, ciphertext[chacha20poly1305.NonceSizeX:], associatedData) 85 if err != nil { 86 return nil, fmt.Errorf("XChaCha20Poly1305.Decrypt: %s", err) 87 } 88 return pt, nil 89} 90