github.com/richardwilkes/toolbox@v1.121.0/xcrypto/stream.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package xcrypto
    11  
    12  import (
    13  	"crypto/aes"
    14  	"crypto/cipher"
    15  	"crypto/rand"
    16  	"crypto/rsa"
    17  	"crypto/sha256"
    18  	"io"
    19  
    20  	"github.com/richardwilkes/toolbox/errs"
    21  )
    22  
    23  // EncryptStreamWithPublicKey copies 'in' to 'out', encrypting the bytes along the way. Note that the output stream will
    24  // be larger than the input stream by aes.BlockSize + publicKey.Size() bytes.
    25  func EncryptStreamWithPublicKey(in io.Reader, out io.Writer, publicKey *rsa.PublicKey) error {
    26  	iv := make([]byte, aes.BlockSize)
    27  	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    28  		return errs.Wrap(err)
    29  	}
    30  	encryptionKey := make([]byte, 32) // aes256
    31  	if _, err := io.ReadFull(rand.Reader, encryptionKey); err != nil {
    32  		return errs.Wrap(err)
    33  	}
    34  	encryptedEncryptionKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, encryptionKey, nil)
    35  	if err != nil {
    36  		return errs.Wrap(err)
    37  	}
    38  	block, err := aes.NewCipher(encryptionKey)
    39  	if err != nil {
    40  		return errs.Wrap(err)
    41  	}
    42  	if _, err = out.Write(encryptedEncryptionKey); err != nil {
    43  		return errs.Wrap(err)
    44  	}
    45  	if _, err = out.Write(iv); err != nil {
    46  		return errs.Wrap(err)
    47  	}
    48  	if _, err = io.Copy(&cipher.StreamWriter{
    49  		S: cipher.NewCFBEncrypter(block, iv),
    50  		W: out,
    51  	}, in); err != nil {
    52  		return errs.Wrap(err)
    53  	}
    54  	return nil
    55  }
    56  
    57  // DecryptStreamWithPrivateKey copies 'in' to 'out', decrypting the bytes along the way. Note that the output stream
    58  // will be smaller than the input stream by aes.BlockSize + publicKey.Size() bytes.
    59  func DecryptStreamWithPrivateKey(in io.Reader, out io.Writer, privateKey *rsa.PrivateKey) error {
    60  	encryptedEncryptionKey := make([]byte, privateKey.PublicKey.Size())
    61  	if _, err := in.Read(encryptedEncryptionKey); err != nil {
    62  		return errs.Wrap(err)
    63  	}
    64  	iv := make([]byte, aes.BlockSize)
    65  	if _, err := in.Read(iv); err != nil {
    66  		return errs.Wrap(err)
    67  	}
    68  	encryptionKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedEncryptionKey, nil)
    69  	if err != nil {
    70  		return errs.Wrap(err)
    71  	}
    72  	block, err := aes.NewCipher(encryptionKey)
    73  	if err != nil {
    74  		return errs.Wrap(err)
    75  	}
    76  	if _, err = io.Copy(out, &cipher.StreamReader{
    77  		S: cipher.NewCFBDecrypter(block, iv),
    78  		R: in,
    79  	}); err != nil {
    80  		return errs.Wrap(err)
    81  	}
    82  	return nil
    83  }