From b074657c900fb6613334ea5356ea0b4d4412fe08 Mon Sep 17 00:00:00 2001 From: wussler Date: Wed, 15 May 2019 13:48:47 +0200 Subject: [PATCH] Add tests, remove duplicate signing functions (#1) --- README.md | 18 ++++------ crypto/attachment_test.go | 8 +---- crypto/keyring.go | 70 ------------------------------------ crypto/keyring_test.go | 49 +++++++++++++++++++++---- crypto/session_test.go | 76 +++++++++++++++++++++++++++++++++++++++ crypto/sign_detached.go | 48 ++++++++++++------------- crypto/signature_test.go | 16 ++++----- 7 files changed, 157 insertions(+), 128 deletions(-) create mode 100644 crypto/session_test.go diff --git a/README.md b/README.md index 8f440bf..88e874c 100644 --- a/README.md +++ b/README.md @@ -168,10 +168,8 @@ const trimNewlines = false signingKeyRing, err := ReadArmoredKeyRing(strings.NewReader(privkey)) -signature, err := pgp.SignTextDetached(plaintext, signingKeyRing, passphrase, trimNewlines) -// or -signingKeyRing.Unlock([]byte(passphrase)) -signature, err := pgp.SignTextDetached(plaintext, signingKeyRing, "", trimNewlines) +signature, err := signingKeyRing.SignTextDetached(plaintext, passphrase, trimNewlines) +// passphrase is optional if the key is already unlocked ``` To verify a signature either private or public keyring can be provided. @@ -188,10 +186,11 @@ const signature = `-----BEGIN PGP SIGNATURE----- -----END PGP SIGNATURE-----` const verifyTime = 0 +const trimNewlines = false signingKeyRing, err := ReadArmoredKeyRing(strings.NewReader(pubkey)) -verified, err := pgp.VerifyTextDetachedSig(signature, signedPlainText, signingKeyRing, verifyTime) +verified, err := signingKeyRing.VerifyTextDetachedSig(signature, signedPlainText, verifyTime, trimNewlines) ``` ### Detached signatures for binary data @@ -204,14 +203,11 @@ const privkey = `-----BEGIN PGP PRIVATE KEY BLOCK----- ... -----END PGP PRIVATE KEY BLOCK-----` // encrypted private key passphrase = "LongSecret" -const trimNewlines = false signingKeyRing, err := ReadArmoredKeyRing(strings.NewReader(privkey)) -signature, err := pgp.SignBinDetached(data, signingKeyRing, passphrase, trimNewlines) -// or -signingKeyRing.Unlock([]byte(passphrase)) -signature, err := pgp.SignBinDetached(data, signingKeyRing, "", trimNewlines) +signature, err := signingKeyRing.SignBinDetached(data, passphrase) +// passphrase is optional if the key is already unlocked ``` To verify a signature either private or public keyring can be provided. @@ -231,5 +227,5 @@ const verifyTime = 0 signingKeyRing, err := ReadArmoredKeyRing(strings.NewReader(pubkey)) -verified, err := pgp.VerifyBinDetachedSig(signature, data, signingKeyRing, verifyTime) +verified, err := signingKeyRing.VerifyBinDetachedSig(signature, data, verifyTime) ``` diff --git a/crypto/attachment_test.go b/crypto/attachment_test.go index c65170e..84f2aa2 100644 --- a/crypto/attachment_test.go +++ b/crypto/attachment_test.go @@ -2,7 +2,6 @@ package crypto import ( "encoding/base64" - "io/ioutil" "strings" "testing" @@ -13,13 +12,8 @@ import ( // `0ksB0fHC6Duezx/0TqpK/82HSl8+qCY0c2BCuyrSFoj6Dubd93T3//32jVYa624NYvfvxX+UxFKYKJxG09gFsU1IVc87cWvUgmUmgjU=` func TestAttachmentGetKey(t *testing.T) { - testKeyPackets, err := ioutil.ReadFile("testdata/attachment_keypacket") - if err != nil { - t.Error("Expected no error while reading from file, got:", err) - return - } + testKeyPacketsDecoded, err := base64.StdEncoding.DecodeString(readTestFile("attachment_keypacket", false)) - testKeyPacketsDecoded, err := base64.StdEncoding.DecodeString(string(testKeyPackets)) if err != nil { t.Fatal("Expected no error while decoding base64 KeyPacket, got:", err) } diff --git a/crypto/keyring.go b/crypto/keyring.go index 1026019..45d949b 100644 --- a/crypto/keyring.go +++ b/crypto/keyring.go @@ -304,76 +304,6 @@ func (kr *KeyRing) DecryptStringIfNeeded(data string) (decrypted string, err err return } -// SignString signs a string message, using this KeyRing. canonicalizeText identifies if newlines are canonicalized -func (kr *KeyRing) SignString(message string, canonicalizeText bool) (signed string, err error) { - var sig bytes.Buffer - err = kr.DetachedSign(&sig, strings.NewReader(message), canonicalizeText, true) - - if err != nil { - return "", err - } - return sig.String(), nil -} - -// DetachedSign will sign a separate ("detached") data from toSign, writing to -// w writer. The canonicalizeText identifies if newlines are canonicalized -func (kr *KeyRing) DetachedSign(w io.Writer, toSign io.Reader, canonicalizeText bool, armored bool) (err error) { - var signEntity *openpgp.Entity - for _, e := range kr.entities { - if e.PrivateKey != nil && !e.PrivateKey.Encrypted { - signEntity = e - break - } - } - - if signEntity == nil { - return errKeyringNotUnlocked - } - - config := &packet.Config{DefaultCipher: packet.CipherAES256, - Time: func() time.Time { - return GetGopenPGP().GetTime() - }, - } - - if canonicalizeText { - err = openpgp.ArmoredDetachSignText(w, signEntity, toSign, config) - } else { - if armored { - err = openpgp.ArmoredDetachSign(w, signEntity, toSign, config) - } else { - err = openpgp.DetachSign(w, signEntity, toSign, config) - } - } - return err -} - -// VerifyString may return errors.ErrSignatureExpired (defined in -// golang.org/x/crypto/openpgp/errors) In this case signature has been verified -// successfully, but it is either expired or in the future. -func (kr *KeyRing) VerifyString(message, signature string, sign *KeyRing) (err error) { - messageReader := strings.NewReader(message) - signatureReader := strings.NewReader(signature) - - err = nil - if sign != nil { - for _, e := range sign.entities { - if e.PrivateKey != nil && !e.PrivateKey.Encrypted { - _, err = openpgp.CheckArmoredDetachedSignature(kr.entities, messageReader, signatureReader, nil) - if err == nil || err == pgperrors.ErrSignatureExpired { - return - } - } - } - } - - if err == nil { - return errKeyringNotUnlocked - } - - return err -} - // Unlock tries to unlock as many keys as possible with the following password. Note // that keyrings can contain keys locked with different passwords, and thus // err == nil does not mean that all keys have been successfully decrypted. diff --git a/crypto/keyring_test.go b/crypto/keyring_test.go index b0efd55..53dfa4a 100644 --- a/crypto/keyring_test.go +++ b/crypto/keyring_test.go @@ -32,10 +32,10 @@ var ( testPublicKeyRing *KeyRing ) -// var testIdentity = &Identity{ -// Name: "UserID", -// Email: "", -// } +var testIdentity = &Identity{ + Name: "UserID", + Email: "", +} func init() { var err error @@ -57,16 +57,16 @@ func init() { } func TestKeyRing_Decrypt(t *testing.T) { - ss, err := testPrivateKeyRing.DecryptString(readTestFile("keyring_token", false)) + decString, err := testPrivateKeyRing.DecryptStringIfNeeded(readTestFile("keyring_token", false)) if err != nil { t.Fatal("Cannot decrypt token:", err) } - assert.Exactly(t, testToken, ss.String) + assert.Exactly(t, testToken, decString) } func TestKeyRing_Encrypt(t *testing.T) { - encrypted, err := testPublicKeyRing.EncryptString(testToken, nil) + encrypted, err := testPublicKeyRing.EncryptString(testToken, testPrivateKeyRing) if err != nil { t.Fatal("Cannot encrypt token:", err) } @@ -79,6 +79,12 @@ func TestKeyRing_Encrypt(t *testing.T) { } assert.Exactly(t, testToken, ss.String) + + signatureKeyRing := ss.Signed.KeyRing() + assert.Exactly(t, testPrivateKeyRing, signatureKeyRing) + + isby := ss.Signed.IsBy(testPublicKeyRing) + assert.Exactly(t, true, isby) } func TestKeyRing_ArmoredPublicKeyString(t *testing.T) { @@ -112,3 +118,32 @@ func TestKeyRing_ArmoredPublicKeyString(t *testing.T) { assert.Exactly(t, eb, b) } + +func TestCheckPassphrase(t *testing.T) { + encryptedKeyRing, _ := ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_privateKey", false))) + is_correct := encryptedKeyRing.CheckPassphrase("Wrong password") + assert.Exactly(t, false, is_correct) + + is_correct = encryptedKeyRing.CheckPassphrase(testMailboxPassword) + assert.Exactly(t, true, is_correct) +} + +func TestIdentities(t *testing.T) { + identities := testPrivateKeyRing.Identities() + assert.Len(t, identities, 1) + assert.Exactly(t, identities[0], testIdentity) +} + + +func TestFilterExpiredKeys(t *testing.T) { + expiredKey, _ := ReadArmoredKeyRing(strings.NewReader(readTestFile("key_expiredKey", false))) + keys := []*KeyRing {testPrivateKeyRing, expiredKey} + unexpired, err := FilterExpiredKeys(keys) + + if err != nil { + t.Fatal("Expected no error while filtering expired keyrings, got:", err) + } + + assert.Len(t, unexpired, 1) + assert.Exactly(t, unexpired[0], testPrivateKeyRing) +} diff --git a/crypto/session_test.go b/crypto/session_test.go new file mode 100644 index 0000000..74b152a --- /dev/null +++ b/crypto/session_test.go @@ -0,0 +1,76 @@ +package crypto + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/ProtonMail/gopenpgp/constants" +) + +var testRandomToken []byte +func TestRandomToken(t *testing.T) { + var err error + testRandomToken, err = pgp.RandomToken() + if err != nil { + t.Fatal("Expected no error while generating random token, got:", err) + } + + assert.Len(t, testRandomToken, 32) +} + +func TestRandomTokenWith(t *testing.T) { + token, err := pgp.RandomTokenWith(40) + if err != nil { + t.Fatal("Expected no error while generating random token, got:", err) + } + + assert.Len(t, token, 40) +} + +func TestAsymmetricKeyPacket(t *testing.T) { + symmetricKey := &SymmetricKey{ + Key: testRandomToken, + Algo: constants.AES256, + } + + privateKeyRing, _ := ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_privateKey", false))) + publicKey, _ := testPrivateKeyRing.GetArmoredPublicKey() + + keyPacket, err := pgp.KeyPacketWithPublicKey(symmetricKey, publicKey) + if err != nil { + t.Fatal("Expected no error while generating key packet, got:", err) + } + + // Password defined in keyring_test + outputSymmetricKey, err := pgp.GetSessionFromKeyPacket(keyPacket, privateKeyRing, testMailboxPassword) + if err != nil { + t.Fatal("Expected no error while decrypting key packet, got:", err) + } + + assert.Exactly(t, symmetricKey, outputSymmetricKey) +} + +func TestSymmetricKeyPacket(t *testing.T) { + symmetricKey := &SymmetricKey{ + Key: testRandomToken, + Algo: constants.AES256, + } + + password := "I like encryption" + + keyPacket, err := pgp.SymmetricKeyPacketWithPassword(symmetricKey, password) + if err != nil { + t.Fatal("Expected no error while generating key packet, got:", err) + } + + _, err = pgp.GetSessionFromSymmetricPacket(keyPacket, "Wrong password") + assert.EqualError(t, err, "password incorrect") + + outputSymmetricKey, err := pgp.GetSessionFromSymmetricPacket(keyPacket, password) + if err != nil { + t.Fatal("Expected no error while decrypting key packet, got:", err) + } + + assert.Exactly(t, symmetricKey, outputSymmetricKey) +} diff --git a/crypto/sign_detached.go b/crypto/sign_detached.go index 01b7e6e..bc7ff0f 100644 --- a/crypto/sign_detached.go +++ b/crypto/sign_detached.go @@ -15,21 +15,18 @@ import ( ) // SignTextDetached signs detached text type -func (pgp *GopenPGP) SignTextDetached( - plainText string, privateKey *KeyRing, passphrase string, trim bool, -) (string, error) { - //sign with 0x01 text - if trim { - plainText = internal.TrimNewlines(plainText) - } - - signEntity, err := privateKey.GetSigningEntity(passphrase) +func (kr *KeyRing) SignTextDetached(plainText string, passphrase string, trimNewlines bool) (string, error) { + signEntity, err := kr.GetSigningEntity(passphrase) if err != nil { return "", err } config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: pgp.getTimeGenerator()} + if trimNewlines { + plainText = internal.TrimNewlines(plainText) + } + att := strings.NewReader(plainText) var outBuf bytes.Buffer @@ -42,9 +39,9 @@ func (pgp *GopenPGP) SignTextDetached( } // SignBinDetached Signs detached bin data using string key -func (pgp *GopenPGP) SignBinDetached(plainData []byte, privateKey *KeyRing, passphrase string) (string, error) { +func (kr *KeyRing) SignBinDetached(plainData []byte, passphrase string) (string, error) { //sign with 0x00 - signEntity, err := privateKey.GetSigningEntity(passphrase) + signEntity, err := kr.GetSigningEntity(passphrase) if err != nil { return "", err } @@ -64,15 +61,26 @@ func (pgp *GopenPGP) SignBinDetached(plainData []byte, privateKey *KeyRing, pass // VerifyTextDetachedSig verifies detached text // - check if signature is valid using a given publicKey in binary format -func (pgp *GopenPGP) VerifyTextDetachedSig( - signature string, plainText string, publicKey *KeyRing, verifyTime int64, +func (kr *KeyRing) VerifyTextDetachedSig( + signature string, plainText string, verifyTime int64, trimNewlines bool, ) (bool, error) { - plainText = internal.TrimNewlines(plainText) + if trimNewlines { + plainText = internal.TrimNewlines(plainText) + } origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes()) - return verifySignature(publicKey.entities, origText, signature, verifyTime) + return verifySignature(kr.GetEntities(), origText, signature, verifyTime) } +// VerifyBinDetachedSig verifies detached text in binary format +// - check if signature is valid using a given publicKey in binary format +func (kr *KeyRing) VerifyBinDetachedSig(signature string, plainData []byte, verifyTime int64) (bool, error) { + origText := bytes.NewReader(plainData) + + return verifySignature(kr.GetEntities(), origText, signature, verifyTime) +} + +// Internal func verifySignature( pubKeyEntries openpgp.EntityList, origText *bytes.Reader, signature string, verifyTime int64, @@ -120,13 +128,3 @@ func verifySignature( // } return true, nil } - -// VerifyBinDetachedSig verifies detached text in binary format -// - check if signature is valid using a given publicKey in binary format -func (pgp *GopenPGP) VerifyBinDetachedSig( - signature string, plainData []byte, publicKey *KeyRing, verifyTime int64, -) (bool, error) { - origText := bytes.NewReader(plainData) - - return verifySignature(publicKey.entities, origText, signature, verifyTime) -} diff --git a/crypto/signature_test.go b/crypto/signature_test.go index 268b910..a88f5ac 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -20,11 +20,11 @@ func TestSignTextDetached(t *testing.T) { t.Fatal("Cannot read private key:", err) } - signature, err = pgp.SignTextDetached(signedPlainText, signingKeyRing, "", true) + signature, err = signingKeyRing.SignTextDetached(signedPlainText, "", true) assert.EqualError(t, err, "gopenpgp: cannot sign message, unable to unlock signer key") // Password defined in keyring_test - signature, err = pgp.SignTextDetached(signedPlainText, signingKeyRing, testMailboxPassword, true) + signature, err = signingKeyRing.SignTextDetached(signedPlainText, testMailboxPassword, true) if err != nil { t.Fatal("Cannot generate signature with encrypted key:", err) } @@ -37,7 +37,7 @@ func TestSignTextDetached(t *testing.T) { t.Fatal("Cannot decrypt private key:", err) } - signatureDec, err := pgp.SignTextDetached(signedPlainText, signingKeyRing, "", true) + signatureDec, err := signingKeyRing.SignTextDetached(signedPlainText, "", true) if err != nil { t.Fatal("Cannot generate signature with decrypted key:", err) } @@ -52,11 +52,11 @@ func TestSignBinDetached(t *testing.T) { // Reset keyring to locked state signingKeyRing, _ = ReadArmoredKeyRing(strings.NewReader(readTestFile("keyring_privateKey", false))) - signatureBin, err = pgp.SignBinDetached([]byte(signedPlainText), signingKeyRing, "") + signatureBin, err = signingKeyRing.SignBinDetached([]byte(signedPlainText), "") assert.EqualError(t, err, "gopenpgp: cannot sign message, unable to unlock signer key") // Password defined in keyring_test - signatureBin, err = pgp.SignBinDetached([]byte(signedPlainText), signingKeyRing, testMailboxPassword) + signatureBin, err = signingKeyRing.SignBinDetached([]byte(signedPlainText), testMailboxPassword) if err != nil { t.Fatal("Cannot generate signature with encrypted key:", err) } @@ -66,7 +66,7 @@ func TestSignBinDetached(t *testing.T) { } func TestVerifyTextDetachedSig(t *testing.T) { - verified, err := pgp.VerifyTextDetachedSig(signature, signedPlainText, signingKeyRing, testTime) + verified, err := signingKeyRing.VerifyTextDetachedSig(signature, signedPlainText, testTime, true) if err != nil { t.Fatal("Cannot verify plaintext signature:", err) } @@ -75,14 +75,14 @@ func TestVerifyTextDetachedSig(t *testing.T) { } func TestVerifyTextDetachedSigWrong(t *testing.T) { - verified, err := pgp.VerifyTextDetachedSig(signature, "wrong text", signingKeyRing, testTime) + verified, err := signingKeyRing.VerifyTextDetachedSig(signature, "wrong text", testTime, true) assert.EqualError(t, err, "gopenpgp: signer is empty") assert.Exactly(t, false, verified) } func TestVerifyBinDetachedSig(t *testing.T) { - verified, err := pgp.VerifyBinDetachedSig(signatureBin, []byte(signedPlainText), signingKeyRing, testTime) + verified, err := signingKeyRing.VerifyBinDetachedSig(signatureBin, []byte(signedPlainText), testTime) if err != nil { t.Fatal("Cannot verify binary signature:", err) }