peridot/vendor/github.com/ProtonMail/gopenpgp/v2/crypto/key.go

490 lines
13 KiB
Go
Raw Permalink Normal View History

2022-07-07 20:11:50 +00:00
package crypto
import (
"bytes"
"crypto"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"math/big"
"strconv"
"strings"
"github.com/ProtonMail/gopenpgp/v2/armor"
"github.com/ProtonMail/gopenpgp/v2/constants"
"github.com/pkg/errors"
openpgp "github.com/ProtonMail/go-crypto/openpgp"
packet "github.com/ProtonMail/go-crypto/openpgp/packet"
)
// Key contains a single private or public key.
type Key struct {
// PGP entities in this keyring.
entity *openpgp.Entity
}
// --- Create Key object
// NewKeyFromArmoredReader reads an armored data into a key.
func NewKeyFromArmoredReader(r io.Reader) (key *Key, err error) {
key = &Key{}
err = key.readFrom(r, true)
if err != nil {
return nil, err
}
return key, nil
}
// NewKeyFromReader reads binary data into a Key object.
func NewKeyFromReader(r io.Reader) (key *Key, err error) {
key = &Key{}
err = key.readFrom(r, false)
if err != nil {
return nil, err
}
return key, nil
}
// NewKey creates a new key from the first key in the unarmored binary data.
func NewKey(binKeys []byte) (key *Key, err error) {
return NewKeyFromReader(bytes.NewReader(clone(binKeys)))
}
// NewKeyFromArmored creates a new key from the first key in an armored string.
func NewKeyFromArmored(armored string) (key *Key, err error) {
return NewKeyFromArmoredReader(strings.NewReader(armored))
}
func NewKeyFromEntity(entity *openpgp.Entity) (*Key, error) {
if entity == nil {
return nil, errors.New("gopenpgp: nil entity provided")
}
return &Key{entity: entity}, nil
}
// GenerateRSAKeyWithPrimes generates a RSA key using the given primes.
func GenerateRSAKeyWithPrimes(
name, email string,
bits int,
primeone, primetwo, primethree, primefour []byte,
) (*Key, error) {
return generateKey(name, email, "rsa", bits, primeone, primetwo, primethree, primefour)
}
// GenerateKey generates a key of the given keyType ("rsa" or "x25519").
// If keyType is "rsa", bits is the RSA bitsize of the key.
// If keyType is "x25519" bits is unused.
func GenerateKey(name, email string, keyType string, bits int) (*Key, error) {
return generateKey(name, email, keyType, bits, nil, nil, nil, nil)
}
// --- Operate on key
// Copy creates a deep copy of the key.
func (key *Key) Copy() (*Key, error) {
serialized, err := key.Serialize()
if err != nil {
return nil, err
}
return NewKey(serialized)
}
// Lock locks a copy of the key.
func (key *Key) Lock(passphrase []byte) (*Key, error) {
unlocked, err := key.IsUnlocked()
if err != nil {
return nil, err
}
if !unlocked {
return nil, errors.New("gopenpgp: key is not unlocked")
}
lockedKey, err := key.Copy()
if err != nil {
return nil, err
}
if passphrase == nil {
return lockedKey, nil
}
if lockedKey.entity.PrivateKey != nil && !lockedKey.entity.PrivateKey.Dummy() {
err = lockedKey.entity.PrivateKey.Encrypt(passphrase)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in locking key")
}
}
for _, sub := range lockedKey.entity.Subkeys {
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() {
if err := sub.PrivateKey.Encrypt(passphrase); err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in locking sub key")
}
}
}
locked, err := lockedKey.IsLocked()
if err != nil {
return nil, err
}
if !locked {
return nil, errors.New("gopenpgp: unable to lock key")
}
return lockedKey, nil
}
// Unlock unlocks a copy of the key.
func (key *Key) Unlock(passphrase []byte) (*Key, error) {
isLocked, err := key.IsLocked()
if err != nil {
return nil, err
}
if !isLocked {
if passphrase == nil {
return key.Copy()
}
return nil, errors.New("gopenpgp: key is not locked")
}
unlockedKey, err := key.Copy()
if err != nil {
return nil, err
}
if unlockedKey.entity.PrivateKey != nil && !unlockedKey.entity.PrivateKey.Dummy() {
err = unlockedKey.entity.PrivateKey.Decrypt(passphrase)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in unlocking key")
}
}
for _, sub := range unlockedKey.entity.Subkeys {
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() {
if err := sub.PrivateKey.Decrypt(passphrase); err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in unlocking sub key")
}
}
}
isUnlocked, err := unlockedKey.IsUnlocked()
if err != nil {
return nil, err
}
if !isUnlocked {
return nil, errors.New("gopenpgp: unable to unlock key")
}
return unlockedKey, nil
}
// --- Export key
func (key *Key) Serialize() ([]byte, error) {
var buffer bytes.Buffer
var err error
if key.entity.PrivateKey == nil {
err = key.entity.Serialize(&buffer)
} else {
err = key.entity.SerializePrivateWithoutSigning(&buffer, nil)
}
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in serializing key")
}
return buffer.Bytes(), nil
}
// Armor returns the armored key as a string with default gopenpgp headers.
func (key *Key) Armor() (string, error) {
serialized, err := key.Serialize()
if err != nil {
return "", err
}
if key.IsPrivate() {
return armor.ArmorWithType(serialized, constants.PrivateKeyHeader)
}
return armor.ArmorWithType(serialized, constants.PublicKeyHeader)
}
// ArmorWithCustomHeaders returns the armored key as a string, with
// the given headers. Empty parameters are omitted from the headers.
func (key *Key) ArmorWithCustomHeaders(comment, version string) (string, error) {
serialized, err := key.Serialize()
if err != nil {
return "", err
}
return armor.ArmorWithTypeAndCustomHeaders(serialized, constants.PrivateKeyHeader, version, comment)
}
// GetArmoredPublicKey returns the armored public keys from this keyring.
func (key *Key) GetArmoredPublicKey() (s string, err error) {
serialized, err := key.GetPublicKey()
if err != nil {
return "", err
}
return armor.ArmorWithType(serialized, constants.PublicKeyHeader)
}
// GetArmoredPublicKeyWithCustomHeaders returns the armored public key as a string, with
// the given headers. Empty parameters are omitted from the headers.
func (key *Key) GetArmoredPublicKeyWithCustomHeaders(comment, version string) (string, error) {
serialized, err := key.GetPublicKey()
if err != nil {
return "", err
}
return armor.ArmorWithTypeAndCustomHeaders(serialized, constants.PublicKeyHeader, version, comment)
}
// GetPublicKey returns the unarmored public keys from this keyring.
func (key *Key) GetPublicKey() (b []byte, err error) {
var outBuf bytes.Buffer
if err = key.entity.Serialize(&outBuf); err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in serializing public key")
}
return outBuf.Bytes(), nil
}
// --- Key object properties
// CanVerify returns true if any of the subkeys can be used for verification.
func (key *Key) CanVerify() bool {
_, canVerify := key.entity.SigningKey(getNow())
return canVerify
}
// CanEncrypt returns true if any of the subkeys can be used for encryption.
func (key *Key) CanEncrypt() bool {
_, canEncrypt := key.entity.EncryptionKey(getNow())
return canEncrypt
}
// IsExpired checks whether the key is expired.
func (key *Key) IsExpired() bool {
i := key.entity.PrimaryIdentity()
return key.entity.PrimaryKey.KeyExpired(i.SelfSignature, getNow()) || // primary key has expired
i.SelfSignature.SigExpired(getNow()) // user ID self-signature has expired
}
// IsRevoked checks whether the key or the primary identity has a valid revocation signature.
func (key *Key) IsRevoked() bool {
return key.entity.Revoked(getNow()) || key.entity.PrimaryIdentity().Revoked(getNow())
}
// IsPrivate returns true if the key is private.
func (key *Key) IsPrivate() bool {
return key.entity.PrivateKey != nil
}
// IsLocked checks if a private key is locked.
func (key *Key) IsLocked() (bool, error) {
if key.entity.PrivateKey == nil {
return true, errors.New("gopenpgp: a public key cannot be locked")
}
encryptedKeys := 0
for _, sub := range key.entity.Subkeys {
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() && sub.PrivateKey.Encrypted {
encryptedKeys++
}
}
if key.entity.PrivateKey.Encrypted {
encryptedKeys++
}
return encryptedKeys > 0, nil
}
// IsUnlocked checks if a private key is unlocked.
func (key *Key) IsUnlocked() (bool, error) {
if key.entity.PrivateKey == nil {
return true, errors.New("gopenpgp: a public key cannot be unlocked")
}
encryptedKeys := 0
for _, sub := range key.entity.Subkeys {
if sub.PrivateKey != nil && !sub.PrivateKey.Dummy() && sub.PrivateKey.Encrypted {
encryptedKeys++
}
}
if key.entity.PrivateKey.Encrypted {
encryptedKeys++
}
return encryptedKeys == 0, nil
}
// Check verifies if the public keys match the private key parameters by
// signing and verifying.
// Deprecated: all keys are now checked on parsing.
func (key *Key) Check() (bool, error) {
return true, nil
}
// PrintFingerprints is a debug helper function that prints the key and subkey fingerprints.
func (key *Key) PrintFingerprints() {
for _, subKey := range key.entity.Subkeys {
if !subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications {
fmt.Println("SubKey:" + hex.EncodeToString(subKey.PublicKey.Fingerprint))
}
}
fmt.Println("PrimaryKey:" + hex.EncodeToString(key.entity.PrimaryKey.Fingerprint))
}
// GetHexKeyID returns the key ID, hex encoded as a string.
func (key *Key) GetHexKeyID() string {
return keyIDToHex(key.GetKeyID())
}
// GetKeyID returns the key ID, encoded as 8-byte int.
func (key *Key) GetKeyID() uint64 {
return key.entity.PrimaryKey.KeyId
}
// GetFingerprint gets the fingerprint from the key.
func (key *Key) GetFingerprint() string {
return hex.EncodeToString(key.entity.PrimaryKey.Fingerprint)
}
// GetSHA256Fingerprints computes the SHA256 fingerprints of the key and subkeys.
func (key *Key) GetSHA256Fingerprints() (fingerprints []string) {
fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(key.entity.PrimaryKey)))
for _, sub := range key.entity.Subkeys {
fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(sub.PublicKey)))
}
return
}
// GetEntity gets x/crypto Entity object.
func (key *Key) GetEntity() *openpgp.Entity {
return key.entity
}
// ToPublic returns the corresponding public key of the given private key.
func (key *Key) ToPublic() (publicKey *Key, err error) {
if !key.IsPrivate() {
return nil, errors.New("gopenpgp: key is already public")
}
publicKey, err = key.Copy()
if err != nil {
return nil, err
}
publicKey.ClearPrivateParams()
return
}
// --- Internal methods
// getSHA256FingerprintBytes computes the SHA256 fingerprint of a public key
// object.
func getSHA256FingerprintBytes(pk *packet.PublicKey) []byte {
fingerPrint := sha256.New()
// Hashing can't return an error, and has already been done when parsing the key,
// hence the error is nil
_ = pk.SerializeForHash(fingerPrint)
return fingerPrint.Sum(nil)
}
// readFrom reads unarmored and armored keys from r and adds them to the keyring.
func (key *Key) readFrom(r io.Reader, armored bool) error {
var err error
var entities openpgp.EntityList
if armored {
entities, err = openpgp.ReadArmoredKeyRing(r)
} else {
entities, err = openpgp.ReadKeyRing(r)
}
if err != nil {
return errors.Wrap(err, "gopenpgp: error in reading key ring")
}
if len(entities) > 1 {
return errors.New("gopenpgp: the key contains too many entities")
}
if len(entities) == 0 {
return errors.New("gopenpgp: the key does not contain any entity")
}
key.entity = entities[0]
return nil
}
func generateKey(
name, email string,
keyType string,
bits int,
prime1, prime2, prime3, prime4 []byte,
) (*Key, error) {
if len(email) == 0 && len(name) == 0 {
return nil, errors.New("gopenpgp: neither name nor email set.")
}
comments := ""
cfg := &packet.Config{
Algorithm: packet.PubKeyAlgoRSA,
RSABits: bits,
Time: getKeyGenerationTimeGenerator(),
DefaultHash: crypto.SHA256,
DefaultCipher: packet.CipherAES256,
DefaultCompressionAlgo: packet.CompressionZLIB,
}
if keyType == "x25519" {
cfg.Algorithm = packet.PubKeyAlgoEdDSA
}
if prime1 != nil && prime2 != nil && prime3 != nil && prime4 != nil {
var bigPrimes [4]*big.Int
bigPrimes[0] = new(big.Int)
bigPrimes[0].SetBytes(prime1)
bigPrimes[1] = new(big.Int)
bigPrimes[1].SetBytes(prime2)
bigPrimes[2] = new(big.Int)
bigPrimes[2].SetBytes(prime3)
bigPrimes[3] = new(big.Int)
bigPrimes[3].SetBytes(prime4)
cfg.RSAPrimes = bigPrimes[:]
}
newEntity, err := openpgp.NewEntity(name, comments, email, cfg)
if err != nil {
return nil, errors.Wrap(err, "gopengpp: error in encoding new entity")
}
if newEntity.PrivateKey == nil {
return nil, errors.New("gopenpgp: error in generating private key")
}
return NewKeyFromEntity(newEntity)
}
// keyIDToHex casts a keyID to hex with the correct padding.
func keyIDToHex(keyID uint64) string {
return fmt.Sprintf("%016v", strconv.FormatUint(keyID, 16))
}