106 lines
3.0 KiB
Go
106 lines
3.0 KiB
Go
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
|
|
}
|