mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-26 20:20:56 +00:00
300 lines
9 KiB
Go
300 lines
9 KiB
Go
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp"
|
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type Reader interface {
|
|
Read(b []byte) (n int, err error)
|
|
}
|
|
|
|
type Writer interface {
|
|
Write(b []byte) (n int, err error)
|
|
}
|
|
|
|
type WriteCloser interface {
|
|
Write(b []byte) (n int, err error)
|
|
Close() (err error)
|
|
}
|
|
|
|
type PlainMessageMetadata struct {
|
|
IsBinary bool
|
|
Filename string
|
|
ModTime int64
|
|
}
|
|
|
|
func NewPlainMessageMetadata(isBinary bool, filename string, modTime int64) *PlainMessageMetadata {
|
|
return &PlainMessageMetadata{IsBinary: isBinary, Filename: filename, ModTime: modTime}
|
|
}
|
|
|
|
// EncryptStream is used to encrypt data as a Writer.
|
|
// It takes a writer for the encrypted data and returns a WriteCloser for the plaintext data
|
|
// If signKeyRing is not nil, it is used to do an embedded signature.
|
|
func (keyRing *KeyRing) EncryptStream(
|
|
pgpMessageWriter Writer,
|
|
plainMessageMetadata *PlainMessageMetadata,
|
|
signKeyRing *KeyRing,
|
|
) (plainMessageWriter WriteCloser, err error) {
|
|
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
|
|
|
if plainMessageMetadata == nil {
|
|
// Use sensible default metadata
|
|
plainMessageMetadata = &PlainMessageMetadata{
|
|
IsBinary: true,
|
|
Filename: "",
|
|
ModTime: GetUnixTime(),
|
|
}
|
|
}
|
|
|
|
hints := &openpgp.FileHints{
|
|
FileName: plainMessageMetadata.Filename,
|
|
IsBinary: plainMessageMetadata.IsBinary,
|
|
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
|
|
}
|
|
|
|
plainMessageWriter, err = asymmetricEncryptStream(hints, pgpMessageWriter, pgpMessageWriter, keyRing, signKeyRing, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return plainMessageWriter, nil
|
|
}
|
|
|
|
// EncryptSplitResult is used to wrap the encryption writecloser while storing the key packet.
|
|
type EncryptSplitResult struct {
|
|
isClosed bool
|
|
keyPacketBuf *bytes.Buffer
|
|
keyPacket []byte
|
|
plainMessageWriter WriteCloser // The writer to writer plaintext data in.
|
|
}
|
|
|
|
func (res *EncryptSplitResult) Write(b []byte) (n int, err error) {
|
|
return res.plainMessageWriter.Write(b)
|
|
}
|
|
|
|
func (res *EncryptSplitResult) Close() (err error) {
|
|
err = res.plainMessageWriter.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res.isClosed = true
|
|
res.keyPacket = res.keyPacketBuf.Bytes()
|
|
return nil
|
|
}
|
|
|
|
// GetKeyPacket returns the Public-Key Encrypted Session Key Packets (https://datatracker.ietf.org/doc/html/rfc4880#section-5.1).
|
|
// This can be retrieved only after the message has been fully written and the writer is closed.
|
|
func (res *EncryptSplitResult) GetKeyPacket() (keyPacket []byte, err error) {
|
|
if !res.isClosed {
|
|
return nil, errors.New("gopenpgp: can't access key packet until the message writer has been closed")
|
|
}
|
|
return res.keyPacket, nil
|
|
}
|
|
|
|
// EncryptSplitStream is used to encrypt data as a stream.
|
|
// It takes a writer for the Symmetrically Encrypted Data Packet
|
|
// (https://datatracker.ietf.org/doc/html/rfc4880#section-5.7)
|
|
// and returns a writer for the plaintext data and the key packet.
|
|
// If signKeyRing is not nil, it is used to do an embedded signature.
|
|
func (keyRing *KeyRing) EncryptSplitStream(
|
|
dataPacketWriter Writer,
|
|
plainMessageMetadata *PlainMessageMetadata,
|
|
signKeyRing *KeyRing,
|
|
) (*EncryptSplitResult, error) {
|
|
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
|
|
|
|
if plainMessageMetadata == nil {
|
|
// Use sensible default metadata
|
|
plainMessageMetadata = &PlainMessageMetadata{
|
|
IsBinary: true,
|
|
Filename: "",
|
|
ModTime: GetUnixTime(),
|
|
}
|
|
}
|
|
|
|
hints := &openpgp.FileHints{
|
|
FileName: plainMessageMetadata.Filename,
|
|
IsBinary: plainMessageMetadata.IsBinary,
|
|
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
|
|
}
|
|
|
|
var keyPacketBuf bytes.Buffer
|
|
plainMessageWriter, err := asymmetricEncryptStream(hints, &keyPacketBuf, dataPacketWriter, keyRing, signKeyRing, config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &EncryptSplitResult{
|
|
keyPacketBuf: &keyPacketBuf,
|
|
plainMessageWriter: plainMessageWriter,
|
|
}, nil
|
|
}
|
|
|
|
// PlainMessageReader is used to wrap the data of the decrypted plain message.
|
|
// It can be used to read the decrypted data and verify the embedded signature.
|
|
type PlainMessageReader struct {
|
|
details *openpgp.MessageDetails
|
|
verifyKeyRing *KeyRing
|
|
verifyTime int64
|
|
readAll bool
|
|
}
|
|
|
|
// GetMetadata returns the metadata of the decrypted message.
|
|
func (msg *PlainMessageReader) GetMetadata() *PlainMessageMetadata {
|
|
return &PlainMessageMetadata{
|
|
Filename: msg.details.LiteralData.FileName,
|
|
IsBinary: msg.details.LiteralData.IsBinary,
|
|
ModTime: int64(msg.details.LiteralData.Time),
|
|
}
|
|
}
|
|
|
|
// Read is used to access the message decrypted data.
|
|
// Makes PlainMessageReader implement the Reader interface.
|
|
func (msg *PlainMessageReader) Read(b []byte) (n int, err error) {
|
|
n, err = msg.details.UnverifiedBody.Read(b)
|
|
if errors.Is(err, io.EOF) {
|
|
msg.readAll = true
|
|
}
|
|
return
|
|
}
|
|
|
|
// VerifySignature is used to verify that the signature is valid.
|
|
// This method needs to be called once all the data has been read.
|
|
// It will return an error if the signature is invalid
|
|
// or if the message hasn't been read entirely.
|
|
func (msg *PlainMessageReader) VerifySignature() (err error) {
|
|
if !msg.readAll {
|
|
return errors.New("gopenpgp: can't verify the signature until the message reader has been read entirely")
|
|
}
|
|
if msg.verifyKeyRing != nil {
|
|
processSignatureExpiration(msg.details, msg.verifyTime)
|
|
err = verifyDetailsSignature(msg.details, msg.verifyKeyRing)
|
|
} else {
|
|
err = errors.New("gopenpgp: no verify keyring was provided before decryption")
|
|
}
|
|
return
|
|
}
|
|
|
|
// DecryptStream is used to decrypt a pgp message as a Reader.
|
|
// It takes a reader for the message data
|
|
// and returns a PlainMessageReader for the plaintext data.
|
|
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
|
|
// verify the embedded signature with the given key ring and verification time.
|
|
func (keyRing *KeyRing) DecryptStream(
|
|
message Reader,
|
|
verifyKeyRing *KeyRing,
|
|
verifyTime int64,
|
|
) (plainMessage *PlainMessageReader, err error) {
|
|
messageDetails, err := asymmetricDecryptStream(
|
|
message,
|
|
keyRing,
|
|
verifyKeyRing,
|
|
verifyTime,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &PlainMessageReader{
|
|
messageDetails,
|
|
verifyKeyRing,
|
|
verifyTime,
|
|
false,
|
|
}, err
|
|
}
|
|
|
|
// DecryptSplitStream is used to decrypt a split pgp message as a Reader.
|
|
// It takes a key packet and a reader for the data packet
|
|
// and returns a PlainMessageReader for the plaintext data.
|
|
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
|
|
// verify the embedded signature with the given key ring and verification time.
|
|
func (keyRing *KeyRing) DecryptSplitStream(
|
|
keypacket []byte,
|
|
dataPacketReader Reader,
|
|
verifyKeyRing *KeyRing, verifyTime int64,
|
|
) (plainMessage *PlainMessageReader, err error) {
|
|
messageReader := io.MultiReader(
|
|
bytes.NewReader(keypacket),
|
|
dataPacketReader,
|
|
)
|
|
return keyRing.DecryptStream(
|
|
messageReader,
|
|
verifyKeyRing,
|
|
verifyTime,
|
|
)
|
|
}
|
|
|
|
// SignDetachedStream generates and returns a PGPSignature for a given message Reader.
|
|
func (keyRing *KeyRing) SignDetachedStream(message Reader) (*PGPSignature, error) {
|
|
signEntity, err := keyRing.getSigningEntity()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
config := &packet.Config{DefaultHash: crypto.SHA512, Time: getTimeGenerator()}
|
|
var outBuf bytes.Buffer
|
|
// sign bin
|
|
if err := openpgp.DetachSign(&outBuf, signEntity, message, config); err != nil {
|
|
return nil, errors.Wrap(err, "gopenpgp: error in signing")
|
|
}
|
|
|
|
return NewPGPSignature(outBuf.Bytes()), nil
|
|
}
|
|
|
|
// VerifyDetachedStream verifies a message reader with a detached PGPSignature
|
|
// and returns a SignatureVerificationError if fails.
|
|
func (keyRing *KeyRing) VerifyDetachedStream(
|
|
message Reader,
|
|
signature *PGPSignature,
|
|
verifyTime int64,
|
|
) error {
|
|
return verifySignature(
|
|
keyRing.entities,
|
|
message,
|
|
signature.GetBinary(),
|
|
verifyTime,
|
|
)
|
|
}
|
|
|
|
// SignDetachedEncryptedStream generates and returns a PGPMessage
|
|
// containing an encrypted detached signature for a given message Reader.
|
|
func (keyRing *KeyRing) SignDetachedEncryptedStream(
|
|
message Reader,
|
|
encryptionKeyRing *KeyRing,
|
|
) (encryptedSignature *PGPMessage, err error) {
|
|
if encryptionKeyRing == nil {
|
|
return nil, errors.New("gopenpgp: no encryption key ring provided")
|
|
}
|
|
signature, err := keyRing.SignDetachedStream(message)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
plainMessage := NewPlainMessage(signature.GetBinary())
|
|
encryptedSignature, err = encryptionKeyRing.Encrypt(plainMessage, nil)
|
|
return
|
|
}
|
|
|
|
// VerifyDetachedEncryptedStream verifies a PlainMessage
|
|
// with a PGPMessage containing an encrypted detached signature
|
|
// and returns a SignatureVerificationError if fails.
|
|
func (keyRing *KeyRing) VerifyDetachedEncryptedStream(
|
|
message Reader,
|
|
encryptedSignature *PGPMessage,
|
|
decryptionKeyRing *KeyRing,
|
|
verifyTime int64,
|
|
) error {
|
|
if decryptionKeyRing == nil {
|
|
return errors.New("gopenpgp: no decryption key ring provided")
|
|
}
|
|
plainMessage, err := decryptionKeyRing.Decrypt(encryptedSignature, nil, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
signature := NewPGPSignature(plainMessage.GetBinary())
|
|
return keyRing.VerifyDetachedStream(message, signature, verifyTime)
|
|
}
|