protonmail: add crypto helpers for contacts
This commit is contained in:
parent
0447370b16
commit
16a9952616
|
@ -1,9 +1,16 @@
|
||||||
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 {
|
||||||
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue