github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controllerutil/encryption.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package controllerutil 21 22 import ( 23 "crypto/aes" 24 "crypto/cipher" 25 "crypto/rand" 26 "crypto/sha256" 27 "encoding/base64" 28 "fmt" 29 "io" 30 31 "golang.org/x/crypto/hkdf" 32 ) 33 34 const purposeEncryptionKey = "encryption" 35 36 type encryptor struct { 37 encryptionKey []byte 38 encryptionHashKey []byte 39 gcm cipher.AEAD 40 } 41 42 func NewEncryptor(encryptionKey string) *encryptor { 43 return &encryptor{ 44 encryptionKey: []byte(encryptionKey), 45 } 46 } 47 48 func (e *encryptor) deriveKey() { 49 key := make([]byte, 32) 50 k := hkdf.New(sha256.New, e.encryptionKey, []byte(purposeEncryptionKey), nil) 51 _, _ = io.ReadFull(k, key) 52 e.encryptionHashKey = key 53 } 54 55 func (e *encryptor) init() error { 56 e.deriveKey() 57 block, err := aes.NewCipher(e.encryptionHashKey) 58 if err != nil { 59 return err 60 } 61 gcm, err := cipher.NewGCM(block) 62 if err != nil { 63 return err 64 } 65 e.gcm = gcm 66 return nil 67 } 68 69 // Encrypt encrypts the plaintext by secretKey by nonce string. 70 func (e *encryptor) Encrypt(plaintext []byte) (string, error) { 71 if err := e.init(); err != nil { 72 return "", err 73 } 74 nonce := make([]byte, e.gcm.NonceSize()) 75 if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 76 return "", err 77 } 78 ciphertext := e.gcm.Seal(nil, nonce, plaintext, e.encryptionHashKey) 79 // append nonce to ciphertext for decrypt 80 ciphertext = append(nonce, ciphertext...) 81 return base64.StdEncoding.EncodeToString(ciphertext), nil 82 } 83 84 // Decrypt decrypts the ciphertext by secretKey. 85 func (e *encryptor) Decrypt(ciphertext []byte) (string, error) { 86 ciphertext, err := base64.StdEncoding.DecodeString(string(ciphertext)) 87 if err != nil { 88 return "", err 89 } 90 if err = e.init(); err != nil { 91 return "", err 92 } 93 // fetch nonce from secret key 94 nonceSize := e.gcm.NonceSize() 95 if len(ciphertext) < nonceSize { 96 return "", fmt.Errorf("ciphertext is too short") 97 } 98 nonce := ciphertext[:nonceSize] 99 ciphertext = ciphertext[nonceSize:] 100 plaintext, err := e.gcm.Open(nil, nonce, ciphertext, e.encryptionHashKey) 101 if err != nil { 102 return "", err 103 } 104 return string(plaintext), nil 105 }