Skip to content
This repository was archived by the owner on Mar 27, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ openpgp-validation-server

coverage.txt

nonce_*
signature_*
nonce_*.eml
signature_*.eml
requests/
messages/
cmd/acquire/acquire
48 changes: 48 additions & 0 deletions cmd/acquire/acquire.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// go-imap-sync provides a simple command line tool to download emails from an IMAP mailbox. Each email is saved as a
// plain text file (per default in the messages/ subdirectory). Emails are only downloaded once if run repeatedly.
package main

import (
"flag"
"log"
"os"

"github.com/JohannesEbke/go-imap-sync"
"github.com/howeyc/gopass"
)

func getPassword(username, server string) (password string) {
password = os.Getenv("IMAP_PASSWORD")

if password == "" {
log.Printf("Enter IMAP Password for %v on %v: ", username, server)
passwordBytes, err := gopass.GetPasswd()
if err != nil {
panic(err)
}
password = string(passwordBytes)
}
return
}

func main() {
var server, username, mailbox, emailDir string
flag.StringVar(&server, "server", "", "sync from this mail server and port (e.g. mail.example.com:993)")
flag.StringVar(&username, "username", "", "username for logging into the mail server")
flag.StringVar(&mailbox, "mailbox", "", "mailbox to read messages from (typically INBOX or INBOX/subfolder)")
flag.StringVar(&emailDir, "messagesDir", "messages", "local directory to save messages in")
flag.Parse()

if server == "" {
log.Println("go-imap-sync copies emails from an IMAP mailbox to your computer. Usage:")
flag.PrintDefaults()
log.Fatal("Required parameters not found.")
}

password := getPassword(username, server)

err := imapsync.Sync(server, username, password, mailbox, emailDir)
if err != nil {
log.Fatal(err)
}
}
2 changes: 1 addition & 1 deletion gpg/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ func verifySignatureTest(t *testing.T, signedIdentity string, signedClientEntity
t.Error("Signature", index, "not valid:", err)
}

assert.Equal(t, uint32(15768000), *signature.SigLifetimeSecs, "Invalid signature expiry")
assert.Equal(t, uint32(34214400), *signature.SigLifetimeSecs, "Invalid signature expiry")
}
}
44 changes: 40 additions & 4 deletions gpg/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@ package gpg

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"time"

"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
)

type validationInfo struct {
Date string `json:"date"`
Approach string `json:"approach"`
Email string `json:"email"`
}

type validationInfoList struct {
Validations []validationInfo `json:"validations"`
}

type validationInfoNotationData struct {
Validation validationInfoList `json:"validation"`
}

// Key is a reference to an OpenPGP entity containing some public keys
type Key *openpgp.Entity

Expand Down Expand Up @@ -98,16 +114,17 @@ func signClientPublicKey(clientEntity *openpgp.Entity, signedIdentity string, se
if !ok {
return errors.New(fmt.Sprint("Client does not have identity:", signedIdentity))
}

err := signIdentity(signedIdentity, clientEntity, serverEntity, nil)
approach := "enc-email-click"
policyURI := "https://github.com/TNG/openpgp-validation-server/blob/d2d11e4d69fa3d050b6bfb48788d8e67d28e7bf4/POLICY-enc-email-click-draft.md"
err := signIdentity(signedIdentity, clientEntity, serverEntity, nil, policyURI, approach)
if err != nil {
return err
}
err = exportArmoredPublicKey(clientEntity, w)
return err
}

func signIdentity(identity string, e, signer *openpgp.Entity, config *packet.Config) error {
func signIdentity(identity string, e, signer *openpgp.Entity, config *packet.Config, policyURI, approach string) error {
if signer.PrivateKey == nil {
return errors.New("signing Entity must have a private key")
}
Expand All @@ -119,14 +136,33 @@ func signIdentity(identity string, e, signer *openpgp.Entity, config *packet.Con
return errors.New("given identity string not found in Entity")
}

lifetime := uint32((3600 * 24 * 365) / 2)
lifetime := uint32(3600 * 24 * 396) // 396 ~= 13 Months
notationData := validationInfoNotationData{
Validation: validationInfoList{
Validations: []validationInfo{
validationInfo{
Email: ident.UserId.Email,
Date: time.Now().Format("2006-01-02"),
Approach: approach,
},
},
},
}

notationDataBytes, err := json.Marshal(notationData)
if err != nil {
return err
}

sig := &packet.Signature{
SigType: packet.SigTypeGenericCert,
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
Hash: config.Hash(),
CreationTime: config.Now(),
IssuerKeyId: &signer.PrivateKey.KeyId,
SigLifetimeSecs: &lifetime,
PolicyURI: policyURI,
NotationData: map[string]string{"validation@openpgp-email.org": string(notationDataBytes)},
}
if err := sig.SignUserId(identity, e.PrimaryKey, signer.PrivateKey, config); err != nil {
return err
Expand Down
12 changes: 12 additions & 0 deletions gpg/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
"golang.org/x/crypto/openpgp"
Expand Down Expand Up @@ -139,5 +140,16 @@ func TestSignClientPublicKey(t *testing.T) {
t.Error("No new signatures found")
}

sig := signedClientEntity.Identities[signedIdentity].Signatures[newSigCount-1]
if sig.PolicyUri != "https://github.com/TNG/openpgp-validation-server/blob/d2d11e4d69fa3d050b6bfb48788d8e67d28e7bf4/POLICY-enc-email-click-draft.md" {
t.Error("Wrong Policy URI:", sig.PolicyUri)
}

expectedNotation := "{\"validation\":{\"validations\":[{\"date\":\"" + time.Now().Format("2006-01-02") + "\",\"approach\":\"enc-email-click\",\"email\":\"test-gpg-validation@client.local\"}]}}"
actutalNotation := sig.NotationData["validation@openpgp-email.org"]
if actutalNotation != expectedNotation {
t.Error("Wrong Notation Data:", actutalNotation, expectedNotation)
}

verifySignatureTest(t, signedIdentity, signedClientEntity)
}
2 changes: 1 addition & 1 deletion lint_metalinter.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

set +e

gometalinter --deadline=480s ./... "$@"
gometalinter -j2 --vendor --deadline=480s ./... "$@"
6 changes: 2 additions & 4 deletions mail/multipart.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ func NewEncodingMultipartWriter(w io.Writer, multitype, protocol string, extraHe
"Content-Type": contentType,
"Content-Transfer-Encoding": "7bit",
}
if extraHeaders != nil {
for key, value := range extraHeaders {
headers[key] = value
}
for key, value := range extraHeaders {
headers[key] = value
}
return &EncodingMultipartWriter{w, headers, false, multipartWriter}
}
Expand Down
Loading