Skip to content

Commit

Permalink
feat: Add AddPhotos to v2 api.
Browse files Browse the repository at this point in the history
  • Loading branch information
izouxv committed Oct 5, 2024
1 parent f6ad483 commit c36a24a
Show file tree
Hide file tree
Showing 8 changed files with 609 additions and 7 deletions.
40 changes: 40 additions & 0 deletions openpgp/packet/public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,46 @@ func (pk *PublicKey) VerifyDirectKeySignature(sig *Signature) (err error) {
return pk.VerifySignature(h, sig)
}

// VerifyUserAttributeSignature returns nil iff sig is a valid signature, made by this
// public key, that uat is an attribute of pub.
func (pk *PublicKey) VerifyUserAttributeSignature(pkt *UserAttribute, pub *PublicKey, sig *Signature) (err error) {
h, err := sig.PrepareVerify()
if err != nil {
return err
}
err = userAttributeSignatureHash(pkt, pub, h)
if err != nil {
return err
}
return pk.VerifySignature(h, sig)
}

// userAttributeSignatureHash returns a Hash of the message that needs to be signed
// to assert that pk is a valid key for uat.
func userAttributeSignatureHash(uat *UserAttribute, pk *PublicKey, h hash.Hash) (err error) {

// RFC 4880, section 5.2.4
if err := pk.SerializeSignaturePrefix(h); err != nil {
return err
}
if err := pk.serializeWithoutHeaders(h); err != nil {
return err
}

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
}

// KeyIdString returns the public key's fingerprint in capital hex
// (e.g. "6C7EE1B8621CC013").
func (pk *PublicKey) KeyIdString() string {
Expand Down
19 changes: 19 additions & 0 deletions openpgp/packet/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,25 @@ func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, co
return sig.Sign(prepareHash, priv, config)
}

// SignUserAttribute computes a signature from priv, asserting that pub has
// user attribute uat. 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")
}
prepareHash, err := sig.PrepareSign(config)
if err != nil {
return err
}
err = userAttributeSignatureHash(uat, pub, prepareHash)
if err != nil {
return err
}
return sig.Sign(prepareHash, priv, config)
}

// SignDirectKeyBinding computes a signature from priv
// On success, the signature is stored in sig.
// Call Serialize to write it out.
Expand Down
45 changes: 38 additions & 7 deletions openpgp/packet/userattribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"image"
"image/jpeg"
"io"

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

const UserAttrImageSubpacket = 1
Expand All @@ -24,6 +26,29 @@ 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
Expand All @@ -38,17 +63,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,6 +83,14 @@ 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 := io.ReadAll(r)
Expand Down
38 changes: 38 additions & 0 deletions openpgp/v2/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Entity struct {
Revocations []*packet.VerifiableSignature
DirectSignatures []*packet.VerifiableSignature // Direct-key self signature of the PrimaryKey (contains primary key properties in v6)}
Subkeys []Subkey
Attributes []*Attribute
}

// A Key identifies a specific public key in an Entity. This is either the
Expand Down Expand Up @@ -503,6 +504,10 @@ EachPacket:
if err != nil {
return nil, err
}
case *packet.UserAttribute:
if err := addUserAttribute(e, packets, pkt); err != nil {
return nil, err
}
case *packet.Signature:
if pkt.SigType == packet.SigTypeKeyRevocation {
e.Revocations = append(e.Revocations, packet.NewVerifiableSig(pkt))
Expand Down Expand Up @@ -592,6 +597,27 @@ func (e *Entity) serializePrivate(w io.Writer, config *packet.Config, reSign boo
return err
}
}
for _, attr := range e.Attributes {
if reSign {
if attr.SelfSignature == nil {
return goerrors.New("openpgp: can't re-sign user attribute without valid self-signature")
}
err = attr.SelfSignature.SignUserAttribute(attr.UserAttribute, e.PrimaryKey, e.PrivateKey, config)
if err != nil {
return
}
}
err = attr.UserAttribute.Serialize(w)
if err != nil {
return
}
for _, sig := range attr.Signatures {
err = sig.Serialize(w)
if err != nil {
return err
}
}
}
for _, subkey := range e.Subkeys {
if reSign {
if err := subkey.ReSign(config); err != nil {
Expand Down Expand Up @@ -627,6 +653,18 @@ func (e *Entity) Serialize(w io.Writer) error {
return err
}
}
for _, uat := range e.Attributes {
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 {
if err := subkey.Serialize(w, false); err != nil {
return err
Expand Down
Loading

0 comments on commit c36a24a

Please sign in to comment.