protonmail: add crypto helpers for contacts
This commit is contained in:
parent
0447370b16
commit
16a9952616
|
@ -1,19 +1,26 @@
|
||||||
package protonmail
|
package protonmail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Contact struct {
|
type Contact struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
UID string
|
UID string
|
||||||
Size int
|
Size int
|
||||||
CreateTime int64
|
CreateTime int64
|
||||||
ModifyTime int64
|
ModifyTime int64
|
||||||
LabelIDs []string
|
LabelIDs []string
|
||||||
|
|
||||||
// Not when using ListContacts
|
// Not when using ListContacts
|
||||||
ContactEmails []*ContactEmail
|
ContactEmails []*ContactEmail
|
||||||
|
@ -68,6 +75,122 @@ type ContactCard struct {
|
||||||
Signature string
|
Signature string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewEncryptedContactCard(r io.Reader, to []*openpgp.Entity, signer *openpgp.Entity) (*ContactCard, error) {
|
||||||
|
// TODO: sign and encrypt at the same time
|
||||||
|
|
||||||
|
var msg, armored bytes.Buffer
|
||||||
|
if signer != nil {
|
||||||
|
// We'll sign the message later, keep a copy of it
|
||||||
|
r = io.TeeReader(r, &msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext, err := armor.Encode(&armored, "PGP MESSAGE", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cleartext, err := openpgp.Encrypt(ciphertext, to, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(cleartext, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cleartext.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ciphertext.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
card := &ContactCard{
|
||||||
|
Type: ContactCardEncrypted,
|
||||||
|
Data: armored.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if signer != nil {
|
||||||
|
var sig bytes.Buffer
|
||||||
|
if err := openpgp.ArmoredDetachSignText(&sig, signer, &msg, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
card.Type = ContactCardEncryptedAndSigned
|
||||||
|
card.Signature = sig.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return card, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignedContactCard(r io.Reader, signer *openpgp.Entity) (*ContactCard, error) {
|
||||||
|
var msg, sig bytes.Buffer
|
||||||
|
r = io.TeeReader(r, &msg)
|
||||||
|
if err := openpgp.ArmoredDetachSignText(&sig, signer, r, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ContactCard{
|
||||||
|
Type: ContactCardSigned,
|
||||||
|
Data: msg.String(),
|
||||||
|
Signature: sig.String(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func entityPrimaryKey(e *openpgp.Entity) *openpgp.Key {
|
||||||
|
var selfSig *packet.Signature
|
||||||
|
for _, ident := range e.Identities {
|
||||||
|
if selfSig == nil {
|
||||||
|
selfSig = ident.SelfSignature
|
||||||
|
} else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||||
|
selfSig = ident.SelfSignature
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &openpgp.Key{e, e.PrimaryKey, e.PrivateKey, selfSig}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (card *ContactCard) Read(keyring openpgp.KeyRing) (*openpgp.MessageDetails, error) {
|
||||||
|
if !card.Type.Encrypted() {
|
||||||
|
md := &openpgp.MessageDetails{
|
||||||
|
IsEncrypted: false,
|
||||||
|
IsSigned: false,
|
||||||
|
UnverifiedBody: strings.NewReader(card.Data),
|
||||||
|
}
|
||||||
|
|
||||||
|
if !card.Type.Signed() {
|
||||||
|
return md, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
signed := strings.NewReader(card.Data)
|
||||||
|
signature := strings.NewReader(card.Signature)
|
||||||
|
signer, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature)
|
||||||
|
md.IsSigned = true
|
||||||
|
md.SignatureError = err
|
||||||
|
if signer != nil {
|
||||||
|
md.SignedByKeyId = signer.PrimaryKey.KeyId
|
||||||
|
md.SignedBy = entityPrimaryKey(signer)
|
||||||
|
}
|
||||||
|
return md, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertextBlock, err := armor.Decode(strings.NewReader(card.Data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var r io.Reader = ciphertextBlock.Body
|
||||||
|
|
||||||
|
if card.Type.Signed() {
|
||||||
|
sigBlock, err := armor.Decode(strings.NewReader(card.Signature))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r = io.MultiReader(r, sigBlock.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return openpgp.ReadMessage(r, keyring, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
type ContactExport struct {
|
type ContactExport struct {
|
||||||
ID string
|
ID string
|
||||||
Cards []*ContactCard
|
Cards []*ContactCard
|
||||||
|
@ -167,7 +290,7 @@ func (c *Client) GetContact(id string) (*Contact, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateContactResp struct {
|
type CreateContactResp struct {
|
||||||
Index int
|
Index int
|
||||||
Response struct {
|
Response struct {
|
||||||
resp
|
resp
|
||||||
Contact *Contact
|
Contact *Contact
|
||||||
|
@ -180,7 +303,7 @@ func (resp *CreateContactResp) Err() error {
|
||||||
|
|
||||||
func (c *Client) CreateContacts(contacts []*ContactImport) ([]*CreateContactResp, error) {
|
func (c *Client) CreateContacts(contacts []*ContactImport) ([]*CreateContactResp, error) {
|
||||||
reqData := struct {
|
reqData := struct {
|
||||||
Contacts []*ContactImport
|
Contacts []*ContactImport
|
||||||
Overwrite, Groups, Labels int
|
Overwrite, Groups, Labels int
|
||||||
}{contacts, 0, 0, 0}
|
}{contacts, 0, 0, 0}
|
||||||
req, err := c.newJSONRequest(http.MethodPost, "/contacts", &reqData)
|
req, err := c.newJSONRequest(http.MethodPost, "/contacts", &reqData)
|
||||||
|
@ -217,7 +340,7 @@ func (c *Client) UpdateContact(id string, contact *ContactImport) (*Contact, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteContactResp struct {
|
type DeleteContactResp struct {
|
||||||
ID string
|
ID string
|
||||||
Response struct {
|
Response struct {
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue