├── go.mod ├── .gitignore ├── README_CN.md ├── README.md ├── EncryptionFile_test.go ├── LICENSE └── EncryptionFile.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jan-bar/EncryptionFile 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | .idea/ 17 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | ## EncryptionFile 2 | 3 | [english document](README.md) 4 | 5 | 具体使用请阅读: [EncryptionFile_test.go](EncryptionFile_test.go) 6 | 7 | ### implementation 8 | 9 | 密码与对应的算法有关 10 | 11 | 密码的格式: `password = [key + 0 + iv/nonce]` 12 | 13 | | rsa密文长度 | rsa加密密码数据 | 具体算法加密内容 | 校验值 | 14 | |--------------------|---------------|--------------------|-------------------------------------------------------------------| 15 | | len(rsa(password)) | rsa(password) | algorithm(content) | hash.Sum(len(rsa(password)) + rsa(password) + algorithm(content)) | 16 | 17 | ### cipher.AEAD 18 | 19 | 密码组成: `password = [key + 0 + nonce]` 20 | 21 | 需要正确解析nonce,因此必须保证key中不存在0 22 | 23 | ### cipher.Stream 24 | 25 | 密码组成: `password = [key + 0 + iv]` 26 | 27 | ### cipher.BlockMode 28 | 29 | 密码组成: `password = [key + 0 + iv]` 30 | 31 | ### example 32 | 33 | 支持golang标准库中的加密方案: `cipher.AEAD,cipher.Stream,cipher.BlockMode` 34 | 35 | 同时内置了AES多种加密方案: `CFB,CTR,OFB,CBC,GCM` 36 | 37 | ```go 38 | // 可以使用内置方法指定加密方案 39 | // GenEncCipher(cipher.NewCFBEncrypter) 40 | // GenEncCipher(cipher.NewCTR) 41 | // GenEncCipher(cipher.NewOFB) 42 | // GenEncCipher(cipher.NewCBCEncrypter) 43 | // GenEncCipher(cipher.NewGCM) 44 | EncData(Reader, Writer, pubKey, md5.New(), GenEncCipher(cipher.NewCFBEncrypter)) 45 | 46 | // 可以使用内置方法指定解密方案 47 | // GenDecCipher(cipher.NewCFBDecrypter) 48 | // GenDecCipher(cipher.NewCTR) 49 | // GenDecCipher(cipher.NewOFB) 50 | // GenDecCipher(cipher.NewCBCDecrypter) 51 | // GenDecCipher(cipher.NewGCM) 52 | DecData(Reader, Writer, priKey, md5.New(), GenDecCipher(cipher.NewCFBDecrypter)) 53 | ``` 54 | 55 | 也可以参考 [GenEncCipher](EncryptionFile.go#GenEncCipher) 编写生成加密的方法 56 | 57 | 也可以参考 [GenDecCipher](EncryptionFile.go#GenDecCipher) 编写生成解密的方法 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## EncryptionFile 2 | 3 | [中文文档](README_CN.md) 4 | 5 | for specific usage, please read: [EncryptionFile_test.go](EncryptionFile_test.go) 6 | 7 | ### implementation 8 | 9 | the password is related to the corresponding algorithm 10 | 11 | I have this format here: `password = [key + 0 + iv/nonce]` 12 | 13 | | rsa ciphertext length | rsa encrypted password data | algorithm encrypted content | hash value | 14 | |-----------------------|-----------------------------|-----------------------------|-------------------------------------------------------------------| 15 | | len(rsa(password)) | rsa(password) | algorithm(content) | hash.Sum(len(rsa(password)) + rsa(password) + algorithm(content)) | 16 | 17 | ### cipher.AEAD 18 | 19 | password composition: `password = [key + 0 + nonce]` 20 | 21 | the nonce needs to be taken out correctly, and it must be ensured that there is no 0 in the key 22 | 23 | ### cipher.Stream 24 | 25 | password composition: `password = [key + 0 + iv]` 26 | 27 | ### cipher.BlockMode 28 | 29 | password composition: `password = [key + 0 + iv]` 30 | 31 | ### example 32 | 33 | support encryption schemes in golang standard library: `cipher.AEAD,cipher.Stream,cipher.BlockMode` 34 | 35 | at the same time, several encryption schemes of aes are built in: `CFB,CTR,OFB,CBC,GCM` 36 | 37 | ```go 38 | // an encryption scheme can be specified with the built-in method 39 | // GenEncCipher(cipher.NewCFBEncrypter) 40 | // GenEncCipher(cipher.NewCTR) 41 | // GenEncCipher(cipher.NewOFB) 42 | // GenEncCipher(cipher.NewCBCEncrypter) 43 | // GenEncCipher(cipher.NewGCM) 44 | EncData(Reader, Writer, pubKey, md5.New(), GenEncCipher(cipher.NewCFBEncrypter)) 45 | 46 | // an decryption scheme can be specified with the built-in method 47 | // GenDecCipher(cipher.NewCFBDecrypter) 48 | // GenDecCipher(cipher.NewCTR) 49 | // GenDecCipher(cipher.NewOFB) 50 | // GenDecCipher(cipher.NewCBCDecrypter) 51 | // GenDecCipher(cipher.NewGCM) 52 | DecData(Reader, Writer, priKey, md5.New(), GenDecCipher(cipher.NewCFBDecrypter)) 53 | ``` 54 | 55 | you can also refer to [GenEncCipher](EncryptionFile.go#GenEncCipher) to write the method of generating encryption 56 | 57 | you can also refer to [GenDecCipher](EncryptionFile.go#GenDecCipher) to write the method of generating decryption 58 | -------------------------------------------------------------------------------- /EncryptionFile_test.go: -------------------------------------------------------------------------------- 1 | package EncryptionFile 2 | 3 | import ( 4 | "bytes" 5 | "crypto/cipher" 6 | "crypto/md5" 7 | "crypto/rand" 8 | "errors" 9 | "fmt" 10 | "hash" 11 | "io" 12 | "log" 13 | rand2 "math/rand" 14 | "os" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | // go test -v -run TestEnc 20 | func TestEnc(t *testing.T) { 21 | rand2.Seed(time.Now().Unix()) 22 | 23 | const bufLen2 = 2 * bufLen 24 | 25 | var ( 26 | tmpBuf0 = bytes.NewBuffer(make([]byte, 0, bufLen2)) 27 | tmpBuf1 = bytes.NewBuffer(make([]byte, 0, bufLen2)) 28 | ) 29 | 30 | err := GenRsaKey(0, tmpBuf0, tmpBuf1) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | var ( 36 | tmp = make([]byte, bufLen2) 37 | h = md5.New() 38 | priKey = make([]byte, tmpBuf1.Len()) 39 | pubKey = make([]byte, tmpBuf0.Len()) 40 | lr = new(limitReader) 41 | 42 | cfbEncStream = GenEncCipher(cipher.NewCFBEncrypter) 43 | cfbDecStream = GenDecCipher(cipher.NewCFBDecrypter) 44 | ) 45 | 46 | copy(priKey, tmpBuf1.Bytes()) // save the private key 47 | copy(pubKey, tmpBuf0.Bytes()) // save the public key 48 | 49 | // go test -v -run "TestEnc/EncryptAndDecryptFile" 50 | //goland:noinspection GoUnhandledErrorResult 51 | t.Run("EncryptAndDecryptFile", func(t *testing.T) { 52 | encFile := func(f string) error { 53 | fr, err := os.Open(f) 54 | if err != nil { 55 | return err 56 | } 57 | defer fr.Close() 58 | 59 | fw, err := os.Create(f + ".enc") 60 | if err != nil { 61 | return err 62 | } 63 | defer fw.Close() 64 | 65 | h.Reset() 66 | return EncData(fr, fw, pubKey, h, cfbEncStream) 67 | } 68 | 69 | decFile := func(f string) error { 70 | fr, err := os.Open(f + ".enc") 71 | if err != nil { 72 | return err 73 | } 74 | defer fr.Close() 75 | 76 | fw, err := os.Create(f + ".dec") 77 | if err != nil { 78 | return err 79 | } 80 | defer fw.Close() 81 | 82 | h.Reset() 83 | return DecData(fr, fw, priKey, h, cfbDecStream) 84 | } 85 | 86 | const fName = "EncryptionFile.go" 87 | err = encFile(fName) 88 | if err != nil { 89 | t.Fatal("encryption error", err) 90 | } 91 | 92 | err = decFile(fName) 93 | if err != nil { 94 | t.Fatal("decrypt error", err) 95 | } 96 | 97 | fOrg, err := os.ReadFile(fName) 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | fDec, err := os.ReadFile(fName + ".dec") 102 | if err != nil { 103 | t.Fatal(err) 104 | } 105 | if !bytes.Equal(fOrg, fDec) { 106 | t.Fatal("original file and decrypted file are different") 107 | } 108 | }) 109 | 110 | test := func(limit int, enc, dec func(r io.Reader, w io.Writer, h hash.Hash) error) error { 111 | _, err = rand.Read(tmp[:limit]) // Generate random encrypted data 112 | if err != nil { 113 | return err 114 | } 115 | 116 | tmpBuf0.Reset() 117 | tmpBuf0.Write(tmp[:limit]) 118 | tmpBuf1.Reset() 119 | h.Reset() 120 | 121 | if err = enc(tmpBuf0, tmpBuf1, h); err != nil { 122 | return err 123 | } 124 | 125 | tmpBuf0.Reset() 126 | h.Reset() 127 | lr.n = rand2.Intn(limit) + 1 // random read block size 128 | lr.r = tmpBuf1 129 | 130 | if err = dec(lr, tmpBuf0, h); err != nil { 131 | return err 132 | } 133 | 134 | if !bytes.Equal(tmp[:limit], tmpBuf0.Bytes()) { 135 | return errors.New("dec(data) != tmp[:i]") 136 | } 137 | 138 | tmpBuf0.Reset() 139 | tmpBuf0.Write(tmp[:limit]) 140 | h.Reset() 141 | tmpBuf1.Reset() 142 | 143 | lr.r = tmpBuf0 144 | if err = dec(lr, tmpBuf1, h); err == nil { 145 | return errors.New("decrypt error data successfully") 146 | } 147 | 148 | return nil 149 | } 150 | 151 | // go test -v -run "TestEnc/TestCipher" 152 | t.Run("TestCipher", func(t *testing.T) { 153 | testCipher := []struct { 154 | enc EncCipher 155 | dec DecCipher 156 | name string 157 | }{ 158 | {name: "cfb", enc: cfbEncStream, dec: cfbDecStream}, 159 | {name: "ctr", enc: GenEncCipher(cipher.NewCTR), dec: GenDecCipher(cipher.NewCTR)}, 160 | {name: "ofb", enc: GenEncCipher(cipher.NewOFB), dec: GenDecCipher(cipher.NewOFB)}, 161 | {name: "cbc", enc: GenEncCipher(cipher.NewCBCEncrypter), dec: GenDecCipher(cipher.NewCBCDecrypter)}, 162 | {name: "gcm", enc: GenEncCipher(cipher.NewGCM), dec: GenDecCipher(cipher.NewGCM)}, 163 | } 164 | 165 | cnt := make(chan int, 1) 166 | go func() { 167 | for { 168 | fmt.Print(" ", <-cnt, "\r") 169 | time.Sleep(time.Second) 170 | } 171 | }() 172 | 173 | // test different length data,fully validated algorithm 174 | for i := 1; i < bufLen2; i++ { 175 | for _, cp := range testCipher { 176 | err = test(i, func(r io.Reader, w io.Writer, h hash.Hash) error { 177 | return EncData(r, w, pubKey, h, cp.enc) 178 | }, func(r io.Reader, w io.Writer, h hash.Hash) error { 179 | return DecData(r, w, priKey, h, cp.dec) 180 | }) 181 | if err != nil { 182 | t.Fatal(cp.name, err) 183 | } 184 | } 185 | 186 | select { 187 | case cnt <- i * 100 / bufLen2: 188 | default: 189 | } 190 | } 191 | }) 192 | } 193 | 194 | type limitReader struct { 195 | r io.Reader 196 | n int 197 | } 198 | 199 | func (l *limitReader) Read(p []byte) (int, error) { 200 | if l.n < len(p) { 201 | return l.r.Read(p[:l.n]) 202 | } 203 | 204 | return l.r.Read(p) 205 | } 206 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /EncryptionFile.go: -------------------------------------------------------------------------------- 1 | package EncryptionFile 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "crypto/rand" 9 | "crypto/rsa" 10 | "crypto/x509" 11 | "encoding/pem" 12 | "errors" 13 | "hash" 14 | "io" 15 | ) 16 | 17 | const ( 18 | bufLen = 32 * 1024 19 | aesKeySize = 32 // AES-256 20 | ) 21 | 22 | type ( 23 | EncCipher func() ([]byte, any, error) 24 | DecCipher func([]byte) (any, error) 25 | ) 26 | 27 | // EncData 28 | // 29 | // @Description: encrypted data 30 | // @param r data source read stream 31 | // @param w encrypted data is written to the stream 32 | // @param encMode public key or encryption function 33 | // @param h specify the hash verification method 34 | // @param gen EncCipher, generate key and specify encryption algorithm 35 | // @return error 36 | func EncData(r io.Reader, w io.Writer, encMode any, h hash.Hash, gen EncCipher) error { 37 | tmp := make([]byte, bufLen) 38 | 39 | // ensure that the data and cipher algorithm objects are legal 40 | data, cp, err := gen() 41 | if err != nil { 42 | return err 43 | } 44 | 45 | var encKey []byte 46 | switch mode := encMode.(type) { 47 | case []byte: 48 | encKey, err = RsaEncrypt(mode, data) 49 | case string: 50 | encKey, err = RsaEncrypt([]byte(mode), data) 51 | case func([]byte) ([]byte, error): 52 | encKey, err = mode(data) 53 | default: 54 | return errors.New("encMode not support") 55 | } 56 | if err != nil { 57 | return err 58 | } 59 | 60 | n := len(encKey) 61 | tmp[0], tmp[1] = byte(n), byte(n>>8) 62 | head := append(tmp[:2], encKey...) 63 | 64 | _, err = w.Write(head) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | h.Write(head) 70 | 71 | var cw io.WriteCloser 72 | 73 | switch cc := cp.(type) { 74 | case cipher.AEAD: 75 | ac := &aeadCipher{ 76 | h: h, w: w, 77 | aead: cc, 78 | oSize: cc.Overhead(), 79 | tmp: make([]byte, bufLen), 80 | buf: bytes.NewBuffer(make([]byte, 0, bufLen)), 81 | } 82 | if err = ac.readNonce(data); err != nil { 83 | return err 84 | } 85 | 86 | cw = ac 87 | // limit the maximum length of plaintext 88 | tmp = tmp[:bufLen-cc.Overhead()] 89 | case cipher.Stream: 90 | cw = &streamCipher{ 91 | h: h, w: w, 92 | stream: cc, 93 | } 94 | case cipher.BlockMode: 95 | cw = &blockCipher{ 96 | h: h, w: w, 97 | block: cc, 98 | bSize: cc.BlockSize(), 99 | tmp: make([]byte, bufLen), 100 | buf: bytes.NewBuffer(make([]byte, 0, bufLen)), 101 | } 102 | default: 103 | return errors.New("gen return parameter error") 104 | } 105 | 106 | _, err = io.CopyBuffer(cw, &onlyRW{r: r}, tmp) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | if err = cw.Close(); err != nil { 112 | return err 113 | } 114 | 115 | _, err = w.Write(h.Sum(tmp[:0])) 116 | 117 | return err 118 | } 119 | 120 | // DecData 121 | // 122 | // @Description: decrypt data 123 | // @param r ciphertext data read stream 124 | // @param w the decrypted data is written to the stream 125 | // @param devMode private key data or decryption function 126 | // @param h specify the hash verification method 127 | // @param gen DecCipher, verify and generate a decryption algorithm based on the key 128 | // @return error 129 | func DecData(r io.Reader, w io.Writer, decMode any, h hash.Hash, gen DecCipher) error { 130 | tmp := make([]byte, bufLen) 131 | 132 | _, err := io.ReadFull(r, tmp[:2]) 133 | if err != nil { 134 | return err 135 | } 136 | 137 | h.Write(tmp[:2]) 138 | 139 | n := int(tmp[0]) | int(tmp[1])<<8 140 | if n > bufLen { 141 | return errors.New("len(rsa) out of index") 142 | } 143 | 144 | _, err = io.ReadFull(r, tmp[:n]) 145 | if err != nil { 146 | return err 147 | } 148 | 149 | h.Write(tmp[:n]) 150 | 151 | var data []byte 152 | switch mode := decMode.(type) { 153 | case []byte: 154 | data, err = RsaDecrypt(mode, tmp[:n]) 155 | case string: 156 | data, err = RsaDecrypt([]byte(mode), tmp[:n]) 157 | case func([]byte) ([]byte, error): 158 | data, err = mode(tmp[:n]) 159 | default: 160 | return errors.New("decMode not support") 161 | } 162 | if err != nil { 163 | return err 164 | } 165 | 166 | // it is necessary to verify the legality of data and generate cipher 167 | cp, err := gen(data) 168 | if err != nil { 169 | return err 170 | } 171 | 172 | var ( 173 | cr io.Reader 174 | sum = make([]byte, h.Size()) 175 | ) 176 | 177 | switch cc := cp.(type) { 178 | case cipher.AEAD: 179 | ac := &aeadCipher{ 180 | h: h, r: r, 181 | aead: cc, 182 | sum: sum, 183 | oSize: cc.Overhead(), 184 | tmp: make([]byte, bufLen), 185 | buf: bytes.NewBuffer(make([]byte, 0, bufLen)), 186 | } 187 | if err = ac.readNonce(data); err != nil { 188 | return err 189 | } 190 | 191 | cr = ac 192 | // limit the maximum length of plaintext 193 | tmp = tmp[:bufLen-cc.Overhead()] 194 | case cipher.Stream: 195 | cr = &streamCipher{ 196 | h: h, r: bufio.NewReader(r), 197 | stream: cc, 198 | sum: sum, 199 | } 200 | case cipher.BlockMode: 201 | cr = &blockCipher{ 202 | h: h, r: r, 203 | block: cc, 204 | bSize: cc.BlockSize(), 205 | sum: sum, 206 | tmp: make([]byte, bufLen), 207 | buf: bytes.NewBuffer(make([]byte, 0, bufLen)), 208 | } 209 | default: 210 | return errors.New("gen return parameter error") 211 | } 212 | 213 | _, err = io.CopyBuffer(&onlyRW{w: w}, cr, tmp) 214 | if err != nil { 215 | return err 216 | } 217 | 218 | if bytes.Equal(h.Sum(tmp[:0]), sum) { 219 | return nil 220 | } 221 | 222 | return errors.New("h.Sum not match") 223 | } 224 | 225 | // ----------------------------------------------------------------------------- 226 | 227 | func GenEncCipher(enc any) EncCipher { 228 | return func() ([]byte, any, error) { 229 | key := make([]byte, aesKeySize) 230 | 231 | _, err := rand.Read(key) 232 | if err != nil { 233 | return nil, nil, err 234 | } 235 | 236 | for i, v := range key { 237 | if v == 0 { 238 | key[i] = byte(i%0xff) + 1 // ensure that the key is not 0 239 | } 240 | } 241 | 242 | block, err := aes.NewCipher(key) 243 | if err != nil { 244 | return nil, nil, err 245 | } 246 | 247 | var ( 248 | gen any 249 | add = func(s int) []byte { 250 | info := make([]byte, s) 251 | _, _ = rand.Read(info) 252 | 253 | // generate data [key + 0 + info] 254 | key = append(key, 0) 255 | key = append(key, info...) 256 | return info 257 | } 258 | ) 259 | 260 | switch e := enc.(type) { 261 | case func(cipher.Block) (cipher.AEAD, error): 262 | ad, err := e(block) 263 | if err != nil { 264 | return nil, nil, err 265 | } 266 | 267 | gen = ad 268 | add(ad.NonceSize()) // nonce 269 | case func(cipher.Block, []byte) cipher.Stream: 270 | gen = e(block, add(block.BlockSize())) // iv 271 | case func(cipher.Block, []byte) cipher.BlockMode: 272 | gen = e(block, add(block.BlockSize())) // iv 273 | default: 274 | return nil, nil, errors.New("enc func type error") 275 | } 276 | 277 | return key, gen, nil 278 | } 279 | } 280 | 281 | func GenDecCipher(dec any) DecCipher { 282 | return func(data []byte) (any, error) { 283 | // verify data validity, data like this [key + 0 + iv/nonce] 284 | key, info, ok := bytes.Cut(data, []byte{0}) 285 | if !ok || len(key) != aesKeySize { 286 | return nil, errors.New("key error") 287 | } 288 | 289 | block, err := aes.NewCipher(key) 290 | if err != nil { 291 | return nil, err 292 | } 293 | 294 | var gen any 295 | 296 | switch d := dec.(type) { 297 | case func(cipher.Block) (cipher.AEAD, error): 298 | ad, err := d(block) 299 | if err != nil { 300 | return nil, err 301 | } 302 | 303 | gen = ad // subsequent verification of nonce by read nonce 304 | case func(cipher.Block, []byte) cipher.Stream: 305 | if len(info) != block.BlockSize() { 306 | return nil, errors.New("len(iv) error") 307 | } 308 | 309 | gen = d(block, info) 310 | case func(cipher.Block, []byte) cipher.BlockMode: 311 | if len(info) != block.BlockSize() { 312 | return nil, errors.New("len(iv) error") 313 | } 314 | 315 | gen = d(block, info) 316 | default: 317 | return nil, errors.New("dec func type error") 318 | } 319 | 320 | return gen, nil 321 | } 322 | } 323 | 324 | // ----------------------------------------------------------------------------- 325 | 326 | type streamCipher struct { 327 | w io.Writer 328 | h hash.Hash 329 | stream cipher.Stream 330 | r *bufio.Reader 331 | sum []byte 332 | } 333 | 334 | func (sc *streamCipher) Write(p []byte) (n int, err error) { 335 | sc.stream.XORKeyStream(p, p) 336 | 337 | n, err = WriteFull(sc.w, p) 338 | if err == nil { 339 | sc.h.Write(p[:n]) 340 | } 341 | 342 | return 343 | } 344 | 345 | func (sc *streamCipher) Read(p []byte) (n int, err error) { 346 | n, err = sc.r.Read(p) 347 | if err == nil { 348 | var this []byte 349 | 350 | this, err = sc.r.Peek(len(sc.sum)) 351 | switch err { 352 | case nil: 353 | case io.EOF: 354 | // last Peek data: len(last) = len(sc.sum) 355 | // this Peek data: len(this) < len(sc.sum) 356 | // there is no len(this) = len(sc.sum), in this case it will Peek again 357 | // | p[:n] | this | the last data obtained 358 | // | xxx | last | this | len(p) > len(sc.sum), so last is in p[:n] 359 | // | xxx + yyy | sum | extract the sum from this data 360 | lp := n - len(sc.sum) + len(this) 361 | copy(sc.sum, append(p[lp:n], this...)) 362 | n = lp 363 | default: 364 | return 365 | } 366 | 367 | if n > 0 { 368 | sc.h.Write(p[:n]) 369 | sc.stream.XORKeyStream(p, p[:n]) 370 | } 371 | } 372 | 373 | return 374 | } 375 | 376 | func (sc *streamCipher) Close() error { 377 | return nil 378 | } 379 | 380 | // ----------------------------------------------------------------------------- 381 | 382 | type blockCipher struct { 383 | r io.Reader 384 | w io.Writer 385 | h hash.Hash 386 | block cipher.BlockMode 387 | buf *bytes.Buffer 388 | sum []byte 389 | tmp []byte 390 | bSize int 391 | } 392 | 393 | func (bc *blockCipher) Write(p []byte) (n int, err error) { 394 | n, err = bc.buf.Write(p) 395 | 396 | if bn := bc.buf.Len() / bc.bSize; bn >= 1 { 397 | // encrypt data in blocks of (bn * BlockSize) 398 | // guaranteed to encrypt at least (1 * BlockSize) block data 399 | bn *= bc.bSize 400 | 401 | // bc.tmp[0] = 0, ordinary data, data length stored in encrypted block 402 | bc.tmp[0], bc.tmp[1], bc.tmp[2] = 0, byte(bn), byte(bn>>8) 403 | 404 | bc.block.CryptBlocks(bc.tmp, bc.tmp[:bc.bSize]) 405 | 406 | _, err = WriteFull(bc.w, bc.tmp[:bc.bSize]) 407 | if err != nil { 408 | return 409 | } 410 | 411 | bc.h.Write(bc.tmp[:bc.bSize]) 412 | 413 | _, err = io.ReadFull(bc.buf, bc.tmp[:bn]) 414 | if err != nil { 415 | return 416 | } 417 | 418 | bc.block.CryptBlocks(bc.tmp, bc.tmp[:bn]) 419 | 420 | _, err = WriteFull(bc.w, bc.tmp[:bn]) 421 | if err != nil { 422 | return 423 | } 424 | 425 | bc.h.Write(bc.tmp[:bn]) 426 | } 427 | 428 | return 429 | } 430 | 431 | func (bc *blockCipher) Read(p []byte) (n int, err error) { 432 | if bc.buf.Len() == 0 { 433 | // read (1 * BlockSize) block data 434 | _, err = io.ReadFull(bc.r, bc.tmp[:bc.bSize]) 435 | if err != nil { 436 | return 437 | } 438 | 439 | bc.h.Write(bc.tmp[:bc.bSize]) 440 | bc.block.CryptBlocks(bc.tmp, bc.tmp[:bc.bSize]) 441 | 442 | if n = int(bc.tmp[0]); n > 0 { 443 | if n == 1 { 444 | n = 0 // no data left to read 445 | } else { 446 | n = copy(p, bc.tmp[1:n]) // read remaining data 447 | } 448 | 449 | _, err = io.ReadFull(bc.r, bc.sum) 450 | if err == nil { 451 | err = io.EOF // after reading the crc, return the data to read 452 | } 453 | 454 | return 455 | } 456 | 457 | n = int(bc.tmp[1]) | int(bc.tmp[2])<<8 458 | 459 | _, err = io.ReadFull(bc.r, bc.tmp[:n]) 460 | if err != nil { 461 | return 462 | } 463 | 464 | bc.h.Write(bc.tmp[:n]) 465 | bc.block.CryptBlocks(bc.tmp, bc.tmp[:n]) 466 | bc.buf.Write(bc.tmp[:n]) 467 | } 468 | 469 | return bc.buf.Read(p) 470 | } 471 | 472 | func (bc *blockCipher) Close() error { 473 | if bc.buf.Len() > 0 { 474 | // remaining data up to (BlockSize - 1) byte 475 | // storage length of bc.tmp[0] is at most just to fill the BlockSize 476 | bc.tmp[0] = byte(bc.buf.Len() + 1) 477 | copy(bc.tmp[1:], bc.buf.Bytes()) 478 | } else { 479 | bc.tmp[0] = 1 // mark no data left 480 | } 481 | 482 | bc.block.CryptBlocks(bc.tmp, bc.tmp[:bc.bSize]) 483 | 484 | _, err := WriteFull(bc.w, bc.tmp[:bc.bSize]) 485 | if err != nil { 486 | return err 487 | } 488 | 489 | bc.h.Write(bc.tmp[:bc.bSize]) 490 | 491 | return nil 492 | } 493 | 494 | // ----------------------------------------------------------------------------- 495 | 496 | //goland:noinspection SpellCheckingInspection 497 | type aeadCipher struct { 498 | r io.Reader 499 | w io.Writer 500 | h hash.Hash 501 | aead cipher.AEAD 502 | buf *bytes.Buffer 503 | tmp []byte 504 | sum []byte 505 | nonce []byte 506 | oSize int 507 | eof bool 508 | } 509 | 510 | func (ac *aeadCipher) readNonce(data []byte) error { 511 | // data must be [key + 0 + nonce], key content does not contain 0 512 | if i := bytes.IndexByte(data, 0); i > 0 { 513 | ac.nonce = data[i+1:] 514 | } 515 | 516 | if len(ac.nonce) != ac.aead.NonceSize() { 517 | return errors.New("can not read nonce") 518 | } 519 | 520 | return nil 521 | } 522 | 523 | func (ac *aeadCipher) Write(p []byte) (n int, err error) { 524 | n, err = ac.buf.Write(p) 525 | 526 | if bn := ac.buf.Len() / ac.oSize; bn >= 1 { 527 | bn *= ac.oSize // logic similar to blockCipher algorithm 528 | 529 | ac.tmp[0], ac.tmp[1], ac.tmp[2] = 0, byte(bn), byte(bn>>8) 530 | 531 | tmp := ac.aead.Seal(ac.tmp[:0], ac.nonce, ac.tmp[:3], nil) 532 | 533 | _, err = WriteFull(ac.w, tmp) 534 | if err != nil { 535 | return 536 | } 537 | 538 | increment(ac.nonce) 539 | ac.h.Write(tmp) 540 | 541 | _, err = io.ReadFull(ac.buf, ac.tmp[:bn]) 542 | if err != nil { 543 | return 544 | } 545 | 546 | tmp = ac.aead.Seal(ac.tmp[:0], ac.nonce, ac.tmp[:bn], nil) 547 | 548 | _, err = WriteFull(ac.w, tmp) 549 | if err != nil { 550 | return 551 | } 552 | 553 | increment(ac.nonce) 554 | ac.h.Write(tmp) 555 | } 556 | 557 | return 558 | } 559 | 560 | func (ac *aeadCipher) Read(p []byte) (n int, err error) { 561 | if ac.buf.Len() == 0 { 562 | if ac.eof { 563 | return 0, io.EOF 564 | } 565 | 566 | n = 3 + ac.oSize // read 3 bytes of length information 567 | 568 | _, err = io.ReadFull(ac.r, ac.tmp[:n]) 569 | if err != nil { 570 | return 571 | } 572 | 573 | ac.h.Write(ac.tmp[:n]) 574 | 575 | var tmp []byte 576 | 577 | tmp, err = ac.aead.Open(ac.tmp[:0], ac.nonce, ac.tmp[:n], nil) 578 | if err != nil { 579 | return 580 | } 581 | 582 | increment(ac.nonce) 583 | 584 | eof := tmp[0] == 1 585 | 586 | if n = int(tmp[1]) | int(tmp[2])<<8; n > 0 { 587 | n += ac.oSize 588 | 589 | _, err = io.ReadFull(ac.r, ac.tmp[:n]) 590 | if err != nil { 591 | return 592 | } 593 | 594 | ac.h.Write(ac.tmp[:n]) 595 | 596 | tmp, err = ac.aead.Open(ac.tmp[:0], ac.nonce, ac.tmp[:n], nil) 597 | if err != nil { 598 | return 599 | } 600 | 601 | increment(ac.nonce) 602 | ac.buf.Write(tmp) 603 | } 604 | 605 | if eof { 606 | _, err = io.ReadFull(ac.r, ac.sum) 607 | if err != nil { 608 | return 609 | } 610 | 611 | ac.eof = true // last piece of data 612 | } 613 | } 614 | 615 | return ac.buf.Read(p) 616 | } 617 | 618 | func (ac *aeadCipher) Close() error { 619 | n := ac.buf.Len() // encrypt the last data length information 620 | ac.tmp[0], ac.tmp[1], ac.tmp[2] = 1, byte(n), byte(n>>8) 621 | 622 | tmp := ac.aead.Seal(ac.tmp[:0], ac.nonce, ac.tmp[:3], nil) 623 | 624 | _, err := WriteFull(ac.w, tmp) 625 | if err != nil { 626 | return err 627 | } 628 | 629 | increment(ac.nonce) 630 | ac.h.Write(tmp) 631 | 632 | if n > 0 { // encrypt the last remaining data 633 | tmp = ac.aead.Seal(ac.tmp[:0], ac.nonce, ac.buf.Bytes(), nil) 634 | 635 | _, err = WriteFull(ac.w, tmp) 636 | if err != nil { 637 | return err 638 | } 639 | 640 | increment(ac.nonce) 641 | ac.h.Write(tmp) 642 | } 643 | 644 | return nil 645 | } 646 | 647 | func increment(b []byte) { 648 | for i := range b { 649 | b[i]++ 650 | if b[i] != 0 { 651 | return 652 | } 653 | } 654 | } 655 | 656 | // ----------------------------------------------------------------------------- 657 | 658 | // GenRsaKey 659 | // 660 | // @Description: generate rsa public-private key pair 661 | // @param bits generated digits 662 | // @param pub public key write stream 663 | // @param pri private key write stream 664 | // @return error 665 | func GenRsaKey(bits int, pub, pri io.Writer) error { 666 | if bits < 2048 { 667 | bits = 2048 668 | } 669 | 670 | privateKey, err := rsa.GenerateKey(rand.Reader, bits) 671 | if err != nil { 672 | return err 673 | } 674 | 675 | block := &pem.Block{ 676 | Type: "RSA PRIVATE KEY", 677 | Bytes: x509.MarshalPKCS1PrivateKey(privateKey), 678 | } 679 | 680 | err = pem.Encode(pri, block) 681 | if err != nil { 682 | return err 683 | } 684 | 685 | derPkix, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) 686 | if err != nil { 687 | return err 688 | } 689 | 690 | block = &pem.Block{ 691 | Type: "PUBLIC KEY", 692 | Bytes: derPkix, 693 | } 694 | 695 | return pem.Encode(pub, block) 696 | } 697 | 698 | // RsaEncrypt 699 | // 700 | // @Description: rsa encryption logic 701 | // @param pubKey public key data 702 | // @param origData data to be encrypted 703 | // @return []byte return encrypted data 704 | // @return error 705 | func RsaEncrypt(pubKey, origData []byte) ([]byte, error) { 706 | block, _ := pem.Decode(pubKey) 707 | if block == nil { 708 | return nil, errors.New("block error") 709 | } 710 | 711 | pub, err := x509.ParsePKIXPublicKey(block.Bytes) 712 | if err != nil { 713 | return nil, err 714 | } 715 | 716 | pk, ok := pub.(*rsa.PublicKey) 717 | if !ok { 718 | return nil, errors.New("public key error") 719 | } 720 | 721 | return rsa.EncryptPKCS1v15(rand.Reader, pk, origData) 722 | } 723 | 724 | // RsaDecrypt 725 | // 726 | // @Description: rsa decryption logic 727 | // @param priKey private key data 728 | // @param cipherText cipher text 729 | // @return []byte decrypted data 730 | // @return error 731 | func RsaDecrypt(priKey, cipherText []byte) ([]byte, error) { 732 | block, _ := pem.Decode(priKey) 733 | if block == nil { 734 | return nil, errors.New("private key error") 735 | } 736 | 737 | prIv, err := x509.ParsePKCS1PrivateKey(block.Bytes) 738 | if err != nil { 739 | return nil, err 740 | } 741 | 742 | return rsa.DecryptPKCS1v15(rand.Reader, prIv, cipherText) 743 | } 744 | 745 | func WriteFull(w io.Writer, b []byte) (n int, err error) { 746 | var now int 747 | 748 | for n < len(b) { 749 | now, err = w.Write(b[n:]) 750 | if err != nil { 751 | return 752 | } 753 | 754 | n += now 755 | } 756 | 757 | return 758 | } 759 | 760 | // eliminate the judgment of io.WriterTo and io.ReaderFrom in io.CopyBuffer 761 | // make sure to only copy data using io.Reader and io.Writer 762 | type onlyRW struct { 763 | r io.Reader 764 | w io.Writer 765 | } 766 | 767 | func (rw *onlyRW) Write(b []byte) (int, error) { 768 | return rw.w.Write(b) 769 | } 770 | 771 | func (rw *onlyRW) Read(b []byte) (int, error) { 772 | return rw.r.Read(b) 773 | } 774 | --------------------------------------------------------------------------------