1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
// Package armor contains a set of helper methods for armoring and unarmoring
// data.
package armor
import (
"bytes"
"fmt"
"io"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/gopenpgp/v3/constants"
"github.com/ProtonMail/gopenpgp/v3/internal"
)
// ArmorKey armors input as a public key.
func ArmorKey(input []byte) (string, error) {
return ArmorWithType(input, constants.PublicKeyHeader)
}
// ArmorWriterWithType returns a io.WriteCloser which, when written to, writes
// armored data to w with the given armorType.
func ArmorWriterWithType(w io.Writer, armorType string) (io.WriteCloser, error) {
return armor.EncodeWithChecksumOption(w, armorType, internal.ArmorHeaders, constants.ArmorChecksumEnabled)
}
// ArmorWriterWithTypeChecksum returns a io.WriteCloser which, when written to, writes
// armored data to w with the given armorType.
// The checksum determines if an armor checksum is written at the end.
func ArmorWriterWithTypeChecksum(w io.Writer, armorType string, checksum bool) (io.WriteCloser, error) {
return armor.EncodeWithChecksumOption(w, armorType, internal.ArmorHeaders, checksum)
}
// ArmorWriterWithTypeAndCustomHeaders returns a io.WriteCloser,
// which armors input with the given armorType and headers.
func ArmorWriterWithTypeAndCustomHeaders(w io.Writer, armorType, version, comment string) (io.WriteCloser, error) {
headers := make(map[string]string)
if version != "" {
headers["Version"] = version
}
if comment != "" {
headers["Comment"] = comment
}
return armor.EncodeWithChecksumOption(w, armorType, headers, constants.ArmorChecksumEnabled)
}
// ArmorWithType armors input with the given armorType.
func ArmorWithType(input []byte, armorType string) (string, error) {
return ArmorWithTypeChecksum(input, armorType, constants.ArmorChecksumEnabled)
}
// ArmorWithTypeChecksum armors input with the given armorType.
// The checksum option determines if an armor checksum is written at the end.
func ArmorWithTypeChecksum(input []byte, armorType string, checksum bool) (string, error) {
buffer, err := armorWithTypeAndHeaders(input, armorType, internal.ArmorHeaders, checksum)
if err != nil {
return "", err
}
return buffer.String(), err
}
// ArmorWithTypeBytes armors input with the given armorType.
func ArmorWithTypeBytes(input []byte, armorType string) ([]byte, error) {
return ArmorWithTypeBytesChecksum(input, armorType, constants.ArmorChecksumEnabled)
}
// ArmorWithTypeBytesChecksum armors input with the given armorType and checksum option.
func ArmorWithTypeBytesChecksum(input []byte, armorType string, checksum bool) ([]byte, error) {
buffer, err := armorWithTypeAndHeaders(input, armorType, internal.ArmorHeaders, checksum)
if err != nil {
return nil, err
}
return buffer.Bytes(), err
}
// ArmorWithTypeAndCustomHeaders armors input with the given armorType and
// headers.
func ArmorWithTypeAndCustomHeaders(input []byte, armorType, version, comment string) (string, error) {
return ArmorWithTypeAndCustomHeadersChecksum(input, armorType, version, comment, constants.ArmorChecksumEnabled)
}
// ArmorWithTypeAndCustomHeadersChecksum armors input with the given armorType and
// headers and checksum option.
func ArmorWithTypeAndCustomHeadersChecksum(input []byte, armorType, version, comment string, checksum bool) (string, error) {
headers := make(map[string]string)
if version != "" {
headers["Version"] = version
}
if comment != "" {
headers["Comment"] = comment
}
buffer, err := armorWithTypeAndHeaders(input, armorType, headers, checksum)
if err != nil {
return "", err
}
return buffer.String(), err
}
// ArmorWithTypeAndCustomHeadersBytes armors input with the given armorType and
// headers.
func ArmorWithTypeAndCustomHeadersBytes(input []byte, armorType, version, comment string) ([]byte, error) {
headers := make(map[string]string)
if version != "" {
headers["Version"] = version
}
if comment != "" {
headers["Comment"] = comment
}
buffer, err := armorWithTypeAndHeaders(input, armorType, headers, constants.ArmorChecksumEnabled)
if err != nil {
return nil, err
}
return buffer.Bytes(), err
}
// ArmorReader returns a io.Reader which, when read, reads
// unarmored data from in.
func ArmorReader(in io.Reader) (io.Reader, error) {
block, err := armor.Decode(in)
if err != nil {
return nil, err
}
return block.Body, nil
}
// Unarmor unarmors an armored input into a byte array.
func Unarmor(input string) ([]byte, error) {
b, err := internal.Unarmor(input)
if err != nil {
return nil, fmt.Errorf("armor: unable to unarmor: %w", err)
}
return io.ReadAll(b.Body)
}
// UnarmorBytes unarmors an armored input into a byte array.
func UnarmorBytes(input []byte) ([]byte, error) {
b, err := internal.UnarmorBytes(input)
if err != nil {
return nil, fmt.Errorf("armor: unable to unarmor: %w", err)
}
return io.ReadAll(b.Body)
}
func ArmorPGPSignatureBinary(signature []byte) ([]byte, error) {
return ArmorWithTypeBytes(signature, constants.PGPSignatureHeader)
}
func ArmorPGPSignature(signature []byte) (string, error) {
return ArmorWithType(signature, constants.PGPSignatureHeader)
}
func ArmorPGPMessageBytes(signature []byte) ([]byte, error) {
return ArmorWithTypeBytes(signature, constants.PGPMessageHeader)
}
func ArmorPGPMessage(signature []byte) (string, error) {
return ArmorWithType(signature, constants.PGPMessageHeader)
}
func ArmorPGPMessageBytesChecksum(signature []byte, checksum bool) ([]byte, error) {
return ArmorWithTypeBytesChecksum(signature, constants.PGPMessageHeader, checksum)
}
func ArmorPGPMessageChecksum(signature []byte, checksum bool) (string, error) {
return ArmorWithTypeChecksum(signature, constants.PGPMessageHeader, checksum)
}
const armorPrefix = "-----BEGIN PGP"
const maxGarbageBytes = 128
// IsPGPArmored reads a prefix from the reader and checks
// if it is equal to a pgp armored message prefix.
// Returns an io.Reader that is reset to the state of the in reader,
// and a bool that indicates if there is a match.
// If reading from the reader fails, the returned bool is set to false.
func IsPGPArmored(in io.Reader) (io.Reader, bool) {
buffer := make([]byte, len(armorPrefix)+maxGarbageBytes)
n, _ := io.ReadFull(in, buffer)
outReader := io.MultiReader(bytes.NewReader(buffer[:n]), in)
if bytes.Contains(buffer[:n], []byte(armorPrefix)) {
return outReader, true
}
return outReader, false
}
func armorWithTypeAndHeaders(input []byte, armorType string, headers map[string]string, writeChecksum bool) (*bytes.Buffer, error) {
var b bytes.Buffer
w, err := armor.EncodeWithChecksumOption(&b, armorType, headers, writeChecksum)
if err != nil {
return nil, fmt.Errorf("armor: unable to encode armoring: %w", err)
}
if _, err = w.Write(input); err != nil {
return nil, fmt.Errorf("armor: unable to write armored to buffer: %w", err)
}
if err := w.Close(); err != nil {
return nil, fmt.Errorf("armor: unable to close armor buffer: %w", err)
}
return &b, nil
}
|