package protonmail import ( "io" "time" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/packet" ) // primaryIdentity returns the Identity marked as primary or the first identity // if none are so marked. func primaryIdentity(e *openpgp.Entity) *openpgp.Identity { var firstIdentity *openpgp.Identity for _, ident := range e.Identities { if firstIdentity == nil { firstIdentity = ident } if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { return ident } } return firstIdentity } // encryptionKey returns the best candidate Key for encrypting a message to the // given Entity. func encryptionKey(e *openpgp.Entity, now time.Time) (openpgp.Key, bool) { candidateSubkey := -1 // Iterate the keys to find the newest key var maxTime time.Time for i, subkey := range e.Subkeys { if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() && !subkey.Sig.KeyExpired(now) && (maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) { candidateSubkey = i maxTime = subkey.Sig.CreationTime } } if candidateSubkey != -1 { subkey := e.Subkeys[candidateSubkey] return openpgp.Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true } // If we don't have any candidate subkeys for encryption and // the primary key doesn't have any usage metadata then we // assume that the primary key is ok. Or, if the primary key is // marked as ok to encrypt to, then we can obviously use it. i := primaryIdentity(e) if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && e.PrimaryKey.PubKeyAlgo.CanEncrypt() && !i.SelfSignature.KeyExpired(now) { return openpgp.Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true } // This Entity appears to be signing only. return openpgp.Key{}, false } // signingKey return the best candidate Key for signing a message with this // Entity. func signingKey(e *openpgp.Entity, now time.Time) (openpgp.Key, bool) { candidateSubkey := -1 for i, subkey := range e.Subkeys { if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() && !subkey.Sig.KeyExpired(now) { candidateSubkey = i break } } if candidateSubkey != -1 { subkey := e.Subkeys[candidateSubkey] return openpgp.Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}, true } // If we have no candidate subkey then we assume that it's ok to sign // with the primary key. i := primaryIdentity(e) if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagSign && !i.SelfSignature.KeyExpired(now) { return openpgp.Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}, true } return openpgp.Key{}, false } func generateUnencryptedKey(cipher packet.CipherFunction, config *packet.Config) (*packet.EncryptedKey, error) { symKey := make([]byte, cipher.KeySize()) if _, err := io.ReadFull(config.Random(), symKey); err != nil { return nil, err } return &packet.EncryptedKey{ CipherFunc: cipher, Key: symKey, }, nil }