diff --git a/openpgp/keys.go b/openpgp/keys.go index 48278e6e..98a293d1 100644 --- a/openpgp/keys.go +++ b/openpgp/keys.go @@ -24,11 +24,12 @@ var PrivateKeyType = "PGP PRIVATE KEY BLOCK" // (which must be a signing key), one or more identities claimed by that key, // and zero or more subkeys, which may be encryption keys. type Entity struct { - PrimaryKey *packet.PublicKey - PrivateKey *packet.PrivateKey - Identities map[string]*Identity // indexed by Identity.Name - Revocations []*packet.Signature - Subkeys []Subkey + PrimaryKey *packet.PublicKey + PrivateKey *packet.PrivateKey + Identities map[string]*Identity // indexed by Identity.Name + Revocations []*packet.Signature + Subkeys []Subkey + UserAttribute []*UserAttribute } // An Identity represents an identity claimed by an Entity and zero or more @@ -41,6 +42,13 @@ type Identity struct { Signatures []*packet.Signature // all (potentially unverified) self-signatures, revocations, and third-party signatures } +type UserAttribute struct { + UserAttribute *packet.UserAttribute + SelfSignature *packet.Signature + Revocations []*packet.Signature + Signatures []*packet.Signature // all (potentially unverified) self-signatures, revocations, and third-party signatures +} + // A Subkey is an additional public key in an Entity. Subkeys can be used for // encryption. type Subkey struct { @@ -162,7 +170,6 @@ func (e *Entity) EncryptionKey(now time.Time) (Key, bool) { return Key{}, false } - // CertificationKey return the best candidate Key for certifying a key with this // Entity. func (e *Entity) CertificationKey(now time.Time) (Key, bool) { @@ -203,8 +210,8 @@ func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key, var maxTime time.Time for idx, subkey := range e.Subkeys { if subkey.Sig.FlagsValid && - (flags & packet.KeyFlagCertify == 0 || subkey.Sig.FlagCertify) && - (flags & packet.KeyFlagSign == 0 || subkey.Sig.FlagSign) && + (flags&packet.KeyFlagCertify == 0 || subkey.Sig.FlagCertify) && + (flags&packet.KeyFlagSign == 0 || subkey.Sig.FlagSign) && subkey.PublicKey.PubKeyAlgo.CanSign() && !subkey.PublicKey.KeyExpired(subkey.Sig, now) && !subkey.Sig.SigExpired(now) && @@ -224,9 +231,8 @@ func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int) (Key, // If we have no candidate subkey then we assume that it's ok to sign // with the primary key. Or, if the primary key is marked as ok to // sign with, then we can use it. - if !i.SelfSignature.FlagsValid || ( - (flags & packet.KeyFlagCertify == 0 || i.SelfSignature.FlagCertify) && - (flags & packet.KeyFlagSign == 0 || i.SelfSignature.FlagSign)) && + if !i.SelfSignature.FlagsValid || ((flags&packet.KeyFlagCertify == 0 || i.SelfSignature.FlagCertify) && + (flags&packet.KeyFlagSign == 0 || i.SelfSignature.FlagSign)) && e.PrimaryKey.PubKeyAlgo.CanSign() && (id == 0 || e.PrimaryKey.KeyId == id) { return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature, e.Revocations}, true @@ -451,6 +457,10 @@ EachPacket: } switch pkt := p.(type) { + case *packet.UserAttribute: + if err := addUserAttribute(e, packets, pkt); err != nil { + return nil, err + } case *packet.UserId: if err := addUserID(e, packets, pkt); err != nil { return nil, err @@ -655,6 +665,37 @@ func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign boo } } } + + for _, uat := range e.UserAttribute { + if reSign { + UserAttribute := uat.UserAttribute + + err = UserAttribute.Serialize(w) + if err != nil { + return + } + + if uat.SelfSignature == nil { + return goerrors.New("openpgp: can't re-sign identity without valid self-signature") + } + err = uat.SelfSignature.SignUserAttribute(UserAttribute, e.PrimaryKey, e.PrivateKey, config) + if err != nil { + return + } + } else { + err = uat.UserAttribute.Serialize(w) + if err != nil { + return + } + } + for _, sig := range uat.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { err = subkey.PrivateKey.Serialize(w) if err != nil { @@ -712,6 +753,20 @@ func (e *Entity) Serialize(w io.Writer) error { } } } + + for _, uat := range e.UserAttribute { + err = uat.UserAttribute.Serialize(w) + if err != nil { + return err + } + for _, sig := range uat.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { err = subkey.PublicKey.Serialize(w) if err != nil { diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go index e0f5f74a..212ea43a 100644 --- a/openpgp/packet/public_key.go +++ b/openpgp/packet/public_key.go @@ -745,6 +745,33 @@ func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash return } +// userAttributeSignatureHash returns a Hash of the message that needs to be signed +// to assert that pk is a valid key for id. +func userAttributeSignatureHash(uat *UserAttribute, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) { + + if !hashFunc.Available() { + return nil, errors.UnsupportedError("hash function") + } + h = hashFunc.New() + + // RFC 4880, section 5.2.4 + pk.SerializeSignaturePrefix(h) + pk.serializeWithoutHeaders(h) + + data := uat.Data() + + var buf [5]byte + buf[0] = 0xd1 + buf[1] = byte(len(data) >> 24) + buf[2] = byte(len(data) >> 16) + buf[3] = byte(len(data) >> 8) + buf[4] = byte(len(data)) + h.Write(buf[:]) + h.Write(data) + + return +} + // VerifyUserIdSignature returns nil iff sig is a valid signature, made by this // public key, that id is the identity of pub. func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signature) (err error) { @@ -755,6 +782,16 @@ func (pk *PublicKey) VerifyUserIdSignature(id string, pub *PublicKey, sig *Signa return pk.VerifySignature(h, sig) } +// VerifyUserAttributeSignature returns nil iff sig is a valid signature, made by this +// public key, that id is the identity of pub. +func (pk *PublicKey) VerifyUserAttributeSignature(pkt *UserAttribute, pub *PublicKey, sig *Signature) (err error) { + h, err := userAttributeSignatureHash(pkt, pub, sig.Hash) + if err != nil { + return err + } + return pk.VerifySignature(h, sig) +} + // KeyIdString returns the public key's fingerprint in capital hex // (e.g. "6C7EE1B8621CC013"). func (pk *PublicKey) KeyIdString() string { diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index 6d2a61ce..d02b0517 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -689,6 +689,21 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co return sig.Sign(h, priv, config) } +// SignUserAttribute computes a signature from priv, asserting that pub is a valid +// key for the identity id. On success, the signature is stored in sig. Call +// Serialize to write it out. +// If config is nil, sensible defaults will be used. +func (sig *Signature) SignUserAttribute(uat *UserAttribute, pub *PublicKey, priv *PrivateKey, config *Config) error { + if priv.Dummy() { + return errors.ErrDummyPrivateKey("dummy key found") + } + h, err := userAttributeSignatureHash(uat, pub, sig.Hash) + if err != nil { + return err + } + return sig.Sign(h, priv, config) +} + // CrossSignKey computes a signature from signingKey on pub hashed using hashKey. On success, // the signature is stored in sig. Call Serialize to write it out. // If config is nil, sensible defaults will be used. @@ -971,7 +986,7 @@ func (sig *Signature) AddMetadataToHashSuffix() { n := sig.HashSuffix[len(sig.HashSuffix)-8:] l := uint64( uint64(n[0])<<56 | uint64(n[1])<<48 | uint64(n[2])<<40 | uint64(n[3])<<32 | - uint64(n[4])<<24 | uint64(n[5])<<16 | uint64(n[6])<<8 | uint64(n[7])) + uint64(n[4])<<24 | uint64(n[5])<<16 | uint64(n[6])<<8 | uint64(n[7])) suffix := bytes.NewBuffer(nil) suffix.Write(sig.HashSuffix[:l]) diff --git a/openpgp/packet/userattribute.go b/openpgp/packet/userattribute.go index 88ec72c6..7a3f717c 100644 --- a/openpgp/packet/userattribute.go +++ b/openpgp/packet/userattribute.go @@ -10,6 +10,8 @@ import ( "image/jpeg" "io" "io/ioutil" + + "github.com/ProtonMail/go-crypto/openpgp/errors" ) const UserAttrImageSubpacket = 1 @@ -25,8 +27,32 @@ type UserAttribute struct { // NewUserAttributePhoto creates a user attribute packet // containing the given images. func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { + var imgBytes [][]byte + for _, photo := range photos { + var buf bytes.Buffer + if err = jpeg.Encode(&buf, photo, nil); err != nil { + return + } + imgBytes = append(imgBytes, buf.Bytes()) + } + return newUserAttributePhotoBytes(imgBytes) +} + +func NewUserAttributePhotoBytes(photos [][]byte) (uat *UserAttribute, err error) { + for _, photo := range photos { + //check jpeg + _, err = jpeg.Decode(bytes.NewBuffer(photo)) + if err != nil { + return nil, errors.InvalidArgumentError("jpeg data err") + } + } + return newUserAttributePhotoBytes(photos) +} + +func newUserAttributePhotoBytes(photos [][]byte) (uat *UserAttribute, err error) { uat = new(UserAttribute) for _, photo := range photos { + var buf bytes.Buffer // RFC 4880, Section 5.12.1. data := []byte{ @@ -39,17 +65,15 @@ func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error if _, err = buf.Write(data); err != nil { return } - if err = jpeg.Encode(&buf, photo, nil); err != nil { - return - } + buf.Write(photo) - lengthBuf := make([]byte, 5) - n := serializeSubpacketLength(lengthBuf, len(buf.Bytes())+1) - lengthBuf = lengthBuf[:n] + encodedLength := make([]byte, 5) + n := serializeSubpacketLength(encodedLength, len(buf.Bytes())+1) + encodedLength = encodedLength[:n] uat.Contents = append(uat.Contents, &OpaqueSubpacket{ SubType: UserAttrImageSubpacket, - EncodedLength: lengthBuf, + EncodedLength: encodedLength, Contents: buf.Bytes(), }) } @@ -60,7 +84,13 @@ func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { return &UserAttribute{Contents: contents} } - +func (uat *UserAttribute) Data() []byte { + buf := bytes.NewBuffer(nil) + for _, osp := range uat.Contents { + osp.Serialize(buf) + } + return buf.Bytes() +} func (uat *UserAttribute) parse(r io.Reader) (err error) { // RFC 4880, section 5.13 b, err := ioutil.ReadAll(r) @@ -74,6 +104,7 @@ func (uat *UserAttribute) parse(r io.Reader) (err error) { // Serialize marshals the user attribute to w in the form of an OpenPGP packet, including // header. func (uat *UserAttribute) Serialize(w io.Writer) (err error) { + var buf bytes.Buffer for _, sp := range uat.Contents { err = sp.Serialize(&buf) diff --git a/openpgp/read_write_test_data.go b/openpgp/read_write_test_data.go index 117a1464..ab12d576 100644 --- a/openpgp/read_write_test_data.go +++ b/openpgp/read_write_test_data.go @@ -171,3 +171,697 @@ y29VPonFXqi2zKkpZrvyvZxg+n5e8Nt9wNbuxeCd3QD/TtO2s+JvjrE4Siwv UQdl5MlBka1QSNbMq2Bz7XwNPg4= =6lbM -----END PGP MESSAGE-----` + +// Generated with the above private key +const pgpPhotoPublicOpenPGP2_2_34 = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +qzSeUQEzjQ7WX5tpXrluTLiyTVHejMy0T9JGp6TUuRldOnJNByP5hSuU5U09RZx4 +0RqG3wwNokxDZVwMJR9jBGMfdjxgGMZOcrK6gun04t37p9ms06babp4lhqqXKn6T +vkyjZsm35T8ZuUl3TcVwWWnXGiNBtJcJBFWWT9a/Q6jat0dmooxJW4nB8P8AJtK8 +mX3/AKBkEf7P71vJ6aknPclFdHwu3tuYmK0qS7KsSqommW/pjX87Y6+I9zWWn0uv +T6dmaiZpxSTOA6yEqXHPT2rUOklRLLhqJGRJR54YBKNIaXv4mP8AzCZ50q9Xy2FH +1sudJ5CU5WqOlNPCo683XLu46NnTNfzXLe+cW4nUpRl0cmS9HcfbZQlcWzamaajy +G5tBIYXS3zlbIiWSH+F6jKJKfKuJkIyti88FA/8AFAxul6ql4dZIokVw1OHTVpET +Dar2ynTEV8aBFdupttYJqoqHFENOVe9yTbohtTbmXqCVVyY+m6ys107Jl1uqYtk7 +0NdZntb3I/zbtbmpiMKcLzx+DSrKc8HC86fZ2ptH+us5bnOeiRuokXb/AC0VkYq2 +HMfcu57aGqSA7IXNk1FLzA/KREas7Ryzep61Faxd2yrKRvNCOrOJF0cZU1X9akrk +V+/oZze5FoxsceT2GGF/aRhktzhlk1H9Ppyc2AmdsamQTZVbP1Cfd2O5NFWFBj31 +mcyRQ0+8nVkw1ZWLlk9SVaTkahtjEeGmHEJaluU1aUp+5uevVper+rXIkp8XkfzI +QFdiFbVpMRE+Y7XNf1W5shbN7tg3yW5H4aqOr6dTUkXqpmobLpWaCqOfIUtMdubN +cu5T1O3Frn3UUlbVsHPmzJB2kzl86VKlm6lKCUv06ojgV4cLJW7GW5aMG4XYYaUI +SfwVDJKl6nc5rVazzZVr91nZJ/PfOYKpaKJDcNy5sIcNFfF1Baqfd09VdDGsZyK5 +p/m2UjepYdb6GM/EOA3NrFVddpnQpLmERJILE5rmN2TfLdeLthpSpa1oS3AY6WHc +N/0qFrMh/wDLbvp33slXWWt9K5bela0m29R2vSN6bpjeXMlt17MiQqymSkKNirp5 +aU1mh3lJl6AmPyf43FdUlJJ4rSHUeL6vWapCDSZ9n7oI/Ocl4QzdY6Ci8FH/ACW0 +xfJtoLyWXsqeWzITXVMOA5Yy4UCU6iZoybIfo9HtxR/GYfMaittpSW3uNIUzkPRC +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 +=Eic6 +-----END PGP PUBLIC KEY BLOCK----- +` + +const pgpPhotoPublicOpenPGP2_3_6 = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYwMEERYJKwYBBAHaRw8BAQdAM+eSw80hM8YBvY1anfN6EMCDfe4rVdHYMyIU +sgk6Y5G0FHRlc3QgPHRlc3RAdGVzdC5jb20+iJkEExYKAEEWIQQt/1rIQwoCvhlz +np31J3BvaZrFogUCYwMEEQIbAwUJA8JnAAULCQgHAgIiAgYVCgkICwIEFgIDAQIe +BwIXgAAKCRD1J3BvaZrFotVSAQDFp1tVbffOiWDjkKEOOP6RtViGi+3c+IiUDi7w +CUH+DgEA4HQwGenZockPDKL7cPRwHUsEJH7zc74s/ERS1N5trwfR/wAAOSj/AAA5 +IwEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMC +AgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUV +DA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQU +FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAEgAPADASIAAhEB +AxEB/8QAHQAAAAcBAQEAAAAAAAAAAAAAAQIDBAUGBwAICf/EABkBAAMBAQEAAAAA +AAAAAAAAAAECAwAEBf/aAAwDAQACEAMQAAAB8uAAzcvDxAcPbAI8CIGHEoKFwKsm +oGdJHTRk1CmOVQWb7JOEnDiRiZKLAS4wuqfG45XiBimcgph4Bw6Tb+roU8otfY/l +cNBd3WQQ4dgNxsVSCVSVQpyTILEGI5SV2cMHTfFMFSuqYmA7inLlS44jEODgbSvQ +GcXjyeu0wlTeo/nqq+gsl7Uq4nN1QTMY6kgn4MVQ0ijRwPmQyRzGbEKuGJCrAM1K +4JRUSrg6Ne4SnKkMp2u54rqfndEDcsxeEalluxO4dPmCvaSh1DPVPSdi7ODyYb14 +qN5Kv+9O+avmmjev4Sg8vD6LiCcMHU62lahz5irFKdJsYh+ZIpR/qN+TIZ70pcXj +5KVtmfwvKxMhFJS0eifJeoQrfMg9JZdy98BuPm5Trh6wPjWsej5MgcilJGORTDq9 +ZcIgtg1TxVonMPSVeuB+98Wz/wBWHWngyufRWoClcl83hk49n7JU6HImjeQ5+2PQ +OAYipSba7pnm/WuHuhq3seZcvowcvGjUbRp3kiU7vN9Xmx7S+vzZPPsWqfHy2yMk +2vlTv2j5XuHsWtRzG78BwNVPnm1S7i6lDIqAu126qWZnMfFArhM5vo2eWadfTVJ0 +Z15npeYWuk1ROuvndEdkXqTkzXIloVfCg7hKx1uKmijbIcu1v0T+3d2eNd3n86uM +fi6imAgL1Zu5SqK5XWZsk8SIaXCqzyW2K8YyPn9/omOpDfY+fehFUt5gYbZRE6Ki +8dqYzaUU+t5kvrBr56fhOOUDqDdnKlqnz5jdUy/i6W6ahMXLiLlUs4dpOQyCblNg +zdEbrbU6jesv5Owuh5e+skpsXmnSlf0g+xaY52t9P1EUfBovf6JO9R33A21+b1wG +Wan63hCIjfn8lZFt+O8t6+VQpZPlQDy0hXp9KiUS4pqdYp3tmN32lrUlZVt3Tzu5 +GLufP0IxNZv6tKt8pv4r6DsOV5DFfU1frsGpWf2R3N9Bm830b2fC844r6DxNZ0JF ++xNOMU2Yk3CvVeT5NylFrvT71z9OVxiPdcnVnbuUax0GfmUeQy2RsJAX0+OzoGj1 +R06xkNJxFDK7Dlb9Cp7S8g+zerz8YynYaHPkwuHs9cNUDF7MYSkzTMvAWyHQtYOj +I9GWT6Eh1xPYIN7KjOzjQFLvYks3RnE+4lsaxShZdE5iLn2py0PbNTZbjr5R6PKy +eAskXBfPlQ1nLReL4xcwgbiXl3znRubpuNS0Skw6qyxn4PoR6xfRQM9N0HYRoavt +drwisd2K3PPz5YPSVm6IYcy9EqGdUtgGpE3E6kKA0kTcxrFA2NInyDA+y85W3nkd +CrwpXdJqE7G2nVKfYc3VAwWp3DonhsJ66lKT856fq69I1mycZ5KF4XQwAOAiXiDi +UhBgTQpOrmA3GTGINMcxTYpR0yfNX5GQ5XUXbqYuFGx2zgERwWUbKnKCkOywpcQq +LcuzkGZCHrdsuQiu5c1nTzcfhJOWM+RMIk8cgnKKJHzKh3A9xjEpgubZuZc2LY6x +sGoPjHMBfiVaqrHIIoJyoH4zrTjlPxurw8wKBhOKIiSAiOLNROtB7gpWgOtRq4ba +xnqj4ieNGyJCpiq4JmKcgxinIEwCQYxTMtMWhZPhq6OkegMPGIAREnuN2zSgaBmq +VlIGJlQ7lOtz+OuqKOKwYvTHIBQDEENxjhMB8vG453G4WH//xAAuEAAABgECBQME +AwADAAAAAAAAAQIDBAURBhIQExQgIQcwMRUiI0EWJDIlM0L/2gAIAQEAAQUC9guG +OGAjweQ75BEC+chzyeA14DbofPc5x/e4Grx7Z/IT8hfyQLgr5CAkwvyrtPur656z +kSYrkR7uTwXwLgfzgJIfoyGBj2YrBypGnNPsUce2rIM9y6ryrrDtLgfAuGOBDPs4 +4YGgKtMuzTNNchZMqQqNDsGdSRmWbPAwMDAwMDAIgSRtG3I2jA2jYNo2jaNo2mNp +jAxwaRzV0DSKqI7iMw1LNYgrcZmXtESpU+rNo8DHZgEgMR1OKfaU0oY4Z4buzIz2 +Nq2K0/O6+01FJ5akSsHXytxNxVy2LeO6xIkQidI0mkyGBjgyjIomklM1IRKmGXkv +jHjwPAwQ2kDSMDbkbS4YGBgaWmFEtdUJ3Sv8qYszjyqu0TOK4rEzGJUPpXW2I6no +GlKSUlOjKtILSdYQ/ilYP4tWhrT0Jk5Oka+Upz09rlhz00jGHvTNwPendi0JGkrK +OHYLzAwfDyPILgRDBiHRTpx2NBIpEtzFSg4raS1mYorRUOTDmokN6ipSfQ5ubVSW +ZVMilt028Tvu7tiijRPUKE86w43KachMulJ0lWSxN9MIT4nemU9gTdP2EAYwcSvk +Tl1vp++8K/ScGAEMJQWvZPOt2PtXNXlwfB6Xtti0TmVpvqvD60G0TEh2Mus1mtlu +LMbltkfYQ9RVKXL5Y0dqNdRJI8lwwFNJcKw0bV2YiQmIiEmkZIGZEVvK6+1JOEuq +3L4MPGw5U2fMJKm5cefAVHdU3lRZFfZSKp6t1ww4UZ9mW3sG0LWhotRQYuoGFQVM +uzIfTrL1EXFZ0vqxN+MDAwCEjUDbQLVmAjVzYn6sQqA2QX/1cTIVsk2nIcvmNuxn +LNqbH5Du8ggGjJ19hJr3K3XgVasrr59vMtTRuQpFw6+xK5j4PTddBPT0StjxeJBU +lSgbhmOYY3mYbCj+0hgY4Eo0Kon+YdO1gtT1BoceZMzUzgYVj9kyRmxJeiHBjLkO +2FHLiit0zGOPCiwIofkIlP0twdNMjyieaS4Rgu0iCfgEQwDIYBpyNPumUuCRMNy2 +ClsTaJyKy5EUgbVbjb8kQ24Cfxrq9QvpjVFOqY9qDTs1556lmxGqnRZ2So8REZk2 +xk0BDpHxIh+0/BAiGAZAyGBppJR3KC06qUxaoccU0hxM6njT0W+lhLqXGFh1brZs +ulIQkthxdQyG2nL6ySo9VyHq/Q0tUqnwMBSMhTeBgYBjPlB+EeQRAk+DIGQMgpzp +qzSeUQEzjQ7WX5tpXrluTLiyTVHejMy0T9JGp6TUuRldOnJNByP5hSuU5U09RZx4 +0RqG3wwNokxDZVwMJR9jBGMfdjxgGMZOcrK6gun04t37p9ms06babp4lhqqXKn6T +vkyjZsm35T8ZuUl3TcVwWWnXGiNBtJcJBFWWT9a/Q6jat0dmooxJW4nB8P8AJtK8 +mX3/AKBkEf7P71vJ6aknPclFdHwu3tuYmK0qS7KsSqommW/pjX87Y6+I9zWWn0uv +T6dmaiZpxSTOA6yEqXHPT2rUOklRLLhqJGRJR54YBKNIaXv4mP8AzCZ50q9Xy2FH +1sudJ5CU5WqOlNPCo683XLu46NnTNfzXLe+cW4nUpRl0cmS9HcfbZQlcWzamaajy +G5tBIYXS3zlbIiWSH+F6jKJKfKuJkIyti88FA/8AFAxul6ql4dZIokVw1OHTVpET +Dar2ynTEV8aBFdupttYJqoqHFENOVe9yTbohtTbmXqCVVyY+m6ys107Jl1uqYtk7 +0NdZntb3I/zbtbmpiMKcLzx+DSrKc8HC86fZ2ptH+us5bnOeiRuokXb/AC0VkYq2 +HMfcu57aGqSA7IXNk1FLzA/KREas7Ryzep61Faxd2yrKRvNCOrOJF0cZU1X9akrk +V+/oZze5FoxsceT2GGF/aRhktzhlk1H9Ppyc2AmdsamQTZVbP1Cfd2O5NFWFBj31 +mcyRQ0+8nVkw1ZWLlk9SVaTkahtjEeGmHEJaluU1aUp+5uevVper+rXIkp8XkfzI +QFdiFbVpMRE+Y7XNf1W5shbN7tg3yW5H4aqOr6dTUkXqpmobLpWaCqOfIUtMdubN +cu5T1O3Frn3UUlbVsHPmzJB2kzl86VKlm6lKCUv06ojgV4cLJW7GW5aMG4XYYaUI +SfwVDJKl6nc5rVazzZVr91nZJ/PfOYKpaKJDcNy5sIcNFfF1Baqfd09VdDGsZyK5 +p/m2UjepYdb6GM/EOA3NrFVddpnQpLmERJILE5rmN2TfLdeLthpSpa1oS3AY6WHc +N/0qFrMh/wDLbvp33slXWWt9K5bela0m29R2vSN6bpjeXMlt17MiQqymSkKNirp5 +aU1mh3lJl6AmPyf43FdUlJJ4rSHUeL6vWapCDSZ9n7oI/Ocl4QzdY6Ci8FH/ACW0 +xfJtoLyWXsqeWzITXVMOA5Yy4UCU6iZoybIfo9HtxR/GYfMaittpSW3uNIUzkPRC +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 +=vfpo +-----END PGP PUBLIC KEY BLOCK----- +` diff --git a/openpgp/uat.go b/openpgp/uat.go new file mode 100644 index 00000000..daf201bc --- /dev/null +++ b/openpgp/uat.go @@ -0,0 +1,134 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import ( + "crypto" + "io" + + "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/packet" +) + +func (t *Entity) AddPhoto(jpegBytes []byte, config *packet.Config) error { + creationTime := config.Now() + keyLifetimeSecs := config.KeyLifetime() + + uat, err := packet.NewUserAttributePhotoBytes([][]byte{jpegBytes}) + if err != nil { + return errors.InvalidArgumentError("add photo field contained invalid characters") + } + + primary := t.PrivateKey + + isPrimaryId := len(t.Identities) == 0 + + selfSignature := &packet.Signature{ + Version: primary.PublicKey.Version, + SigType: packet.SigTypePositiveCert, + PubKeyAlgo: primary.PublicKey.PubKeyAlgo, + Hash: config.Hash(), + CreationTime: creationTime, + KeyLifetimeSecs: &keyLifetimeSecs, + IssuerKeyId: &primary.PublicKey.KeyId, + IssuerFingerprint: primary.PublicKey.Fingerprint, + IsPrimaryId: &isPrimaryId, + FlagsValid: true, + FlagSign: true, + FlagCertify: true, + MDC: true, // true by default, see 5.8 vs. 5.14 + AEAD: config.AEAD() != nil, + V5Keys: config != nil && config.V5Keys, + } + + // Set the PreferredHash for the SelfSignature from the packet.Config. + // If it is not the must-implement algorithm from rfc4880bis, append that. + selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())} + if config.Hash() != crypto.SHA256 { + selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256)) + } + + // Likewise for DefaultCipher. + selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())} + if config.Cipher() != packet.CipherAES128 { + selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128)) + } + + // We set CompressionNone as the preferred compression algorithm because + // of compression side channel attacks, then append the configured + // DefaultCompressionAlgo if any is set (to signal support for cases + // where the application knows that using compression is safe). + selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)} + if config.Compression() != packet.CompressionNone { + selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression())) + } + + // And for DefaultMode. + selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())} + if config.AEAD().Mode() != packet.AEADModeEAX { + selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX)) + } + + // User ID binding signature + err = selfSignature.SignUserAttribute(uat, &primary.PublicKey, primary, config) + if err != nil { + return err + } + userAttribute := &UserAttribute{ + UserAttribute: uat, + SelfSignature: selfSignature, + Signatures: []*packet.Signature{selfSignature}, + } + t.UserAttribute = append(t.UserAttribute, userAttribute) + return nil +} + +func addUserAttribute(e *Entity, packets *packet.Reader, pkt *packet.UserAttribute) error { + // Make a new Identity object, that we might wind up throwing away. + // We'll only add it if we get a valid self-signature over this + // userID. + uat := new(UserAttribute) + uat.UserAttribute = pkt + e.UserAttribute = append(e.UserAttribute, uat) + + for { + p, err := packets.Next() + if err == io.EOF { + break + } else if err != nil { + return err + } + + sig, ok := p.(*packet.Signature) + if !ok { + packets.Unread(p) + break + } + + if sig.SigType != packet.SigTypeGenericCert && + sig.SigType != packet.SigTypePersonaCert && + sig.SigType != packet.SigTypeCasualCert && + sig.SigType != packet.SigTypePositiveCert && + sig.SigType != packet.SigTypeCertificationRevocation { + return errors.StructuralError("user ID signature with wrong type") + } + + if sig.CheckKeyIdOrFingerprint(e.PrimaryKey) { + if err = e.PrimaryKey.VerifyUserAttributeSignature(pkt, e.PrimaryKey, sig); err != nil { + return errors.StructuralError("user attribute self-signature invalid: " + err.Error()) + } + if sig.SigType == packet.SigTypeCertificationRevocation { + uat.Revocations = append(uat.Revocations, sig) + } else if uat.SelfSignature == nil || sig.CreationTime.After(uat.SelfSignature.CreationTime) { + uat.SelfSignature = sig + } + uat.Signatures = append(uat.Signatures, sig) + } else { + uat.Signatures = append(uat.Signatures, sig) + } + } + + return nil +} diff --git a/openpgp/uat_test.go b/openpgp/uat_test.go new file mode 100644 index 00000000..30239715 --- /dev/null +++ b/openpgp/uat_test.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package openpgp + +import ( + "bytes" + "encoding/base64" + "testing" + + "github.com/ProtonMail/go-crypto/openpgp/armor" + "github.com/ProtonMail/go-crypto/openpgp/packet" +) + +func TestUserattribute_CompatibleGunPG2_3_6(t *testing.T) { + + _, err := ReadArmoredKeyRing(bytes.NewBufferString((pgpPhotoPublicOpenPGP2_3_6))) + if err != nil { + t.Fatal(err) + } +} + +func TestAddPhoto(t *testing.T) { + entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", nil) + if err != nil { + t.Fatal(err) + } + + jpegBase64 := "/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAyAEsDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9EKKKKACipLKzm1K/gtbeMy3F1KkMSAgF3ZgqrzxySBzxzXXXnwXm0+6kt7jxZ8P4LiFzHLFJrRV43U4ZWHlcEEEEdiKAONorrf8AhUf/AFOPw7/8HZ/+NUf8Kj/6nH4d/wDg7P8A8aoA5Kitrxh4Eu/BcdjLNdaVqFpqSSNbXWnXX2iCUxtsdQ2AcqxAPGMnGcggYtABRRRQAUUUUAa3w+/5KH4d/wCwtZ/+j0r1LwH4h8DaP4q8dR+KoLF719dvHjkurM3CmASt8qfK2GDbyQACcr1xx5b8Pv8Akofh3/sLWf8A6PSvX/hB8ILDxr8S/FmvakwuIdN8RXtvDaMvyPKspfe/qBvXC+oOc8CgDx3RvDVx448Wf2foNnNIbqVzbxO2TDFu4MjcgBVIy3r0ySAev+Lv7PmofDDTLfUI5v7SsNirdSqm37NL3yP+eZPRj06HsT9GeEvhzongS4vpdJsobOTUpfNmK/oq/wB1ByQowoLHAFbVzDHeQtFIqSRyAq6MAyuDwQR3BoA+RfGP/JHPh/8A9xb/ANKxXI11ni0/8WX+Hv01X/0rWuToAKKKKACiiigCbTdQm0fVLW8t2VbizmS4iLLuAdGDKSO/IHFddqXxP0PWb+a7vPAehXN3dSNNNKbmUeY7EszY7ZYk4964uigDrf8AhPPDP/RPNB/8CpaP+E88M/8ARPNB/wDAqWuSooA3vG3jr/hL7XTbWDTbPSdP0lJVtra3LNtMrh5CWY5OWAPbHPrWDRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB//Z" + jpegBytes, err := base64.RawStdEncoding.DecodeString(jpegBase64) + if err != nil { + t.Fatal(err) + } + + err = entity.AddPhoto(jpegBytes, nil) + if err != nil { + t.Fatal(err) + } + + serializedEntity := bytes.NewBuffer(nil) + entity.SerializePrivate(serializedEntity, nil) + + entity2, err := ReadEntity(packet.NewReader(bytes.NewBuffer(serializedEntity.Bytes()))) + if err != nil { + t.Fatal(err) + } + + if len(entity2.UserAttribute) != 1 { + t.Fatal("data err, entity have no uat") + } + + imgBytes := entity2.UserAttribute[0].UserAttribute.ImageData() + if !bytes.Equal(imgBytes[0], jpegBytes) { + t.Fatal("image data not equal") + } + + if true { + + keybuf := bytes.NewBuffer(nil) + writer, err := armor.Encode(keybuf, PrivateKeyType, nil) + if err != nil { + return + } + err = entity.SerializePrivate(writer, nil) + if err != nil { + return + } + writer.Close() + gpgpstr := keybuf.String() + t.Logf("data: %v\n", gpgpstr) + } +}