github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/caam/sha.go (about)

     1  // NXP Cryptographic Acceleration and Assurance Module (CAAM) driver
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // Copyright (c) WithSecure Corporation
     5  // https://foundry.withsecure.com
     6  //
     7  // Use of this source code is governed by the license
     8  // that can be found in the LICENSE file.
     9  
    10  package caam
    11  
    12  import (
    13  	"crypto/sha256"
    14  	"errors"
    15  	"io"
    16  
    17  	"golang.org/x/sync/semaphore"
    18  )
    19  
    20  // A single CAAM job ring is used for all operations, this entails that only
    21  // one digest state can be kept at any given time.
    22  var sem = semaphore.NewWeighted(1)
    23  
    24  // Hash is the common interface to CAAM hardware backed hash functions.
    25  //
    26  // While similar to Go native hash.Hash, this interface is not fully compatible
    27  // with it as hardware errors must be checked and checksum computation affects
    28  // state.
    29  type Hash interface {
    30  	// Write (via the embedded io.Writer interface) adds more data to the running hash.
    31  	// It can return an error. It returns an error if Sum has been already invoked.
    32  	io.Writer
    33  
    34  	// Sum appends the current hash to b and returns the resulting slice.
    35  	// Its invocation terminates the digest instance, for this reason Write
    36  	// will return errors after Sum is invoked.
    37  	Sum(b []byte) ([]byte, error)
    38  
    39  	// BlockSize returns the hash's underlying block size.
    40  	// The Write method must be able to accept any amount
    41  	// of data, but it may operate more efficiently if all writes
    42  	// are a multiple of the block size.
    43  	BlockSize() int
    44  }
    45  
    46  type digest struct {
    47  	caam *CAAM
    48  	mode int
    49  	n    int
    50  	bs   int
    51  	init bool
    52  	buf  []byte
    53  	sum  []byte
    54  }
    55  
    56  // Write adds more data to the running hash. It returns an error if Sum has
    57  // been already invoked or in case of hardware errors.
    58  //
    59  // There must be sufficient DMA memory allocated to hold the data, otherwise
    60  // the function will panic.
    61  func (d *digest) Write(p []byte) (n int, err error) {
    62  	if len(d.sum) != 0 {
    63  		return 0, errors.New("digest instance can no longer be used")
    64  	}
    65  
    66  	// If we still don't have enough data for a block, accumulate and early
    67  	// out.
    68  	if len(d.buf)+len(p) < d.bs {
    69  		d.buf = append(d.buf, p...)
    70  		return len(p), nil
    71  	}
    72  
    73  	pl := len(p)
    74  
    75  	// top up partial block buffer, and process that
    76  	cut := d.bs - len(d.buf)
    77  	d.buf = append(d.buf, p[:cut]...)
    78  	p = p[cut:]
    79  
    80  	if _, err = d.caam.hash(d.buf, d.mode, d.n, d.init, false); err != nil {
    81  		return
    82  	}
    83  
    84  	if d.init {
    85  		d.init = false
    86  	}
    87  
    88  	// work through any more full blocks in p
    89  	if l := len(p); l > d.bs {
    90  		r := l % d.bs
    91  
    92  		if _, err = d.caam.hash(p[:l-r], d.mode, d.n, d.init, false); err != nil {
    93  			return
    94  		}
    95  
    96  		p = p[l-r:]
    97  	}
    98  
    99  	// save off any partial block remaining
   100  	d.buf = append(d.buf[0:0], p...)
   101  
   102  	return pl, nil
   103  }
   104  
   105  // Sum appends the current hash to in and returns the resulting slice.  Its
   106  // invocation terminates the digest instance, for this reason Write will return
   107  // errors after Sum is invoked.
   108  func (d *digest) Sum(in []byte) (sum []byte, err error) {
   109  	if len(d.sum) != 0 {
   110  		return append(in, d.sum[:]...), nil
   111  	}
   112  
   113  	defer sem.Release(1)
   114  
   115  	if d.init && len(d.buf) == 0 {
   116  		d.sum = sha256.New().Sum(nil)
   117  	} else {
   118  		s, err := d.caam.hash(d.buf, ALG_SHA256, d.n, d.init, true)
   119  
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  
   124  		d.sum = s
   125  	}
   126  
   127  	return append(in, d.sum[:]...), nil
   128  }
   129  
   130  // BlockSize returns the hash's underlying block size.
   131  func (d *digest) BlockSize() int {
   132  	return d.bs
   133  }
   134  
   135  // New256 returns a new Digest computing the SHA256 checksum.
   136  //
   137  // A single CAAM channel is used for all operations, this entails that only one
   138  // digest instance can be kept at any given time, if this condition is not met
   139  // an error is returned.
   140  //
   141  // The digest instance starts with New256() and terminates when when Sum() is
   142  // invoked, after which the digest state can no longer be changed.
   143  func (hw *CAAM) New256() (Hash, error) {
   144  	if !sem.TryAcquire(1) {
   145  		return nil, errors.New("another digest instance is already in use")
   146  	}
   147  
   148  	d := &digest{
   149  		caam: hw,
   150  		mode: ALG_SHA256,
   151  		n:    sha256.Size,
   152  		bs:   sha256.BlockSize,
   153  		init: true,
   154  		buf:  make([]byte, 0, sha256.BlockSize),
   155  	}
   156  
   157  	return d, nil
   158  }
   159  
   160  // Sum256 returns the SHA256 checksum of the data.
   161  //
   162  // There must be sufficient DMA memory allocated to hold the data, otherwise
   163  // the function will panic.
   164  func (hw *CAAM) Sum256(data []byte) (sum [32]byte, err error) {
   165  	s, err := hw.hash(data, ALG_SHA256, len(sum), true, true)
   166  
   167  	if err != nil {
   168  		return
   169  	}
   170  
   171  	copy(sum[:], s)
   172  
   173  	return
   174  }