├── .github ├── dependabot.yml └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── LICENSE.md ├── Makefile ├── README.md ├── crypt2go.go ├── ecb ├── common_test.go ├── ecb.go ├── ecb_aes_test.go ├── ecb_blowfish_test.go ├── ecb_error_test.go └── example_test.go ├── examples ├── aes │ └── main.go └── blowfish │ └── main.go ├── go.mod ├── go.sum └── padding ├── example_test.go └── padding.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '16 7 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Andre Burgaud. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Andre Burgaud nor the names of any 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | VERSION := v1.8.0 3 | 4 | fmt: 5 | gofmt -l . 6 | 7 | help: 8 | @echo 'Makefile for Crypt2go' 9 | @echo 10 | @echo 'Usage:' 11 | @echo ' make help Display this help message' 12 | @echo ' make version Display current package version' 13 | @echo ' make update Update dependencies' 14 | @echo ' make release Perform all checks (fmt, test, and lint)' 15 | @echo ' make fmt Format Go files' 16 | @echo ' make test Execute tests' 17 | @echo ' make lint Lint code with golangci-lint' 18 | @echo ' make run Execute program examples' 19 | @echo ' make tag GitHub tag (after manual git commit)' 20 | 21 | lint: 22 | golangci-lint run 23 | 24 | release: update fmt test lint vet 25 | 26 | run: 27 | go run examples/aes/main.go 28 | go run examples/blowfish/main.go 29 | 30 | tag: 31 | git push 32 | git tag -a ${VERSION} -m 'Version ${VERSION}' 33 | git push --tags 34 | 35 | test: 36 | go test -v ./... 37 | 38 | version: 39 | @go version 40 | @echo 'Crypt2go version: ${VERSION}' 41 | 42 | vet: 43 | go list ./... 44 | go vet ./... 45 | 46 | update: 47 | go get -u ./... 48 | go mod tidy 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crypt2go 2 | 3 | [![GoDoc](https://godoc.org/github.com/andreburgaud/crypt2go?status.svg)](https://godoc.org/github.com/andreburgaud/crypt2go) 4 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/andreburgaud/crypt2go)](https://goreportcard.com/report/github.com/andreburgaud/crypt2go) 6 | 7 | **Crypt2go** includes [Go](https://go.dev/) packages complementing existing standard library [crypto](https://pkg.go.dev/crypto) packages and the extension packages [x/crypto](https://pkg.go.dev/golang.org/x/crypto). 8 | 9 | ## Installation 10 | 11 | ``` 12 | go get github.com/andreburgaud/crypt2go 13 | ``` 14 | 15 | Or to update to the latest version: 16 | 17 | ``` 18 | go get -u github.com/andreburgaud/crypt2go 19 | ``` 20 | 21 | ## Development 22 | 23 | In the `crypt2go` directory, execute `make` or `make help` to display the build commands. 24 | 25 | ### Test 26 | 27 | ``` 28 | make test 29 | ``` 30 | 31 | ### Run Examples 32 | 33 | To execute the code examples found in this document (`README`): 34 | 35 | ``` 36 | make run 37 | ``` 38 | 39 | ## Disclaimer 40 | 41 | I'm not an expert in cryptography, and I welcome any comments or suggestions to improve the code included in this repository. 42 | 43 | ## ECB (Electronic Codebook) 44 | 45 | The [ECB mode of operation](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) should **NOT** be used anymore. I wrote **Crypt2go** to facilitate migrating legacy data encrypted with ECB. Plenty of literature explains why ECB is not recommended in cryptographic protocols. Nevertheless, this code might be helpful if someone needs to solve a problem with legacy software using ECB. 46 | 47 | ## Examples 48 | 49 | ### AES Encryption in ECB mode with padding 50 | 51 | ```go 52 | package main 53 | 54 | import ( 55 | "fmt" 56 | 57 | "crypto/aes" 58 | 59 | "github.com/andreburgaud/crypt2go/ecb" 60 | "github.com/andreburgaud/crypt2go/padding" 61 | ) 62 | 63 | func encrypt(pt, key []byte) []byte { 64 | block, err := aes.NewCipher(key) 65 | if err != nil { 66 | panic(err.Error()) 67 | } 68 | mode := ecb.NewECBEncrypter(block) 69 | padder := padding.NewPkcs7Padding(mode.BlockSize()) 70 | pt, err = padder.Pad(pt) // pad last block of plaintext if block size less than block cipher size 71 | if err != nil { 72 | panic(err.Error()) 73 | } 74 | ct := make([]byte, len(pt)) 75 | mode.CryptBlocks(ct, pt) 76 | return ct 77 | } 78 | 79 | func decrypt(ct, key []byte) []byte { 80 | block, err := aes.NewCipher(key) 81 | if err != nil { 82 | panic(err.Error()) 83 | } 84 | mode := ecb.NewECBDecrypter(block) 85 | pt := make([]byte, len(ct)) 86 | mode.CryptBlocks(pt, ct) 87 | padder := padding.NewPkcs7Padding(mode.BlockSize()) 88 | pt, err = padder.Unpad(pt) // unpad plaintext after decryption 89 | if err != nil { 90 | panic(err.Error()) 91 | } 92 | return pt 93 | } 94 | 95 | func example() { 96 | pt := []byte("Some plain text") 97 | // Key size for AES is either: 16 bytes (128 bits), 24 bytes (192 bits), or 32 bytes (256 bits) 98 | key := []byte("secretkey16bytes") 99 | 100 | ct := encrypt(pt, key) 101 | fmt.Printf("Ciphertext: %x\n", ct) 102 | 103 | recoveredPt := decrypt(ct, key) 104 | fmt.Printf("Recovered plaintext: %s\n", recoveredPt) 105 | } 106 | 107 | func main() { 108 | fmt.Println("AES encryption with ECB and PKCS7 padding") 109 | example() 110 | } 111 | ``` 112 | 113 | ### Blowfish encryption in ECB mode with padding 114 | 115 | **Note**: The Golang blowfish package is deprecated and should not be used for any new system (https://pkg.go.dev/golang.org/x/crypto/blowfish). Nevertheless, you could still benefit from the following example if you needed to convert legacy encryption from blowfish with ECB to a secure encryption algorithm and mode of encryption, for example, AES with GCM ([Gallois Counter Mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode)). 116 | 117 | ```go 118 | package main 119 | 120 | import ( 121 | "fmt" 122 | 123 | "golang.org/x/crypto/blowfish" 124 | 125 | "github.com/andreburgaud/crypt2go/ecb" 126 | "github.com/andreburgaud/crypt2go/padding" 127 | ) 128 | 129 | func encrypt(pt, key []byte) []byte { 130 | block, err := blowfish.NewCipher(key) 131 | if err != nil { 132 | panic(err.Error()) 133 | } 134 | mode := ecb.NewECBEncrypter(block) 135 | padder := padding.NewPkcs5Padding() 136 | pt, err = padder.Pad(pt) // pad last block of plaintext if block size less than block cipher size 137 | if err != nil { 138 | panic(err.Error()) 139 | } 140 | ct := make([]byte, len(pt)) 141 | mode.CryptBlocks(ct, pt) 142 | return ct 143 | } 144 | 145 | func decrypt(ct, key []byte) []byte { 146 | block, err := blowfish.NewCipher(key) 147 | if err != nil { 148 | panic(err.Error()) 149 | } 150 | mode := ecb.NewECBDecrypter(block) 151 | pt := make([]byte, len(ct)) 152 | mode.CryptBlocks(pt, ct) 153 | padder := padding.NewPkcs5Padding() 154 | pt, err = padder.Unpad(pt) // unpad plaintext after decryption 155 | if err != nil { 156 | panic(err.Error()) 157 | } 158 | return pt 159 | } 160 | 161 | func example() { 162 | pt := []byte("Some plain text") 163 | key := []byte("a_very_secret_key") 164 | 165 | ct := encrypt(pt, key) 166 | fmt.Printf("Ciphertext: %x\n", ct) 167 | 168 | recoveredPt := decrypt(ct, key) 169 | fmt.Printf("Recovered plaintext: %s\n", recoveredPt) 170 | } 171 | 172 | func main() { 173 | fmt.Println("Blowfish encryption with ECB and PKCS5 padding") 174 | example() 175 | } 176 | ``` 177 | 178 | 179 | ## Padding 180 | 181 | ECB (Electronic Codebook) and CBC (Cipher Block Chaining) require fixed-size blocks. Encryption with ECB and CBC modes requires padding the plain text to a size multiple of the block size. 182 | 183 | The `padding` package exposes simple functions to provide a way to `pad` before encryption and `unpad` a given plaintext after decryption. 184 | 185 | The code examples in the previous sections show encryption patterns with [Blowfish](https://en.wikipedia.org/wiki/Blowfish_(cipher)) and [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) in ECB mode. Blowfish encrypts blocks of 8 bytes (64 bits) using the padding type described in the [PKCS #5: Password-Based Cryptography Specification Version 2.1](https://tools.ietf.org/html/rfc8018). In contrast, AES requires blocks of 16 bytes (128 bits). The padding type in the second example follows the specs documented in [PKCS #7: Cryptographic Message Syntax Version 1.5](https://tools.ietf.org/html/rfc2315). 186 | 187 | The only difference between the two specs is that PKCS #5 accommodates only blocks of 8 bytes. The `padding` package reflects that and exposes two builders, respectively `NewPkcs5Padding()` that embeds a hard-coded value for a block size of 8, while `NewPkcs7Padding(int blockSize)` takes a parameter for the block size. Nothing prevents using `NewPkcs7Padding` with a block size of 8 to work with an encryption scheme on blocks of 8 bytes, like *Blowfish*. 188 | 189 | ### Full block of padding 190 | 191 | Padding is always performed to ensure there is no ambiguity for the receiver of a message, even if the message is of an exact multiple-block size. It is intentional and complies with the [NIST Recommendation for Block Cipher Modes of Operation](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf) **Appendix A: Padding** (page 17), and the following RFCs: 192 | 193 | * [PKCS #5: Password-Based Cryptography Specification Version 2.1](https://tools.ietf.org/html/rfc8018) 194 | * [Privacy Enhancement for Internet Electronic Mail: Part III: Algorithms, Modes, and Identifiers](https://tools.ietf.org/html/rfc1423) 195 | 196 | The padding goes as follows for blocks of 8 bytes (8 octets): 197 | 198 | Given a message `M`, we obtain an encoded message `EM` by concatenating `M` with a padding string `PS`: 199 | 200 | ``` 201 | EM = M || PS 202 | ``` 203 | 204 | The padding string `PS` consists of `8 - (||M|| mod 8)` octets, each with value `8 - (||M|| mod 8)`. Examples: 205 | 206 | ``` 207 | PS = 01, if ||M|| mod 8 = 7 208 | PS = 02 02, if ||M|| mod 8 = 6 209 | ... 210 | PS = 08 08 08 08 08 08 08 08, if ||M|| mod 8 = 0 211 | ``` 212 | 213 | The last example is essential. Yes, it will intentionally add an entire padding block. Doing so removes the ambiguity for the receiver, that expects every message to be padded. 214 | 215 | To illustrate what would happen if some messages are not padded, let's take an example of a message with the last octet with the value `01`. As the receiver of this message, should I remove the padding `01`? Or, is the last byte `01` part of a message not padded because it was an exact multiple-block size? 216 | 217 | If the receiver knows that every message is padded, even if this results in a message padded with a whole block of `08`, there is no ambiguity. 218 | 219 | Another approach to removing this ambiguity would be to provide a separate indicator that would remove this ambiguity. An example would be to give a message length indicator. 220 | 221 | The implementation in this package relies on padding every message (see method `padding.Pad()`). 222 | 223 | ## Additional Examples 224 | 225 | See the unit tests or the example tests in the respective package directories. 226 | 227 | ## License 228 | 229 | The **Crypt2go** `ecb` package is directly modeled after the [CBC Go code](https://go.dev/src/crypto/cipher/cbc.go) released under a [BSD license](https://go.dev/LICENSE). To avoid license conflicts, **Crypt2go** is also released under a BSD license. 230 | 231 | See the [LICENSE](LICENSE.md) file in the repository. 232 | -------------------------------------------------------------------------------- /crypt2go.go: -------------------------------------------------------------------------------- 1 | package crypt2go 2 | -------------------------------------------------------------------------------- /ecb/common_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found at https://golang.org/LICENSE 4 | 5 | package ecb 6 | 7 | type ecbTest struct { 8 | name string 9 | key []byte 10 | in []byte 11 | out []byte 12 | } 13 | 14 | // Common values for tests. 15 | 16 | var commonInput = []byte{ 17 | 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 18 | 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 19 | 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 20 | 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, 21 | } 22 | 23 | var commonKey128 = []byte{ 24 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} 25 | 26 | var commonKey192 = []byte{ 27 | 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 28 | 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b, 29 | } 30 | 31 | var commonKey256 = []byte{ 32 | 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 33 | 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4, 34 | } 35 | -------------------------------------------------------------------------------- /ecb/ecb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Andre Burgaud. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Electronic Code Book (ECB) mode. 6 | 7 | // Implemented for legacy purpose only. ECB should be avoided 8 | // as a mode of operation. Favor other modes available 9 | // in the Go crypto/cipher package (i.e. CBC, GCM, CFB, OFB or CTR). 10 | 11 | // See NIST SP 800-38A, pp 9 12 | 13 | // The source code in this file is a modified copy of 14 | // https://golang.org/src/crypto/cipher/cbc.go 15 | // and released under the following 16 | // Go Authors copyright and license: 17 | 18 | // Copyright 2009 The Go Authors. All rights reserved. 19 | // Use of this source code is governed by a BSD-style 20 | // license that can be found at https://golang.org/LICENSE 21 | 22 | // Package ecb implements block cipher mode of encryption ECB (Electronic Code 23 | // Book) functions. This is implemented for legacy purposes only and should not 24 | // be used for any new encryption needs. Use CBC (Cipher Block Chaining) instead. 25 | package ecb 26 | 27 | import ( 28 | "crypto/cipher" 29 | "fmt" 30 | ) 31 | 32 | type ecb struct { 33 | b cipher.Block 34 | blockSize int 35 | tmp []byte 36 | } 37 | 38 | func newECB(b cipher.Block) *ecb { 39 | return &ecb{ 40 | b: b, 41 | blockSize: b.BlockSize(), 42 | tmp: make([]byte, b.BlockSize()), 43 | } 44 | } 45 | 46 | type ecbEncrypter ecb 47 | 48 | // NewECBEncrypter returns a BlockMode which encrypts in elecronic codebook (ECB) 49 | // mode, using the given Block (Cipher). 50 | func NewECBEncrypter(b cipher.Block) cipher.BlockMode { 51 | return (*ecbEncrypter)(newECB(b)) 52 | } 53 | 54 | func (x *ecbEncrypter) BlockSize() int { return x.blockSize } 55 | 56 | func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { 57 | var errorMessage string 58 | if len(src)%x.blockSize != 0 { 59 | errorMessage = fmt.Sprintf("crypto/cipher: input not full blocks, received source len '%v' blocksize '%v'", 60 | len(src), x.blockSize) 61 | panic(errorMessage) 62 | } 63 | if len(dst) < len(src) { 64 | errorMessage = fmt.Sprintf("crypto/cipher: output '%v' smaller than input '%v'", len(dst), len(src)) 65 | panic(errorMessage) 66 | } 67 | for len(src) > 0 { 68 | x.b.Encrypt(dst[:x.blockSize], src[:x.blockSize]) 69 | src = src[x.blockSize:] 70 | dst = dst[x.blockSize:] 71 | } 72 | } 73 | 74 | type ecbDecrypter ecb 75 | 76 | // NewECBDecrypter returns a BlockMode which decrypts in electronic codebook (ECB) 77 | // mode, using the given Block. 78 | func NewECBDecrypter(b cipher.Block) cipher.BlockMode { 79 | return (*ecbDecrypter)(newECB(b)) 80 | } 81 | 82 | func (x *ecbDecrypter) BlockSize() int { return x.blockSize } 83 | 84 | func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { 85 | var errorMessage string 86 | if len(src)%x.blockSize != 0 { 87 | errorMessage = fmt.Sprintf("crypto/cipher: input not full blocks, received source len '%v' blocksize '%v'", len(src), x.blockSize) 88 | panic(errorMessage) 89 | } 90 | if len(dst) < len(src) { 91 | errorMessage = fmt.Sprintf("crypto/cipher: output '%v' smaller than input '%v'", len(dst), len(src)) 92 | panic(errorMessage) 93 | } 94 | if len(src) == 0 { 95 | 96 | return 97 | } 98 | for len(src) > 0 { 99 | x.b.Decrypt(dst[:x.blockSize], src[:x.blockSize]) 100 | src = src[x.blockSize:] 101 | dst = dst[x.blockSize:] 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /ecb/ecb_aes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Andre Burgaud. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // ECB AES test vectors. 6 | 7 | // This source code is modeled after 8 | // https://golang.org/src/crypto/cipher/cbc_aes_test.go 9 | // released under the following Go Authors copyright and license: 10 | 11 | // Copyright 2009 The Go Authors. All rights reserved. 12 | // Use of this source code is governed by a BSD-style 13 | // license that can be found at https://golang.org/LICENSE 14 | 15 | // See U.S. National Institute of Standards and Technology (NIST) 16 | // Special Publication 800-38A, ``Recommendation for Block Cipher 17 | // Modes of Operation,'' 2001 Edition, pp. 24-27. 18 | 19 | package ecb 20 | 21 | import ( 22 | "bytes" 23 | "crypto/aes" 24 | "testing" 25 | ) 26 | 27 | var ecbAESTests = []ecbTest{ 28 | 29 | { 30 | "ECB-AES128", 31 | commonKey128, 32 | commonInput, 33 | []byte{ 34 | 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97, 35 | 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf, 36 | 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88, 37 | 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4, 38 | }, 39 | }, 40 | 41 | { 42 | "ECB-AES192", 43 | commonKey192, 44 | commonInput, 45 | []byte{ 46 | 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc, 47 | 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef, 48 | 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e, 49 | 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e, 50 | }, 51 | }, 52 | 53 | { 54 | "ECB-AES256", 55 | commonKey256, 56 | commonInput, 57 | []byte{ 58 | 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8, 59 | 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70, 60 | 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d, 61 | 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7, 62 | }, 63 | }, 64 | } 65 | 66 | func TestECBEncrypterAES(t *testing.T) { 67 | for _, tc := range ecbAESTests { 68 | t.Run(tc.name, func(t *testing.T) { 69 | c, err := aes.NewCipher(tc.key) 70 | if err != nil { 71 | t.Fatalf("%s: NewCipher(%d bytes) = %s", tc.name, len(tc.key), err) 72 | } 73 | encrypter := NewECBEncrypter(c) 74 | data := make([]byte, len(tc.in)) 75 | copy(data, tc.in) 76 | encrypter.CryptBlocks(data, data) 77 | if !bytes.Equal(tc.out, data) { 78 | t.Errorf("%s: ECBEncrypter\nhave %x\nwant %x", tc.name, data, tc.out) 79 | } 80 | }) 81 | } 82 | } 83 | 84 | func TestECBDecrypterAES(t *testing.T) { 85 | for _, tc := range ecbAESTests { 86 | c, err := aes.NewCipher(tc.key) 87 | t.Run(tc.name, func(t *testing.T) { 88 | if err != nil { 89 | t.Fatalf("%s: NewCipher(%d bytes) = %s", tc.name, len(tc.key), err) 90 | } 91 | decrypter := NewECBDecrypter(c) 92 | data := make([]byte, len(tc.out)) 93 | copy(data, tc.out) 94 | decrypter.CryptBlocks(data, data) 95 | if !bytes.Equal(tc.in, data) { 96 | t.Errorf("%s: ECBDecrypter\nhave %x\nwant %x", tc.name, data, tc.in) 97 | } 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ecb/ecb_blowfish_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Andre Burgaud. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ecb 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | //nolint:staticcheck // SA1019 ignore this 12 | "golang.org/x/crypto/blowfish" 13 | ) 14 | 15 | var ecbBlowfishTests = []ecbTest{ 16 | 17 | // NIST SP 800-38A pp 24-27 18 | { 19 | "ECB-Blowfish128", 20 | commonKey128, 21 | commonInput, 22 | []byte{ 23 | 0x05, 0xac, 0x65, 0x4c, 0x24, 0x4c, 0x58, 0x65, 0x7b, 0x82, 0x8d, 0xcf, 0xe6, 0xf8, 0x38, 0x9e, 24 | 0x1d, 0xfa, 0x4a, 0x38, 0x17, 0xb1, 0x3e, 0xd3, 0x1a, 0x5d, 0xe0, 0xd6, 0x0f, 0x53, 0xac, 0x22, 25 | 0x4b, 0x46, 0x34, 0xdd, 0xa3, 0xdf, 0xb3, 0xc9, 0x75, 0x70, 0x3f, 0xef, 0xa9, 0xe6, 0x91, 0x56, 26 | 0x0e, 0xa0, 0x10, 0x1b, 0x30, 0x60, 0xb1, 0x16, 0x8a, 0x8c, 0x4a, 0x9f, 0xf6, 0x88, 0x48, 0x16, 27 | }, 28 | }, 29 | 30 | { 31 | "ECB-Blowfish192", 32 | commonKey192, 33 | commonInput, 34 | []byte{ 35 | 0xda, 0x42, 0x91, 0x7c, 0x1f, 0x7a, 0x20, 0x20, 0xb5, 0x07, 0x08, 0x91, 0x60, 0xef, 0xf0, 0xf1, 36 | 0x74, 0xa0, 0x62, 0x6c, 0xad, 0x63, 0x94, 0xe6, 0x0c, 0xdc, 0x58, 0xf3, 0x12, 0x9e, 0xf5, 0x53, 37 | 0xa3, 0x77, 0x30, 0x66, 0xf9, 0x1d, 0x2c, 0x28, 0x69, 0x92, 0x43, 0xe3, 0xfc, 0xbf, 0x81, 0xdc, 38 | 0x8d, 0x1e, 0x4c, 0x81, 0xe3, 0xab, 0x64, 0xda, 0x6c, 0x71, 0xfe, 0xc5, 0xfd, 0xd4, 0xfb, 0x4c, 39 | }, 40 | }, 41 | 42 | { 43 | "ECB-Blowfish256", 44 | commonKey256, 45 | commonInput, 46 | []byte{ 47 | 0xd7, 0xf8, 0xbe, 0x57, 0xaa, 0xe1, 0xc6, 0x36, 0xf5, 0x03, 0xae, 0x7f, 0xf6, 0xcb, 0x1d, 0x25, 48 | 0x7c, 0xf7, 0xc9, 0x56, 0xf3, 0x69, 0x5f, 0x2f, 0x08, 0x09, 0x57, 0xa7, 0x44, 0x86, 0x95, 0x7c, 49 | 0x1b, 0xa0, 0x57, 0x64, 0xe8, 0x10, 0x2a, 0xb8, 0x45, 0x24, 0x40, 0x40, 0xd1, 0x7c, 0x97, 0xa0, 50 | 0xf6, 0x5a, 0x48, 0x58, 0x47, 0x82, 0x4d, 0xe4, 0xae, 0x78, 0x7a, 0xc3, 0xe0, 0xa1, 0x4e, 0x48, 51 | }, 52 | }, 53 | } 54 | 55 | func TestECBEncrypterBlowfish(t *testing.T) { 56 | for _, tc := range ecbBlowfishTests { 57 | t.Run(tc.name, func(t *testing.T) { 58 | c, err := blowfish.NewCipher(tc.key) 59 | if err != nil { 60 | t.Errorf("%s: NewCipher(%d bytes) = %s", tc.name, len(tc.key), err) 61 | } 62 | encrypter := NewECBEncrypter(c) 63 | data := make([]byte, len(tc.in)) 64 | copy(data, tc.in) 65 | encrypter.CryptBlocks(data, data) 66 | if !bytes.Equal(tc.out, data) { 67 | t.Errorf("%s: ECBEncrypter\nhave %x\nwant %x", tc.name, data, tc.out) 68 | } 69 | }) 70 | } 71 | } 72 | 73 | func TestECBDecrypterBlowfish(t *testing.T) { 74 | for _, tc := range ecbBlowfishTests { 75 | t.Run(tc.name, func(t *testing.T) { 76 | c, err := blowfish.NewCipher(tc.key) 77 | if err != nil { 78 | t.Fatalf("%s: NewCipher(%d bytes) = %s", tc.name, len(tc.key), err) 79 | } 80 | decrypter := NewECBDecrypter(c) 81 | data := make([]byte, len(tc.out)) 82 | copy(data, tc.out) 83 | decrypter.CryptBlocks(data, data) 84 | if !bytes.Equal(tc.in, data) { 85 | t.Errorf("%s: ECBDecrypter\nhave %x\nwant %x", tc.name, data, tc.in) 86 | } 87 | }) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ecb/ecb_error_test.go: -------------------------------------------------------------------------------- 1 | package ecb 2 | 3 | import ( 4 | "crypto/aes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestECBEncrypterAESPanic(t *testing.T) { 10 | // if the encryption does not panic the test will fail 11 | defer func() { 12 | if r := recover(); r == nil { 13 | t.Errorf("input plaintext was too short expected to panic") 14 | } 15 | }() 16 | key := []byte("AES256Key-32Characters1234567890") 17 | plaintext := []byte("exampleplaintex") // input is too short not a multiple of 16, should be padded, therefore will panic 18 | c, err := aes.NewCipher(key) 19 | if err != nil { 20 | t.Fatalf("NewCipher(%d bytes) = %s", len(key), err) 21 | } 22 | encrypter := NewECBEncrypter(c) 23 | ciphertext := make([]byte, len(plaintext)) 24 | encrypter.CryptBlocks(ciphertext, plaintext) 25 | } 26 | 27 | func TestECBDecrypterAESPanic(t *testing.T) { 28 | // if the decryption does not panic the test will fail 29 | defer func() { 30 | if r := recover(); r == nil { 31 | t.Errorf("input plaintext was too short expected to panic") 32 | } 33 | }() 34 | key := []byte("AES256Key-32Characters1234567890") 35 | ciphertext, _ := hex.DecodeString("717FADE7B97198A8C2F67766FBAC7B") // correct value was "717FADE7B97198A8C2F67766FBAC7B07" (1 byte missing => should panic) 36 | c, err := aes.NewCipher(key) 37 | if err != nil { 38 | t.Fatalf("NewCipher(%d bytes) = %s", len(key), err) 39 | } 40 | encrypter := NewECBEncrypter(c) 41 | plaintext := make([]byte, len(ciphertext)) 42 | encrypter.CryptBlocks(plaintext, ciphertext) 43 | } 44 | -------------------------------------------------------------------------------- /ecb/example_test.go: -------------------------------------------------------------------------------- 1 | package ecb 2 | 3 | import ( 4 | "crypto/aes" 5 | "encoding/hex" 6 | "fmt" 7 | 8 | //nolint:staticcheck // SA1019 ignore this 9 | "golang.org/x/crypto/blowfish" 10 | ) 11 | 12 | func ExampleNewECBEncrypter() { 13 | key := []byte("SomeBlowfishKey") 14 | plaintext := []byte("exampleplaintext") 15 | block, err := blowfish.NewCipher(key) 16 | if err != nil { 17 | panic(err.Error()) 18 | } 19 | mode := NewECBEncrypter(block) 20 | ciphertext := make([]byte, len(plaintext)) 21 | mode.CryptBlocks(ciphertext, plaintext) 22 | fmt.Printf("%X\n", ciphertext) 23 | // Output: 66E6E737F23E570ACA5710BA3C0E321A 24 | } 25 | 26 | func ExampleNewECBEncrypter_second() { 27 | key := []byte("AES256Key-32Characters1234567890") 28 | plaintext := []byte("exampleplaintext") 29 | block, err := aes.NewCipher(key) 30 | if err != nil { 31 | panic(err.Error()) 32 | } 33 | mode := NewECBEncrypter(block) 34 | ciphertext := make([]byte, len(plaintext)) 35 | mode.CryptBlocks(ciphertext, plaintext) 36 | fmt.Printf("%X\n", ciphertext) 37 | // Output: 717FADE7B97198A8C2F67766FBAC7B07 38 | } 39 | 40 | func ExampleNewECBDecrypter() { 41 | key := []byte("SomeBlowfishKey") 42 | ciphertext, err := hex.DecodeString("66E6E737F23E570ACA5710BA3C0E321A") 43 | if err != nil { 44 | panic(err.Error()) 45 | } 46 | block, err := blowfish.NewCipher(key) 47 | if err != nil { 48 | panic(err.Error()) 49 | } 50 | mode := NewECBDecrypter(block) 51 | plaintext := make([]byte, len(ciphertext)) 52 | mode.CryptBlocks(plaintext, ciphertext) 53 | fmt.Printf("%s\n", string(plaintext)) 54 | // Output: exampleplaintext 55 | } 56 | 57 | func ExampleNewECBDecrypter_second() { 58 | key := []byte("AES256Key-32Characters1234567890") 59 | ciphertext, err := hex.DecodeString("717FADE7B97198A8C2F67766FBAC7B07") 60 | if err != nil { 61 | panic(err.Error()) 62 | } 63 | block, err := aes.NewCipher(key) 64 | if err != nil { 65 | panic(err.Error()) 66 | } 67 | mode := NewECBDecrypter(block) 68 | plaintext := make([]byte, len(ciphertext)) 69 | mode.CryptBlocks(plaintext, ciphertext) 70 | fmt.Printf("%s\n", string(plaintext)) 71 | // Output: exampleplaintext 72 | } 73 | -------------------------------------------------------------------------------- /examples/aes/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "crypto/aes" 7 | 8 | "github.com/andreburgaud/crypt2go/ecb" 9 | "github.com/andreburgaud/crypt2go/padding" 10 | ) 11 | 12 | func encrypt(pt, key []byte) []byte { 13 | block, err := aes.NewCipher(key) 14 | if err != nil { 15 | panic(err.Error()) 16 | } 17 | mode := ecb.NewECBEncrypter(block) 18 | padder := padding.NewPkcs7Padding(mode.BlockSize()) 19 | pt, err = padder.Pad(pt) // padd last block of plaintext if block size less than block cipher size 20 | if err != nil { 21 | panic(err.Error()) 22 | } 23 | ct := make([]byte, len(pt)) 24 | mode.CryptBlocks(ct, pt) 25 | return ct 26 | } 27 | 28 | func decrypt(ct, key []byte) []byte { 29 | block, err := aes.NewCipher(key) 30 | if err != nil { 31 | panic(err.Error()) 32 | } 33 | mode := ecb.NewECBDecrypter(block) 34 | pt := make([]byte, len(ct)) 35 | mode.CryptBlocks(pt, ct) 36 | padder := padding.NewPkcs7Padding(mode.BlockSize()) 37 | pt, err = padder.Unpad(pt) // unpad plaintext after decryption 38 | if err != nil { 39 | panic(err.Error()) 40 | } 41 | return pt 42 | } 43 | 44 | func example() { 45 | pt := []byte("Some plain text") 46 | // Key size for AES is either: 16 bytes (128 bits), 24 bytes (192 bits) or 32 bytes (256 bits) 47 | key := []byte("secretkey16bytes") 48 | 49 | ct := encrypt(pt, key) 50 | fmt.Printf("Ciphertext: %x\n", ct) 51 | 52 | recoveredPt := decrypt(ct, key) 53 | fmt.Printf("Recovered plaintext: %s\n", recoveredPt) 54 | } 55 | 56 | func main() { 57 | fmt.Println("AES encryption with ECB and PKCS7 padding") 58 | example() 59 | } 60 | -------------------------------------------------------------------------------- /examples/blowfish/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/crypto/blowfish" 7 | 8 | "github.com/andreburgaud/crypt2go/ecb" 9 | "github.com/andreburgaud/crypt2go/padding" 10 | ) 11 | 12 | func encrypt(pt, key []byte) []byte { 13 | block, err := blowfish.NewCipher(key) 14 | if err != nil { 15 | panic(err.Error()) 16 | } 17 | mode := ecb.NewECBEncrypter(block) 18 | padder := padding.NewPkcs5Padding() 19 | pt, err = padder.Pad(pt) // pad last block of plaintext if block size less than block cipher size 20 | if err != nil { 21 | panic(err.Error()) 22 | } 23 | ct := make([]byte, len(pt)) 24 | mode.CryptBlocks(ct, pt) 25 | return ct 26 | } 27 | 28 | func decrypt(ct, key []byte) []byte { 29 | block, err := blowfish.NewCipher(key) 30 | if err != nil { 31 | panic(err.Error()) 32 | } 33 | mode := ecb.NewECBDecrypter(block) 34 | pt := make([]byte, len(ct)) 35 | mode.CryptBlocks(pt, ct) 36 | padder := padding.NewPkcs5Padding() 37 | pt, err = padder.Unpad(pt) // unpad plaintext after decryption 38 | if err != nil { 39 | panic(err.Error()) 40 | } 41 | return pt 42 | } 43 | 44 | func example() { 45 | pt := []byte("Some plain text") 46 | key := []byte("a_very_secret_key") 47 | 48 | ct := encrypt(pt, key) 49 | fmt.Printf("Ciphertext: %x\n", ct) 50 | 51 | recoveredPt := decrypt(ct, key) 52 | fmt.Printf("Recovered plaintext: %s\n", recoveredPt) 53 | } 54 | 55 | func main() { 56 | fmt.Println("Blowfish encryption with ECB and PKCS5 padding") 57 | example() 58 | } 59 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/andreburgaud/crypt2go 2 | 3 | go 1.23 4 | 5 | require golang.org/x/crypto v0.33.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= 2 | golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= 3 | -------------------------------------------------------------------------------- /padding/example_test.go: -------------------------------------------------------------------------------- 1 | package padding 2 | 3 | import "fmt" 4 | 5 | func ExamplePadder_Pad() { 6 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D} 7 | fmt.Printf("%X\n", p) 8 | padder := NewPkcs5Padding() 9 | p, err := padder.Pad(p) 10 | if err != nil { 11 | panic(err.Error()) 12 | } 13 | fmt.Printf("%X\n", p) 14 | // Output: 15 | // 0A0B0C0D 16 | // 0A0B0C0D04040404 17 | } 18 | 19 | func ExamplePadder_Pad_second() { 20 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x0A, 0x0B, 0x0C, 0x0D} 21 | fmt.Printf("%X\n", p) 22 | padder := NewPkcs5Padding() 23 | p, err := padder.Pad(p) 24 | if err != nil { 25 | panic(err.Error()) 26 | } 27 | fmt.Printf("%X\n", p) 28 | // Output: 29 | // 0A0B0C0D0A0B0C0D 30 | // 0A0B0C0D0A0B0C0D0808080808080808 31 | } 32 | 33 | func ExamplePadder_Pad_third() { 34 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D} 35 | fmt.Printf("%X\n", p) 36 | padder := NewPkcs7Padding(16) // 16-byte block size 37 | p, err := padder.Pad(p) 38 | if err != nil { 39 | panic(err.Error()) 40 | } 41 | fmt.Printf("%X\n", p) 42 | // Output: 43 | // 0A0B0C0D 44 | // 0A0B0C0D0C0C0C0C0C0C0C0C0C0C0C0C 45 | } 46 | 47 | func ExamplePadder_Unpad() { 48 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x04, 0x04, 0x04, 0x04} 49 | fmt.Printf("%X\n", p) 50 | padder := NewPkcs5Padding() 51 | p, err := padder.Unpad(p) 52 | if err != nil { 53 | panic(err.Error()) 54 | } 55 | fmt.Printf("%X\n", p) 56 | // Output: 57 | // 0A0B0C0D04040404 58 | // 0A0B0C0D 59 | } 60 | 61 | func ExamplePadder_Unpad_second() { 62 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x0A, 0x0B, 0x0C, 0x0D, 63 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08} 64 | fmt.Printf("%X\n", p) 65 | padder := NewPkcs5Padding() 66 | p, err := padder.Unpad(p) 67 | if err != nil { 68 | panic(err.Error()) 69 | } 70 | fmt.Printf("%X\n", p) 71 | // Output: 72 | // 0A0B0C0D0A0B0C0D0808080808080808 73 | // 0A0B0C0D0A0B0C0D 74 | } 75 | 76 | func ExamplePadder_Unpad_third() { 77 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 78 | 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C} 79 | fmt.Printf("%X\n", p) 80 | padder := NewPkcs7Padding(16) // 16-byte block size 81 | p, err := padder.Unpad(p) 82 | if err != nil { 83 | panic(err.Error()) 84 | } 85 | fmt.Printf("%X\n", p) 86 | // Output: 87 | // 0A0B0C0D0C0C0C0C0C0C0C0C0C0C0C0C 88 | // 0A0B0C0D 89 | } 90 | 91 | func ExamplePadder_Unpad_empty() { 92 | p := []byte{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08} 93 | fmt.Printf("%X\n", p) 94 | padder := NewPkcs7Padding(8) // 8-byte block size 95 | p, err := padder.Unpad(p) 96 | if err != nil { 97 | panic(err.Error()) 98 | } 99 | fmt.Printf("%v\n", p) 100 | // Output: 101 | // 0808080808080808 102 | // [] 103 | } 104 | 105 | func ExamplePadder_Unpad_lastzero() { 106 | p := []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x04, 0x04, 0x04, 0x00} 107 | fmt.Printf("%X\n", p) 108 | padder := NewPkcs5Padding() 109 | p, err := padder.Unpad(p) 110 | if err != nil { 111 | fmt.Println(err) 112 | } else { 113 | fmt.Printf("%X\n", p) 114 | } 115 | // Output: 116 | // 0A0B0C0D04040400 117 | // crypto/padding: invalid last byte of padding 118 | } 119 | -------------------------------------------------------------------------------- /padding/padding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Andre Burgaud. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package padding provides functions for padding blocks of plain text in the 6 | // context of block cipher mode of encryption like ECB or CBC. 7 | package padding 8 | 9 | import ( 10 | "bytes" 11 | "errors" 12 | ) 13 | 14 | // Padding interface defines functions Pad and Unpad implemented for PKCS #5 and 15 | // PKCS #7 types of padding. 16 | type Padding interface { 17 | Pad(p []byte) ([]byte, error) 18 | Unpad(p []byte) ([]byte, error) 19 | } 20 | 21 | // Padder struct embeds attributes necessary for the padding calculation 22 | // (e.g. block size). It implements the Padding interface. 23 | type Padder struct { 24 | blockSize int 25 | } 26 | 27 | // NewPkcs5Padding returns a PKCS5 padding type structure. The blocksize 28 | // defaults to 8 bytes (64-bit). 29 | // See https://tools.ietf.org/html/rfc2898 PKCS #5: Password-Based Cryptography. 30 | // Specification Version 2.0 31 | func NewPkcs5Padding() Padding { 32 | return &Padder{blockSize: 8} 33 | } 34 | 35 | // NewPkcs7Padding returns a PKCS7 padding type structure. The blocksize is 36 | // passed as a parameter. 37 | // See https://tools.ietf.org/html/rfc2315 PKCS #7: Cryptographic Message 38 | // Syntax Version 1.5. 39 | // For example the block size for AES is 16 bytes (128 bits). 40 | func NewPkcs7Padding(blockSize int) Padding { 41 | return &Padder{blockSize: blockSize} 42 | } 43 | 44 | // Pad returns the byte array passed as a parameter padded with bytes such that 45 | // the new byte array will be an exact multiple of the expected block size. 46 | // For example, if the expected block size is 8 bytes (e.g. PKCS #5) and that 47 | // the initial byte array is: 48 | // 49 | // []byte{0x0A, 0x0B, 0x0C, 0x0D} 50 | // 51 | // the returned array will be: 52 | // 53 | // []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x04, 0x04, 0x04, 0x04} 54 | // 55 | // The value of each octet of the padding is the size of the padding. If the 56 | // array passed as a parameter is already an exact multiple of the block size, 57 | // the original array will be padded with a full block. 58 | func (p *Padder) Pad(buf []byte) ([]byte, error) { 59 | bufLen := len(buf) 60 | padLen := p.blockSize - (bufLen % p.blockSize) 61 | padText := bytes.Repeat([]byte{byte(padLen)}, padLen) 62 | return append(buf, padText...), nil 63 | } 64 | 65 | // Unpad removes the padding of a given byte array, according to the same rules 66 | // as described in the Pad function. For example if the byte array passed as a 67 | // parameter is: 68 | // 69 | // []byte{0x0A, 0x0B, 0x0C, 0x0D, 0x04, 0x04, 0x04, 0x04} 70 | // 71 | // the returned array will be: 72 | // 73 | // []byte{0x0A, 0x0B, 0x0C, 0x0D} 74 | func (p *Padder) Unpad(buf []byte) ([]byte, error) { 75 | bufLen := len(buf) 76 | if bufLen == 0 { 77 | return nil, errors.New("crypto/padding: invalid padding size") 78 | } 79 | 80 | pad := buf[bufLen-1] 81 | if pad == 0 { 82 | return nil, errors.New("crypto/padding: invalid last byte of padding") 83 | } 84 | 85 | padLen := int(pad) 86 | if padLen > bufLen || padLen > p.blockSize { 87 | return nil, errors.New("crypto/padding: invalid padding size") 88 | } 89 | 90 | for _, v := range buf[bufLen-padLen : bufLen-1] { 91 | if v != pad { 92 | return nil, errors.New("crypto/padding: invalid padding") 93 | } 94 | } 95 | 96 | return buf[:bufLen-padLen], nil 97 | } 98 | --------------------------------------------------------------------------------