Skip to content

Commit

Permalink
support add photo
Browse files Browse the repository at this point in the history
  • Loading branch information
izouxv committed Oct 27, 2022
1 parent cf6655e commit 2b6bf6e
Show file tree
Hide file tree
Showing 7 changed files with 1,059 additions and 20 deletions.
77 changes: 66 additions & 11 deletions openpgp/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) &&
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
37 changes: 37 additions & 0 deletions openpgp/packet/public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
17 changes: 16 additions & 1 deletion openpgp/packet/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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])
Expand Down
47 changes: 39 additions & 8 deletions openpgp/packet/userattribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"image/jpeg"
"io"
"io/ioutil"

"github.com/ProtonMail/go-crypto/openpgp/errors"
)

const UserAttrImageSubpacket = 1
Expand All @@ -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{
Expand All @@ -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(),
})
}
Expand All @@ -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)
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 2b6bf6e

Please sign in to comment.