passforios-gopenpgp/crypto/message.go

362 lines
10 KiB
Go
Raw Normal View History

package crypto
2018-06-04 16:05:14 -07:00
import (
"bytes"
"encoding/base64"
"errors"
2019-05-13 12:42:29 +00:00
"fmt"
2018-11-22 10:53:14 +01:00
"io"
2018-06-04 16:05:14 -07:00
"io/ioutil"
"regexp"
"runtime"
2018-06-04 16:05:14 -07:00
"github.com/ProtonMail/gopenpgp/armor"
2019-05-13 14:07:18 +02:00
"github.com/ProtonMail/gopenpgp/constants"
"github.com/ProtonMail/gopenpgp/internal"
"golang.org/x/crypto/openpgp/clearsign"
"golang.org/x/crypto/openpgp/packet"
)
2018-06-04 16:05:14 -07:00
// ---- MODELS -----
2018-11-09 02:03:19 +01:00
// PlainMessage stores an unencrypted message.
type PlainMessage struct {
// The content of the message
Data []byte
// if the content is text or binary
TextType bool
2018-06-04 16:05:14 -07:00
}
// PGPMessage stores a PGP-encrypted message.
type PGPMessage struct {
// The content of the message
Data []byte
}
2018-06-04 16:05:14 -07:00
// PGPSignature stores a PGP-encoded detached signature.
type PGPSignature struct {
// The content of the signature
Data []byte
2018-06-04 16:05:14 -07:00
}
// PGPSplitMessage contains a separate session key packet and symmetrically
// encrypted data packet.
type PGPSplitMessage struct {
DataPacket []byte
KeyPacket []byte
}
2018-06-04 16:05:14 -07:00
// ClearTextMessage, split signed clear text message container
type ClearTextMessage struct {
Data []byte
Signature []byte
}
// ---- GENERATORS -----
2018-06-04 16:05:14 -07:00
// NewPlainMessage generates a new binary PlainMessage ready for encryption,
// signature, or verification from the unencrypted binary data.
func NewPlainMessage(data []byte) *PlainMessage {
return &PlainMessage{
Data: data,
TextType: false,
}
}
// NewPlainMessageFromString generates a new text PlainMessage,
// ready for encryption, signature, or verification from an unencrypted string.
func NewPlainMessageFromString(text string) *PlainMessage {
return &PlainMessage{
Data: []byte(text),
TextType: true,
}
}
// NewPGPMessage generates a new PGPMessage from the unarmored binary data.
func NewPGPMessage(data []byte) *PGPMessage {
return &PGPMessage{
Data: data,
}
}
// NewPGPMessageFromArmored generates a new PGPMessage from an armored string ready for decryption.
func NewPGPMessageFromArmored(armored string) (*PGPMessage, error) {
encryptedIO, err := internal.Unarmor(armored)
if err != nil {
return nil, err
}
2018-06-04 16:05:14 -07:00
message, err := ioutil.ReadAll(encryptedIO.Body)
2018-06-04 16:05:14 -07:00
if err != nil {
return nil, err
}
return &PGPMessage{
Data: message,
}, nil
2018-06-04 16:05:14 -07:00
}
// NewPGPSplitMessage generates a new PGPSplitMessage from the binary unarmored keypacket,
// datapacket, and encryption algorithm.
func NewPGPSplitMessage(keyPacket []byte, dataPacket []byte) *PGPSplitMessage {
return &PGPSplitMessage{
KeyPacket: keyPacket,
DataPacket: dataPacket,
}
}
// NewPGPSplitMessageFromArmored generates a new PGPSplitMessage by splitting an armored message into its
// session key packet and symmetrically encrypted data packet.
func NewPGPSplitMessageFromArmored(encrypted string) (*PGPSplitMessage, error) {
message, err := NewPGPMessageFromArmored(encrypted)
2018-11-09 02:03:19 +01:00
if err != nil {
return nil, err
2018-11-09 02:03:19 +01:00
}
return message.SeparateKeyAndData(len(encrypted), -1)
}
// NewPGPSignature generates a new PGPSignature from the unarmored binary data.
func NewPGPSignature(data []byte) *PGPSignature {
return &PGPSignature{
Data: data,
2018-11-09 02:03:19 +01:00
}
}
// NewPGPSignatureFromArmored generates a new PGPSignature from the armored string ready for verification.
func NewPGPSignatureFromArmored(armored string) (*PGPSignature, error) {
encryptedIO, err := internal.Unarmor(armored)
2018-06-04 16:05:14 -07:00
if err != nil {
return nil, err
2018-06-04 16:05:14 -07:00
}
signature, err := ioutil.ReadAll(encryptedIO.Body)
2018-11-09 02:03:19 +01:00
if err != nil {
return nil, err
2018-11-09 02:03:19 +01:00
}
return &PGPSignature{
Data: signature,
}, nil
2018-06-04 16:05:14 -07:00
}
// NewClearTextMessage generates a new ClearTextMessage from data and signature
func NewClearTextMessage(data []byte, signature []byte) *ClearTextMessage {
return &ClearTextMessage{
Data: data,
Signature: signature,
}
}
// NewClearTextMessageFromArmored returns the message body and unarmored signature from a clearsigned message.
func NewClearTextMessageFromArmored(signedMessage string) (*ClearTextMessage, error) {
modulusBlock, rest := clearsign.Decode([]byte(signedMessage))
if len(rest) != 0 {
return nil, errors.New("pmapi: extra data after modulus")
}
signature, err := ioutil.ReadAll(modulusBlock.ArmoredSignature.Body)
if err != nil {
return nil, err
}
return NewClearTextMessage(modulusBlock.Bytes, signature), nil
}
// ---- MODEL METHODS -----
2018-06-04 16:05:14 -07:00
// GetBinary returns the binary content of the message as a []byte
func (msg *PlainMessage) GetBinary() []byte {
return msg.Data
}
2018-06-04 16:05:14 -07:00
// GetString returns the content of the message as a string
func (msg *PlainMessage) GetString() string {
return string(msg.Data)
}
2018-06-04 16:05:14 -07:00
// GetBase64 returns the base-64 encoded binary content of the message as a string
func (msg *PlainMessage) GetBase64() string {
return base64.StdEncoding.EncodeToString(msg.Data)
}
2018-06-04 16:05:14 -07:00
// NewReader returns a New io.Reader for the bianry data of the message
func (msg *PlainMessage) NewReader() io.Reader {
return bytes.NewReader(msg.GetBinary())
}
// IsText returns whether the message is a text message
func (msg *PlainMessage) IsText() bool {
return msg.TextType
}
// IsBinary returns whether the message is a binary message
func (msg *PlainMessage) IsBinary() bool {
return !msg.TextType
}
// GetBinary returns the unarmored binary content of the message as a []byte
func (msg *PGPMessage) GetBinary() []byte {
return msg.Data
}
// NewReader returns a New io.Reader for the unarmored bianry data of the message
func (msg *PGPMessage) NewReader() io.Reader {
return bytes.NewReader(msg.GetBinary())
}
// GetArmored returns the armored message as a string
func (msg *PGPMessage) GetArmored() (string, error) {
return armor.ArmorWithType(msg.Data, constants.PGPMessageHeader)
}
// GetDataPacket returns the unarmored binary datapacket as a []byte
func (msg *PGPSplitMessage) GetDataPacket() []byte {
return msg.DataPacket
}
2018-11-21 23:44:33 +01:00
// GetKeyPacket returns the unarmored binary keypacket as a []byte
func (msg *PGPSplitMessage) GetKeyPacket() []byte {
return msg.KeyPacket
}
// SeparateKeyAndData returns the first keypacket and the (hopefully unique) dataPacket (not verified)
func (msg *PGPMessage) SeparateKeyAndData(estimatedLength, garbageCollector int) (outSplit *PGPSplitMessage, err error) {
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
packets := packet.NewReader(bytes.NewReader(msg.Data))
outSplit = &PGPSplitMessage{}
gcCounter := 0
// Store encrypted key and symmetrically encrypted packet separately
var encryptedKey *packet.EncryptedKey
var decryptErr error
for {
var p packet.Packet
if p, err = packets.Next(); err == io.EOF {
err = nil
break
}
switch p := p.(type) {
case *packet.EncryptedKey:
if encryptedKey != nil && encryptedKey.Key != nil {
break
}
encryptedKey = p
case *packet.SymmetricallyEncrypted:
// FIXME: add support for multiple keypackets
var b bytes.Buffer
// 2^16 is an estimation of the size difference between input and output, the size difference is most probably
// 16 bytes at a maximum though.
// We need to avoid triggering a grow from the system as this will allocate too much memory causing problems
// in low-memory environments
b.Grow(1<<16 + estimatedLength)
// empty encoded length + start byte
b.Write(make([]byte, 6))
b.WriteByte(byte(1))
actualLength := 1
block := make([]byte, 128)
for {
n, err := p.Contents.Read(block)
if err == io.EOF {
break
}
b.Write(block[:n])
actualLength += n
gcCounter += n
if gcCounter > garbageCollector && garbageCollector > 0 {
runtime.GC()
gcCounter = 0
}
}
2018-11-21 23:44:33 +01:00
// quick encoding
symEncryptedData := b.Bytes()
if actualLength < 192 {
symEncryptedData[4] = byte(210)
symEncryptedData[5] = byte(actualLength)
symEncryptedData = symEncryptedData[4:]
} else if actualLength < 8384 {
actualLength = actualLength - 192
symEncryptedData[3] = byte(210)
symEncryptedData[4] = 192 + byte(actualLength>>8)
symEncryptedData[5] = byte(actualLength)
symEncryptedData = symEncryptedData[3:]
} else {
symEncryptedData[0] = byte(210)
symEncryptedData[1] = byte(255)
symEncryptedData[2] = byte(actualLength >> 24)
symEncryptedData[3] = byte(actualLength >> 16)
symEncryptedData[4] = byte(actualLength >> 8)
symEncryptedData[5] = byte(actualLength)
}
outSplit.DataPacket = symEncryptedData
}
}
if decryptErr != nil {
return nil, fmt.Errorf("gopenpgp: cannot decrypt encrypted key packet: %v", decryptErr)
}
if encryptedKey == nil {
return nil, errors.New("gopenpgp: packets don't include an encrypted key packet")
2018-11-21 23:44:33 +01:00
}
var buf bytes.Buffer
if err := encryptedKey.Serialize(&buf); err != nil {
return nil, fmt.Errorf("gopenpgp: cannot serialize encrypted key: %v", err)
2018-11-21 23:44:33 +01:00
}
outSplit.KeyPacket = buf.Bytes()
return outSplit, nil
}
// GetBinary returns the unarmored binary content of the signature as a []byte
func (msg *PGPSignature) GetBinary() []byte {
return msg.Data
}
// GetArmored returns the armored signature as a string
func (msg *PGPSignature) GetArmored() (string, error) {
return armor.ArmorWithType(msg.Data, constants.PGPSignatureHeader)
}
// GetBinary returns the unarmored signed data as a []byte
func (msg *ClearTextMessage) GetBinary() []byte {
return msg.Data
}
// GetString returns the unarmored signed data as a string
func (msg *ClearTextMessage) GetString() string {
return string(msg.Data)
}
// GetSignature returns the unarmored binary signature as a []byte
func (msg *ClearTextMessage) GetSignature() []byte {
return msg.Signature
}
// GetArmored armors plaintext and signature with the PGP SIGNED MESSAGE armoring
func (msg *ClearTextMessage) GetArmored() (string, error) {
armSignature, err := armor.ArmorWithType(msg.GetSignature(), constants.PGPSignatureHeader)
if err != nil {
return "", err
}
str := "-----BEGIN PGP SIGNED MESSAGE-----\r\nHash:SHA512\r\n\r\n"
str += msg.GetString()
str += "\r\n"
str += armSignature
return str, nil
}
// ---- UTILS -----
2018-11-21 23:44:33 +01:00
// IsPGPMessage checks if data if has armored PGP message format.
func (pgp *GopenPGP) IsPGPMessage(data string) bool {
re := regexp.MustCompile("^-----BEGIN " + constants.PGPMessageHeader + "-----(?s:.+)-----END " +
constants.PGPMessageHeader + "-----")
return re.MatchString(data)
2018-11-21 23:44:33 +01:00
}