package openpgp
import (
+ "crypto"
"crypto/openpgp/armor"
- "crypto/openpgp/error"
+ error_ "crypto/openpgp/error"
"crypto/openpgp/packet"
+ "crypto/rsa"
"io"
- "os"
+ "time"
)
// PublicKeyType is the armor type for a PGP public key.
DecryptionKeys() []Key
}
+// primaryIdentity returns the Identity marked as primary or the first identity
+// if none are so marked.
+func (e *Entity) primaryIdentity() *Identity {
+ var firstIdentity *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 (e *Entity) encryptionKey() Key {
+ candidateSubkey := -1
+
+ for i, subkey := range e.Subkeys {
+ if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() {
+ candidateSubkey = i
+ break
+ }
+ }
+
+ i := e.primaryIdentity()
+
+ if e.PrimaryKey.PubKeyAlgo.CanEncrypt() {
+ // 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.
+ if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid {
+ return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}
+ }
+ }
+
+ if candidateSubkey != -1 {
+ subkey := e.Subkeys[candidateSubkey]
+ return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}
+ }
+
+ // This Entity appears to be signing only.
+ return Key{}
+}
+
+// signingKey return the best candidate Key for signing a message with this
+// Entity.
+func (e *Entity) signingKey() Key {
+ candidateSubkey := -1
+
+ for i, subkey := range e.Subkeys {
+ if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() {
+ candidateSubkey = i
+ break
+ }
+ }
+
+ i := e.primaryIdentity()
+
+ // If we have no candidate subkey then we assume that it's ok to sign
+ // with the primary key.
+ if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign {
+ return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}
+ }
+
+ subkey := e.Subkeys[candidateSubkey]
+ return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}
+}
+
// An EntityList contains one or more Entities.
type EntityList []*Entity
}
// ReadArmoredKeyRing reads one or more public/private keys from an armor keyring file.
-func ReadArmoredKeyRing(r io.Reader) (EntityList, os.Error) {
+func ReadArmoredKeyRing(r io.Reader) (EntityList, error) {
block, err := armor.Decode(r)
- if err == os.EOF {
- return nil, error.InvalidArgumentError("no armored data found")
+ if err == io.EOF {
+ return nil, error_.InvalidArgumentError("no armored data found")
}
if err != nil {
return nil, err
}
if block.Type != PublicKeyType && block.Type != PrivateKeyType {
- return nil, error.InvalidArgumentError("expected public or private key block, got: " + block.Type)
+ return nil, error_.InvalidArgumentError("expected public or private key block, got: " + block.Type)
}
return ReadKeyRing(block.Body)
// ReadKeyRing reads one or more public/private keys. Unsupported keys are
// ignored as long as at least a single valid key is found.
-func ReadKeyRing(r io.Reader) (el EntityList, err os.Error) {
+func ReadKeyRing(r io.Reader) (el EntityList, err error) {
packets := packet.NewReader(r)
- var lastUnsupportedError os.Error
+ var lastUnsupportedError error
for {
var e *Entity
e, err = readEntity(packets)
if err != nil {
- if _, ok := err.(error.UnsupportedError); ok {
+ if _, ok := err.(error_.UnsupportedError); ok {
lastUnsupportedError = err
err = readToNextPublicKey(packets)
}
- if err == os.EOF {
+ if err == io.EOF {
err = nil
break
}
// readToNextPublicKey reads packets until the start of the entity and leaves
// the first packet of the new entity in the Reader.
-func readToNextPublicKey(packets *packet.Reader) (err os.Error) {
+func readToNextPublicKey(packets *packet.Reader) (err error) {
var p packet.Packet
for {
p, err = packets.Next()
- if err == os.EOF {
+ if err == io.EOF {
return
} else if err != nil {
- if _, ok := err.(error.UnsupportedError); ok {
+ if _, ok := err.(error_.UnsupportedError); ok {
err = nil
continue
}
// readEntity reads an entity (public key, identities, subkeys etc) from the
// given Reader.
-func readEntity(packets *packet.Reader) (*Entity, os.Error) {
+func readEntity(packets *packet.Reader) (*Entity, error) {
e := new(Entity)
e.Identities = make(map[string]*Identity)
if e.PrimaryKey, ok = p.(*packet.PublicKey); !ok {
if e.PrivateKey, ok = p.(*packet.PrivateKey); !ok {
packets.Unread(p)
- return nil, error.StructuralError("first packet was not a public/private key")
+ return nil, error_.StructuralError("first packet was not a public/private key")
} else {
e.PrimaryKey = &e.PrivateKey.PublicKey
}
}
+ if !e.PrimaryKey.PubKeyAlgo.CanSign() {
+ return nil, error_.StructuralError("primary key cannot be used for signatures")
+ }
+
var current *Identity
EachPacket:
for {
p, err := packets.Next()
- if err == os.EOF {
+ if err == io.EOF {
break
} else if err != nil {
return nil, err
for {
p, err = packets.Next()
- if err == os.EOF {
+ if err == io.EOF {
return nil, io.ErrUnexpectedEOF
} else if err != nil {
return nil, err
sig, ok := p.(*packet.Signature)
if !ok {
- return nil, error.StructuralError("user ID packet not followed by self-signature")
+ return nil, error_.StructuralError("user ID packet not followed by self-signature")
}
- if sig.SigType == packet.SigTypePositiveCert && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
+ if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, sig); err != nil {
- return nil, error.StructuralError("user ID self-signature invalid: " + err.String())
+ return nil, error_.StructuralError("user ID self-signature invalid: " + err.Error())
}
current.SelfSignature = sig
break
}
case *packet.Signature:
if current == nil {
- return nil, error.StructuralError("signature packet found before user id packet")
+ return nil, error_.StructuralError("signature packet found before user id packet")
}
current.Signatures = append(current.Signatures, pkt)
case *packet.PrivateKey:
}
if len(e.Identities) == 0 {
- return nil, error.StructuralError("entity without any identities")
+ return nil, error_.StructuralError("entity without any identities")
}
return e, nil
}
-func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) os.Error {
+func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
var subKey Subkey
subKey.PublicKey = pub
subKey.PrivateKey = priv
p, err := packets.Next()
- if err == os.EOF {
+ if err == io.EOF {
return io.ErrUnexpectedEOF
}
if err != nil {
- return error.StructuralError("subkey signature invalid: " + err.String())
+ return error_.StructuralError("subkey signature invalid: " + err.Error())
}
var ok bool
subKey.Sig, ok = p.(*packet.Signature)
if !ok {
- return error.StructuralError("subkey packet not followed by signature")
+ return error_.StructuralError("subkey packet not followed by signature")
}
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding {
- return error.StructuralError("subkey signature with wrong type")
+ return error_.StructuralError("subkey signature with wrong type")
}
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
if err != nil {
- return error.StructuralError("subkey signature invalid: " + err.String())
+ return error_.StructuralError("subkey signature invalid: " + err.Error())
}
e.Subkeys = append(e.Subkeys, subKey)
return nil
}
+
+const defaultRSAKeyBits = 2048
+
+// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
+// single identity composed of the given full name, comment and email, any of
+// which may be empty but must not contain any of "()<>\x00".
+func NewEntity(rand io.Reader, currentTimeSecs int64, name, comment, email string) (*Entity, error) {
+ uid := packet.NewUserId(name, comment, email)
+ if uid == nil {
+ return nil, error_.InvalidArgumentError("user id field contained invalid characters")
+ }
+ signingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits)
+ if err != nil {
+ return nil, err
+ }
+ encryptingPriv, err := rsa.GenerateKey(rand, defaultRSAKeyBits)
+ if err != nil {
+ return nil, err
+ }
+
+ t := uint32(currentTimeSecs)
+
+ e := &Entity{
+ PrimaryKey: packet.NewRSAPublicKey(t, &signingPriv.PublicKey, false /* not a subkey */ ),
+ PrivateKey: packet.NewRSAPrivateKey(t, signingPriv, false /* not a subkey */ ),
+ Identities: make(map[string]*Identity),
+ }
+ isPrimaryId := true
+ e.Identities[uid.Id] = &Identity{
+ Name: uid.Name,
+ UserId: uid,
+ SelfSignature: &packet.Signature{
+ CreationTime: t,
+ SigType: packet.SigTypePositiveCert,
+ PubKeyAlgo: packet.PubKeyAlgoRSA,
+ Hash: crypto.SHA256,
+ IsPrimaryId: &isPrimaryId,
+ FlagsValid: true,
+ FlagSign: true,
+ FlagCertify: true,
+ IssuerKeyId: &e.PrimaryKey.KeyId,
+ },
+ }
+
+ e.Subkeys = make([]Subkey, 1)
+ e.Subkeys[0] = Subkey{
+ PublicKey: packet.NewRSAPublicKey(t, &encryptingPriv.PublicKey, true /* is a subkey */ ),
+ PrivateKey: packet.NewRSAPrivateKey(t, encryptingPriv, true /* is a subkey */ ),
+ Sig: &packet.Signature{
+ CreationTime: t,
+ SigType: packet.SigTypeSubkeyBinding,
+ PubKeyAlgo: packet.PubKeyAlgoRSA,
+ Hash: crypto.SHA256,
+ FlagsValid: true,
+ FlagEncryptStorage: true,
+ FlagEncryptCommunications: true,
+ IssuerKeyId: &e.PrimaryKey.KeyId,
+ },
+ }
+
+ return e, nil
+}
+
+// SerializePrivate serializes an Entity, including private key material, to
+// the given Writer. For now, it must only be used on an Entity returned from
+// NewEntity.
+func (e *Entity) SerializePrivate(w io.Writer) (err error) {
+ err = e.PrivateKey.Serialize(w)
+ if err != nil {
+ return
+ }
+ for _, ident := range e.Identities {
+ err = ident.UserId.Serialize(w)
+ if err != nil {
+ return
+ }
+ err = ident.SelfSignature.SignUserId(ident.UserId.Id, e.PrimaryKey, e.PrivateKey)
+ if err != nil {
+ return
+ }
+ err = ident.SelfSignature.Serialize(w)
+ if err != nil {
+ return
+ }
+ }
+ for _, subkey := range e.Subkeys {
+ err = subkey.PrivateKey.Serialize(w)
+ if err != nil {
+ return
+ }
+ err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey)
+ if err != nil {
+ return
+ }
+ err = subkey.Sig.Serialize(w)
+ if err != nil {
+ return
+ }
+ }
+ return nil
+}
+
+// Serialize writes the public part of the given Entity to w. (No private
+// key material will be output).
+func (e *Entity) Serialize(w io.Writer) error {
+ err := e.PrimaryKey.Serialize(w)
+ if err != nil {
+ return err
+ }
+ for _, ident := range e.Identities {
+ err = ident.UserId.Serialize(w)
+ if err != nil {
+ return err
+ }
+ err = ident.SelfSignature.Serialize(w)
+ if err != nil {
+ return err
+ }
+ for _, sig := range ident.Signatures {
+ err = sig.Serialize(w)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ for _, subkey := range e.Subkeys {
+ err = subkey.PublicKey.Serialize(w)
+ if err != nil {
+ return err
+ }
+ err = subkey.Sig.Serialize(w)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SignIdentity adds a signature to e, from signer, attesting that identity is
+// associated with e. The provided identity must already be an element of
+// e.Identities and the private key of signer must have been decrypted if
+// necessary.
+func (e *Entity) SignIdentity(identity string, signer *Entity) error {
+ if signer.PrivateKey == nil {
+ return error_.InvalidArgumentError("signing Entity must have a private key")
+ }
+ if signer.PrivateKey.Encrypted {
+ return error_.InvalidArgumentError("signing Entity's private key must be decrypted")
+ }
+ ident, ok := e.Identities[identity]
+ if !ok {
+ return error_.InvalidArgumentError("given identity string not found in Entity")
+ }
+
+ sig := &packet.Signature{
+ SigType: packet.SigTypeGenericCert,
+ PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
+ Hash: crypto.SHA256,
+ CreationTime: uint32(time.Seconds()),
+ IssuerKeyId: &signer.PrivateKey.KeyId,
+ }
+ if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil {
+ return err
+ }
+ ident.Signatures = append(ident.Signatures, sig)
+ return nil
+}