687 lines
18 KiB
Go
687 lines
18 KiB
Go
package jess
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/safing/structures/container"
|
|
)
|
|
|
|
// Close encrypts (and possibly signs) the given data and returns a Letter. Storyline: Close takes an envelope, inserts the message and closes it, resulting in a letter.
|
|
func (s *Session) Close(data []byte) (*Letter, error) { //nolint:gocognit
|
|
var err error
|
|
var associatedData []byte
|
|
letter := &Letter{}
|
|
|
|
if s.wire == nil || s.wire.msgNo == 0 {
|
|
letter.Version = s.envelope.Version
|
|
letter.SuiteID = s.envelope.SuiteID
|
|
}
|
|
|
|
// Check for additional data in slice, which we should not touch.
|
|
// TODO: Pre-allocate needed overhead for AEAD and others.
|
|
if len(data) != cap(data) {
|
|
// Make a copy of the data in order to not modify unrelated data.
|
|
copiedData := make([]byte, len(data))
|
|
copy(copiedData, data)
|
|
data = copiedData
|
|
}
|
|
|
|
// ==============
|
|
// key management
|
|
// ==============
|
|
|
|
// create nonce
|
|
nonce, err := RandomBytes(s.NonceSize())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get nonce: %w", err)
|
|
}
|
|
letter.Nonce = nonce
|
|
|
|
if s.kdf != nil {
|
|
// if we require a key
|
|
|
|
// key establishment
|
|
if s.wire != nil {
|
|
err = s.wire.sendHandshakeAndInitKDF(letter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
keyMaterial, err := s.setupClosingKeyMaterial(letter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// init KDF
|
|
err = s.kdf.InitKeyDerivation(letter.Nonce, keyMaterial...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to init %s kdf: %w", s.kdf.Info().Name, err)
|
|
}
|
|
}
|
|
|
|
// ==========
|
|
// encryption
|
|
// ==========
|
|
|
|
// setup tools
|
|
err = s.setup()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer s.reset() //nolint:errcheck // TODO: handle error? Currently there should be none.
|
|
|
|
// Ciphers
|
|
for _, tool := range s.ciphers {
|
|
data, err = tool.Encrypt(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to encrypt with %s: %w", tool.Info().Name, err)
|
|
}
|
|
}
|
|
|
|
// build associated data
|
|
if len(s.integratedCiphers) > 0 || len(s.macs) > 0 {
|
|
associatedData = letter.compileAssociatedData()
|
|
}
|
|
|
|
// Integrated Ciphers / AEAD
|
|
for _, tool := range s.integratedCiphers {
|
|
data, err = tool.AuthenticatedEncrypt(data, associatedData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to auth-encrypt with %s: %w", tool.Info().Name, err)
|
|
}
|
|
}
|
|
|
|
if len(s.macs) > 0 {
|
|
// run managed mac hashers
|
|
if s.managedMACHashers != nil {
|
|
err = s.feedManagedHashers(s.managedMACHashers, data, associatedData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer s.resetManagedHashers(s.managedMACHashers)
|
|
}
|
|
|
|
// run MAC tools
|
|
allMacs := container.New()
|
|
for _, tool := range s.macs {
|
|
mac, err := tool.MAC(data, associatedData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to calculate MAC with %s: %w", tool.Info().Name, err)
|
|
}
|
|
allMacs.Append(mac)
|
|
}
|
|
letter.Mac = allMacs.CompileData()
|
|
}
|
|
|
|
} else if len(s.ciphers) > 0 || len(s.integratedCiphers) > 0 || len(s.macs) > 0 {
|
|
// check if there is really nothing to do with a key
|
|
return nil, errors.New("missing a kdf tool")
|
|
}
|
|
|
|
// data processing is complete
|
|
letter.Data = data
|
|
|
|
// Signature
|
|
if len(s.signers) > 0 {
|
|
associatedSigningData := letter.compileAssociatedSigningData(associatedData)
|
|
|
|
// run managed signing hashers
|
|
if s.managedSigningHashers != nil {
|
|
err = s.feedManagedHashers(s.managedSigningHashers, data, associatedSigningData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer s.resetManagedHashers(s.managedSigningHashers)
|
|
}
|
|
|
|
// run signers
|
|
for _, tool := range s.signers {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopSenders(tool.Info().Name, func(signet *Signet) error {
|
|
sig, err := tool.Sign(data, associatedSigningData, signet)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to sign with %s: %w", tool.Info().Name, err)
|
|
}
|
|
|
|
letter.Signatures = append(letter.Signatures, &Seal{
|
|
Scheme: tool.Info().Name,
|
|
ID: signet.ID,
|
|
Value: sig,
|
|
})
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
return letter, nil
|
|
}
|
|
|
|
// Open decrypts (and possibly verifies) the given letter and returns the original data. Storyline: Open takes a letter, checks any seals, opens it and returns the message.
|
|
func (s *Session) Open(letter *Letter) ([]byte, error) { //nolint:gocognit,gocyclo
|
|
|
|
// debugging:
|
|
/*
|
|
fmt.Printf("opening: %+v\n", letter)
|
|
for _, seal := range letter.Keys {
|
|
fmt.Printf("key: %+v\n", seal)
|
|
}
|
|
*/
|
|
|
|
var err error
|
|
if s.wire == nil && letter.Version != 1 {
|
|
return nil, fmt.Errorf("unsupported letter version: %d", letter.Version)
|
|
}
|
|
|
|
// ======
|
|
// verify
|
|
// ======
|
|
|
|
// TODO: signature verification is run before tool setup. Currently, this is ok, but might change in the future. This might break additional signing algorithms that actually need setup.
|
|
|
|
data := letter.Data
|
|
|
|
// build associated data
|
|
var associatedData []byte
|
|
if len(s.integratedCiphers) > 0 || len(s.macs) > 0 {
|
|
associatedData = letter.compileAssociatedData()
|
|
}
|
|
|
|
// Signature
|
|
if len(s.signers) > 0 {
|
|
associatedSigningData := letter.compileAssociatedSigningData(associatedData)
|
|
|
|
// run managed signing hashers
|
|
if s.managedSigningHashers != nil {
|
|
err = s.feedManagedHashers(s.managedSigningHashers, data, associatedSigningData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer s.resetManagedHashers(s.managedSigningHashers)
|
|
}
|
|
|
|
// run signers
|
|
if len(s.envelope.Senders) != len(letter.Signatures) {
|
|
return nil, errors.New("mismatch regarding available signatures and senders")
|
|
}
|
|
sigIndex := 0
|
|
|
|
for _, tool := range s.signers {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopSenders(tool.Info().Name, func(signet *Signet) error {
|
|
err := tool.Verify(data, associatedSigningData, letter.Signatures[sigIndex].Value, signet)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to verify signature (%s) with ID %s: %w", tool.Info().Name, letter.Signatures[sigIndex].ID, err)
|
|
}
|
|
|
|
sigIndex++
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
// end early if we are only verifying sigs
|
|
if s.kdf == nil {
|
|
// check if there is really nothing to do with a key
|
|
if len(s.ciphers) > 0 || len(s.integratedCiphers) > 0 || len(s.macs) > 0 {
|
|
return nil, errors.New("missing a kdf tool")
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// ==============
|
|
// key management
|
|
// ==============
|
|
|
|
// key establishment
|
|
if s.wire != nil {
|
|
err = s.wire.recvHandshakeAndInitKDF(letter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
keyMaterial, err := s.setupOpeningKeyMaterial(letter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// init KDF
|
|
err = s.kdf.InitKeyDerivation(letter.Nonce, keyMaterial...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to init %s kdf: %w", s.kdf.Info().Name, err)
|
|
}
|
|
}
|
|
|
|
// ==========
|
|
// decryption
|
|
// ==========
|
|
|
|
// setup tools
|
|
err = s.setup()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer s.reset() //nolint:errcheck // TODO: handle error? Currently there should be none.
|
|
|
|
// MAC
|
|
if len(s.macs) > 0 {
|
|
// run managed mac hashers
|
|
if s.managedMACHashers != nil {
|
|
err = s.feedManagedHashers(s.managedMACHashers, data, associatedData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer s.resetManagedHashers(s.managedMACHashers)
|
|
}
|
|
|
|
// run MAC tools
|
|
allMacs := container.New()
|
|
for _, tool := range s.macs {
|
|
mac, err := tool.MAC(data, associatedData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to calculate MAC with %s: %w", tool.Info().Name, err)
|
|
}
|
|
allMacs.Append(mac)
|
|
}
|
|
if subtle.ConstantTimeCompare(letter.Mac, allMacs.CompileData()) != 1 {
|
|
return nil, fmt.Errorf("%w: MAC verification failed", ErrIntegrityViolation)
|
|
}
|
|
}
|
|
|
|
// Integrated Ciphers / AEAD (in reversed order)
|
|
for i := len(s.integratedCiphers) - 1; i >= 0; i-- {
|
|
data, err = s.integratedCiphers[i].AuthenticatedDecrypt(data, associatedData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: [%s] %w", ErrIntegrityViolation, s.integratedCiphers[i].Info().Name, err)
|
|
}
|
|
}
|
|
|
|
// Ciphers (in reversed order)
|
|
for i := len(s.ciphers) - 1; i >= 0; i-- {
|
|
data, err = s.ciphers[i].Decrypt(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: decryption failed: [%s] %w", ErrIntegrityViolation, s.ciphers[i].Info().Name, err)
|
|
}
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// Verify verifies signatures of the given letter.
|
|
func (s *Session) Verify(letter *Letter) error {
|
|
// debugging:
|
|
/*
|
|
fmt.Printf("opening: %+v\n", letter)
|
|
for _, sig := range letter.Signatures {
|
|
fmt.Printf("sig: %+v\n", sig)
|
|
}
|
|
*/
|
|
|
|
var err error
|
|
if s.wire == nil && letter.Version != 1 {
|
|
return fmt.Errorf("unsupported letter version: %d", letter.Version)
|
|
}
|
|
|
|
// ======
|
|
// verify
|
|
// ======
|
|
|
|
// TODO: signature verification is run before tool setup. Currently, this is ok, but might change in the future. This might break additional signing algorithms that actually need setup.
|
|
|
|
data := letter.Data
|
|
|
|
// build associated data
|
|
var associatedData []byte
|
|
if len(s.integratedCiphers) > 0 || len(s.macs) > 0 {
|
|
associatedData = letter.compileAssociatedData()
|
|
}
|
|
|
|
// Signature
|
|
if len(s.signers) > 0 {
|
|
associatedSigningData := letter.compileAssociatedSigningData(associatedData)
|
|
|
|
// run managed signing hashers
|
|
if s.managedSigningHashers != nil {
|
|
err = s.feedManagedHashers(s.managedSigningHashers, data, associatedSigningData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer s.resetManagedHashers(s.managedSigningHashers)
|
|
}
|
|
|
|
// run signers
|
|
if len(s.envelope.Senders) != len(letter.Signatures) {
|
|
return errors.New("mismatch regarding available signatures and senders")
|
|
}
|
|
sigIndex := 0
|
|
|
|
for _, tool := range s.signers {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopSenders(tool.Info().Name, func(signet *Signet) error {
|
|
err := tool.Verify(data, associatedSigningData, letter.Signatures[sigIndex].Value, signet)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to verify signature (%s) with ID %s: %w", tool.Info().Name, letter.Signatures[sigIndex].ID, err)
|
|
}
|
|
|
|
sigIndex++
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
return errors.New("no signatures to verify")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Session) setupClosingKeyMaterial(letter *Letter) ([][]byte, error) {
|
|
signetsUsed := 0
|
|
var keyMaterial [][]byte
|
|
|
|
// add raw keys
|
|
_ = s.envelope.LoopSecrets(SignetSchemeKey, func(signet *Signet) error {
|
|
letter.Keys = append(letter.Keys, &Seal{
|
|
Scheme: SignetSchemeKey,
|
|
ID: signet.ID,
|
|
})
|
|
|
|
keyMaterial = append(keyMaterial, signet.Key)
|
|
signetsUsed++
|
|
return nil
|
|
})
|
|
|
|
// add passwords
|
|
err := s.envelope.LoopSecrets(SignetSchemePassword, func(signet *Signet) error {
|
|
if len(signet.Key) == 0 {
|
|
return fmt.Errorf("signet [%s] is missing it's password", signet.ID)
|
|
}
|
|
pwKey, err := s.passDerivator.DeriveKeyFromPassword(signet.Key, letter.Nonce)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get derive key from password with %s: %w", s.passDerivator.Info().Name, err)
|
|
}
|
|
letter.Keys = append(letter.Keys, &Seal{
|
|
Scheme: SignetSchemePassword,
|
|
ID: signet.ID,
|
|
})
|
|
|
|
keyMaterial = append(keyMaterial, pwKey)
|
|
signetsUsed++
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// add key exchange
|
|
for _, tool := range s.keyExchangers {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopRecipients(tool.Info().Name, func(recipient *Signet) error {
|
|
// generate new sender exchange signet
|
|
senderSignet := NewSignetBase(tool.Definition())
|
|
err := senderSignet.GenerateKey()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate new sender signet for %s: %w", tool.Info().Name, err)
|
|
}
|
|
|
|
// create exchange and add to letter
|
|
exchKey, err := tool.MakeSharedKey(senderSignet, recipient)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to make managed key with %s: %w", tool.Info().Name, err)
|
|
}
|
|
|
|
// add to letter
|
|
senderRcpt, err := senderSignet.AsRecipient() // convert to public signet
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get public sender signet for %s: %w", tool.Info().Name, err)
|
|
}
|
|
err = senderRcpt.StoreKey()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to serialize sender public key for %s: %w", tool.Info().Name, err)
|
|
}
|
|
letter.Keys = append(letter.Keys, &Seal{
|
|
ID: recipient.ID,
|
|
Value: senderRcpt.Key,
|
|
})
|
|
|
|
// save sender signet to state (or burn)
|
|
if s.wire == nil {
|
|
_ = senderSignet.Burn()
|
|
} else {
|
|
s.wire.eKXSignets = append(s.wire.eKXSignets, &kxPair{
|
|
tool: tool,
|
|
signet: senderSignet,
|
|
})
|
|
}
|
|
|
|
// add key
|
|
keyMaterial = append(keyMaterial, exchKey)
|
|
return nil
|
|
})
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// add key encapsulation
|
|
for _, tool := range s.keyEncapsulators {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopRecipients(tool.Info().Name, func(recipient *Signet) error {
|
|
// save to state
|
|
if s.wire != nil {
|
|
s.wire.eKESignets = append(s.wire.eKESignets, &kePair{
|
|
tool: tool,
|
|
})
|
|
}
|
|
|
|
// generate new key
|
|
newKey, err := RandomBytes(tool.Helper().DefaultSymmetricKeySize())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate new key for %s: %w", tool.Info().Name, err)
|
|
}
|
|
|
|
// encapsulate key
|
|
wrappedKey, err := tool.EncapsulateKey(newKey, recipient)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to encapsulate key with %s: %w", tool.Info().Name, err)
|
|
}
|
|
|
|
// add to letter
|
|
letter.Keys = append(letter.Keys, &Seal{
|
|
ID: recipient.ID,
|
|
Value: wrappedKey,
|
|
})
|
|
|
|
// add key
|
|
keyMaterial = append(keyMaterial, newKey)
|
|
return nil
|
|
})
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return keyMaterial, nil
|
|
}
|
|
|
|
func (s *Session) setupOpeningKeyMaterial(letter *Letter) ([][]byte, error) {
|
|
// Hint: Signets are loaded from the seals in the letter, so the order will always match.
|
|
|
|
var keyMaterial [][]byte
|
|
sealIndex := 0
|
|
|
|
// sanity check
|
|
if s.wire == nil {
|
|
// TODO:
|
|
// initial wire handshake is special:
|
|
// key encapsulators send two seals in the initial handshake messages
|
|
// one of them is added to the recipients
|
|
// the other is a new ephermal key
|
|
if len(s.envelope.Secrets)+
|
|
len(s.envelope.Senders)+
|
|
len(s.envelope.Recipients) < len(letter.Keys) {
|
|
return nil, fmt.Errorf("missing Keys in letter")
|
|
}
|
|
}
|
|
|
|
// add raw keys
|
|
_ = s.envelope.LoopSecrets(SignetSchemeKey, func(signet *Signet) error {
|
|
keyMaterial = append(keyMaterial, signet.Key)
|
|
sealIndex++ // basically just skip, because key has to be loaded from the Signet anyway
|
|
return nil
|
|
})
|
|
|
|
// add passwords
|
|
err := s.envelope.LoopSecrets(SignetSchemePassword, func(signet *Signet) error {
|
|
if len(signet.Key) == 0 {
|
|
return fmt.Errorf("signet [%s] is missing it's password", signet.ID)
|
|
}
|
|
pwKey, err := s.passDerivator.DeriveKeyFromPassword(signet.Key, letter.Nonce)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get derive key from password with %s: %w", s.passDerivator.Info().Name, err)
|
|
}
|
|
|
|
keyMaterial = append(keyMaterial, pwKey)
|
|
sealIndex++ // basically just skip, because password has to be loaded from the Signet anyway
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// add key exchange
|
|
for _, tool := range s.keyExchangers {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopRecipients(tool.Info().Name, func(signet *Signet) error {
|
|
// get senderRcpt
|
|
peerSignet := &Signet{
|
|
Version: letter.Version,
|
|
tool: tool.Definition(),
|
|
Key: letter.Keys[sealIndex].Value,
|
|
Public: true,
|
|
}
|
|
sealIndex++
|
|
// load key
|
|
err := peerSignet.LoadKey()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load ephermal signet for key exchange: %w", err)
|
|
}
|
|
// save to state
|
|
if s.wire != nil {
|
|
s.wire.eKXSignets = append(s.wire.eKXSignets, &kxPair{
|
|
tool: tool,
|
|
peer: peerSignet,
|
|
})
|
|
}
|
|
|
|
// make shared key
|
|
exchKey, err := tool.MakeSharedKey(signet, peerSignet)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to make shared key with %s: %w", tool.Info().Name, err)
|
|
}
|
|
|
|
// add key
|
|
keyMaterial = append(keyMaterial, exchKey)
|
|
return nil
|
|
})
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// add key encapsulation
|
|
for _, tool := range s.keyEncapsulators {
|
|
//nolint:scopelint // function is executed immediately within loop
|
|
err = s.envelope.LoopRecipients(tool.Info().Name, func(signet *Signet) error {
|
|
// save to state
|
|
if s.wire != nil {
|
|
s.wire.eKESignets = append(s.wire.eKESignets, &kePair{
|
|
tool: tool,
|
|
})
|
|
}
|
|
|
|
unwrappedKey, err := tool.UnwrapKey(letter.Keys[sealIndex].Value, signet)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sealIndex++
|
|
|
|
// add key
|
|
keyMaterial = append(keyMaterial, unwrappedKey)
|
|
return nil
|
|
})
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return keyMaterial, nil
|
|
}
|
|
|
|
// setup runs the setup function on all tools.
|
|
func (s *Session) setup() error {
|
|
for _, tool := range s.toolsWithState {
|
|
err := tool.Setup()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to run tool %s setup: %w", tool.Info().Name, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// reset runs the reset function on all tools and managed hashers.
|
|
func (s *Session) reset() error {
|
|
// reset all tools
|
|
for _, tool := range s.toolsWithState {
|
|
err := tool.Reset()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to run tool %s reset: %w", tool.Info().Name, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Session) feedManagedHashers(managedHashers map[string]*managedHasher, data, associatedData []byte) error {
|
|
for _, mngdHasher := range managedHashers {
|
|
n, err := mngdHasher.hash.Write(data)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write data to managed hasher %s: %w", mngdHasher.tool.Name, err)
|
|
}
|
|
if n != len(data) {
|
|
return fmt.Errorf("failed to fully write data to managed hasher %s", mngdHasher.tool.Name)
|
|
}
|
|
|
|
n, err = mngdHasher.hash.Write(associatedData)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write associated data to managed hasher %s: %w", mngdHasher.tool.Name, err)
|
|
}
|
|
if n != len(associatedData) {
|
|
return fmt.Errorf("failed to fully write associated data to managed hasher %s", mngdHasher.tool.Name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Session) resetManagedHashers(managedHashers map[string]*managedHasher) {
|
|
for _, mngdHasher := range managedHashers {
|
|
mngdHasher.hash.Reset()
|
|
}
|
|
}
|