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  }