protonmail: add crypto helpers for contacts

This commit is contained in:
emersion 2017-09-13 11:42:19 +02:00
parent 0447370b16
commit 16a9952616
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
1 changed files with 131 additions and 8 deletions

View File

@ -1,9 +1,16 @@
package protonmail
import (
"bytes"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
)
type Contact struct {
@ -68,6 +75,122 @@ type ContactCard struct {
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 {
ID string
Cards []*ContactCard