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 }