...

Source file src/github.com/ThalesIgnite/crypto11/blockmode.go

Documentation: github.com/ThalesIgnite/crypto11

     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  

View as plain text