mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-27 12:40:55 +00:00
251 lines
6.2 KiB
Go
251 lines
6.2 KiB
Go
|
package crypto
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ProtonMail/go-crypto/openpgp"
|
||
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
// KeyRing contains multiple private and public keys.
|
||
|
type KeyRing struct {
|
||
|
// PGP entities in this keyring.
|
||
|
entities openpgp.EntityList
|
||
|
|
||
|
// FirstKeyID as obtained from API to match salt
|
||
|
FirstKeyID string
|
||
|
}
|
||
|
|
||
|
// Identity contains the name and the email of a key holder.
|
||
|
type Identity struct {
|
||
|
Name string
|
||
|
Email string
|
||
|
}
|
||
|
|
||
|
// --- New keyrings
|
||
|
|
||
|
// NewKeyRing creates a new KeyRing, empty if key is nil.
|
||
|
func NewKeyRing(key *Key) (*KeyRing, error) {
|
||
|
keyRing := &KeyRing{}
|
||
|
var err error
|
||
|
if key != nil {
|
||
|
err = keyRing.AddKey(key)
|
||
|
}
|
||
|
return keyRing, err
|
||
|
}
|
||
|
|
||
|
// AddKey adds the given key to the keyring.
|
||
|
func (keyRing *KeyRing) AddKey(key *Key) error {
|
||
|
if key.IsPrivate() {
|
||
|
unlocked, err := key.IsUnlocked()
|
||
|
if err != nil || !unlocked {
|
||
|
return errors.New("gopenpgp: unable to add locked key to a keyring")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
keyRing.appendKey(key)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// --- Extract keys from keyring
|
||
|
|
||
|
// GetKeys returns openpgp keys contained in this KeyRing.
|
||
|
func (keyRing *KeyRing) GetKeys() []*Key {
|
||
|
keys := make([]*Key, keyRing.CountEntities())
|
||
|
for i, entity := range keyRing.entities {
|
||
|
keys[i] = &Key{entity}
|
||
|
}
|
||
|
return keys
|
||
|
}
|
||
|
|
||
|
// GetKey returns the n-th openpgp key contained in this KeyRing.
|
||
|
func (keyRing *KeyRing) GetKey(n int) (*Key, error) {
|
||
|
if n >= keyRing.CountEntities() {
|
||
|
return nil, errors.New("gopenpgp: out of bound when fetching key")
|
||
|
}
|
||
|
return &Key{keyRing.entities[n]}, nil
|
||
|
}
|
||
|
|
||
|
// getSigningEntity returns first private unlocked signing entity from keyring.
|
||
|
func (keyRing *KeyRing) getSigningEntity() (*openpgp.Entity, error) {
|
||
|
var signEntity *openpgp.Entity
|
||
|
|
||
|
for _, e := range keyRing.entities {
|
||
|
// Entity.PrivateKey must be a signing key
|
||
|
if e.PrivateKey != nil {
|
||
|
if !e.PrivateKey.Encrypted {
|
||
|
signEntity = e
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if signEntity == nil {
|
||
|
return nil, errors.New("gopenpgp: cannot sign message, unable to unlock signer key")
|
||
|
}
|
||
|
|
||
|
return signEntity, nil
|
||
|
}
|
||
|
|
||
|
// --- Extract info from key
|
||
|
|
||
|
// CountEntities returns the number of entities in the keyring.
|
||
|
func (keyRing *KeyRing) CountEntities() int {
|
||
|
return len(keyRing.entities)
|
||
|
}
|
||
|
|
||
|
// CountDecryptionEntities returns the number of entities in the keyring.
|
||
|
func (keyRing *KeyRing) CountDecryptionEntities() int {
|
||
|
return len(keyRing.entities.DecryptionKeys())
|
||
|
}
|
||
|
|
||
|
// GetIdentities returns the list of identities associated with this key ring.
|
||
|
func (keyRing *KeyRing) GetIdentities() []*Identity {
|
||
|
var identities []*Identity
|
||
|
for _, e := range keyRing.entities {
|
||
|
for _, id := range e.Identities {
|
||
|
identities = append(identities, &Identity{
|
||
|
Name: id.UserId.Name,
|
||
|
Email: id.UserId.Email,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
return identities
|
||
|
}
|
||
|
|
||
|
// CanVerify returns true if any of the keys in the keyring can be used for verification.
|
||
|
func (keyRing *KeyRing) CanVerify() bool {
|
||
|
keys := keyRing.GetKeys()
|
||
|
for _, key := range keys {
|
||
|
if key.CanVerify() {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// CanEncrypt returns true if any of the keys in the keyring can be used for encryption.
|
||
|
func (keyRing *KeyRing) CanEncrypt() bool {
|
||
|
keys := keyRing.GetKeys()
|
||
|
for _, key := range keys {
|
||
|
if key.CanEncrypt() {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// GetKeyIDs returns array of IDs of keys in this KeyRing.
|
||
|
func (keyRing *KeyRing) GetKeyIDs() []uint64 {
|
||
|
var res = make([]uint64, len(keyRing.entities))
|
||
|
for id, e := range keyRing.entities {
|
||
|
res[id] = e.PrimaryKey.KeyId
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
// --- Filter keyrings
|
||
|
|
||
|
// FilterExpiredKeys takes a given KeyRing list and it returns only those
|
||
|
// KeyRings which contain at least, one unexpired Key. It returns only unexpired
|
||
|
// parts of these KeyRings.
|
||
|
func FilterExpiredKeys(contactKeys []*KeyRing) (filteredKeys []*KeyRing, err error) {
|
||
|
now := time.Now()
|
||
|
hasExpiredEntity := false //nolint:ifshort
|
||
|
filteredKeys = make([]*KeyRing, 0)
|
||
|
|
||
|
for _, contactKeyRing := range contactKeys {
|
||
|
keyRingHasUnexpiredEntity := false
|
||
|
keyRingHasTotallyExpiredEntity := false
|
||
|
for _, entity := range contactKeyRing.entities {
|
||
|
hasExpired := false
|
||
|
hasUnexpired := false
|
||
|
for _, subkey := range entity.Subkeys {
|
||
|
if subkey.PublicKey.KeyExpired(subkey.Sig, now) {
|
||
|
hasExpired = true
|
||
|
} else {
|
||
|
hasUnexpired = true
|
||
|
}
|
||
|
}
|
||
|
if hasExpired && !hasUnexpired {
|
||
|
keyRingHasTotallyExpiredEntity = true
|
||
|
} else if hasUnexpired {
|
||
|
keyRingHasUnexpiredEntity = true
|
||
|
}
|
||
|
}
|
||
|
if keyRingHasUnexpiredEntity {
|
||
|
keyRingCopy, err := contactKeyRing.Copy()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
filteredKeys = append(filteredKeys, keyRingCopy)
|
||
|
} else if keyRingHasTotallyExpiredEntity {
|
||
|
hasExpiredEntity = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(filteredKeys) == 0 && hasExpiredEntity {
|
||
|
return filteredKeys, errors.New("gopenpgp: all contacts keys are expired")
|
||
|
}
|
||
|
|
||
|
return filteredKeys, nil
|
||
|
}
|
||
|
|
||
|
// FirstKey returns a KeyRing with only the first key of the original one.
|
||
|
func (keyRing *KeyRing) FirstKey() (*KeyRing, error) {
|
||
|
if len(keyRing.entities) == 0 {
|
||
|
return nil, errors.New("gopenpgp: No key available in this keyring")
|
||
|
}
|
||
|
newKeyRing := &KeyRing{}
|
||
|
newKeyRing.entities = keyRing.entities[:1]
|
||
|
|
||
|
return newKeyRing.Copy()
|
||
|
}
|
||
|
|
||
|
// Copy creates a deep copy of the keyring.
|
||
|
func (keyRing *KeyRing) Copy() (*KeyRing, error) {
|
||
|
newKeyRing := &KeyRing{}
|
||
|
|
||
|
entities := make([]*openpgp.Entity, len(keyRing.entities))
|
||
|
for id, entity := range keyRing.entities {
|
||
|
var buffer bytes.Buffer
|
||
|
var err error
|
||
|
|
||
|
if entity.PrivateKey == nil {
|
||
|
err = entity.Serialize(&buffer)
|
||
|
} else {
|
||
|
err = entity.SerializePrivateWithoutSigning(&buffer, nil)
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "gopenpgp: unable to copy key: error in serializing entity")
|
||
|
}
|
||
|
|
||
|
bt := buffer.Bytes()
|
||
|
entities[id], err = openpgp.ReadEntity(packet.NewReader(bytes.NewReader(bt)))
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "gopenpgp: unable to copy key: error in reading entity")
|
||
|
}
|
||
|
}
|
||
|
newKeyRing.entities = entities
|
||
|
newKeyRing.FirstKeyID = keyRing.FirstKeyID
|
||
|
|
||
|
return newKeyRing, nil
|
||
|
}
|
||
|
|
||
|
func (keyRing *KeyRing) ClearPrivateParams() {
|
||
|
for _, key := range keyRing.GetKeys() {
|
||
|
key.ClearPrivateParams()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// INTERNAL FUNCTIONS
|
||
|
|
||
|
// appendKey appends a key to the keyring.
|
||
|
func (keyRing *KeyRing) appendKey(key *Key) {
|
||
|
keyRing.entities = append(keyRing.entities, key.entity)
|
||
|
}
|