1 // Copyright 2018 Thales e-Security, Inc 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining 4 // a copy of this software and associated documentation files (the 5 // "Software"), to deal in the Software without restriction, including 6 // without limitation the rights to use, copy, modify, merge, publish, 7 // distribute, sublicense, and/or sell copies of the Software, and to 8 // permit persons to whom the Software is furnished to do so, subject to 9 // the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be 12 // included in all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 package crypto11 23 24 import ( 25 "crypto/cipher" 26 "runtime" 27 28 "github.com/miekg/pkcs11" 29 ) 30 31 // cipher.BlockMode ----------------------------------------------------- 32 33 // BlockModeCloser represents a block cipher running in a block-based mode (e.g. CBC). 34 // 35 // BlockModeCloser embeds cipher.BlockMode, and can be used as such. 36 // However, in this case 37 // (or if the Close() method is not explicitly called for any other reason), 38 // resources allocated to it may remain live indefinitely. 39 type BlockModeCloser interface { 40 cipher.BlockMode 41 42 // Close() releases resources associated with the block mode. 43 Close() 44 } 45 46 const ( 47 modeEncrypt = iota // blockModeCloser is in encrypt mode 48 modeDecrypt // blockModeCloser is in decrypt mode 49 ) 50 51 // NewCBCEncrypter returns a cipher.BlockMode which encrypts in cipher block chaining mode, using the given key. 52 // The length of iv must be the same as the key's block size. 53 // 54 // The new BlockMode acquires persistent resources which are released (eventually) by a finalizer. 55 // If this is a problem for your application then use NewCBCEncrypterCloser instead. 56 // 57 // If that is not possible then adding calls to runtime.GC() may help. 58 func (key *SecretKey) NewCBCEncrypter(iv []byte) (cipher.BlockMode, error) { 59 return key.newBlockModeCloser(key.Cipher.CBCMech, modeEncrypt, iv, true) 60 } 61 62 // NewCBCDecrypter returns a cipher.BlockMode which decrypts in cipher block chaining mode, using the given key. 63 // The length of iv must be the same as the key's block size and must match the iv used to encrypt the data. 64 // 65 // The new BlockMode acquires persistent resources which are released (eventually) by a finalizer. 66 // If this is a problem for your application then use NewCBCDecrypterCloser instead. 67 // 68 // If that is not possible then adding calls to runtime.GC() may help. 69 func (key *SecretKey) NewCBCDecrypter(iv []byte) (cipher.BlockMode, error) { 70 return key.newBlockModeCloser(key.Cipher.CBCMech, modeDecrypt, iv, true) 71 } 72 73 // NewCBCEncrypterCloser returns a BlockModeCloser which encrypts in cipher block chaining mode, using the given key. 74 // The length of iv must be the same as the key's block size. 75 // 76 // Use of NewCBCEncrypterCloser rather than NewCBCEncrypter represents a commitment to call the Close() method 77 // of the returned BlockModeCloser. 78 func (key *SecretKey) NewCBCEncrypterCloser(iv []byte) (BlockModeCloser, error) { 79 return key.newBlockModeCloser(key.Cipher.CBCMech, modeEncrypt, iv, false) 80 } 81 82 // NewCBCDecrypterCloser returns a BlockModeCloser which decrypts in cipher block chaining mode, using the given key. 83 // The length of iv must be the same as the key's block size and must match the iv used to encrypt the data. 84 // 85 // Use of NewCBCDecrypterCloser rather than NewCBCEncrypter represents a commitment to call the Close() method 86 // of the returned BlockModeCloser. 87 func (key *SecretKey) NewCBCDecrypterCloser(iv []byte) (BlockModeCloser, error) { 88 return key.newBlockModeCloser(key.Cipher.CBCMech, modeDecrypt, iv, false) 89 } 90 91 // blockModeCloser is a concrete implementation of BlockModeCloser supporting CBC. 92 type blockModeCloser struct { 93 // PKCS#11 session to use 94 session *pkcs11Session 95 96 // Cipher block size 97 blockSize int 98 99 // modeDecrypt or modeEncrypt 100 mode int 101 102 // Cleanup function 103 cleanup func() 104 } 105 106 // newBlockModeCloser creates a new blockModeCloser for the chosen mechanism and mode. 107 func (key *SecretKey) newBlockModeCloser(mech uint, mode int, iv []byte, setFinalizer bool) (*blockModeCloser, error) { 108 109 session, err := key.context.getSession() 110 if err != nil { 111 return nil, err 112 } 113 114 bmc := &blockModeCloser{ 115 session: session, 116 blockSize: key.Cipher.BlockSize, 117 mode: mode, 118 cleanup: func() { 119 key.context.pool.Put(session) 120 }, 121 } 122 mechDescription := []*pkcs11.Mechanism{pkcs11.NewMechanism(mech, iv)} 123 124 switch mode { 125 case modeDecrypt: 126 err = session.ctx.DecryptInit(session.handle, mechDescription, key.handle) 127 case modeEncrypt: 128 err = session.ctx.EncryptInit(bmc.session.handle, mechDescription, key.handle) 129 default: 130 panic("unexpected mode") 131 } 132 if err != nil { 133 bmc.cleanup() 134 return nil, err 135 } 136 if setFinalizer { 137 runtime.SetFinalizer(bmc, finalizeBlockModeCloser) 138 } 139 140 return bmc, nil 141 } 142 143 func finalizeBlockModeCloser(obj interface{}) { 144 obj.(*blockModeCloser).Close() 145 } 146 147 func (bmc *blockModeCloser) BlockSize() int { 148 return bmc.blockSize 149 } 150 151 func (bmc *blockModeCloser) CryptBlocks(dst, src []byte) { 152 if len(dst) < len(src) { 153 panic("destination buffer too small") 154 } 155 if len(src)%bmc.blockSize != 0 { 156 panic("input is not a whole number of blocks") 157 } 158 var result []byte 159 var err error 160 switch bmc.mode { 161 case modeDecrypt: 162 result, err = bmc.session.ctx.DecryptUpdate(bmc.session.handle, src) 163 case modeEncrypt: 164 result, err = bmc.session.ctx.EncryptUpdate(bmc.session.handle, src) 165 } 166 if err != nil { 167 panic(err) 168 } 169 // PKCS#11 2.40 s5.2 says that the operation must produce as much output 170 // as possible, so we should never have less than we submitted for CBC. 171 // This could be different for other modes but we don't implement any yet. 172 if len(result) != len(src) { 173 panic("nontrivial result from *Final operation") 174 } 175 copy(dst[:len(result)], result) 176 runtime.KeepAlive(bmc) 177 } 178 179 func (bmc *blockModeCloser) Close() { 180 if bmc.session == nil { 181 return 182 } 183 var result []byte 184 var err error 185 switch bmc.mode { 186 case modeDecrypt: 187 result, err = bmc.session.ctx.DecryptFinal(bmc.session.handle) 188 case modeEncrypt: 189 result, err = bmc.session.ctx.EncryptFinal(bmc.session.handle) 190 } 191 bmc.session = nil 192 bmc.cleanup() 193 if err != nil { 194 panic(err) 195 } 196 // PKCS#11 2.40 s5.2 says that the operation must produce as much output 197 // as possible, so we should never have any left over for CBC. 198 // This could be different for other modes but we don't implement any yet. 199 if len(result) > 0 { 200 panic("nontrivial result from *Final operation") 201 } 202 } 203