github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/common/encryptor/chacha.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package encryptor 20 21 import ( 22 "crypto/rand" 23 "encoding/hex" 24 "errors" 25 "io" 26 27 "github.com/e154/smart-home/common/logger" 28 "golang.org/x/crypto/chacha20poly1305" 29 ) 30 31 var ( 32 log = logger.MustGetLogger("encryptor") 33 ) 34 35 var additionalData = []byte("SMART-HOME") 36 37 const ( 38 // 39 // keyLength is the exact key length accepted by Encryptor 40 // 41 keyLength = 32 42 nonceLen = 24 43 tagLen = 16 44 ) 45 46 // Encryptor contains info to encrypt/decrypt sensitive data 47 type Encryptor struct { 48 key []byte 49 } 50 51 // New creates Encryptor using key. 52 func New(key []byte) (*Encryptor, error) { 53 54 if len(key) != keyLength { 55 return nil, errors.New("key must be exactly 32 bytes") 56 } 57 58 return &Encryptor{ 59 key: key, 60 }, nil 61 } 62 63 // Encrypt performs XChacha20Poly1305 encryption using saved key. 64 func (e *Encryptor) Encrypt(data []byte) ([]byte, error) { 65 66 nonce := make([]byte, nonceLen) 67 _, err := io.ReadFull(rand.Reader, nonce) 68 if err != nil { 69 return nil, err 70 } 71 72 cipher, err := chacha20poly1305.NewX(e.key) 73 if err != nil { 74 return nil, err 75 } 76 ct := make([]byte, nonceLen, nonceLen+len(data)+cipher.Overhead()) 77 copy(ct, nonce) 78 79 return cipher.Seal(ct[:nonceLen], nonce, data, additionalData), nil 80 } 81 82 // Decrypt performs XChacha20Poly1305 decryption using saved key. 83 func (e *Encryptor) Decrypt(ciphertext []byte) ([]byte, error) { 84 85 if len(ciphertext) < (nonceLen + tagLen) { 86 return nil, errors.New("invalid ciphertext length") 87 } 88 89 nonce := ciphertext[:nonceLen] 90 91 cipher, err := chacha20poly1305.NewX(e.key) 92 if err != nil { 93 return nil, err 94 } 95 dst := make([]byte, 0, len(ciphertext)-nonceLen-tagLen) 96 return cipher.Open(dst, nonce, ciphertext[nonceLen:], additionalData) 97 98 } 99 100 var key []byte 101 102 func SetKey(value []byte) { 103 key = value 104 } 105 106 func GenKey() []byte { 107 arr := make([]byte, keyLength) 108 rand.Read(arr) 109 return arr 110 } 111 112 func Encrypt(value string) (string, error) { 113 encryptor, err := New(key) 114 if err != nil { 115 return "", err 116 } 117 b, err := encryptor.Encrypt([]byte(value)) 118 return hex.EncodeToString(b), err 119 } 120 121 func Decrypt(value string) (string, error) { 122 encryptor, err := New(key) 123 if err != nil { 124 return "", err 125 } 126 b, _ := hex.DecodeString(value) 127 decr, err := encryptor.Decrypt(b) 128 return string(decr), err 129 } 130 131 func EncryptBind(data string) string { 132 value, err := Encrypt(data) 133 if err != nil { 134 135 log.Error(err.Error()) 136 return "" 137 } 138 return value 139 } 140 141 func DecryptBind(data string) string { 142 value, err := Decrypt(data) 143 if err != nil { 144 log.Error(err.Error()) 145 return "" 146 } 147 return value 148 }