forked from toolshed/abra
chore: make deps, go mod vendor
This commit is contained in:
36
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
generated
vendored
36
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.go
generated
vendored
@ -88,17 +88,20 @@ func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
|
||||
if errRead != nil && errRead != io.EOF {
|
||||
return 0, errRead
|
||||
}
|
||||
decrypted, errChunk := ar.openChunk(cipherChunk)
|
||||
if errChunk != nil {
|
||||
return 0, errChunk
|
||||
}
|
||||
|
||||
// Return decrypted bytes, buffering if necessary
|
||||
if len(dst) < len(decrypted) {
|
||||
n = copy(dst, decrypted[:len(dst)])
|
||||
ar.buffer.Write(decrypted[len(dst):])
|
||||
} else {
|
||||
n = copy(dst, decrypted)
|
||||
if len(cipherChunk) > 0 {
|
||||
decrypted, errChunk := ar.openChunk(cipherChunk)
|
||||
if errChunk != nil {
|
||||
return 0, errChunk
|
||||
}
|
||||
|
||||
// Return decrypted bytes, buffering if necessary
|
||||
if len(dst) < len(decrypted) {
|
||||
n = copy(dst, decrypted[:len(dst)])
|
||||
ar.buffer.Write(decrypted[len(dst):])
|
||||
} else {
|
||||
n = copy(dst, decrypted)
|
||||
}
|
||||
}
|
||||
|
||||
// Check final authentication tag
|
||||
@ -116,6 +119,12 @@ func (ar *aeadDecrypter) Read(dst []byte) (n int, err error) {
|
||||
// checked in the last Read call. In the future, this function could be used to
|
||||
// wipe the reader and peeked, decrypted bytes, if necessary.
|
||||
func (ar *aeadDecrypter) Close() (err error) {
|
||||
if !ar.eof {
|
||||
errChunk := ar.validateFinalTag(ar.peekedBytes)
|
||||
if errChunk != nil {
|
||||
return errChunk
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -138,7 +147,7 @@ func (ar *aeadDecrypter) openChunk(data []byte) ([]byte, error) {
|
||||
nonce := ar.computeNextNonce()
|
||||
plainChunk, err := ar.aead.Open(nil, nonce, chunk, adata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.ErrAEADTagVerification
|
||||
}
|
||||
ar.bytesProcessed += len(plainChunk)
|
||||
if err = ar.aeadCrypter.incrementIndex(); err != nil {
|
||||
@ -163,9 +172,8 @@ func (ar *aeadDecrypter) validateFinalTag(tag []byte) error {
|
||||
// ... and total number of encrypted octets
|
||||
adata = append(adata, amountBytes...)
|
||||
nonce := ar.computeNextNonce()
|
||||
_, err := ar.aead.Open(nil, nonce, tag, adata)
|
||||
if err != nil {
|
||||
return err
|
||||
if _, err := ar.aead.Open(nil, nonce, tag, adata); err != nil {
|
||||
return errors.ErrAEADTagVerification
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
44
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
generated
vendored
44
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.go
generated
vendored
@ -8,9 +8,10 @@ import (
|
||||
"compress/bzip2"
|
||||
"compress/flate"
|
||||
"compress/zlib"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// Compressed represents a compressed OpenPGP packet. The decompressed contents
|
||||
@ -39,6 +40,37 @@ type CompressionConfig struct {
|
||||
Level int
|
||||
}
|
||||
|
||||
// decompressionReader ensures that the whole compression packet is read.
|
||||
type decompressionReader struct {
|
||||
compressed io.Reader
|
||||
decompressed io.ReadCloser
|
||||
readAll bool
|
||||
}
|
||||
|
||||
func newDecompressionReader(r io.Reader, decompressor io.ReadCloser) *decompressionReader {
|
||||
return &decompressionReader{
|
||||
compressed: r,
|
||||
decompressed: decompressor,
|
||||
}
|
||||
}
|
||||
|
||||
func (dr *decompressionReader) Read(data []byte) (n int, err error) {
|
||||
if dr.readAll {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err = dr.decompressed.Read(data)
|
||||
if err == io.EOF {
|
||||
dr.readAll = true
|
||||
// Close the decompressor.
|
||||
if errDec := dr.decompressed.Close(); errDec != nil {
|
||||
return n, errDec
|
||||
}
|
||||
// Consume all remaining data from the compressed packet.
|
||||
consumeAll(dr.compressed)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *Compressed) parse(r io.Reader) error {
|
||||
var buf [1]byte
|
||||
_, err := readFull(r, buf[:])
|
||||
@ -50,11 +82,15 @@ func (c *Compressed) parse(r io.Reader) error {
|
||||
case 0:
|
||||
c.Body = r
|
||||
case 1:
|
||||
c.Body = flate.NewReader(r)
|
||||
c.Body = newDecompressionReader(r, flate.NewReader(r))
|
||||
case 2:
|
||||
c.Body, err = zlib.NewReader(r)
|
||||
decompressor, err := zlib.NewReader(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Body = newDecompressionReader(r, decompressor)
|
||||
case 3:
|
||||
c.Body = bzip2.NewReader(r)
|
||||
c.Body = newDecompressionReader(r, io.NopCloser(bzip2.NewReader(r)))
|
||||
default:
|
||||
err = errors.UnsupportedError("unknown compression algorithm: " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
170
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
170
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
@ -14,6 +14,34 @@ import (
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultRejectPublicKeyAlgorithms = map[PublicKeyAlgorithm]bool{
|
||||
PubKeyAlgoElGamal: true,
|
||||
PubKeyAlgoDSA: true,
|
||||
}
|
||||
defaultRejectHashAlgorithms = map[crypto.Hash]bool{
|
||||
crypto.MD5: true,
|
||||
crypto.RIPEMD160: true,
|
||||
}
|
||||
defaultRejectMessageHashAlgorithms = map[crypto.Hash]bool{
|
||||
crypto.SHA1: true,
|
||||
crypto.MD5: true,
|
||||
crypto.RIPEMD160: true,
|
||||
}
|
||||
defaultRejectCurves = map[Curve]bool{
|
||||
CurveSecP256k1: true,
|
||||
}
|
||||
)
|
||||
|
||||
// A global feature flag to indicate v5 support.
|
||||
// Can be set via a build tag, e.g.: `go build -tags v5 ./...`
|
||||
// If the build tag is missing config_v5.go will set it to true.
|
||||
//
|
||||
// Disables parsing of v5 keys and v5 signatures.
|
||||
// These are non-standard entities, which in the crypto-refresh have been superseded
|
||||
// by v6 keys, v6 signatures and SEIPDv2 encrypted data, respectively.
|
||||
var V5Disabled = false
|
||||
|
||||
// Config collects a number of parameters along with sensible defaults.
|
||||
// A nil *Config is valid and results in all default values.
|
||||
type Config struct {
|
||||
@ -73,9 +101,16 @@ type Config struct {
|
||||
// **Note: using this option may break compatibility with other OpenPGP
|
||||
// implementations, as well as future versions of this library.**
|
||||
AEADConfig *AEADConfig
|
||||
// V5Keys configures version 5 key generation. If false, this package still
|
||||
// supports version 5 keys, but produces version 4 keys.
|
||||
V5Keys bool
|
||||
// V6Keys configures version 6 key generation. If false, this package still
|
||||
// supports version 6 keys, but produces version 4 keys.
|
||||
V6Keys bool
|
||||
// Minimum RSA key size allowed for key generation and message signing, verification and encryption.
|
||||
MinRSABits uint16
|
||||
// Reject insecure algorithms, only works with v2 api
|
||||
RejectPublicKeyAlgorithms map[PublicKeyAlgorithm]bool
|
||||
RejectHashAlgorithms map[crypto.Hash]bool
|
||||
RejectMessageHashAlgorithms map[crypto.Hash]bool
|
||||
RejectCurves map[Curve]bool
|
||||
// "The validity period of the key. This is the number of seconds after
|
||||
// the key creation time that the key expires. If this is not present
|
||||
// or has a value of zero, the key never expires. This is found only on
|
||||
@ -104,12 +139,40 @@ type Config struct {
|
||||
// might be no other way than to tolerate the missing MDC. Setting this flag, allows this
|
||||
// mode of operation. It should be considered a measure of last resort.
|
||||
InsecureAllowUnauthenticatedMessages bool
|
||||
// InsecureAllowDecryptionWithSigningKeys allows decryption with keys marked as signing keys in the v2 API.
|
||||
// This setting is potentially insecure, but it is needed as some libraries
|
||||
// ignored key flags when selecting a key for encryption.
|
||||
// Not relevant for the v1 API, as all keys were allowed in decryption.
|
||||
InsecureAllowDecryptionWithSigningKeys bool
|
||||
// KnownNotations is a map of Notation Data names to bools, which controls
|
||||
// the notation names that are allowed to be present in critical Notation Data
|
||||
// signature subpackets.
|
||||
KnownNotations map[string]bool
|
||||
// SignatureNotations is a list of Notations to be added to any signatures.
|
||||
SignatureNotations []*Notation
|
||||
// CheckIntendedRecipients controls, whether the OpenPGP Intended Recipient Fingerprint feature
|
||||
// should be enabled for encryption and decryption.
|
||||
// (See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-12.html#name-intended-recipient-fingerpr).
|
||||
// When the flag is set, encryption produces Intended Recipient Fingerprint signature sub-packets and decryption
|
||||
// checks whether the key it was encrypted to is one of the included fingerprints in the signature.
|
||||
// If the flag is disabled, no Intended Recipient Fingerprint sub-packets are created or checked.
|
||||
// The default behavior, when the config or flag is nil, is to enable the feature.
|
||||
CheckIntendedRecipients *bool
|
||||
// CacheSessionKey controls if decryption should return the session key used for decryption.
|
||||
// If the flag is set, the session key is cached in the message details struct.
|
||||
CacheSessionKey bool
|
||||
// CheckPacketSequence is a flag that controls if the pgp message reader should strictly check
|
||||
// that the packet sequence conforms with the grammar mandated by rfc4880.
|
||||
// The default behavior, when the config or flag is nil, is to check the packet sequence.
|
||||
CheckPacketSequence *bool
|
||||
// NonDeterministicSignaturesViaNotation is a flag to enable randomization of signatures.
|
||||
// If true, a salt notation is used to randomize signatures generated by v4 and v5 keys
|
||||
// (v6 signatures are always non-deterministic, by design).
|
||||
// This protects EdDSA signatures from potentially leaking the secret key in case of faults (i.e. bitflips) which, in principle, could occur
|
||||
// during the signing computation. It is added to signatures of any algo for simplicity, and as it may also serve as protection in case of
|
||||
// weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
|
||||
// The default behavior, when the config or flag is nil, is to enable the feature.
|
||||
NonDeterministicSignaturesViaNotation *bool
|
||||
}
|
||||
|
||||
func (c *Config) Random() io.Reader {
|
||||
@ -197,7 +260,7 @@ func (c *Config) S2K() *s2k.Config {
|
||||
return nil
|
||||
}
|
||||
// for backwards compatibility
|
||||
if c != nil && c.S2KCount > 0 && c.S2KConfig == nil {
|
||||
if c.S2KCount > 0 && c.S2KConfig == nil {
|
||||
return &s2k.Config{
|
||||
S2KCount: c.S2KCount,
|
||||
}
|
||||
@ -233,6 +296,13 @@ func (c *Config) AllowUnauthenticatedMessages() bool {
|
||||
return c.InsecureAllowUnauthenticatedMessages
|
||||
}
|
||||
|
||||
func (c *Config) AllowDecryptionWithSigningKeys() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureAllowDecryptionWithSigningKeys
|
||||
}
|
||||
|
||||
func (c *Config) KnownNotation(notationName string) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
@ -246,3 +316,95 @@ func (c *Config) Notations() []*Notation {
|
||||
}
|
||||
return c.SignatureNotations
|
||||
}
|
||||
|
||||
func (c *Config) V6() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.V6Keys
|
||||
}
|
||||
|
||||
func (c *Config) IntendedRecipients() bool {
|
||||
if c == nil || c.CheckIntendedRecipients == nil {
|
||||
return true
|
||||
}
|
||||
return *c.CheckIntendedRecipients
|
||||
}
|
||||
|
||||
func (c *Config) RetrieveSessionKey() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.CacheSessionKey
|
||||
}
|
||||
|
||||
func (c *Config) MinimumRSABits() uint16 {
|
||||
if c == nil || c.MinRSABits == 0 {
|
||||
return 2047
|
||||
}
|
||||
return c.MinRSABits
|
||||
}
|
||||
|
||||
func (c *Config) RejectPublicKeyAlgorithm(alg PublicKeyAlgorithm) bool {
|
||||
var rejectedAlgorithms map[PublicKeyAlgorithm]bool
|
||||
if c == nil || c.RejectPublicKeyAlgorithms == nil {
|
||||
// Default
|
||||
rejectedAlgorithms = defaultRejectPublicKeyAlgorithms
|
||||
} else {
|
||||
rejectedAlgorithms = c.RejectPublicKeyAlgorithms
|
||||
}
|
||||
return rejectedAlgorithms[alg]
|
||||
}
|
||||
|
||||
func (c *Config) RejectHashAlgorithm(hash crypto.Hash) bool {
|
||||
var rejectedAlgorithms map[crypto.Hash]bool
|
||||
if c == nil || c.RejectHashAlgorithms == nil {
|
||||
// Default
|
||||
rejectedAlgorithms = defaultRejectHashAlgorithms
|
||||
} else {
|
||||
rejectedAlgorithms = c.RejectHashAlgorithms
|
||||
}
|
||||
return rejectedAlgorithms[hash]
|
||||
}
|
||||
|
||||
func (c *Config) RejectMessageHashAlgorithm(hash crypto.Hash) bool {
|
||||
var rejectedAlgorithms map[crypto.Hash]bool
|
||||
if c == nil || c.RejectMessageHashAlgorithms == nil {
|
||||
// Default
|
||||
rejectedAlgorithms = defaultRejectMessageHashAlgorithms
|
||||
} else {
|
||||
rejectedAlgorithms = c.RejectMessageHashAlgorithms
|
||||
}
|
||||
return rejectedAlgorithms[hash]
|
||||
}
|
||||
|
||||
func (c *Config) RejectCurve(curve Curve) bool {
|
||||
var rejectedCurve map[Curve]bool
|
||||
if c == nil || c.RejectCurves == nil {
|
||||
// Default
|
||||
rejectedCurve = defaultRejectCurves
|
||||
} else {
|
||||
rejectedCurve = c.RejectCurves
|
||||
}
|
||||
return rejectedCurve[curve]
|
||||
}
|
||||
|
||||
func (c *Config) StrictPacketSequence() bool {
|
||||
if c == nil || c.CheckPacketSequence == nil {
|
||||
return true
|
||||
}
|
||||
return *c.CheckPacketSequence
|
||||
}
|
||||
|
||||
func (c *Config) RandomizeSignaturesViaNotation() bool {
|
||||
if c == nil || c.NonDeterministicSignaturesViaNotation == nil {
|
||||
return true
|
||||
}
|
||||
return *c.NonDeterministicSignaturesViaNotation
|
||||
}
|
||||
|
||||
// BoolPointer is a helper function to set a boolean pointer in the Config.
|
||||
// e.g., config.CheckPacketSequence = BoolPointer(true)
|
||||
func BoolPointer(value bool) *bool {
|
||||
return &value
|
||||
}
|
||||
|
7
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go
generated
vendored
Normal file
7
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config_v5.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
//go:build !v5
|
||||
|
||||
package packet
|
||||
|
||||
func init() {
|
||||
V5Disabled = true
|
||||
}
|
422
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
generated
vendored
422
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.go
generated
vendored
@ -5,9 +5,11 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"strconv"
|
||||
@ -16,32 +18,85 @@ import (
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x448"
|
||||
)
|
||||
|
||||
const encryptedKeyVersion = 3
|
||||
|
||||
// EncryptedKey represents a public-key encrypted session key. See RFC 4880,
|
||||
// section 5.1.
|
||||
type EncryptedKey struct {
|
||||
KeyId uint64
|
||||
Algo PublicKeyAlgorithm
|
||||
CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet
|
||||
Key []byte // only valid after a successful Decrypt
|
||||
Version int
|
||||
KeyId uint64
|
||||
KeyVersion int // v6
|
||||
KeyFingerprint []byte // v6
|
||||
Algo PublicKeyAlgorithm
|
||||
CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet
|
||||
Key []byte // only valid after a successful Decrypt
|
||||
|
||||
encryptedMPI1, encryptedMPI2 encoding.Field
|
||||
ephemeralPublicX25519 *x25519.PublicKey // used for x25519
|
||||
ephemeralPublicX448 *x448.PublicKey // used for x448
|
||||
encryptedSession []byte // used for x25519 and x448
|
||||
}
|
||||
|
||||
func (e *EncryptedKey) parse(r io.Reader) (err error) {
|
||||
var buf [10]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
var buf [8]byte
|
||||
_, err = readFull(r, buf[:versionSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != encryptedKeyVersion {
|
||||
e.Version = int(buf[0])
|
||||
if e.Version != 3 && e.Version != 6 {
|
||||
return errors.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
e.KeyId = binary.BigEndian.Uint64(buf[1:9])
|
||||
e.Algo = PublicKeyAlgorithm(buf[9])
|
||||
if e.Version == 6 {
|
||||
//Read a one-octet size of the following two fields.
|
||||
if _, err = readFull(r, buf[:1]); err != nil {
|
||||
return
|
||||
}
|
||||
// The size may also be zero, and the key version and
|
||||
// fingerprint omitted for an "anonymous recipient"
|
||||
if buf[0] != 0 {
|
||||
// non-anonymous case
|
||||
_, err = readFull(r, buf[:versionSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.KeyVersion = int(buf[0])
|
||||
if e.KeyVersion != 4 && e.KeyVersion != 6 {
|
||||
return errors.UnsupportedError("unknown public key version " + strconv.Itoa(e.KeyVersion))
|
||||
}
|
||||
var fingerprint []byte
|
||||
if e.KeyVersion == 6 {
|
||||
fingerprint = make([]byte, fingerprintSizeV6)
|
||||
} else if e.KeyVersion == 4 {
|
||||
fingerprint = make([]byte, fingerprintSize)
|
||||
}
|
||||
_, err = readFull(r, fingerprint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.KeyFingerprint = fingerprint
|
||||
if e.KeyVersion == 6 {
|
||||
e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[:keyIdSize])
|
||||
} else if e.KeyVersion == 4 {
|
||||
e.KeyId = binary.BigEndian.Uint64(e.KeyFingerprint[fingerprintSize-keyIdSize : fingerprintSize])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err = readFull(r, buf[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.KeyId = binary.BigEndian.Uint64(buf[:keyIdSize])
|
||||
}
|
||||
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e.Algo = PublicKeyAlgorithm(buf[0])
|
||||
var cipherFunction byte
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
e.encryptedMPI1 = new(encoding.MPI)
|
||||
@ -68,26 +123,39 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
|
||||
if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoX25519:
|
||||
e.ephemeralPublicX25519, e.encryptedSession, cipherFunction, err = x25519.DecodeFields(r, e.Version == 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case PubKeyAlgoX448:
|
||||
e.ephemeralPublicX448, e.encryptedSession, cipherFunction, err = x448.DecodeFields(r, e.Version == 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if e.Version < 6 {
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
e.CipherFunc = CipherFunction(cipherFunction)
|
||||
// Check for validiy is in the Decrypt method
|
||||
}
|
||||
}
|
||||
|
||||
_, err = consumeAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
func checksumKeyMaterial(key []byte) uint16 {
|
||||
var checksum uint16
|
||||
for _, v := range key {
|
||||
checksum += uint16(v)
|
||||
}
|
||||
return checksum
|
||||
}
|
||||
|
||||
// Decrypt decrypts an encrypted session key with the given private key. The
|
||||
// private key must have been decrypted first.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
|
||||
if e.KeyId != 0 && e.KeyId != priv.KeyId {
|
||||
if e.Version < 6 && e.KeyId != 0 && e.KeyId != priv.KeyId {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key for key id " + strconv.FormatUint(e.KeyId, 16) + " with private key id " + strconv.FormatUint(priv.KeyId, 16))
|
||||
}
|
||||
if e.Version == 6 && e.KeyVersion != 0 && !bytes.Equal(e.KeyFingerprint, priv.Fingerprint) {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key for key fingerprint " + hex.EncodeToString(e.KeyFingerprint) + " with private key fingerprint " + hex.EncodeToString(priv.Fingerprint))
|
||||
}
|
||||
if e.Algo != priv.PubKeyAlgo {
|
||||
return errors.InvalidArgumentError("cannot decrypt encrypted session key of type " + strconv.Itoa(int(e.Algo)) + " with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
@ -113,52 +181,116 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
|
||||
vsG := e.encryptedMPI1.Bytes()
|
||||
m := e.encryptedMPI2.Bytes()
|
||||
oid := priv.PublicKey.oid.EncodedBytes()
|
||||
b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, priv.PublicKey.Fingerprint[:])
|
||||
fp := priv.PublicKey.Fingerprint[:]
|
||||
if priv.PublicKey.Version == 5 {
|
||||
// For v5 the, the fingerprint must be restricted to 20 bytes
|
||||
fp = fp[:20]
|
||||
}
|
||||
b, err = ecdh.Decrypt(priv.PrivateKey.(*ecdh.PrivateKey), vsG, m, oid, fp)
|
||||
case PubKeyAlgoX25519:
|
||||
b, err = x25519.Decrypt(priv.PrivateKey.(*x25519.PrivateKey), e.ephemeralPublicX25519, e.encryptedSession)
|
||||
case PubKeyAlgoX448:
|
||||
b, err = x448.Decrypt(priv.PrivateKey.(*x448.PrivateKey), e.ephemeralPublicX448, e.encryptedSession)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("cannot decrypt encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.CipherFunc = CipherFunction(b[0])
|
||||
if !e.CipherFunc.IsSupported() {
|
||||
return errors.UnsupportedError("unsupported encryption function")
|
||||
var key []byte
|
||||
switch priv.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
|
||||
keyOffset := 0
|
||||
if e.Version < 6 {
|
||||
e.CipherFunc = CipherFunction(b[0])
|
||||
keyOffset = 1
|
||||
if !e.CipherFunc.IsSupported() {
|
||||
return errors.UnsupportedError("unsupported encryption function")
|
||||
}
|
||||
}
|
||||
key, err = decodeChecksumKey(b[keyOffset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
if e.Version < 6 {
|
||||
switch e.CipherFunc {
|
||||
case CipherAES128, CipherAES192, CipherAES256:
|
||||
break
|
||||
default:
|
||||
return errors.StructuralError("v3 PKESK mandates AES as cipher function for x25519 and x448")
|
||||
}
|
||||
}
|
||||
key = b[:]
|
||||
default:
|
||||
return errors.UnsupportedError("unsupported algorithm for decryption")
|
||||
}
|
||||
|
||||
e.Key = b[1 : len(b)-2]
|
||||
expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
|
||||
checksum := checksumKeyMaterial(e.Key)
|
||||
if checksum != expectedChecksum {
|
||||
return errors.StructuralError("EncryptedKey checksum incorrect")
|
||||
}
|
||||
|
||||
e.Key = key
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize writes the encrypted key packet, e, to w.
|
||||
func (e *EncryptedKey) Serialize(w io.Writer) error {
|
||||
var mpiLen int
|
||||
var encodedLength int
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
mpiLen = int(e.encryptedMPI1.EncodedLength())
|
||||
encodedLength = int(e.encryptedMPI1.EncodedLength())
|
||||
case PubKeyAlgoElGamal:
|
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
case PubKeyAlgoECDH:
|
||||
mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
|
||||
case PubKeyAlgoX25519:
|
||||
encodedLength = x25519.EncodedFieldsLength(e.encryptedSession, e.Version == 6)
|
||||
case PubKeyAlgoX448:
|
||||
encodedLength = x448.EncodedFieldsLength(e.encryptedSession, e.Version == 6)
|
||||
default:
|
||||
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
|
||||
}
|
||||
|
||||
err := serializeHeader(w, packetTypeEncryptedKey, 1 /* version */ +8 /* key id */ +1 /* algo */ +mpiLen)
|
||||
packetLen := versionSize /* version */ + keyIdSize /* key id */ + algorithmSize /* algo */ + encodedLength
|
||||
if e.Version == 6 {
|
||||
packetLen = versionSize /* version */ + algorithmSize /* algo */ + encodedLength + keyVersionSize /* key version */
|
||||
if e.KeyVersion == 6 {
|
||||
packetLen += fingerprintSizeV6
|
||||
} else if e.KeyVersion == 4 {
|
||||
packetLen += fingerprintSize
|
||||
}
|
||||
}
|
||||
|
||||
err := serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Write([]byte{encryptedKeyVersion})
|
||||
binary.Write(w, binary.BigEndian, e.KeyId)
|
||||
w.Write([]byte{byte(e.Algo)})
|
||||
_, err = w.Write([]byte{byte(e.Version)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.Version == 6 {
|
||||
_, err = w.Write([]byte{byte(e.KeyVersion)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The key version number may also be zero,
|
||||
// and the fingerprint omitted
|
||||
if e.KeyVersion != 0 {
|
||||
_, err = w.Write(e.KeyFingerprint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Write KeyID
|
||||
err = binary.Write(w, binary.BigEndian, e.KeyId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = w.Write([]byte{byte(e.Algo)})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch e.Algo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
@ -176,34 +308,115 @@ func (e *EncryptedKey) Serialize(w io.Writer) error {
|
||||
}
|
||||
_, err := w.Write(e.encryptedMPI2.EncodedBytes())
|
||||
return err
|
||||
case PubKeyAlgoX25519:
|
||||
err := x25519.EncodeFields(w, e.ephemeralPublicX25519, e.encryptedSession, byte(e.CipherFunc), e.Version == 6)
|
||||
return err
|
||||
case PubKeyAlgoX448:
|
||||
err := x448.EncodeFields(w, e.ephemeralPublicX448, e.encryptedSession, byte(e.CipherFunc), e.Version == 6)
|
||||
return err
|
||||
default:
|
||||
panic("internal error")
|
||||
}
|
||||
}
|
||||
|
||||
// SerializeEncryptedKey serializes an encrypted key packet to w that contains
|
||||
// SerializeEncryptedKeyAEAD serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// If aeadSupported is set, PKESK v6 is used, otherwise v3.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
|
||||
var buf [10]byte
|
||||
buf[0] = encryptedKeyVersion
|
||||
binary.BigEndian.PutUint64(buf[1:9], pub.KeyId)
|
||||
buf[9] = byte(pub.PubKeyAlgo)
|
||||
func SerializeEncryptedKeyAEAD(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, config *Config) error {
|
||||
return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, aeadSupported, key, false, config)
|
||||
}
|
||||
|
||||
keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */)
|
||||
keyBlock[0] = byte(cipherFunc)
|
||||
copy(keyBlock[1:], key)
|
||||
checksum := checksumKeyMaterial(key)
|
||||
keyBlock[1+len(key)] = byte(checksum >> 8)
|
||||
keyBlock[1+len(key)+1] = byte(checksum)
|
||||
// SerializeEncryptedKeyAEADwithHiddenOption serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// Offers the hidden flag option to indicated if the PKESK packet should include a wildcard KeyID.
|
||||
// If aeadSupported is set, PKESK v6 is used, otherwise v3.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeEncryptedKeyAEADwithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, aeadSupported bool, key []byte, hidden bool, config *Config) error {
|
||||
var buf [36]byte // max possible header size is v6
|
||||
lenHeaderWritten := versionSize
|
||||
version := 3
|
||||
|
||||
if aeadSupported {
|
||||
version = 6
|
||||
}
|
||||
// An implementation MUST NOT generate ElGamal v6 PKESKs.
|
||||
if version == 6 && pub.PubKeyAlgo == PubKeyAlgoElGamal {
|
||||
return errors.InvalidArgumentError("ElGamal v6 PKESK are not allowed")
|
||||
}
|
||||
// In v3 PKESKs, for x25519 and x448, mandate using AES
|
||||
if version == 3 && (pub.PubKeyAlgo == PubKeyAlgoX25519 || pub.PubKeyAlgo == PubKeyAlgoX448) {
|
||||
switch cipherFunc {
|
||||
case CipherAES128, CipherAES192, CipherAES256:
|
||||
break
|
||||
default:
|
||||
return errors.InvalidArgumentError("v3 PKESK mandates AES for x25519 and x448")
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = byte(version)
|
||||
|
||||
// If hidden is set, the key should be hidden
|
||||
// An implementation MAY accept or use a Key ID of all zeros,
|
||||
// or a key version of zero and no key fingerprint, to hide the intended decryption key.
|
||||
// See Section 5.1.8. in the open pgp crypto refresh
|
||||
if version == 6 {
|
||||
if !hidden {
|
||||
// A one-octet size of the following two fields.
|
||||
buf[1] = byte(keyVersionSize + len(pub.Fingerprint))
|
||||
// A one octet key version number.
|
||||
buf[2] = byte(pub.Version)
|
||||
lenHeaderWritten += keyVersionSize + 1
|
||||
// The fingerprint of the public key
|
||||
copy(buf[lenHeaderWritten:lenHeaderWritten+len(pub.Fingerprint)], pub.Fingerprint)
|
||||
lenHeaderWritten += len(pub.Fingerprint)
|
||||
} else {
|
||||
// The size may also be zero, and the key version
|
||||
// and fingerprint omitted for an "anonymous recipient"
|
||||
buf[1] = 0
|
||||
lenHeaderWritten += 1
|
||||
}
|
||||
} else {
|
||||
if !hidden {
|
||||
binary.BigEndian.PutUint64(buf[versionSize:(versionSize+keyIdSize)], pub.KeyId)
|
||||
}
|
||||
lenHeaderWritten += keyIdSize
|
||||
}
|
||||
buf[lenHeaderWritten] = byte(pub.PubKeyAlgo)
|
||||
lenHeaderWritten += algorithmSize
|
||||
|
||||
var keyBlock []byte
|
||||
switch pub.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
|
||||
lenKeyBlock := len(key) + 2
|
||||
if version < 6 {
|
||||
lenKeyBlock += 1 // cipher type included
|
||||
}
|
||||
keyBlock = make([]byte, lenKeyBlock)
|
||||
keyOffset := 0
|
||||
if version < 6 {
|
||||
keyBlock[0] = byte(cipherFunc)
|
||||
keyOffset = 1
|
||||
}
|
||||
encodeChecksumKey(keyBlock[keyOffset:], key)
|
||||
case PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
// algorithm is added in plaintext below
|
||||
keyBlock = key
|
||||
}
|
||||
|
||||
switch pub.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
|
||||
return serializeEncryptedKeyRSA(w, config.Random(), buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
|
||||
return serializeEncryptedKeyRSA(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*rsa.PublicKey), keyBlock)
|
||||
case PubKeyAlgoElGamal:
|
||||
return serializeEncryptedKeyElGamal(w, config.Random(), buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock)
|
||||
return serializeEncryptedKeyElGamal(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*elgamal.PublicKey), keyBlock)
|
||||
case PubKeyAlgoECDH:
|
||||
return serializeEncryptedKeyECDH(w, config.Random(), buf, pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
|
||||
return serializeEncryptedKeyECDH(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*ecdh.PublicKey), keyBlock, pub.oid, pub.Fingerprint)
|
||||
case PubKeyAlgoX25519:
|
||||
return serializeEncryptedKeyX25519(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x25519.PublicKey), keyBlock, byte(cipherFunc), version)
|
||||
case PubKeyAlgoX448:
|
||||
return serializeEncryptedKeyX448(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x448.PublicKey), keyBlock, byte(cipherFunc), version)
|
||||
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
|
||||
return errors.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
@ -211,14 +424,32 @@ func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunctio
|
||||
return errors.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) error {
|
||||
// SerializeEncryptedKey serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub.
|
||||
// PKESKv6 is used if config.AEAD() is not nil.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
// Deprecated: Use SerializeEncryptedKeyAEAD instead.
|
||||
func SerializeEncryptedKey(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, config *Config) error {
|
||||
return SerializeEncryptedKeyAEAD(w, pub, cipherFunc, config.AEAD() != nil, key, config)
|
||||
}
|
||||
|
||||
// SerializeEncryptedKeyWithHiddenOption serializes an encrypted key packet to w that contains
|
||||
// key, encrypted to pub. PKESKv6 is used if config.AEAD() is not nil.
|
||||
// The hidden option controls if the packet should be anonymous, i.e., omit key metadata.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
// Deprecated: Use SerializeEncryptedKeyAEADwithHiddenOption instead.
|
||||
func SerializeEncryptedKeyWithHiddenOption(w io.Writer, pub *PublicKey, cipherFunc CipherFunction, key []byte, hidden bool, config *Config) error {
|
||||
return SerializeEncryptedKeyAEADwithHiddenOption(w, pub, cipherFunc, config.AEAD() != nil, key, hidden, config)
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header []byte, pub *rsa.PublicKey, keyBlock []byte) error {
|
||||
cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("RSA encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
cipherMPI := encoding.NewMPI(cipherText)
|
||||
packetLen := 10 /* header length */ + int(cipherMPI.EncodedLength())
|
||||
packetLen := len(header) /* header length */ + int(cipherMPI.EncodedLength())
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
@ -232,13 +463,13 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error {
|
||||
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header []byte, pub *elgamal.PublicKey, keyBlock []byte) error {
|
||||
c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ElGamal encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := 10 /* header length */
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8
|
||||
packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8
|
||||
|
||||
@ -257,7 +488,7 @@ func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte,
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error {
|
||||
func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header []byte, pub *ecdh.PublicKey, keyBlock []byte, oid encoding.Field, fingerprint []byte) error {
|
||||
vsG, c, err := ecdh.Encrypt(rand, pub, keyBlock, oid.EncodedBytes(), fingerprint)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("ECDH encryption failed: " + err.Error())
|
||||
@ -266,7 +497,7 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub
|
||||
g := encoding.NewMPI(vsG)
|
||||
m := encoding.NewOID(c)
|
||||
|
||||
packetLen := 10 /* header length */
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += int(g.EncodedLength()) + int(m.EncodedLength())
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
@ -284,3 +515,70 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub
|
||||
_, err = w.Write(m.EncodedBytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyX25519(w io.Writer, rand io.Reader, header []byte, pub *x25519.PublicKey, keyBlock []byte, cipherFunc byte, version int) error {
|
||||
ephemeralPublicX25519, ciphertext, err := x25519.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("x25519 encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += x25519.EncodedFieldsLength(ciphertext, version == 6)
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return x25519.EncodeFields(w, ephemeralPublicX25519, ciphertext, cipherFunc, version == 6)
|
||||
}
|
||||
|
||||
func serializeEncryptedKeyX448(w io.Writer, rand io.Reader, header []byte, pub *x448.PublicKey, keyBlock []byte, cipherFunc byte, version int) error {
|
||||
ephemeralPublicX448, ciphertext, err := x448.Encrypt(rand, pub, keyBlock)
|
||||
if err != nil {
|
||||
return errors.InvalidArgumentError("x448 encryption failed: " + err.Error())
|
||||
}
|
||||
|
||||
packetLen := len(header) /* header length */
|
||||
packetLen += x448.EncodedFieldsLength(ciphertext, version == 6)
|
||||
|
||||
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(header[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return x448.EncodeFields(w, ephemeralPublicX448, ciphertext, cipherFunc, version == 6)
|
||||
}
|
||||
|
||||
func checksumKeyMaterial(key []byte) uint16 {
|
||||
var checksum uint16
|
||||
for _, v := range key {
|
||||
checksum += uint16(v)
|
||||
}
|
||||
return checksum
|
||||
}
|
||||
|
||||
func decodeChecksumKey(msg []byte) (key []byte, err error) {
|
||||
key = msg[:len(msg)-2]
|
||||
expectedChecksum := uint16(msg[len(msg)-2])<<8 | uint16(msg[len(msg)-1])
|
||||
checksum := checksumKeyMaterial(key)
|
||||
if checksum != expectedChecksum {
|
||||
err = errors.StructuralError("session key checksum is incorrect")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func encodeChecksumKey(buffer []byte, key []byte) {
|
||||
copy(buffer, key)
|
||||
checksum := checksumKeyMaterial(key)
|
||||
buffer[len(key)] = byte(checksum >> 8)
|
||||
buffer[len(key)+1] = byte(checksum)
|
||||
}
|
||||
|
6
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
generated
vendored
6
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.go
generated
vendored
@ -58,9 +58,9 @@ func (l *LiteralData) parse(r io.Reader) (err error) {
|
||||
// on completion. The fileName is truncated to 255 bytes.
|
||||
func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) {
|
||||
var buf [4]byte
|
||||
buf[0] = 't'
|
||||
if isBinary {
|
||||
buf[0] = 'b'
|
||||
buf[0] = 'b'
|
||||
if !isBinary {
|
||||
buf[0] = 'u'
|
||||
}
|
||||
if len(fileName) > 255 {
|
||||
fileName = fileName[:255]
|
||||
|
33
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go
generated
vendored
Normal file
33
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/marker.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
type Marker struct{}
|
||||
|
||||
const markerString = "PGP"
|
||||
|
||||
// parse just checks if the packet contains "PGP".
|
||||
func (m *Marker) parse(reader io.Reader) error {
|
||||
var buffer [3]byte
|
||||
if _, err := io.ReadFull(reader, buffer[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if string(buffer[:]) != markerString {
|
||||
return errors.StructuralError("invalid marker packet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeMarker writes a marker packet to writer.
|
||||
func SerializeMarker(writer io.Writer) error {
|
||||
err := serializeHeader(writer, packetTypeMarker, len(markerString))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write([]byte(markerString))
|
||||
return err
|
||||
}
|
132
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
generated
vendored
132
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.go
generated
vendored
@ -7,34 +7,37 @@ package packet
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
)
|
||||
|
||||
// OnePassSignature represents a one-pass signature packet. See RFC 4880,
|
||||
// section 5.4.
|
||||
type OnePassSignature struct {
|
||||
SigType SignatureType
|
||||
Hash crypto.Hash
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
KeyId uint64
|
||||
IsLast bool
|
||||
Version int
|
||||
SigType SignatureType
|
||||
Hash crypto.Hash
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
KeyId uint64
|
||||
IsLast bool
|
||||
Salt []byte // v6 only
|
||||
KeyFingerprint []byte // v6 only
|
||||
}
|
||||
|
||||
const onePassSignatureVersion = 3
|
||||
|
||||
func (ops *OnePassSignature) parse(r io.Reader) (err error) {
|
||||
var buf [13]byte
|
||||
|
||||
_, err = readFull(r, buf[:])
|
||||
var buf [8]byte
|
||||
// Read: version | signature type | hash algorithm | public-key algorithm
|
||||
_, err = readFull(r, buf[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != onePassSignatureVersion {
|
||||
err = errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
|
||||
if buf[0] != 3 && buf[0] != 6 {
|
||||
return errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
ops.Version = int(buf[0])
|
||||
|
||||
var ok bool
|
||||
ops.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2])
|
||||
@ -44,15 +47,69 @@ func (ops *OnePassSignature) parse(r io.Reader) (err error) {
|
||||
|
||||
ops.SigType = SignatureType(buf[1])
|
||||
ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3])
|
||||
ops.KeyId = binary.BigEndian.Uint64(buf[4:12])
|
||||
ops.IsLast = buf[12] != 0
|
||||
|
||||
if ops.Version == 6 {
|
||||
// Only for v6, a variable-length field containing the salt
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
saltLength := int(buf[0])
|
||||
var expectedSaltLength int
|
||||
expectedSaltLength, err = SaltLengthForHash(ops.Hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if saltLength != expectedSaltLength {
|
||||
err = errors.StructuralError("unexpected salt size for the given hash algorithm")
|
||||
return
|
||||
}
|
||||
salt := make([]byte, expectedSaltLength)
|
||||
_, err = readFull(r, salt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.Salt = salt
|
||||
|
||||
// Only for v6 packets, 32 octets of the fingerprint of the signing key.
|
||||
fingerprint := make([]byte, 32)
|
||||
_, err = readFull(r, fingerprint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.KeyFingerprint = fingerprint
|
||||
ops.KeyId = binary.BigEndian.Uint64(ops.KeyFingerprint[:8])
|
||||
} else {
|
||||
_, err = readFull(r, buf[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.KeyId = binary.BigEndian.Uint64(buf[:8])
|
||||
}
|
||||
|
||||
_, err = readFull(r, buf[:1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ops.IsLast = buf[0] != 0
|
||||
return
|
||||
}
|
||||
|
||||
// Serialize marshals the given OnePassSignature to w.
|
||||
func (ops *OnePassSignature) Serialize(w io.Writer) error {
|
||||
var buf [13]byte
|
||||
buf[0] = onePassSignatureVersion
|
||||
//v3 length 1+1+1+1+8+1 =
|
||||
packetLength := 13
|
||||
if ops.Version == 6 {
|
||||
// v6 length 1+1+1+1+1+len(salt)+32+1 =
|
||||
packetLength = 38 + len(ops.Salt)
|
||||
}
|
||||
|
||||
if err := serializeHeader(w, packetTypeOnePassSignature, packetLength); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf [8]byte
|
||||
buf[0] = byte(ops.Version)
|
||||
buf[1] = uint8(ops.SigType)
|
||||
var ok bool
|
||||
buf[2], ok = algorithm.HashToHashIdWithSha1(ops.Hash)
|
||||
@ -60,14 +117,41 @@ func (ops *OnePassSignature) Serialize(w io.Writer) error {
|
||||
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash)))
|
||||
}
|
||||
buf[3] = uint8(ops.PubKeyAlgo)
|
||||
binary.BigEndian.PutUint64(buf[4:12], ops.KeyId)
|
||||
if ops.IsLast {
|
||||
buf[12] = 1
|
||||
}
|
||||
|
||||
if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil {
|
||||
_, err := w.Write(buf[:4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(buf[:])
|
||||
|
||||
if ops.Version == 6 {
|
||||
// write salt for v6 signatures
|
||||
_, err := w.Write([]byte{uint8(len(ops.Salt))})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(ops.Salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write fingerprint v6 signatures
|
||||
_, err = w.Write(ops.KeyFingerprint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
binary.BigEndian.PutUint64(buf[:8], ops.KeyId)
|
||||
_, err := w.Write(buf[:8])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
isLast := []byte{byte(0)}
|
||||
if ops.IsLast {
|
||||
isLast[0] = 1
|
||||
}
|
||||
|
||||
_, err = w.Write(isLast)
|
||||
return err
|
||||
}
|
||||
|
3
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
generated
vendored
3
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.go
generated
vendored
@ -7,7 +7,6 @@ package packet
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
@ -26,7 +25,7 @@ type OpaquePacket struct {
|
||||
}
|
||||
|
||||
func (op *OpaquePacket) parse(r io.Reader) (err error) {
|
||||
op.Contents, err = ioutil.ReadAll(r)
|
||||
op.Contents, err = io.ReadAll(r)
|
||||
return
|
||||
}
|
||||
|
||||
|
154
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
generated
vendored
154
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.go
generated
vendored
@ -311,12 +311,15 @@ const (
|
||||
packetTypePrivateSubkey packetType = 7
|
||||
packetTypeCompressed packetType = 8
|
||||
packetTypeSymmetricallyEncrypted packetType = 9
|
||||
packetTypeMarker packetType = 10
|
||||
packetTypeLiteralData packetType = 11
|
||||
packetTypeTrust packetType = 12
|
||||
packetTypeUserId packetType = 13
|
||||
packetTypePublicSubkey packetType = 14
|
||||
packetTypeUserAttribute packetType = 17
|
||||
packetTypeSymmetricallyEncryptedIntegrityProtected packetType = 18
|
||||
packetTypeAEADEncrypted packetType = 20
|
||||
packetPadding packetType = 21
|
||||
)
|
||||
|
||||
// EncryptedDataPacket holds encrypted data. It is currently implemented by
|
||||
@ -328,7 +331,7 @@ type EncryptedDataPacket interface {
|
||||
// Read reads a single OpenPGP packet from the given io.Reader. If there is an
|
||||
// error parsing a packet, the whole packet is consumed from the input.
|
||||
func Read(r io.Reader) (p Packet, err error) {
|
||||
tag, _, contents, err := readHeader(r)
|
||||
tag, len, contents, err := readHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -367,8 +370,93 @@ func Read(r io.Reader) (p Packet, err error) {
|
||||
p = se
|
||||
case packetTypeAEADEncrypted:
|
||||
p = new(AEADEncrypted)
|
||||
default:
|
||||
case packetPadding:
|
||||
p = Padding(len)
|
||||
case packetTypeMarker:
|
||||
p = new(Marker)
|
||||
case packetTypeTrust:
|
||||
// Not implemented, just consume
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
default:
|
||||
// Packet Tags from 0 to 39 are critical.
|
||||
// Packet Tags from 40 to 63 are non-critical.
|
||||
if tag < 40 {
|
||||
err = errors.CriticalUnknownPacketTypeError(tag)
|
||||
} else {
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
}
|
||||
}
|
||||
if p != nil {
|
||||
err = p.parse(contents)
|
||||
}
|
||||
if err != nil {
|
||||
consumeAll(contents)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadWithCheck reads a single OpenPGP message packet from the given io.Reader. If there is an
|
||||
// error parsing a packet, the whole packet is consumed from the input.
|
||||
// ReadWithCheck additionally checks if the OpenPGP message packet sequence adheres
|
||||
// to the packet composition rules in rfc4880, if not throws an error.
|
||||
func ReadWithCheck(r io.Reader, sequence *SequenceVerifier) (p Packet, msgErr error, err error) {
|
||||
tag, len, contents, err := readHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch tag {
|
||||
case packetTypeEncryptedKey:
|
||||
msgErr = sequence.Next(ESKSymbol)
|
||||
p = new(EncryptedKey)
|
||||
case packetTypeSignature:
|
||||
msgErr = sequence.Next(SigSymbol)
|
||||
p = new(Signature)
|
||||
case packetTypeSymmetricKeyEncrypted:
|
||||
msgErr = sequence.Next(ESKSymbol)
|
||||
p = new(SymmetricKeyEncrypted)
|
||||
case packetTypeOnePassSignature:
|
||||
msgErr = sequence.Next(OPSSymbol)
|
||||
p = new(OnePassSignature)
|
||||
case packetTypeCompressed:
|
||||
msgErr = sequence.Next(CompSymbol)
|
||||
p = new(Compressed)
|
||||
case packetTypeSymmetricallyEncrypted:
|
||||
msgErr = sequence.Next(EncSymbol)
|
||||
p = new(SymmetricallyEncrypted)
|
||||
case packetTypeLiteralData:
|
||||
msgErr = sequence.Next(LDSymbol)
|
||||
p = new(LiteralData)
|
||||
case packetTypeSymmetricallyEncryptedIntegrityProtected:
|
||||
msgErr = sequence.Next(EncSymbol)
|
||||
se := new(SymmetricallyEncrypted)
|
||||
se.IntegrityProtected = true
|
||||
p = se
|
||||
case packetTypeAEADEncrypted:
|
||||
msgErr = sequence.Next(EncSymbol)
|
||||
p = new(AEADEncrypted)
|
||||
case packetPadding:
|
||||
p = Padding(len)
|
||||
case packetTypeMarker:
|
||||
p = new(Marker)
|
||||
case packetTypeTrust:
|
||||
// Not implemented, just consume
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
case packetTypePrivateKey,
|
||||
packetTypePrivateSubkey,
|
||||
packetTypePublicKey,
|
||||
packetTypePublicSubkey,
|
||||
packetTypeUserId,
|
||||
packetTypeUserAttribute:
|
||||
msgErr = sequence.Next(UnknownSymbol)
|
||||
consumeAll(contents)
|
||||
default:
|
||||
// Packet Tags from 0 to 39 are critical.
|
||||
// Packet Tags from 40 to 63 are non-critical.
|
||||
if tag < 40 {
|
||||
err = errors.CriticalUnknownPacketTypeError(tag)
|
||||
} else {
|
||||
err = errors.UnknownPacketTypeError(tag)
|
||||
}
|
||||
}
|
||||
if p != nil {
|
||||
err = p.parse(contents)
|
||||
@ -385,17 +473,17 @@ type SignatureType uint8
|
||||
|
||||
const (
|
||||
SigTypeBinary SignatureType = 0x00
|
||||
SigTypeText = 0x01
|
||||
SigTypeGenericCert = 0x10
|
||||
SigTypePersonaCert = 0x11
|
||||
SigTypeCasualCert = 0x12
|
||||
SigTypePositiveCert = 0x13
|
||||
SigTypeSubkeyBinding = 0x18
|
||||
SigTypePrimaryKeyBinding = 0x19
|
||||
SigTypeDirectSignature = 0x1F
|
||||
SigTypeKeyRevocation = 0x20
|
||||
SigTypeSubkeyRevocation = 0x28
|
||||
SigTypeCertificationRevocation = 0x30
|
||||
SigTypeText SignatureType = 0x01
|
||||
SigTypeGenericCert SignatureType = 0x10
|
||||
SigTypePersonaCert SignatureType = 0x11
|
||||
SigTypeCasualCert SignatureType = 0x12
|
||||
SigTypePositiveCert SignatureType = 0x13
|
||||
SigTypeSubkeyBinding SignatureType = 0x18
|
||||
SigTypePrimaryKeyBinding SignatureType = 0x19
|
||||
SigTypeDirectSignature SignatureType = 0x1F
|
||||
SigTypeKeyRevocation SignatureType = 0x20
|
||||
SigTypeSubkeyRevocation SignatureType = 0x28
|
||||
SigTypeCertificationRevocation SignatureType = 0x30
|
||||
)
|
||||
|
||||
// PublicKeyAlgorithm represents the different public key system specified for
|
||||
@ -412,6 +500,11 @@ const (
|
||||
PubKeyAlgoECDSA PublicKeyAlgorithm = 19
|
||||
// https://www.ietf.org/archive/id/draft-koch-eddsa-for-openpgp-04.txt
|
||||
PubKeyAlgoEdDSA PublicKeyAlgorithm = 22
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh
|
||||
PubKeyAlgoX25519 PublicKeyAlgorithm = 25
|
||||
PubKeyAlgoX448 PublicKeyAlgorithm = 26
|
||||
PubKeyAlgoEd25519 PublicKeyAlgorithm = 27
|
||||
PubKeyAlgoEd448 PublicKeyAlgorithm = 28
|
||||
|
||||
// Deprecated in RFC 4880, Section 13.5. Use key flags instead.
|
||||
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
|
||||
@ -422,7 +515,7 @@ const (
|
||||
// key of the given type.
|
||||
func (pka PublicKeyAlgorithm) CanEncrypt() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH:
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal, PubKeyAlgoECDH, PubKeyAlgoX25519, PubKeyAlgoX448:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -432,7 +525,7 @@ func (pka PublicKeyAlgorithm) CanEncrypt() bool {
|
||||
// sign a message.
|
||||
func (pka PublicKeyAlgorithm) CanSign() bool {
|
||||
switch pka {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA, PubKeyAlgoEd25519, PubKeyAlgoEd448:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -512,6 +605,11 @@ func (mode AEADMode) TagLength() int {
|
||||
return algorithm.AEADMode(mode).TagLength()
|
||||
}
|
||||
|
||||
// IsSupported returns true if the aead mode is supported from the library
|
||||
func (mode AEADMode) IsSupported() bool {
|
||||
return algorithm.AEADMode(mode).TagLength() > 0
|
||||
}
|
||||
|
||||
// new returns a fresh instance of the given mode.
|
||||
func (mode AEADMode) new(block cipher.Block) cipher.AEAD {
|
||||
return algorithm.AEADMode(mode).New(block)
|
||||
@ -526,8 +624,17 @@ const (
|
||||
KeySuperseded ReasonForRevocation = 1
|
||||
KeyCompromised ReasonForRevocation = 2
|
||||
KeyRetired ReasonForRevocation = 3
|
||||
UserIDNotValid ReasonForRevocation = 32
|
||||
Unknown ReasonForRevocation = 200
|
||||
)
|
||||
|
||||
func NewReasonForRevocation(value byte) ReasonForRevocation {
|
||||
if value < 4 || value == 32 {
|
||||
return ReasonForRevocation(value)
|
||||
}
|
||||
return Unknown
|
||||
}
|
||||
|
||||
// Curve is a mapping to supported ECC curves for key generation.
|
||||
// See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-06.html#name-curve-specific-wire-formats
|
||||
type Curve string
|
||||
@ -549,3 +656,20 @@ type TrustLevel uint8
|
||||
|
||||
// TrustAmount represents a trust amount per RFC4880 5.2.3.13
|
||||
type TrustAmount uint8
|
||||
|
||||
const (
|
||||
// versionSize is the length in bytes of the version value.
|
||||
versionSize = 1
|
||||
// algorithmSize is the length in bytes of the key algorithm value.
|
||||
algorithmSize = 1
|
||||
// keyVersionSize is the length in bytes of the key version value
|
||||
keyVersionSize = 1
|
||||
// keyIdSize is the length in bytes of the key identifier value.
|
||||
keyIdSize = 8
|
||||
// timestampSize is the length in bytes of encoded timestamps.
|
||||
timestampSize = 4
|
||||
// fingerprintSizeV6 is the length in bytes of the key fingerprint in v6.
|
||||
fingerprintSizeV6 = 32
|
||||
// fingerprintSize is the length in bytes of the key fingerprint.
|
||||
fingerprintSize = 20
|
||||
)
|
||||
|
222
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go
generated
vendored
Normal file
222
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_sequence.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
package packet
|
||||
|
||||
// This file implements the pushdown automata (PDA) from PGPainless (Paul Schaub)
|
||||
// to verify pgp packet sequences. See Paul's blogpost for more details:
|
||||
// https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
func NewErrMalformedMessage(from State, input InputSymbol, stackSymbol StackSymbol) errors.ErrMalformedMessage {
|
||||
return errors.ErrMalformedMessage(fmt.Sprintf("state %d, input symbol %d, stack symbol %d ", from, input, stackSymbol))
|
||||
}
|
||||
|
||||
// InputSymbol defines the input alphabet of the PDA
|
||||
type InputSymbol uint8
|
||||
|
||||
const (
|
||||
LDSymbol InputSymbol = iota
|
||||
SigSymbol
|
||||
OPSSymbol
|
||||
CompSymbol
|
||||
ESKSymbol
|
||||
EncSymbol
|
||||
EOSSymbol
|
||||
UnknownSymbol
|
||||
)
|
||||
|
||||
// StackSymbol defines the stack alphabet of the PDA
|
||||
type StackSymbol int8
|
||||
|
||||
const (
|
||||
MsgStackSymbol StackSymbol = iota
|
||||
OpsStackSymbol
|
||||
KeyStackSymbol
|
||||
EndStackSymbol
|
||||
EmptyStackSymbol
|
||||
)
|
||||
|
||||
// State defines the states of the PDA
|
||||
type State int8
|
||||
|
||||
const (
|
||||
OpenPGPMessage State = iota
|
||||
ESKMessage
|
||||
LiteralMessage
|
||||
CompressedMessage
|
||||
EncryptedMessage
|
||||
ValidMessage
|
||||
)
|
||||
|
||||
// transition represents a state transition in the PDA
|
||||
type transition func(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error)
|
||||
|
||||
// SequenceVerifier is a pushdown automata to verify
|
||||
// PGP messages packet sequences according to rfc4880.
|
||||
type SequenceVerifier struct {
|
||||
stack []StackSymbol
|
||||
state State
|
||||
}
|
||||
|
||||
// Next performs a state transition with the given input symbol.
|
||||
// If the transition fails a ErrMalformedMessage is returned.
|
||||
func (sv *SequenceVerifier) Next(input InputSymbol) error {
|
||||
for {
|
||||
stackSymbol := sv.popStack()
|
||||
transitionFunc := getTransition(sv.state)
|
||||
nextState, newStackSymbols, redo, err := transitionFunc(input, stackSymbol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if redo {
|
||||
sv.pushStack(stackSymbol)
|
||||
}
|
||||
for _, newStackSymbol := range newStackSymbols {
|
||||
sv.pushStack(newStackSymbol)
|
||||
}
|
||||
sv.state = nextState
|
||||
if !redo {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Valid returns true if RDA is in a valid state.
|
||||
func (sv *SequenceVerifier) Valid() bool {
|
||||
return sv.state == ValidMessage && len(sv.stack) == 0
|
||||
}
|
||||
|
||||
func (sv *SequenceVerifier) AssertValid() error {
|
||||
if !sv.Valid() {
|
||||
return errors.ErrMalformedMessage("invalid message")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSequenceVerifier() *SequenceVerifier {
|
||||
return &SequenceVerifier{
|
||||
stack: []StackSymbol{EndStackSymbol, MsgStackSymbol},
|
||||
state: OpenPGPMessage,
|
||||
}
|
||||
}
|
||||
|
||||
func (sv *SequenceVerifier) popStack() StackSymbol {
|
||||
if len(sv.stack) == 0 {
|
||||
return EmptyStackSymbol
|
||||
}
|
||||
elemIndex := len(sv.stack) - 1
|
||||
stackSymbol := sv.stack[elemIndex]
|
||||
sv.stack = sv.stack[:elemIndex]
|
||||
return stackSymbol
|
||||
}
|
||||
|
||||
func (sv *SequenceVerifier) pushStack(stackSymbol StackSymbol) {
|
||||
sv.stack = append(sv.stack, stackSymbol)
|
||||
}
|
||||
|
||||
func getTransition(from State) transition {
|
||||
switch from {
|
||||
case OpenPGPMessage:
|
||||
return fromOpenPGPMessage
|
||||
case LiteralMessage:
|
||||
return fromLiteralMessage
|
||||
case CompressedMessage:
|
||||
return fromCompressedMessage
|
||||
case EncryptedMessage:
|
||||
return fromEncryptedMessage
|
||||
case ESKMessage:
|
||||
return fromESKMessage
|
||||
case ValidMessage:
|
||||
return fromValidMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fromOpenPGPMessage is the transition for the state OpenPGPMessage.
|
||||
func fromOpenPGPMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
if stackSymbol != MsgStackSymbol {
|
||||
return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol)
|
||||
}
|
||||
switch input {
|
||||
case LDSymbol:
|
||||
return LiteralMessage, nil, false, nil
|
||||
case SigSymbol:
|
||||
return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, false, nil
|
||||
case OPSSymbol:
|
||||
return OpenPGPMessage, []StackSymbol{OpsStackSymbol, MsgStackSymbol}, false, nil
|
||||
case CompSymbol:
|
||||
return CompressedMessage, nil, false, nil
|
||||
case ESKSymbol:
|
||||
return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil
|
||||
case EncSymbol:
|
||||
return EncryptedMessage, nil, false, nil
|
||||
}
|
||||
return 0, nil, false, NewErrMalformedMessage(OpenPGPMessage, input, stackSymbol)
|
||||
}
|
||||
|
||||
// fromESKMessage is the transition for the state ESKMessage.
|
||||
func fromESKMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
if stackSymbol != KeyStackSymbol {
|
||||
return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol)
|
||||
}
|
||||
switch input {
|
||||
case ESKSymbol:
|
||||
return ESKMessage, []StackSymbol{KeyStackSymbol}, false, nil
|
||||
case EncSymbol:
|
||||
return EncryptedMessage, nil, false, nil
|
||||
}
|
||||
return 0, nil, false, NewErrMalformedMessage(ESKMessage, input, stackSymbol)
|
||||
}
|
||||
|
||||
// fromLiteralMessage is the transition for the state LiteralMessage.
|
||||
func fromLiteralMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
switch input {
|
||||
case SigSymbol:
|
||||
if stackSymbol == OpsStackSymbol {
|
||||
return LiteralMessage, nil, false, nil
|
||||
}
|
||||
case EOSSymbol:
|
||||
if stackSymbol == EndStackSymbol {
|
||||
return ValidMessage, nil, false, nil
|
||||
}
|
||||
}
|
||||
return 0, nil, false, NewErrMalformedMessage(LiteralMessage, input, stackSymbol)
|
||||
}
|
||||
|
||||
// fromLiteralMessage is the transition for the state CompressedMessage.
|
||||
func fromCompressedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
switch input {
|
||||
case SigSymbol:
|
||||
if stackSymbol == OpsStackSymbol {
|
||||
return CompressedMessage, nil, false, nil
|
||||
}
|
||||
case EOSSymbol:
|
||||
if stackSymbol == EndStackSymbol {
|
||||
return ValidMessage, nil, false, nil
|
||||
}
|
||||
}
|
||||
return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil
|
||||
}
|
||||
|
||||
// fromEncryptedMessage is the transition for the state EncryptedMessage.
|
||||
func fromEncryptedMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
switch input {
|
||||
case SigSymbol:
|
||||
if stackSymbol == OpsStackSymbol {
|
||||
return EncryptedMessage, nil, false, nil
|
||||
}
|
||||
case EOSSymbol:
|
||||
if stackSymbol == EndStackSymbol {
|
||||
return ValidMessage, nil, false, nil
|
||||
}
|
||||
}
|
||||
return OpenPGPMessage, []StackSymbol{MsgStackSymbol}, true, nil
|
||||
}
|
||||
|
||||
// fromValidMessage is the transition for the state ValidMessage.
|
||||
func fromValidMessage(input InputSymbol, stackSymbol StackSymbol) (State, []StackSymbol, bool, error) {
|
||||
return 0, nil, false, NewErrMalformedMessage(ValidMessage, input, stackSymbol)
|
||||
}
|
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go
generated
vendored
Normal file
24
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
// UnsupportedPackage represents a OpenPGP packet with a known packet type
|
||||
// but with unsupported content.
|
||||
type UnsupportedPacket struct {
|
||||
IncompletePacket Packet
|
||||
Error errors.UnsupportedError
|
||||
}
|
||||
|
||||
// Implements the Packet interface
|
||||
func (up *UnsupportedPacket) parse(read io.Reader) error {
|
||||
err := up.IncompletePacket.parse(read)
|
||||
if castedErr, ok := err.(errors.UnsupportedError); ok {
|
||||
up.Error = castedErr
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
26
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go
generated
vendored
Normal file
26
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/padding.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Padding type represents a Padding Packet (Tag 21).
|
||||
// The padding type is represented by the length of its padding.
|
||||
// see https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21
|
||||
type Padding int
|
||||
|
||||
// parse just ignores the padding content.
|
||||
func (pad Padding) parse(reader io.Reader) error {
|
||||
_, err := io.CopyN(io.Discard, reader, int64(pad))
|
||||
return err
|
||||
}
|
||||
|
||||
// SerializePadding writes the padding to writer.
|
||||
func (pad Padding) SerializePadding(writer io.Writer, rand io.Reader) error {
|
||||
err := serializeHeader(writer, packetPadding, int(pad))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.CopyN(writer, rand, int64(pad))
|
||||
return err
|
||||
}
|
540
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
generated
vendored
540
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.go
generated
vendored
@ -9,22 +9,28 @@ import (
|
||||
"crypto"
|
||||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ed25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ed448"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x448"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
||||
@ -35,14 +41,14 @@ type PrivateKey struct {
|
||||
encryptedData []byte
|
||||
cipher CipherFunction
|
||||
s2k func(out, in []byte)
|
||||
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or
|
||||
aead AEADMode // only relevant if S2KAEAD is enabled
|
||||
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519|ed448}.PrivateKey or
|
||||
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
|
||||
PrivateKey interface{}
|
||||
sha1Checksum bool
|
||||
iv []byte
|
||||
PrivateKey interface{}
|
||||
iv []byte
|
||||
|
||||
// Type of encryption of the S2K packet
|
||||
// Allowed values are 0 (Not encrypted), 254 (SHA1), or
|
||||
// Allowed values are 0 (Not encrypted), 253 (AEAD), 254 (SHA1), or
|
||||
// 255 (2-byte checksum)
|
||||
s2kType S2KType
|
||||
// Full parameters of the S2K packet
|
||||
@ -55,6 +61,8 @@ type S2KType uint8
|
||||
const (
|
||||
// S2KNON unencrypt
|
||||
S2KNON S2KType = 0
|
||||
// S2KAEAD use authenticated encryption
|
||||
S2KAEAD S2KType = 253
|
||||
// S2KSHA1 sha1 sum check
|
||||
S2KSHA1 S2KType = 254
|
||||
// S2KCHECKSUM sum check
|
||||
@ -103,6 +111,34 @@ func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKe
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewX25519PrivateKey(creationTime time.Time, priv *x25519.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewX25519PublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewX448PrivateKey(creationTime time.Time, priv *x448.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewX448PublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewEd25519PrivateKey(creationTime time.Time, priv *ed25519.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewEd25519PublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewEd448PrivateKey(creationTime time.Time, priv *ed448.PrivateKey) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
pk.PublicKey = *NewEd448PublicKey(creationTime, &priv.PublicKey)
|
||||
pk.PrivateKey = priv
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that
|
||||
// implements RSA, ECDSA or EdDSA.
|
||||
func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey {
|
||||
@ -122,6 +158,14 @@ func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey
|
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey)
|
||||
case eddsa.PrivateKey:
|
||||
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey)
|
||||
case *ed25519.PrivateKey:
|
||||
pk.PublicKey = *NewEd25519PublicKey(creationTime, &pubkey.PublicKey)
|
||||
case ed25519.PrivateKey:
|
||||
pk.PublicKey = *NewEd25519PublicKey(creationTime, &pubkey.PublicKey)
|
||||
case *ed448.PrivateKey:
|
||||
pk.PublicKey = *NewEd448PublicKey(creationTime, &pubkey.PublicKey)
|
||||
case ed448.PrivateKey:
|
||||
pk.PublicKey = *NewEd448PublicKey(creationTime, &pubkey.PublicKey)
|
||||
default:
|
||||
panic("openpgp: unknown signer type in NewSignerPrivateKey")
|
||||
}
|
||||
@ -129,7 +173,7 @@ func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey
|
||||
return pk
|
||||
}
|
||||
|
||||
// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey.
|
||||
// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh|x25519|x448}.PrivateKey.
|
||||
func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey {
|
||||
pk := new(PrivateKey)
|
||||
switch priv := decrypter.(type) {
|
||||
@ -139,6 +183,10 @@ func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *Priv
|
||||
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
|
||||
case *ecdh.PrivateKey:
|
||||
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
|
||||
case *x25519.PrivateKey:
|
||||
pk.PublicKey = *NewX25519PublicKey(creationTime, &priv.PublicKey)
|
||||
case *x448.PrivateKey:
|
||||
pk.PublicKey = *NewX448PublicKey(creationTime, &priv.PublicKey)
|
||||
default:
|
||||
panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey")
|
||||
}
|
||||
@ -152,6 +200,11 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
v5 := pk.PublicKey.Version == 5
|
||||
v6 := pk.PublicKey.Version == 6
|
||||
|
||||
if V5Disabled && v5 {
|
||||
return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
|
||||
}
|
||||
|
||||
var buf [1]byte
|
||||
_, err = readFull(r, buf[:])
|
||||
@ -160,7 +213,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
}
|
||||
pk.s2kType = S2KType(buf[0])
|
||||
var optCount [1]byte
|
||||
if v5 {
|
||||
if v5 || (v6 && pk.s2kType != S2KNON) {
|
||||
if _, err = readFull(r, optCount[:]); err != nil {
|
||||
return
|
||||
}
|
||||
@ -170,9 +223,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
case S2KNON:
|
||||
pk.s2k = nil
|
||||
pk.Encrypted = false
|
||||
case S2KSHA1, S2KCHECKSUM:
|
||||
if v5 && pk.s2kType == S2KCHECKSUM {
|
||||
return errors.StructuralError("wrong s2k identifier for version 5")
|
||||
case S2KSHA1, S2KCHECKSUM, S2KAEAD:
|
||||
if (v5 || v6) && pk.s2kType == S2KCHECKSUM {
|
||||
return errors.StructuralError(fmt.Sprintf("wrong s2k identifier for version %d", pk.Version))
|
||||
}
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
@ -182,6 +235,29 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
if pk.cipher != 0 && !pk.cipher.IsSupported() {
|
||||
return errors.UnsupportedError("unsupported cipher function in private key")
|
||||
}
|
||||
// [Optional] If string-to-key usage octet was 253,
|
||||
// a one-octet AEAD algorithm.
|
||||
if pk.s2kType == S2KAEAD {
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.aead = AEADMode(buf[0])
|
||||
if !pk.aead.IsSupported() {
|
||||
return errors.UnsupportedError("unsupported aead mode in private key")
|
||||
}
|
||||
}
|
||||
|
||||
// [Optional] Only for a version 6 packet,
|
||||
// and if string-to-key usage octet was 255, 254, or 253,
|
||||
// an one-octet count of the following field.
|
||||
if v6 {
|
||||
_, err = readFull(r, buf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pk.s2kParams, err = s2k.ParseIntoParams(r)
|
||||
if err != nil {
|
||||
return
|
||||
@ -189,28 +265,43 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
if pk.s2kParams.Dummy() {
|
||||
return
|
||||
}
|
||||
if pk.s2kParams.Mode() == s2k.Argon2S2K && pk.s2kType != S2KAEAD {
|
||||
return errors.StructuralError("using Argon2 S2K without AEAD is not allowed")
|
||||
}
|
||||
if pk.s2kParams.Mode() == s2k.SimpleS2K && pk.Version == 6 {
|
||||
return errors.StructuralError("using Simple S2K with version 6 keys is not allowed")
|
||||
}
|
||||
pk.s2k, err = pk.s2kParams.Function()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pk.Encrypted = true
|
||||
if pk.s2kType == S2KSHA1 {
|
||||
pk.sha1Checksum = true
|
||||
}
|
||||
default:
|
||||
return errors.UnsupportedError("deprecated s2k function in private key")
|
||||
}
|
||||
|
||||
if pk.Encrypted {
|
||||
blockSize := pk.cipher.blockSize()
|
||||
if blockSize == 0 {
|
||||
var ivSize int
|
||||
// If the S2K usage octet was 253, the IV is of the size expected by the AEAD mode,
|
||||
// unless it's a version 5 key, in which case it's the size of the symmetric cipher's block size.
|
||||
// For all other S2K modes, it's always the block size.
|
||||
if !v5 && pk.s2kType == S2KAEAD {
|
||||
ivSize = pk.aead.IvLength()
|
||||
} else {
|
||||
ivSize = pk.cipher.blockSize()
|
||||
}
|
||||
|
||||
if ivSize == 0 {
|
||||
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher)))
|
||||
}
|
||||
pk.iv = make([]byte, blockSize)
|
||||
pk.iv = make([]byte, ivSize)
|
||||
_, err = readFull(r, pk.iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if v5 && pk.s2kType == S2KAEAD {
|
||||
pk.iv = pk.iv[:pk.aead.IvLength()]
|
||||
}
|
||||
}
|
||||
|
||||
var privateKeyData []byte
|
||||
@ -230,7 +321,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
privateKeyData, err = ioutil.ReadAll(r)
|
||||
privateKeyData, err = io.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -239,16 +330,22 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
||||
if len(privateKeyData) < 2 {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
var sum uint16
|
||||
for i := 0; i < len(privateKeyData)-2; i++ {
|
||||
sum += uint16(privateKeyData[i])
|
||||
if pk.Version != 6 {
|
||||
// checksum
|
||||
var sum uint16
|
||||
for i := 0; i < len(privateKeyData)-2; i++ {
|
||||
sum += uint16(privateKeyData[i])
|
||||
}
|
||||
if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) ||
|
||||
privateKeyData[len(privateKeyData)-1] != uint8(sum) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
privateKeyData = privateKeyData[:len(privateKeyData)-2]
|
||||
return pk.parsePrivateKey(privateKeyData)
|
||||
} else {
|
||||
// No checksum
|
||||
return pk.parsePrivateKey(privateKeyData)
|
||||
}
|
||||
if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) ||
|
||||
privateKeyData[len(privateKeyData)-1] != uint8(sum) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
privateKeyData = privateKeyData[:len(privateKeyData)-2]
|
||||
return pk.parsePrivateKey(privateKeyData)
|
||||
}
|
||||
|
||||
pk.encryptedData = privateKeyData
|
||||
@ -280,18 +377,59 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
||||
|
||||
optional := bytes.NewBuffer(nil)
|
||||
if pk.Encrypted || pk.Dummy() {
|
||||
optional.Write([]byte{uint8(pk.cipher)})
|
||||
if err := pk.s2kParams.Serialize(optional); err != nil {
|
||||
// [Optional] If string-to-key usage octet was 255, 254, or 253,
|
||||
// a one-octet symmetric encryption algorithm.
|
||||
if _, err = optional.Write([]byte{uint8(pk.cipher)}); err != nil {
|
||||
return
|
||||
}
|
||||
// [Optional] If string-to-key usage octet was 253,
|
||||
// a one-octet AEAD algorithm.
|
||||
if pk.s2kType == S2KAEAD {
|
||||
if _, err = optional.Write([]byte{uint8(pk.aead)}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s2kBuffer := bytes.NewBuffer(nil)
|
||||
if err := pk.s2kParams.Serialize(s2kBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
// [Optional] Only for a version 6 packet, and if string-to-key
|
||||
// usage octet was 255, 254, or 253, an one-octet
|
||||
// count of the following field.
|
||||
if pk.Version == 6 {
|
||||
if _, err = optional.Write([]byte{uint8(s2kBuffer.Len())}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// [Optional] If string-to-key usage octet was 255, 254, or 253,
|
||||
// a string-to-key (S2K) specifier. The length of the string-to-key specifier
|
||||
// depends on its type
|
||||
if _, err = io.Copy(optional, s2kBuffer); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// IV
|
||||
if pk.Encrypted {
|
||||
optional.Write(pk.iv)
|
||||
if _, err = optional.Write(pk.iv); err != nil {
|
||||
return
|
||||
}
|
||||
if pk.Version == 5 && pk.s2kType == S2KAEAD {
|
||||
// Add padding for version 5
|
||||
padding := make([]byte, pk.cipher.blockSize()-len(pk.iv))
|
||||
if _, err = optional.Write(padding); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if pk.Version == 5 {
|
||||
if pk.Version == 5 || (pk.Version == 6 && pk.s2kType != S2KNON) {
|
||||
contents.Write([]byte{uint8(optional.Len())})
|
||||
}
|
||||
io.Copy(contents, optional)
|
||||
|
||||
if _, err := io.Copy(contents, optional); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !pk.Dummy() {
|
||||
l := 0
|
||||
@ -303,8 +441,10 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
||||
return err
|
||||
}
|
||||
l = buf.Len()
|
||||
checksum := mod64kHash(buf.Bytes())
|
||||
buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
|
||||
if pk.Version != 6 {
|
||||
checksum := mod64kHash(buf.Bytes())
|
||||
buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
|
||||
}
|
||||
priv = buf.Bytes()
|
||||
} else {
|
||||
priv, l = pk.encryptedData, len(pk.encryptedData)
|
||||
@ -370,6 +510,26 @@ func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeX25519PrivateKey(w io.Writer, priv *x25519.PrivateKey) error {
|
||||
_, err := w.Write(priv.Secret)
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeX448PrivateKey(w io.Writer, priv *x448.PrivateKey) error {
|
||||
_, err := w.Write(priv.Secret)
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEd25519PrivateKey(w io.Writer, priv *ed25519.PrivateKey) error {
|
||||
_, err := w.Write(priv.MarshalByteSecret())
|
||||
return err
|
||||
}
|
||||
|
||||
func serializeEd448PrivateKey(w io.Writer, priv *ed448.PrivateKey) error {
|
||||
_, err := w.Write(priv.MarshalByteSecret())
|
||||
return err
|
||||
}
|
||||
|
||||
// decrypt decrypts an encrypted private key using a decryption key.
|
||||
func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
|
||||
if pk.Dummy() {
|
||||
@ -378,37 +538,51 @@ func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
|
||||
if !pk.Encrypted {
|
||||
return nil
|
||||
}
|
||||
|
||||
block := pk.cipher.new(decryptionKey)
|
||||
cfb := cipher.NewCFBDecrypter(block, pk.iv)
|
||||
|
||||
data := make([]byte, len(pk.encryptedData))
|
||||
cfb.XORKeyStream(data, pk.encryptedData)
|
||||
|
||||
if pk.sha1Checksum {
|
||||
if len(data) < sha1.Size {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
var data []byte
|
||||
switch pk.s2kType {
|
||||
case S2KAEAD:
|
||||
aead := pk.aead.new(block)
|
||||
additionalData, err := pk.additionalData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := sha1.New()
|
||||
h.Write(data[:len(data)-sha1.Size])
|
||||
sum := h.Sum(nil)
|
||||
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
// Decrypt the encrypted key material with aead
|
||||
data, err = aead.Open(nil, pk.iv, pk.encryptedData, additionalData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = data[:len(data)-sha1.Size]
|
||||
} else {
|
||||
if len(data) < 2 {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
case S2KSHA1, S2KCHECKSUM:
|
||||
cfb := cipher.NewCFBDecrypter(block, pk.iv)
|
||||
data = make([]byte, len(pk.encryptedData))
|
||||
cfb.XORKeyStream(data, pk.encryptedData)
|
||||
if pk.s2kType == S2KSHA1 {
|
||||
if len(data) < sha1.Size {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
h := sha1.New()
|
||||
h.Write(data[:len(data)-sha1.Size])
|
||||
sum := h.Sum(nil)
|
||||
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-sha1.Size]
|
||||
} else {
|
||||
if len(data) < 2 {
|
||||
return errors.StructuralError("truncated private key data")
|
||||
}
|
||||
var sum uint16
|
||||
for i := 0; i < len(data)-2; i++ {
|
||||
sum += uint16(data[i])
|
||||
}
|
||||
if data[len(data)-2] != uint8(sum>>8) ||
|
||||
data[len(data)-1] != uint8(sum) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-2]
|
||||
}
|
||||
var sum uint16
|
||||
for i := 0; i < len(data)-2; i++ {
|
||||
sum += uint16(data[i])
|
||||
}
|
||||
if data[len(data)-2] != uint8(sum>>8) ||
|
||||
data[len(data)-1] != uint8(sum) {
|
||||
return errors.StructuralError("private key checksum failure")
|
||||
}
|
||||
data = data[:len(data)-2]
|
||||
default:
|
||||
return errors.InvalidArgumentError("invalid s2k type")
|
||||
}
|
||||
|
||||
err := pk.parsePrivateKey(data)
|
||||
@ -424,7 +598,6 @@ func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
|
||||
pk.s2k = nil
|
||||
pk.Encrypted = false
|
||||
pk.encryptedData = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -440,6 +613,9 @@ func (pk *PrivateKey) decryptWithCache(passphrase []byte, keyCache *s2k.Cache) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pk.s2kType == S2KAEAD {
|
||||
key = pk.applyHKDF(key)
|
||||
}
|
||||
return pk.decrypt(key)
|
||||
}
|
||||
|
||||
@ -454,11 +630,14 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
|
||||
|
||||
key := make([]byte, pk.cipher.KeySize())
|
||||
pk.s2k(key, passphrase)
|
||||
if pk.s2kType == S2KAEAD {
|
||||
key = pk.applyHKDF(key)
|
||||
}
|
||||
return pk.decrypt(key)
|
||||
}
|
||||
|
||||
// DecryptPrivateKeys decrypts all encrypted keys with the given config and passphrase.
|
||||
// Avoids recomputation of similar s2k key derivations.
|
||||
// Avoids recomputation of similar s2k key derivations.
|
||||
func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error {
|
||||
// Create a cache to avoid recomputation of key derviations for the same passphrase.
|
||||
s2kCache := &s2k.Cache{}
|
||||
@ -474,7 +653,7 @@ func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error {
|
||||
}
|
||||
|
||||
// encrypt encrypts an unencrypted private key.
|
||||
func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction CipherFunction) error {
|
||||
func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, s2kType S2KType, cipherFunction CipherFunction, rand io.Reader) error {
|
||||
if pk.Dummy() {
|
||||
return errors.ErrDummyPrivateKey("dummy key found")
|
||||
}
|
||||
@ -485,7 +664,15 @@ func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction Cip
|
||||
if len(key) != cipherFunction.KeySize() {
|
||||
return errors.InvalidArgumentError("supplied encryption key has the wrong size")
|
||||
}
|
||||
|
||||
|
||||
if params.Mode() == s2k.Argon2S2K && s2kType != S2KAEAD {
|
||||
return errors.InvalidArgumentError("using Argon2 S2K without AEAD is not allowed")
|
||||
}
|
||||
if params.Mode() != s2k.Argon2S2K && params.Mode() != s2k.IteratedSaltedS2K &&
|
||||
params.Mode() != s2k.SaltedS2K { // only allowed for high-entropy passphrases
|
||||
return errors.InvalidArgumentError("insecure S2K mode")
|
||||
}
|
||||
|
||||
priv := bytes.NewBuffer(nil)
|
||||
err := pk.serializePrivateKey(priv)
|
||||
if err != nil {
|
||||
@ -497,35 +684,53 @@ func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction Cip
|
||||
pk.s2k, err = pk.s2kParams.Function()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
privateKeyBytes := priv.Bytes()
|
||||
pk.sha1Checksum = true
|
||||
pk.s2kType = s2kType
|
||||
block := pk.cipher.new(key)
|
||||
pk.iv = make([]byte, pk.cipher.blockSize())
|
||||
_, err = rand.Read(pk.iv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfb := cipher.NewCFBEncrypter(block, pk.iv)
|
||||
|
||||
if pk.sha1Checksum {
|
||||
pk.s2kType = S2KSHA1
|
||||
h := sha1.New()
|
||||
h.Write(privateKeyBytes)
|
||||
sum := h.Sum(nil)
|
||||
privateKeyBytes = append(privateKeyBytes, sum...)
|
||||
} else {
|
||||
pk.s2kType = S2KCHECKSUM
|
||||
var sum uint16
|
||||
for _, b := range privateKeyBytes {
|
||||
sum += uint16(b)
|
||||
switch s2kType {
|
||||
case S2KAEAD:
|
||||
if pk.aead == 0 {
|
||||
return errors.StructuralError("aead mode is not set on key")
|
||||
}
|
||||
priv.Write([]byte{uint8(sum >> 8), uint8(sum)})
|
||||
aead := pk.aead.new(block)
|
||||
additionalData, err := pk.additionalData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk.iv = make([]byte, aead.NonceSize())
|
||||
_, err = io.ReadFull(rand, pk.iv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Decrypt the encrypted key material with aead
|
||||
pk.encryptedData = aead.Seal(nil, pk.iv, privateKeyBytes, additionalData)
|
||||
case S2KSHA1, S2KCHECKSUM:
|
||||
pk.iv = make([]byte, pk.cipher.blockSize())
|
||||
_, err = io.ReadFull(rand, pk.iv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfb := cipher.NewCFBEncrypter(block, pk.iv)
|
||||
if s2kType == S2KSHA1 {
|
||||
h := sha1.New()
|
||||
h.Write(privateKeyBytes)
|
||||
sum := h.Sum(nil)
|
||||
privateKeyBytes = append(privateKeyBytes, sum...)
|
||||
} else {
|
||||
var sum uint16
|
||||
for _, b := range privateKeyBytes {
|
||||
sum += uint16(b)
|
||||
}
|
||||
privateKeyBytes = append(privateKeyBytes, []byte{uint8(sum >> 8), uint8(sum)}...)
|
||||
}
|
||||
pk.encryptedData = make([]byte, len(privateKeyBytes))
|
||||
cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
|
||||
default:
|
||||
return errors.InvalidArgumentError("invalid s2k type for encryption")
|
||||
}
|
||||
|
||||
pk.encryptedData = make([]byte, len(privateKeyBytes))
|
||||
cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
|
||||
pk.Encrypted = true
|
||||
pk.PrivateKey = nil
|
||||
return err
|
||||
@ -544,8 +749,15 @@ func (pk *PrivateKey) EncryptWithConfig(passphrase []byte, config *Config) error
|
||||
return err
|
||||
}
|
||||
s2k(key, passphrase)
|
||||
s2kType := S2KSHA1
|
||||
if config.AEAD() != nil {
|
||||
s2kType = S2KAEAD
|
||||
pk.aead = config.AEAD().Mode()
|
||||
pk.cipher = config.Cipher()
|
||||
key = pk.applyHKDF(key)
|
||||
}
|
||||
// Encrypt the private key with the derived encryption key.
|
||||
return pk.encrypt(key, params, config.Cipher())
|
||||
return pk.encrypt(key, params, s2kType, config.Cipher(), config.Random())
|
||||
}
|
||||
|
||||
// EncryptPrivateKeys encrypts all unencrypted keys with the given config and passphrase.
|
||||
@ -564,7 +776,16 @@ func EncryptPrivateKeys(keys []*PrivateKey, passphrase []byte, config *Config) e
|
||||
s2k(encryptionKey, passphrase)
|
||||
for _, key := range keys {
|
||||
if key != nil && !key.Dummy() && !key.Encrypted {
|
||||
err = key.encrypt(encryptionKey, params, config.Cipher())
|
||||
s2kType := S2KSHA1
|
||||
if config.AEAD() != nil {
|
||||
s2kType = S2KAEAD
|
||||
key.aead = config.AEAD().Mode()
|
||||
key.cipher = config.Cipher()
|
||||
derivedKey := key.applyHKDF(encryptionKey)
|
||||
err = key.encrypt(derivedKey, params, s2kType, config.Cipher(), config.Random())
|
||||
} else {
|
||||
err = key.encrypt(encryptionKey, params, s2kType, config.Cipher(), config.Random())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -581,7 +802,7 @@ func (pk *PrivateKey) Encrypt(passphrase []byte) error {
|
||||
S2KMode: s2k.IteratedSaltedS2K,
|
||||
S2KCount: 65536,
|
||||
Hash: crypto.SHA256,
|
||||
} ,
|
||||
},
|
||||
DefaultCipher: CipherAES256,
|
||||
}
|
||||
return pk.EncryptWithConfig(passphrase, config)
|
||||
@ -601,6 +822,14 @@ func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) {
|
||||
err = serializeEdDSAPrivateKey(w, priv)
|
||||
case *ecdh.PrivateKey:
|
||||
err = serializeECDHPrivateKey(w, priv)
|
||||
case *x25519.PrivateKey:
|
||||
err = serializeX25519PrivateKey(w, priv)
|
||||
case *x448.PrivateKey:
|
||||
err = serializeX448PrivateKey(w, priv)
|
||||
case *ed25519.PrivateKey:
|
||||
err = serializeEd25519PrivateKey(w, priv)
|
||||
case *ed448.PrivateKey:
|
||||
err = serializeEd448PrivateKey(w, priv)
|
||||
default:
|
||||
err = errors.InvalidArgumentError("unknown private key type")
|
||||
}
|
||||
@ -621,8 +850,18 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
|
||||
return pk.parseECDHPrivateKey(data)
|
||||
case PubKeyAlgoEdDSA:
|
||||
return pk.parseEdDSAPrivateKey(data)
|
||||
case PubKeyAlgoX25519:
|
||||
return pk.parseX25519PrivateKey(data)
|
||||
case PubKeyAlgoX448:
|
||||
return pk.parseX448PrivateKey(data)
|
||||
case PubKeyAlgoEd25519:
|
||||
return pk.parseEd25519PrivateKey(data)
|
||||
case PubKeyAlgoEd448:
|
||||
return pk.parseEd448PrivateKey(data)
|
||||
default:
|
||||
err = errors.StructuralError("unknown private key type")
|
||||
return
|
||||
}
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
|
||||
@ -743,6 +982,86 @@ func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseX25519PrivateKey(data []byte) (err error) {
|
||||
publicKey := pk.PublicKey.PublicKey.(*x25519.PublicKey)
|
||||
privateKey := x25519.NewPrivateKey(*publicKey)
|
||||
privateKey.PublicKey = *publicKey
|
||||
|
||||
privateKey.Secret = make([]byte, x25519.KeySize)
|
||||
|
||||
if len(data) != x25519.KeySize {
|
||||
err = errors.StructuralError("wrong x25519 key size")
|
||||
return err
|
||||
}
|
||||
subtle.ConstantTimeCopy(1, privateKey.Secret, data)
|
||||
if err = x25519.Validate(privateKey); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = privateKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseX448PrivateKey(data []byte) (err error) {
|
||||
publicKey := pk.PublicKey.PublicKey.(*x448.PublicKey)
|
||||
privateKey := x448.NewPrivateKey(*publicKey)
|
||||
privateKey.PublicKey = *publicKey
|
||||
|
||||
privateKey.Secret = make([]byte, x448.KeySize)
|
||||
|
||||
if len(data) != x448.KeySize {
|
||||
err = errors.StructuralError("wrong x448 key size")
|
||||
return err
|
||||
}
|
||||
subtle.ConstantTimeCopy(1, privateKey.Secret, data)
|
||||
if err = x448.Validate(privateKey); err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = privateKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseEd25519PrivateKey(data []byte) (err error) {
|
||||
publicKey := pk.PublicKey.PublicKey.(*ed25519.PublicKey)
|
||||
privateKey := ed25519.NewPrivateKey(*publicKey)
|
||||
privateKey.PublicKey = *publicKey
|
||||
|
||||
if len(data) != ed25519.SeedSize {
|
||||
err = errors.StructuralError("wrong ed25519 key size")
|
||||
return err
|
||||
}
|
||||
err = privateKey.UnmarshalByteSecret(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ed25519.Validate(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = privateKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseEd448PrivateKey(data []byte) (err error) {
|
||||
publicKey := pk.PublicKey.PublicKey.(*ed448.PublicKey)
|
||||
privateKey := ed448.NewPrivateKey(*publicKey)
|
||||
privateKey.PublicKey = *publicKey
|
||||
|
||||
if len(data) != ed448.SeedSize {
|
||||
err = errors.StructuralError("wrong ed448 key size")
|
||||
return err
|
||||
}
|
||||
err = privateKey.UnmarshalByteSecret(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ed448.Validate(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk.PrivateKey = privateKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
|
||||
eddsaPub := pk.PublicKey.PublicKey.(*eddsa.PublicKey)
|
||||
eddsaPriv := eddsa.NewPrivateKey(*eddsaPub)
|
||||
@ -767,6 +1086,41 @@ func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) additionalData() ([]byte, error) {
|
||||
additionalData := bytes.NewBuffer(nil)
|
||||
// Write additional data prefix based on packet type
|
||||
var packetByte byte
|
||||
if pk.PublicKey.IsSubkey {
|
||||
packetByte = 0xc7
|
||||
} else {
|
||||
packetByte = 0xc5
|
||||
}
|
||||
// Write public key to additional data
|
||||
_, err := additionalData.Write([]byte{packetByte})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = pk.PublicKey.serializeWithoutHeaders(additionalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return additionalData.Bytes(), nil
|
||||
}
|
||||
|
||||
func (pk *PrivateKey) applyHKDF(inputKey []byte) []byte {
|
||||
var packetByte byte
|
||||
if pk.PublicKey.IsSubkey {
|
||||
packetByte = 0xc7
|
||||
} else {
|
||||
packetByte = 0xc5
|
||||
}
|
||||
associatedData := []byte{packetByte, byte(pk.Version), byte(pk.cipher), byte(pk.aead)}
|
||||
hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData)
|
||||
encryptionKey := make([]byte, pk.cipher.KeySize())
|
||||
_, _ = readFull(hkdfReader, encryptionKey)
|
||||
return encryptionKey
|
||||
}
|
||||
|
||||
func validateDSAParameters(priv *dsa.PrivateKey) error {
|
||||
p := priv.P // group prime
|
||||
q := priv.Q // subgroup order
|
||||
|
482
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
482
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
@ -5,7 +5,6 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
@ -21,23 +20,24 @@ import (
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ed25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/ed448"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x25519"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/x448"
|
||||
)
|
||||
|
||||
type kdfHashFunction byte
|
||||
type kdfAlgorithm byte
|
||||
|
||||
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
|
||||
type PublicKey struct {
|
||||
Version int
|
||||
CreationTime time.Time
|
||||
PubKeyAlgo PublicKeyAlgorithm
|
||||
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey
|
||||
PublicKey interface{} // *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or *eddsa.PublicKey, *x25519.PublicKey, *x448.PublicKey, *ed25519.PublicKey, *ed448.PublicKey
|
||||
Fingerprint []byte
|
||||
KeyId uint64
|
||||
IsSubkey bool
|
||||
@ -61,11 +61,19 @@ func (pk *PublicKey) UpgradeToV5() {
|
||||
pk.setFingerprintAndKeyId()
|
||||
}
|
||||
|
||||
// UpgradeToV6 updates the version of the key to v6, and updates all necessary
|
||||
// fields.
|
||||
func (pk *PublicKey) UpgradeToV6() error {
|
||||
pk.Version = 6
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk.checkV6Compatibility()
|
||||
}
|
||||
|
||||
// signingKey provides a convenient abstraction over signature verification
|
||||
// for v3 and v4 public keys.
|
||||
type signingKey interface {
|
||||
SerializeForHash(io.Writer) error
|
||||
SerializeSignaturePrefix(io.Writer)
|
||||
SerializeSignaturePrefix(io.Writer) error
|
||||
serializeWithoutHeaders(io.Writer) error
|
||||
}
|
||||
|
||||
@ -174,6 +182,54 @@ func NewEdDSAPublicKey(creationTime time.Time, pub *eddsa.PublicKey) *PublicKey
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewX25519PublicKey(creationTime time.Time, pub *x25519.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoX25519,
|
||||
PublicKey: pub,
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewX448PublicKey(creationTime time.Time, pub *x448.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoX448,
|
||||
PublicKey: pub,
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewEd25519PublicKey(creationTime time.Time, pub *ed25519.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoEd25519,
|
||||
PublicKey: pub,
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func NewEd448PublicKey(creationTime time.Time, pub *ed448.PublicKey) *PublicKey {
|
||||
pk := &PublicKey{
|
||||
Version: 4,
|
||||
CreationTime: creationTime,
|
||||
PubKeyAlgo: PubKeyAlgoEd448,
|
||||
PublicKey: pub,
|
||||
}
|
||||
|
||||
pk.setFingerprintAndKeyId()
|
||||
return pk
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.5.2
|
||||
var buf [6]byte
|
||||
@ -181,12 +237,19 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if buf[0] != 4 && buf[0] != 5 {
|
||||
|
||||
pk.Version = int(buf[0])
|
||||
if pk.Version != 4 && pk.Version != 5 && pk.Version != 6 {
|
||||
return errors.UnsupportedError("public key version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
pk.Version = int(buf[0])
|
||||
if pk.Version == 5 {
|
||||
if V5Disabled && pk.Version == 5 {
|
||||
return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
|
||||
}
|
||||
|
||||
if pk.Version >= 5 {
|
||||
// Read the four-octet scalar octet count
|
||||
// The count is not used in this implementation
|
||||
var n [4]byte
|
||||
_, err = readFull(r, n[:])
|
||||
if err != nil {
|
||||
@ -195,6 +258,7 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
}
|
||||
pk.CreationTime = time.Unix(int64(uint32(buf[1])<<24|uint32(buf[2])<<16|uint32(buf[3])<<8|uint32(buf[4])), 0)
|
||||
pk.PubKeyAlgo = PublicKeyAlgorithm(buf[5])
|
||||
// Ignore four-ocet length
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
err = pk.parseRSA(r)
|
||||
@ -208,6 +272,14 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
err = pk.parseECDH(r)
|
||||
case PubKeyAlgoEdDSA:
|
||||
err = pk.parseEdDSA(r)
|
||||
case PubKeyAlgoX25519:
|
||||
err = pk.parseX25519(r)
|
||||
case PubKeyAlgoX448:
|
||||
err = pk.parseX448(r)
|
||||
case PubKeyAlgoEd25519:
|
||||
err = pk.parseEd25519(r)
|
||||
case PubKeyAlgoEd448:
|
||||
err = pk.parseEd448(r)
|
||||
default:
|
||||
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
|
||||
}
|
||||
@ -221,21 +293,44 @@ func (pk *PublicKey) parse(r io.Reader) (err error) {
|
||||
|
||||
func (pk *PublicKey) setFingerprintAndKeyId() {
|
||||
// RFC 4880, section 12.2
|
||||
if pk.Version == 5 {
|
||||
if pk.Version >= 5 {
|
||||
fingerprint := sha256.New()
|
||||
pk.SerializeForHash(fingerprint)
|
||||
if err := pk.SerializeForHash(fingerprint); err != nil {
|
||||
// Should not happen for a hash.
|
||||
panic(err)
|
||||
}
|
||||
pk.Fingerprint = make([]byte, 32)
|
||||
copy(pk.Fingerprint, fingerprint.Sum(nil))
|
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[:8])
|
||||
} else {
|
||||
fingerprint := sha1.New()
|
||||
pk.SerializeForHash(fingerprint)
|
||||
if err := pk.SerializeForHash(fingerprint); err != nil {
|
||||
// Should not happen for a hash.
|
||||
panic(err)
|
||||
}
|
||||
pk.Fingerprint = make([]byte, 20)
|
||||
copy(pk.Fingerprint, fingerprint.Sum(nil))
|
||||
pk.KeyId = binary.BigEndian.Uint64(pk.Fingerprint[12:20])
|
||||
}
|
||||
}
|
||||
|
||||
func (pk *PublicKey) checkV6Compatibility() error {
|
||||
// Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoECDH:
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
||||
}
|
||||
if curveInfo.GenName == ecc.Curve25519GenName {
|
||||
return errors.StructuralError("cannot generate v6 key with deprecated OID: Curve25519Legacy")
|
||||
}
|
||||
case PubKeyAlgoEdDSA:
|
||||
return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
|
||||
// section 5.5.2.
|
||||
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
|
||||
@ -324,16 +419,17 @@ func (pk *PublicKey) parseECDSA(r io.Reader) (err error) {
|
||||
if _, err = pk.oid.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
||||
}
|
||||
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c, ok := curveInfo.Curve.(ecc.ECDSACurve)
|
||||
if !ok {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
||||
@ -353,6 +449,17 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
|
||||
if _, err = pk.oid.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
||||
}
|
||||
|
||||
if pk.Version == 6 && curveInfo.GenName == ecc.Curve25519GenName {
|
||||
// Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
|
||||
return errors.StructuralError("cannot read v6 key with deprecated OID: Curve25519Legacy")
|
||||
}
|
||||
|
||||
pk.p = new(encoding.MPI)
|
||||
if _, err = pk.p.ReadFrom(r); err != nil {
|
||||
return
|
||||
@ -362,12 +469,6 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
|
||||
if curveInfo == nil {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
||||
}
|
||||
|
||||
c, ok := curveInfo.Curve.(ecc.ECDHCurve)
|
||||
if !ok {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
|
||||
@ -396,10 +497,16 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
|
||||
if pk.Version == 6 {
|
||||
// Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
|
||||
return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy")
|
||||
}
|
||||
|
||||
pk.oid = new(encoding.OID)
|
||||
if _, err = pk.oid.ReadFrom(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil {
|
||||
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
||||
@ -435,75 +542,145 @@ func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parseX25519(r io.Reader) (err error) {
|
||||
point := make([]byte, x25519.KeySize)
|
||||
_, err = io.ReadFull(r, point)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pub := &x25519.PublicKey{
|
||||
Point: point,
|
||||
}
|
||||
pk.PublicKey = pub
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parseX448(r io.Reader) (err error) {
|
||||
point := make([]byte, x448.KeySize)
|
||||
_, err = io.ReadFull(r, point)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pub := &x448.PublicKey{
|
||||
Point: point,
|
||||
}
|
||||
pk.PublicKey = pub
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parseEd25519(r io.Reader) (err error) {
|
||||
point := make([]byte, ed25519.PublicKeySize)
|
||||
_, err = io.ReadFull(r, point)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pub := &ed25519.PublicKey{
|
||||
Point: point,
|
||||
}
|
||||
pk.PublicKey = pub
|
||||
return
|
||||
}
|
||||
|
||||
func (pk *PublicKey) parseEd448(r io.Reader) (err error) {
|
||||
point := make([]byte, ed448.PublicKeySize)
|
||||
_, err = io.ReadFull(r, point)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pub := &ed448.PublicKey{
|
||||
Point: point,
|
||||
}
|
||||
pk.PublicKey = pub
|
||||
return
|
||||
}
|
||||
|
||||
// SerializeForHash serializes the PublicKey to w with the special packet
|
||||
// header format needed for hashing.
|
||||
func (pk *PublicKey) SerializeForHash(w io.Writer) error {
|
||||
pk.SerializeSignaturePrefix(w)
|
||||
if err := pk.SerializeSignaturePrefix(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.serializeWithoutHeaders(w)
|
||||
}
|
||||
|
||||
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
|
||||
// The prefix is used when calculating a signature over this public key. See
|
||||
// RFC 4880, section 5.2.4.
|
||||
func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) {
|
||||
func (pk *PublicKey) SerializeSignaturePrefix(w io.Writer) error {
|
||||
var pLength = pk.algorithmSpecificByteCount()
|
||||
if pk.Version == 5 {
|
||||
pLength += 10 // version, timestamp (4), algorithm, key octet count (4).
|
||||
w.Write([]byte{
|
||||
0x9A,
|
||||
// version, timestamp, algorithm
|
||||
pLength += versionSize + timestampSize + algorithmSize
|
||||
if pk.Version >= 5 {
|
||||
// key octet count (4).
|
||||
pLength += 4
|
||||
_, err := w.Write([]byte{
|
||||
// When a v4 signature is made over a key, the hash data starts with the octet 0x99, followed by a two-octet length
|
||||
// of the key, and then the body of the key packet. When a v6 signature is made over a key, the hash data starts
|
||||
// with the salt, then octet 0x9B, followed by a four-octet length of the key, and then the body of the key packet.
|
||||
0x95 + byte(pk.Version),
|
||||
byte(pLength >> 24),
|
||||
byte(pLength >> 16),
|
||||
byte(pLength >> 8),
|
||||
byte(pLength),
|
||||
})
|
||||
return
|
||||
return err
|
||||
}
|
||||
pLength += 6
|
||||
w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)})
|
||||
if _, err := w.Write([]byte{0x99, byte(pLength >> 8), byte(pLength)}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pk *PublicKey) Serialize(w io.Writer) (err error) {
|
||||
length := 6 // 6 byte header
|
||||
length := uint32(versionSize + timestampSize + algorithmSize) // 6 byte header
|
||||
length += pk.algorithmSpecificByteCount()
|
||||
if pk.Version == 5 {
|
||||
if pk.Version >= 5 {
|
||||
length += 4 // octet key count
|
||||
}
|
||||
packetType := packetTypePublicKey
|
||||
if pk.IsSubkey {
|
||||
packetType = packetTypePublicSubkey
|
||||
}
|
||||
err = serializeHeader(w, packetType, length)
|
||||
err = serializeHeader(w, packetType, int(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return pk.serializeWithoutHeaders(w)
|
||||
}
|
||||
|
||||
func (pk *PublicKey) algorithmSpecificByteCount() int {
|
||||
length := 0
|
||||
func (pk *PublicKey) algorithmSpecificByteCount() uint32 {
|
||||
length := uint32(0)
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
|
||||
length += int(pk.n.EncodedLength())
|
||||
length += int(pk.e.EncodedLength())
|
||||
length += uint32(pk.n.EncodedLength())
|
||||
length += uint32(pk.e.EncodedLength())
|
||||
case PubKeyAlgoDSA:
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += int(pk.q.EncodedLength())
|
||||
length += int(pk.g.EncodedLength())
|
||||
length += int(pk.y.EncodedLength())
|
||||
length += uint32(pk.p.EncodedLength())
|
||||
length += uint32(pk.q.EncodedLength())
|
||||
length += uint32(pk.g.EncodedLength())
|
||||
length += uint32(pk.y.EncodedLength())
|
||||
case PubKeyAlgoElGamal:
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += int(pk.g.EncodedLength())
|
||||
length += int(pk.y.EncodedLength())
|
||||
length += uint32(pk.p.EncodedLength())
|
||||
length += uint32(pk.g.EncodedLength())
|
||||
length += uint32(pk.y.EncodedLength())
|
||||
case PubKeyAlgoECDSA:
|
||||
length += int(pk.oid.EncodedLength())
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += uint32(pk.oid.EncodedLength())
|
||||
length += uint32(pk.p.EncodedLength())
|
||||
case PubKeyAlgoECDH:
|
||||
length += int(pk.oid.EncodedLength())
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += int(pk.kdf.EncodedLength())
|
||||
length += uint32(pk.oid.EncodedLength())
|
||||
length += uint32(pk.p.EncodedLength())
|
||||
length += uint32(pk.kdf.EncodedLength())
|
||||
case PubKeyAlgoEdDSA:
|
||||
length += int(pk.oid.EncodedLength())
|
||||
length += int(pk.p.EncodedLength())
|
||||
length += uint32(pk.oid.EncodedLength())
|
||||
length += uint32(pk.p.EncodedLength())
|
||||
case PubKeyAlgoX25519:
|
||||
length += x25519.KeySize
|
||||
case PubKeyAlgoX448:
|
||||
length += x448.KeySize
|
||||
case PubKeyAlgoEd25519:
|
||||
length += ed25519.PublicKeySize
|
||||
case PubKeyAlgoEd448:
|
||||
length += ed448.PublicKeySize
|
||||
default:
|
||||
panic("unknown public key algorithm")
|
||||
}
|
||||
@ -522,7 +699,7 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if pk.Version == 5 {
|
||||
if pk.Version >= 5 {
|
||||
n := pk.algorithmSpecificByteCount()
|
||||
if _, err = w.Write([]byte{
|
||||
byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n),
|
||||
@ -580,6 +757,22 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) {
|
||||
}
|
||||
_, err = w.Write(pk.p.EncodedBytes())
|
||||
return
|
||||
case PubKeyAlgoX25519:
|
||||
publicKey := pk.PublicKey.(*x25519.PublicKey)
|
||||
_, err = w.Write(publicKey.Point)
|
||||
return
|
||||
case PubKeyAlgoX448:
|
||||
publicKey := pk.PublicKey.(*x448.PublicKey)
|
||||
_, err = w.Write(publicKey.Point)
|
||||
return
|
||||
case PubKeyAlgoEd25519:
|
||||
publicKey := pk.PublicKey.(*ed25519.PublicKey)
|
||||
_, err = w.Write(publicKey.Point)
|
||||
return
|
||||
case PubKeyAlgoEd448:
|
||||
publicKey := pk.PublicKey.(*ed448.PublicKey)
|
||||
_, err = w.Write(publicKey.Point)
|
||||
return
|
||||
}
|
||||
return errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
@ -589,6 +782,20 @@ func (pk *PublicKey) CanSign() bool {
|
||||
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal && pk.PubKeyAlgo != PubKeyAlgoECDH
|
||||
}
|
||||
|
||||
// VerifyHashTag returns nil iff sig appears to be a plausible signature of the data
|
||||
// hashed into signed, based solely on its HashTag. signed is mutated by this call.
|
||||
func VerifyHashTag(signed hash.Hash, sig *Signature) (err error) {
|
||||
if sig.Version == 5 && (sig.SigType == 0x00 || sig.SigType == 0x01) {
|
||||
sig.AddMetadataToHashSuffix()
|
||||
}
|
||||
signed.Write(sig.HashSuffix)
|
||||
hashBytes := signed.Sum(nil)
|
||||
if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] {
|
||||
return errors.SignatureError("hash tag doesn't match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of the data hashed into signed. signed is mutated by this call.
|
||||
func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err error) {
|
||||
@ -600,7 +807,8 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
|
||||
}
|
||||
signed.Write(sig.HashSuffix)
|
||||
hashBytes := signed.Sum(nil)
|
||||
if sig.Version == 5 && (hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1]) {
|
||||
// see discussion https://github.com/ProtonMail/go-crypto/issues/107
|
||||
if sig.Version >= 5 && (hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1]) {
|
||||
return errors.SignatureError("hash tag doesn't match")
|
||||
}
|
||||
|
||||
@ -639,6 +847,18 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
|
||||
return errors.SignatureError("EdDSA verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoEd25519:
|
||||
ed25519PublicKey := pk.PublicKey.(*ed25519.PublicKey)
|
||||
if !ed25519.Verify(ed25519PublicKey, hashBytes, sig.EdSig) {
|
||||
return errors.SignatureError("Ed25519 verification failure")
|
||||
}
|
||||
return nil
|
||||
case PubKeyAlgoEd448:
|
||||
ed448PublicKey := pk.PublicKey.(*ed448.PublicKey)
|
||||
if !ed448.Verify(ed448PublicKey, hashBytes, sig.EdSig) {
|
||||
return errors.SignatureError("ed448 verification failure")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return errors.SignatureError("Unsupported public key algorithm used in signature")
|
||||
}
|
||||
@ -646,11 +866,8 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err erro
|
||||
|
||||
// keySignatureHash returns a Hash of the message that needs to be signed for
|
||||
// pk to assert a subkey relationship to signed.
|
||||
func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
func keySignatureHash(pk, signed signingKey, hashFunc hash.Hash) (h hash.Hash, err error) {
|
||||
h = hashFunc
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
err = pk.SerializeForHash(h)
|
||||
@ -662,10 +879,28 @@ func keySignatureHash(pk, signed signingKey, hashFunc crypto.Hash) (h hash.Hash,
|
||||
return
|
||||
}
|
||||
|
||||
// VerifyKeyHashTag returns nil iff sig appears to be a plausible signature over this
|
||||
// primary key and subkey, based solely on its HashTag.
|
||||
func (pk *PublicKey) VerifyKeyHashTag(signed *PublicKey, sig *Signature) error {
|
||||
preparedHash, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h, err := keySignatureHash(pk, signed, preparedHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return VerifyHashTag(h, sig)
|
||||
}
|
||||
|
||||
// VerifyKeySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, of signed.
|
||||
func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error {
|
||||
h, err := keySignatureHash(pk, signed, sig.Hash)
|
||||
preparedHash, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h, err := keySignatureHash(pk, signed, preparedHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -679,10 +914,14 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error
|
||||
if sig.EmbeddedSignature == nil {
|
||||
return errors.StructuralError("signing subkey is missing cross-signature")
|
||||
}
|
||||
preparedHashEmbedded, err := sig.EmbeddedSignature.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Verify the cross-signature. This is calculated over the same
|
||||
// data as the main signature, so we cannot just recursively
|
||||
// call signed.VerifyKeySignature(...)
|
||||
if h, err = keySignatureHash(pk, signed, sig.EmbeddedSignature.Hash); err != nil {
|
||||
if h, err = keySignatureHash(pk, signed, preparedHashEmbedded); err != nil {
|
||||
return errors.StructuralError("error while hashing for cross-signature: " + err.Error())
|
||||
}
|
||||
if err := signed.VerifySignature(h, sig.EmbeddedSignature); err != nil {
|
||||
@ -693,32 +932,44 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
func keyRevocationHash(pk signingKey, hashFunc hash.Hash) (err error) {
|
||||
return pk.SerializeForHash(hashFunc)
|
||||
}
|
||||
|
||||
// VerifyRevocationHashTag returns nil iff sig appears to be a plausible signature
|
||||
// over this public key, based solely on its HashTag.
|
||||
func (pk *PublicKey) VerifyRevocationHashTag(sig *Signature) (err error) {
|
||||
preparedHash, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h = hashFunc.New()
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
err = pk.SerializeForHash(h)
|
||||
|
||||
return
|
||||
if err = keyRevocationHash(pk, preparedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
return VerifyHashTag(preparedHash, sig)
|
||||
}
|
||||
|
||||
// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key.
|
||||
func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
|
||||
h, err := keyRevocationHash(pk, sig.Hash)
|
||||
preparedHash, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
if err = keyRevocationHash(pk, preparedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(preparedHash, sig)
|
||||
}
|
||||
|
||||
// VerifySubkeyRevocationSignature returns nil iff sig is a valid subkey revocation signature,
|
||||
// made by this public key, of signed.
|
||||
func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *PublicKey) (err error) {
|
||||
h, err := keySignatureHash(pk, signed, sig.Hash)
|
||||
preparedHash, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h, err := keySignatureHash(pk, signed, preparedHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -727,15 +978,15 @@ func (pk *PublicKey) VerifySubkeyRevocationSignature(sig *Signature, signed *Pub
|
||||
|
||||
// userIdSignatureHash returns a Hash of the message that needs to be signed
|
||||
// to assert that pk is a valid key for id.
|
||||
func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
|
||||
if !hashFunc.Available() {
|
||||
return nil, errors.UnsupportedError("hash function")
|
||||
}
|
||||
h = hashFunc.New()
|
||||
func userIdSignatureHash(id string, pk *PublicKey, h hash.Hash) (err error) {
|
||||
|
||||
// RFC 4880, section 5.2.4
|
||||
pk.SerializeSignaturePrefix(h)
|
||||
pk.serializeWithoutHeaders(h)
|
||||
if err := pk.SerializeSignaturePrefix(h); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pk.serializeWithoutHeaders(h); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf [5]byte
|
||||
buf[0] = 0xb4
|
||||
@ -746,16 +997,51 @@ func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash
|
||||
h.Write(buf[:])
|
||||
h.Write([]byte(id))
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// directKeySignatureHash returns a Hash of the message that needs to be signed.
|
||||
func directKeySignatureHash(pk *PublicKey, h hash.Hash) (err error) {
|
||||
return pk.SerializeForHash(h)
|
||||
}
|
||||
|
||||
// VerifyUserIdHashTag returns nil iff sig appears to be a plausible signature over this
|
||||
// public key and UserId, based solely on its HashTag
|
||||
func (pk *PublicKey) VerifyUserIdHashTag(id string, sig *Signature) (err error) {
|
||||
preparedHash, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = userIdSignatureHash(id, pk, preparedHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return VerifyHashTag(preparedHash, sig)
|
||||
}
|
||||
|
||||
// VerifyUserIdSignature returns nil iff sig is a valid signature, made by this
|
||||
// public key, that id is the identity of pub.
|
||||
func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) {
|
||||
h, err := userIdSignatureHash(id, pub, sig.Hash)
|
||||
h, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := userIdSignatureHash(id, pub, h); err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
// VerifyDirectKeySignature returns nil iff sig is a valid signature, made by this
|
||||
// public key.
|
||||
func (pk *PublicKey) VerifyDirectKeySignature(sig *Signature) (err error) {
|
||||
h, err := sig.PrepareVerify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := directKeySignatureHash(pk, h); err != nil {
|
||||
return err
|
||||
}
|
||||
return pk.VerifySignature(h, sig)
|
||||
}
|
||||
|
||||
@ -786,21 +1072,49 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
|
||||
bitLength = pk.p.BitLength()
|
||||
case PubKeyAlgoEdDSA:
|
||||
bitLength = pk.p.BitLength()
|
||||
case PubKeyAlgoX25519:
|
||||
bitLength = x25519.KeySize * 8
|
||||
case PubKeyAlgoX448:
|
||||
bitLength = x448.KeySize * 8
|
||||
case PubKeyAlgoEd25519:
|
||||
bitLength = ed25519.PublicKeySize * 8
|
||||
case PubKeyAlgoEd448:
|
||||
bitLength = ed448.PublicKeySize * 8
|
||||
default:
|
||||
err = errors.InvalidArgumentError("bad public-key algorithm")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Curve returns the used elliptic curve of this public key.
|
||||
// Returns an error if no elliptic curve is used.
|
||||
func (pk *PublicKey) Curve() (curve Curve, err error) {
|
||||
switch pk.PubKeyAlgo {
|
||||
case PubKeyAlgoECDSA, PubKeyAlgoECDH, PubKeyAlgoEdDSA:
|
||||
curveInfo := ecc.FindByOid(pk.oid)
|
||||
if curveInfo == nil {
|
||||
return "", errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
|
||||
}
|
||||
curve = Curve(curveInfo.GenName)
|
||||
case PubKeyAlgoEd25519, PubKeyAlgoX25519:
|
||||
curve = Curve25519
|
||||
case PubKeyAlgoEd448, PubKeyAlgoX448:
|
||||
curve = Curve448
|
||||
default:
|
||||
err = errors.InvalidArgumentError("public key does not operate with an elliptic curve")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KeyExpired returns whether sig is a self-signature of a key that has
|
||||
// expired or is created in the future.
|
||||
func (pk *PublicKey) KeyExpired(sig *Signature, currentTime time.Time) bool {
|
||||
if pk.CreationTime.After(currentTime) {
|
||||
if pk.CreationTime.Unix() > currentTime.Unix() {
|
||||
return true
|
||||
}
|
||||
if sig.KeyLifetimeSecs == nil || *sig.KeyLifetimeSecs == 0 {
|
||||
return false
|
||||
}
|
||||
expiry := pk.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second)
|
||||
return currentTime.After(expiry)
|
||||
return currentTime.Unix() > expiry.Unix()
|
||||
}
|
||||
|
159
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
generated
vendored
159
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.go
generated
vendored
@ -10,6 +10,12 @@ import (
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
type PacketReader interface {
|
||||
Next() (p Packet, err error)
|
||||
Push(reader io.Reader) (err error)
|
||||
Unread(p Packet)
|
||||
}
|
||||
|
||||
// Reader reads packets from an io.Reader and allows packets to be 'unread' so
|
||||
// that they result from the next call to Next.
|
||||
type Reader struct {
|
||||
@ -26,37 +32,81 @@ type Reader struct {
|
||||
const maxReaders = 32
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown packet types are skipped.
|
||||
// the top-most io.Reader. Unknown/unsupported/Marker packet types are skipped.
|
||||
func (r *Reader) Next() (p Packet, err error) {
|
||||
for {
|
||||
p, err := r.read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
switch p.(type) {
|
||||
case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
} else {
|
||||
//A marker packet MUST be ignored when received
|
||||
switch p.(type) {
|
||||
case *Marker:
|
||||
continue
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown/Marker packet types are skipped while unsupported
|
||||
// packets are returned as UnsupportedPacket type.
|
||||
func (r *Reader) NextWithUnsupported() (p Packet, err error) {
|
||||
for {
|
||||
p, err = r.read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if casteErr, ok := err.(errors.UnsupportedError); ok {
|
||||
return &UnsupportedPacket{
|
||||
IncompletePacket: p,
|
||||
Error: casteErr,
|
||||
}, nil
|
||||
}
|
||||
return
|
||||
} else {
|
||||
//A marker packet MUST be ignored when received
|
||||
switch p.(type) {
|
||||
case *Marker:
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (r *Reader) read() (p Packet, err error) {
|
||||
if len(r.q) > 0 {
|
||||
p = r.q[len(r.q)-1]
|
||||
r.q = r.q[:len(r.q)-1]
|
||||
return
|
||||
}
|
||||
|
||||
for len(r.readers) > 0 {
|
||||
p, err = Read(r.readers[len(r.readers)-1])
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == io.EOF {
|
||||
r.readers = r.readers[:len(r.readers)-1]
|
||||
continue
|
||||
}
|
||||
// TODO: Add strict mode that rejects unknown packets, instead of ignoring them.
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
switch p.(type) {
|
||||
case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
return p, err
|
||||
}
|
||||
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
@ -84,3 +134,76 @@ func NewReader(r io.Reader) *Reader {
|
||||
readers: []io.Reader{r},
|
||||
}
|
||||
}
|
||||
|
||||
// CheckReader is similar to Reader but additionally
|
||||
// uses the pushdown automata to verify the read packet sequence.
|
||||
type CheckReader struct {
|
||||
Reader
|
||||
verifier *SequenceVerifier
|
||||
fullyRead bool
|
||||
}
|
||||
|
||||
// Next returns the most recently unread Packet, or reads another packet from
|
||||
// the top-most io.Reader. Unknown packet types are skipped.
|
||||
// If the read packet sequence does not conform to the packet composition
|
||||
// rules in rfc4880, it returns an error.
|
||||
func (r *CheckReader) Next() (p Packet, err error) {
|
||||
if r.fullyRead {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if len(r.q) > 0 {
|
||||
p = r.q[len(r.q)-1]
|
||||
r.q = r.q[:len(r.q)-1]
|
||||
return
|
||||
}
|
||||
var errMsg error
|
||||
for len(r.readers) > 0 {
|
||||
p, errMsg, err = ReadWithCheck(r.readers[len(r.readers)-1], r.verifier)
|
||||
if errMsg != nil {
|
||||
err = errMsg
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == io.EOF {
|
||||
r.readers = r.readers[:len(r.readers)-1]
|
||||
continue
|
||||
}
|
||||
//A marker packet MUST be ignored when received
|
||||
switch p.(type) {
|
||||
case *Marker:
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnknownPacketTypeError); ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(errors.UnsupportedError); ok {
|
||||
switch p.(type) {
|
||||
case *SymmetricallyEncrypted, *AEADEncrypted, *Compressed, *LiteralData:
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if errMsg = r.verifier.Next(EOSSymbol); errMsg != nil {
|
||||
return nil, errMsg
|
||||
}
|
||||
if errMsg = r.verifier.AssertValid(); errMsg != nil {
|
||||
return nil, errMsg
|
||||
}
|
||||
r.fullyRead = true
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func NewCheckReader(r io.Reader) *CheckReader {
|
||||
return &CheckReader{
|
||||
Reader: Reader{
|
||||
q: nil,
|
||||
readers: []io.Reader{r},
|
||||
},
|
||||
verifier: NewSequenceVerifier(),
|
||||
fullyRead: false,
|
||||
}
|
||||
}
|
||||
|
15
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go
generated
vendored
Normal file
15
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/recipient.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package packet
|
||||
|
||||
// Recipient type represents a Intended Recipient Fingerprint subpacket
|
||||
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr
|
||||
type Recipient struct {
|
||||
KeyVersion int
|
||||
Fingerprint []byte
|
||||
}
|
||||
|
||||
func (r *Recipient) Serialize() []byte {
|
||||
packet := make([]byte, len(r.Fingerprint)+1)
|
||||
packet[0] = byte(r.KeyVersion)
|
||||
copy(packet[1:], r.Fingerprint)
|
||||
return packet
|
||||
}
|
747
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
747
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
File diff suppressed because it is too large
Load Diff
89
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
89
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.go
generated
vendored
@ -7,11 +7,13 @@ package packet
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// This is the largest session key that we'll support. Since at most 256-bit cipher
|
||||
@ -39,10 +41,21 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
ske.Version = int(buf[0])
|
||||
if ske.Version != 4 && ske.Version != 5 {
|
||||
if ske.Version != 4 && ske.Version != 5 && ske.Version != 6 {
|
||||
return errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
|
||||
}
|
||||
|
||||
if V5Disabled && ske.Version == 5 {
|
||||
return errors.UnsupportedError("support for parsing v5 entities is disabled; build with `-tags v5` if needed")
|
||||
}
|
||||
|
||||
if ske.Version > 5 {
|
||||
// Scalar octet count
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Cipher function
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
@ -52,7 +65,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
||||
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
|
||||
if ske.Version == 5 {
|
||||
if ske.Version >= 5 {
|
||||
// AEAD mode
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return errors.StructuralError("cannot read AEAD octet from packet")
|
||||
@ -60,6 +73,13 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
||||
ske.Mode = AEADMode(buf[0])
|
||||
}
|
||||
|
||||
if ske.Version > 5 {
|
||||
// Scalar octet count
|
||||
if _, err := readFull(r, buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if ske.s2k, err = s2k.Parse(r); err != nil {
|
||||
if _, ok := err.(errors.ErrDummyPrivateKey); ok {
|
||||
@ -68,7 +88,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if ske.Version == 5 {
|
||||
if ske.Version >= 5 {
|
||||
// AEAD IV
|
||||
iv := make([]byte, ske.Mode.IvLength())
|
||||
_, err := readFull(r, iv)
|
||||
@ -109,8 +129,8 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunc
|
||||
case 4:
|
||||
plaintextKey, cipherFunc, err := ske.decryptV4(key)
|
||||
return plaintextKey, cipherFunc, err
|
||||
case 5:
|
||||
plaintextKey, err := ske.decryptV5(key)
|
||||
case 5, 6:
|
||||
plaintextKey, err := ske.aeadDecrypt(ske.Version, key)
|
||||
return plaintextKey, CipherFunction(0), err
|
||||
}
|
||||
err := errors.UnsupportedError("unknown SymmetricKeyEncrypted version")
|
||||
@ -136,9 +156,9 @@ func (ske *SymmetricKeyEncrypted) decryptV4(key []byte) ([]byte, CipherFunction,
|
||||
return plaintextKey, cipherFunc, nil
|
||||
}
|
||||
|
||||
func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) {
|
||||
adata := []byte{0xc3, byte(5), byte(ske.CipherFunc), byte(ske.Mode)}
|
||||
aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata)
|
||||
func (ske *SymmetricKeyEncrypted) aeadDecrypt(version int, key []byte) ([]byte, error) {
|
||||
adata := []byte{0xc3, byte(version), byte(ske.CipherFunc), byte(ske.Mode)}
|
||||
aead := getEncryptedKeyAeadInstance(ske.CipherFunc, ske.Mode, key, adata, version)
|
||||
|
||||
plaintextKey, err := aead.Open(nil, ske.iv, ske.encryptedKey, adata)
|
||||
if err != nil {
|
||||
@ -175,10 +195,22 @@ func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Conf
|
||||
// the given passphrase. The returned session key must be passed to
|
||||
// SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
// Deprecated: Use SerializeSymmetricKeyEncryptedAEADReuseKey instead.
|
||||
func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, config *Config) (err error) {
|
||||
return SerializeSymmetricKeyEncryptedAEADReuseKey(w, sessionKey, passphrase, config.AEAD() != nil, config)
|
||||
}
|
||||
|
||||
// SerializeSymmetricKeyEncryptedAEADReuseKey serializes a symmetric key packet to w.
|
||||
// The packet contains the given session key, encrypted by a key derived from
|
||||
// the given passphrase. The returned session key must be passed to
|
||||
// SerializeSymmetricallyEncrypted.
|
||||
// If aeadSupported is set, SKESK v6 is used, otherwise v4.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeSymmetricallyEncrypted.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricKeyEncryptedAEADReuseKey(w io.Writer, sessionKey []byte, passphrase []byte, aeadSupported bool, config *Config) (err error) {
|
||||
var version int
|
||||
if config.AEAD() != nil {
|
||||
version = 5
|
||||
if aeadSupported {
|
||||
version = 6
|
||||
} else {
|
||||
version = 4
|
||||
}
|
||||
@ -203,11 +235,15 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
|
||||
switch version {
|
||||
case 4:
|
||||
packetLength = 2 /* header */ + len(s2kBytes) + 1 /* cipher type */ + keySize
|
||||
case 5:
|
||||
case 5, 6:
|
||||
ivLen := config.AEAD().Mode().IvLength()
|
||||
tagLen := config.AEAD().Mode().TagLength()
|
||||
packetLength = 3 + len(s2kBytes) + ivLen + keySize + tagLen
|
||||
}
|
||||
if version > 5 {
|
||||
packetLength += 2 // additional octet count fields
|
||||
}
|
||||
|
||||
err = serializeHeader(w, packetTypeSymmetricKeyEncrypted, packetLength)
|
||||
if err != nil {
|
||||
return
|
||||
@ -216,13 +252,22 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
|
||||
// Symmetric Key Encrypted Version
|
||||
buf := []byte{byte(version)}
|
||||
|
||||
if version > 5 {
|
||||
// Scalar octet count
|
||||
buf = append(buf, byte(3+len(s2kBytes)+config.AEAD().Mode().IvLength()))
|
||||
}
|
||||
|
||||
// Cipher function
|
||||
buf = append(buf, byte(cipherFunc))
|
||||
|
||||
if version == 5 {
|
||||
if version >= 5 {
|
||||
// AEAD mode
|
||||
buf = append(buf, byte(config.AEAD().Mode()))
|
||||
}
|
||||
if version > 5 {
|
||||
// Scalar octet count
|
||||
buf = append(buf, byte(len(s2kBytes)))
|
||||
}
|
||||
_, err = w.Write(buf)
|
||||
if err != nil {
|
||||
return
|
||||
@ -243,10 +288,10 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case 5:
|
||||
case 5, 6:
|
||||
mode := config.AEAD().Mode()
|
||||
adata := []byte{0xc3, byte(5), byte(cipherFunc), byte(mode)}
|
||||
aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata)
|
||||
adata := []byte{0xc3, byte(version), byte(cipherFunc), byte(mode)}
|
||||
aead := getEncryptedKeyAeadInstance(cipherFunc, mode, keyEncryptingKey, adata, version)
|
||||
|
||||
// Sample iv using random reader
|
||||
iv := make([]byte, config.AEAD().Mode().IvLength())
|
||||
@ -270,7 +315,17 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
|
||||
return
|
||||
}
|
||||
|
||||
func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte) (aead cipher.AEAD) {
|
||||
blockCipher := c.new(inputKey)
|
||||
func getEncryptedKeyAeadInstance(c CipherFunction, mode AEADMode, inputKey, associatedData []byte, version int) (aead cipher.AEAD) {
|
||||
var blockCipher cipher.Block
|
||||
if version > 5 {
|
||||
hkdfReader := hkdf.New(sha256.New, inputKey, []byte{}, associatedData)
|
||||
|
||||
encryptionKey := make([]byte, c.KeySize())
|
||||
_, _ = readFull(hkdfReader, encryptionKey)
|
||||
|
||||
blockCipher = c.new(encryptionKey)
|
||||
} else {
|
||||
blockCipher = c.new(inputKey)
|
||||
}
|
||||
return mode.new(blockCipher)
|
||||
}
|
||||
|
4
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
4
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.go
generated
vendored
@ -74,6 +74,10 @@ func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.Read
|
||||
// SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet
|
||||
// to w and returns a WriteCloser to which the to-be-encrypted packets can be
|
||||
// written.
|
||||
// If aeadSupported is set to true, SEIPDv2 is used with the indicated CipherSuite.
|
||||
// Otherwise, SEIPDv1 is used with the indicated CipherFunction.
|
||||
// Note: aeadSupported MUST match the value passed to SerializeEncryptedKeyAEAD
|
||||
// and/or SerializeSymmetricKeyEncryptedAEADReuseKey.
|
||||
// If config is nil, sensible defaults will be used.
|
||||
func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, aeadSupported bool, cipherSuite CipherSuite, key []byte, config *Config) (Contents io.WriteCloser, err error) {
|
||||
writeCloser := noOpCloser{w}
|
||||
|
15
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
generated
vendored
15
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.go
generated
vendored
@ -7,7 +7,9 @@ package packet
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
@ -25,19 +27,19 @@ func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error {
|
||||
se.Cipher = CipherFunction(headerData[0])
|
||||
// cipherFunc must have block size 16 to use AEAD
|
||||
if se.Cipher.blockSize() != 16 {
|
||||
return errors.UnsupportedError("invalid aead cipher: " + string(se.Cipher))
|
||||
return errors.UnsupportedError("invalid aead cipher: " + strconv.Itoa(int(se.Cipher)))
|
||||
}
|
||||
|
||||
// Mode
|
||||
se.Mode = AEADMode(headerData[1])
|
||||
if se.Mode.TagLength() == 0 {
|
||||
return errors.UnsupportedError("unknown aead mode: " + string(se.Mode))
|
||||
return errors.UnsupportedError("unknown aead mode: " + strconv.Itoa(int(se.Mode)))
|
||||
}
|
||||
|
||||
// Chunk size
|
||||
se.ChunkSizeByte = headerData[2]
|
||||
if se.ChunkSizeByte > 16 {
|
||||
return errors.UnsupportedError("invalid aead chunk size byte: " + string(se.ChunkSizeByte))
|
||||
return errors.UnsupportedError("invalid aead chunk size byte: " + strconv.Itoa(int(se.ChunkSizeByte)))
|
||||
}
|
||||
|
||||
// Salt
|
||||
@ -62,8 +64,11 @@ func (se *SymmetricallyEncrypted) associatedData() []byte {
|
||||
// decryptAead decrypts a V2 SEIPD packet (AEAD) as specified in
|
||||
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
|
||||
func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, error) {
|
||||
aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
|
||||
if se.Cipher.KeySize() != len(inputKey) {
|
||||
return nil, errors.StructuralError(fmt.Sprintf("invalid session key length for cipher: got %d bytes, but expected %d bytes", len(inputKey), se.Cipher.KeySize()))
|
||||
}
|
||||
|
||||
aead, nonce := getSymmetricallyEncryptedAeadInstance(se.Cipher, se.Mode, inputKey, se.Salt[:], se.associatedData())
|
||||
// Carry the first tagLen bytes
|
||||
tagLen := se.Mode.TagLength()
|
||||
peekedBytes := make([]byte, tagLen)
|
||||
@ -115,7 +120,7 @@ func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite
|
||||
|
||||
// Random salt
|
||||
salt := make([]byte, aeadSaltSize)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
if _, err := io.ReadFull(rand, salt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
10
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
generated
vendored
10
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.go
generated
vendored
@ -148,7 +148,7 @@ const mdcPacketTagByte = byte(0x80) | 0x40 | 19
|
||||
|
||||
func (ser *seMDCReader) Close() error {
|
||||
if ser.error {
|
||||
return errors.ErrMDCMissing
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
|
||||
for !ser.eof {
|
||||
@ -159,7 +159,7 @@ func (ser *seMDCReader) Close() error {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return errors.ErrMDCMissing
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ func (ser *seMDCReader) Close() error {
|
||||
// The hash already includes the MDC header, but we still check its value
|
||||
// to confirm encryption correctness
|
||||
if ser.trailer[0] != mdcPacketTagByte || ser.trailer[1] != sha1.Size {
|
||||
return errors.ErrMDCMissing
|
||||
return errors.ErrMDCHashMismatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -237,9 +237,9 @@ func serializeSymmetricallyEncryptedMdc(ciphertext io.WriteCloser, c CipherFunct
|
||||
block := c.new(key)
|
||||
blockSize := block.BlockSize()
|
||||
iv := make([]byte, blockSize)
|
||||
_, err = config.Random().Read(iv)
|
||||
_, err = io.ReadFull(config.Random(), iv)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
s, prefix := NewOCFBEncrypter(block, iv, OCFBNoResync)
|
||||
_, err = ciphertext.Write(prefix)
|
||||
|
3
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
generated
vendored
3
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.go
generated
vendored
@ -9,7 +9,6 @@ import (
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const UserAttrImageSubpacket = 1
|
||||
@ -63,7 +62,7 @@ func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute {
|
||||
|
||||
func (uat *UserAttribute) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.13
|
||||
b, err := ioutil.ReadAll(r)
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
3
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
generated
vendored
3
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.go
generated
vendored
@ -6,7 +6,6 @@ package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -66,7 +65,7 @@ func NewUserId(name, comment, email string) *UserId {
|
||||
|
||||
func (uid *UserId) parse(r io.Reader) (err error) {
|
||||
// RFC 4880, section 5.11
|
||||
b, err := ioutil.ReadAll(r)
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user