github.com/iDigitalFlame/xmt@v0.5.4/data/crypto/crypto.go (about)

     1  // Copyright (C) 2020 - 2023 iDigitalFlame
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  //
    16  
    17  // Package crypto contains helper functions and interfaces that can be used to
    18  // easily read and write different types of encrypted data.
    19  package crypto
    20  
    21  import (
    22  	"crypto/aes"
    23  	"crypto/cipher"
    24  	"io"
    25  
    26  	"github.com/iDigitalFlame/xmt/data/crypto/subtle"
    27  	"github.com/iDigitalFlame/xmt/util/xerr"
    28  )
    29  
    30  type reader struct {
    31  	_ [0]func()
    32  	r io.Reader
    33  	c *CBK
    34  }
    35  type writer struct {
    36  	_ [0]func()
    37  	w io.Writer
    38  	c *CBK
    39  }
    40  type nopCloser struct {
    41  	io.Reader
    42  }
    43  type flusher interface {
    44  	Flush() error
    45  }
    46  
    47  func (w *writer) Flush() error {
    48  	if err := w.c.Flush(w.w); err != nil {
    49  		return err
    50  	}
    51  	if f, ok := w.w.(flusher); ok {
    52  		return f.Flush()
    53  	}
    54  	return nil
    55  }
    56  func (w *writer) Close() error {
    57  	if err := w.Flush(); err != nil {
    58  		return err
    59  	}
    60  	if c, ok := w.w.(io.Closer); ok {
    61  		return c.Close()
    62  	}
    63  	return nil
    64  }
    65  func (nopCloser) Close() error {
    66  	return nil
    67  }
    68  
    69  // UnwrapString is used to un-encode a string written in a XOR byte array "encrypted"
    70  // by the specified key.
    71  //
    72  // This function returns the string value of the result but also modifies the
    73  // input array, which can be used to re-use the resulting string.
    74  func UnwrapString(key, data []byte) string {
    75  	if len(key) == 0 || len(data) == 0 {
    76  		return ""
    77  	}
    78  	subtle.XorOp(data, key)
    79  	return string(data)
    80  }
    81  
    82  // NewAes attempts to create a new AES block cipher from the provided key data.
    83  // Errors will be returned if the key length is invalid.
    84  func NewAes(k []byte) (cipher.Block, error) {
    85  	return aes.NewCipher(k)
    86  }
    87  func (r *reader) Read(b []byte) (int, error) {
    88  	return r.c.Read(r.r, b)
    89  }
    90  func (w *writer) Write(b []byte) (int, error) {
    91  	return w.c.Write(w.w, b)
    92  }
    93  
    94  // NewXORReader creates an io.WriteCloser type from the specified XOR cipher and
    95  // Reader.
    96  //
    97  // This creates a Block cipher with a auto-generated IV based on the key value.
    98  // To control the IV value, use the 'NewCBKReader' function instead.
    99  func NewXORReader(x XOR, r io.Reader) io.Reader {
   100  	v := make([]byte, len(x))
   101  	for i := range x {
   102  		v[i] = (x[i] + byte(i)) ^ 2
   103  	}
   104  	return nopCloser{&cipher.StreamReader{R: r, S: cipher.NewCTR(x, v)}}
   105  }
   106  
   107  // NewCBKReader creates an io.ReadCloser type from the specified CBK cipher and
   108  // Reader.
   109  func NewCBKReader(c CBK, r io.Reader) io.Reader {
   110  	return &reader{c: &c, r: r}
   111  }
   112  
   113  // NewXORWriter creates an io.WriteCloser type from the specified XOR cipher and
   114  // Writer.
   115  //
   116  // This creates a Block cipher with a auto-generated IV based on the key value.
   117  // To control the IV value, use the 'NewBlockWriter' function instead.
   118  func NewXORWriter(x XOR, w io.Writer) io.WriteCloser {
   119  	v := make([]byte, len(x))
   120  	for i := range x {
   121  		v[i] = (x[i] + byte(i)) ^ 2
   122  	}
   123  	// return &cipher.StreamWriter{W: w, S: cipher.NewCFBEncrypter(x, v)}
   124  	return &cipher.StreamWriter{W: w, S: cipher.NewCTR(x, v)}
   125  }
   126  
   127  // NewCBKWriter creates an io.WriteCloser type from the specified CBK cipher and
   128  // Writer.
   129  func NewCBKWriter(c CBK, w io.Writer) io.WriteCloser {
   130  	return &writer{c: &c, w: w}
   131  }
   132  
   133  // NewBlockReader creates a data.Reader type from the specified block cipher,
   134  // IV and Reader.
   135  //
   136  // This is used to Decrypt data. This function returns an error if the blocksize
   137  // of the Block does not equal the length of the supplied IV.
   138  //
   139  // This uses CFB mode.
   140  func NewBlockReader(b cipher.Block, iv []byte, r io.Reader) (io.ReadCloser, error) {
   141  	if len(iv) != b.BlockSize() {
   142  		return nil, xerr.Sub("block size must equal IV size", 0x29)
   143  	}
   144  	return nopCloser{&cipher.StreamReader{R: r, S: cipher.NewCTR(b, iv)}}, nil
   145  }
   146  
   147  // NewBlockWriter creates a data.Reader type from the specified block cipher,
   148  // IV and Writer.
   149  //
   150  // This is used to Encrypt data. This function returns an error if the blocksize
   151  // of the Block does not equal the length of the supplied IV.
   152  //
   153  // This uses CFB mode.
   154  func NewBlockWriter(b cipher.Block, iv []byte, w io.Writer) (io.WriteCloser, error) {
   155  	if len(iv) != b.BlockSize() {
   156  		return nil, xerr.Sub("block size must equal IV size", 0x29)
   157  	}
   158  	return &cipher.StreamWriter{W: w, S: cipher.NewCTR(b, iv)}, nil
   159  }