github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/imx6/dcp/sha.go (about)

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