diff --git a/crypto/key_test.go b/crypto/key_test.go index b0d3803..c655bda 100644 --- a/crypto/key_test.go +++ b/crypto/key_test.go @@ -43,7 +43,7 @@ func TestGenerateKeyRings(t *testing.T) { t.Fatal("Cannot read RSA key:", err) } - rsaPublicKey, err = rsaPrivateKeyRing.ArmoredPublicKeyString() + rsaPublicKey, err = rsaPrivateKeyRing.GetArmoredPublicKey() if err != nil { t.Fatal("Cannot extract RSA public key:", err) } @@ -63,7 +63,7 @@ func TestGenerateKeyRings(t *testing.T) { t.Fatal("Cannot read EC key:", err) } - ecPublicKey, err = ecPrivateKeyRing.ArmoredPublicKeyString() + ecPublicKey, err = ecPrivateKeyRing.GetArmoredPublicKey() if err != nil { t.Fatal("Cannot extract EC public key:", err) } @@ -142,4 +142,14 @@ func TestIsKeyExpired(t *testing.T) { assert.Exactly(t, false, rsaRes) assert.Exactly(t, false, ecRes) + + pmCrypto.UpdateTime(1557754627) // 2019-05-13T13:37:07+00:00 + + expRes, expErr := pmCrypto.IsKeyExpired(readTestFile("key_expiredKey")) + futureRes, futureErr := pmCrypto.IsKeyExpired(readTestFile("key_futureKey")) + + assert.Exactly(t, true, expRes) + assert.Exactly(t, true, futureRes) + assert.EqualError(t, expErr, "keys expired") + assert.EqualError(t, futureErr, "keys expired") } diff --git a/crypto/keyring.go b/crypto/keyring.go index 6399e02..a191321 100644 --- a/crypto/keyring.go +++ b/crypto/keyring.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/ecdsa" "crypto/rsa" + "encoding/hex" "encoding/json" "errors" "io" @@ -456,8 +457,8 @@ func (kr *KeyRing) WriteArmoredPublicKey(w io.Writer) (err error) { return } -// ArmoredPublicKeyString returns the armored public keys from this keyring. -func (kr *KeyRing) ArmoredPublicKeyString() (s string, err error) { +// GetArmoredPublicKey returns the armored public keys from this keyring. +func (kr *KeyRing) GetArmoredPublicKey() (s string, err error) { b := &bytes.Buffer{} if err = kr.WriteArmoredPublicKey(b); err != nil { return @@ -467,6 +468,60 @@ func (kr *KeyRing) ArmoredPublicKeyString() (s string, err error) { return } +// WritePublicKey outputs unarmored public keys from the keyring to w. +func (kr *KeyRing) WritePublicKey(w io.Writer) (err error) { + for _, e := range kr.entities { + if err = e.Serialize(w); err != nil { + return + } + } + + return +} + +// GetPublicKey returns the unarmored public keys from this keyring. +func (kr *KeyRing) GetPublicKey() (b []byte, err error) { + var outBuf bytes.Buffer + if err = kr.WritePublicKey(&outBuf); err != nil { + return + } + + b = outBuf.Bytes() + return +} + +// GetFingerprint gets the fingerprint from the keyring +func (kr *KeyRing) GetFingerprint() (string, error) { + for _, entity := range kr.entities { + fp := entity.PrimaryKey.Fingerprint + return hex.EncodeToString(fp[:]), nil + } + return "", errors.New("can't find public key") +} + +// CheckPassphrase checks if private key passphrase ok +func (kr *KeyRing) CheckPassphrase(passphrase string) bool { + var keys []*packet.PrivateKey + + for _, entity := range kr.entities { + keys = append(keys, entity.PrivateKey) + } + var decryptError error + var n int + for _, key := range keys { + if !key.Encrypted { + continue // Key already decrypted + } + if decryptError = key.Decrypt([]byte(passphrase)); decryptError == nil { + n++ + } + } + if n == 0 { + return false + } + return true +} + // readFrom reads unarmored and armored keys from r and adds them to the keyring. func (kr *KeyRing) readFrom(r io.Reader, armored bool) error { var err error diff --git a/crypto/keyring_test.go b/crypto/keyring_test.go index 9dac9e2..e3f6191 100644 --- a/crypto/keyring_test.go +++ b/crypto/keyring_test.go @@ -75,7 +75,7 @@ func TestKeyRing_Encrypt(t *testing.T) { } func TestKeyRing_ArmoredPublicKeyString(t *testing.T) { - s, err := testPrivateKeyRing.ArmoredPublicKeyString() + s, err := testPrivateKeyRing.GetArmoredPublicKey() if err != nil { t.Fatal("Expected no error while getting armored public key, got:", err) } diff --git a/crypto/sign_detached.go b/crypto/sign_detached.go index f713454..5b07d34 100644 --- a/crypto/sign_detached.go +++ b/crypto/sign_detached.go @@ -61,8 +61,8 @@ func (pm *PmCrypto) SignBinDetached(plainData []byte, privateKey *KeyRing, passp return outBuf.String(), nil } -// VerifyTextSignDetachedBinKey Verifies detached text - check if signature is valid using a given publicKey in binary format -func (pm *PmCrypto) VerifyTextSignDetachedBinKey(signature string, plainText string, publicKey *KeyRing, verifyTime int64) (bool, error) { +// VerifyTextDetachedSig verifies detached text - check if signature is valid using a given publicKey in binary format +func (pm *PmCrypto) VerifyTextDetachedSig(signature string, plainText string, publicKey *KeyRing, verifyTime int64) (bool, error) { plainText = internal.TrimNewlines(plainText) origText := bytes.NewReader(bytes.NewBufferString(plainText).Bytes()) @@ -77,7 +77,7 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s } } else { config.Time = func() time.Time { - return time.Unix(verifyTime + internal.CreationTimeOffset, 0) + return time.Unix(verifyTime+internal.CreationTimeOffset, 0) } } signatureReader := strings.NewReader(signature) @@ -112,8 +112,8 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText *bytes.Reader, s return true, nil } -// VerifyBinSignDetachedBinKey Verifies detached text in binary format - check if signature is valid using a given publicKey in binary format -func (pm *PmCrypto) VerifyBinSignDetachedBinKey(signature string, plainData []byte, publicKey *KeyRing, verifyTime int64) (bool, error) { +// VerifyBinDetachedSig verifies detached text in binary format - check if signature is valid using a given publicKey in binary format +func (pm *PmCrypto) 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/testdata/key_expiredKey b/crypto/testdata/key_expiredKey new file mode 100644 index 0000000..3c15eaf --- /dev/null +++ b/crypto/testdata/key_expiredKey @@ -0,0 +1,13 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xcA4BAAAAAEBAgCgONc0J8rfO6cJw5YTP38x1ze2tAYIO7EcmRCNYwMkXngb +0Qdzg34Q5RW0rNiR56VB6KElPUhePRPVklLFiIvHABEBAAEAAf9qabYMzsz/ +/LeRVZSsTgTljmJTdzd2ambUbpi+vt8MXJsbaWh71vjoLMWSXajaKSPDjVU5 +waFNt9kLqwGGGLqpAQD5ZdMH2XzTq6GU9Ka69iZs6Pbnzwdz59Vc3i8hXlUj +zQEApHargCTsrtvSrm+hK/pN51/BHAy9lxCAw9f2etx+AeMA/RGrijkFZtYt +jeWdv/usXL3mgHvEcJv63N5zcEvDX5X4W1bND3Rlc3QxIDxhQGIuY29tPsJ7 +BBABCAAvBQIAAAABBQMAAAU5BgsJBwgDAgkQzcF99nGrkAkEFQgKAgMWAgEC +GQECGwMCHgEAABAlAfwPehmLZs+gOhOTTaSslqQ50bl/REjmv42Nyr1ZBlQS +DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ +=/7PI +-----END PGP PRIVATE KEY BLOCK----- diff --git a/crypto/testdata/key_futureKey b/crypto/testdata/key_futureKey new file mode 100644 index 0000000..3d48f86 --- /dev/null +++ b/crypto/testdata/key_futureKey @@ -0,0 +1,55 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +0uyeibPdgJTjARMuQzDFA8bdbM540vBf5Q== +=WLIN +-----END PGP PRIVATE KEY BLOCK----- diff --git a/key/fingerprint.go b/key/fingerprint.go deleted file mode 100644 index 532fa88..0000000 --- a/key/fingerprint.go +++ /dev/null @@ -1,33 +0,0 @@ -package key - -import ( - "bytes" - "encoding/hex" - "errors" - - "github.com/ProtonMail/go-pm-crypto/armor" - "golang.org/x/crypto/openpgp" -) - -// GetFingerprint gets an armored public key fingerprint -func GetFingerprint(publicKey string) (string, error) { - rawPubKey, err := armor.Unarmor(publicKey) - if err != nil { - return "", err - } - return GetFingerprintBinKey(rawPubKey) -} - -// GetFingerprintBinKey gets an unarmored public key fingerprint -func GetFingerprintBinKey(publicKey []byte) (string, error) { - pubKeyReader := bytes.NewReader(publicKey) - pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader) - if err != nil { - return "", err - } - for _, e := range pubKeyEntries { - fp := e.PrimaryKey.Fingerprint - return hex.EncodeToString(fp[:]), nil - } - return "", errors.New("can't find public key") -} diff --git a/key/key.go b/key/key.go deleted file mode 100644 index f37b508..0000000 --- a/key/key.go +++ /dev/null @@ -1,83 +0,0 @@ -// Provides key manipulation helper methods -package key - -import ( - "bytes" - "fmt" - "github.com/ProtonMail/go-pm-crypto/armor" - "github.com/ProtonMail/go-pm-crypto/constants" - "golang.org/x/crypto/openpgp" - "golang.org/x/crypto/openpgp/packet" - "strings" -) - -// CheckPassphrase checks if private key passphrase ok -func CheckPassphrase(privateKey string, passphrase string) bool { - privKeyReader := strings.NewReader(privateKey) - entries, err := openpgp.ReadArmoredKeyRing(privKeyReader) - if err != nil { - fmt.Println(err) - return false - } - - var keys []*packet.PrivateKey - - for _, e := range entries { - keys = append(keys, e.PrivateKey) - } - var decryptError error - var n int - for _, key := range keys { - if !key.Encrypted { - continue // Key already decrypted - } - if decryptError = key.Decrypt([]byte(passphrase)); decryptError == nil { - n++ - } - } - if n == 0 { - return false - } - return true -} - -// PublicKey gets a public key from a private key -func PublicKey(privateKey string) (string, error) { - privKeyReader := strings.NewReader(privateKey) - entries, err := openpgp.ReadArmoredKeyRing(privKeyReader) - if err != nil { - return "", err - } - - var outBuf bytes.Buffer - for _, e := range entries { - if err := e.Serialize(&outBuf); err != nil { - return "", err - } - } - - outString, err := armor.ArmorWithType(outBuf.Bytes(), constants.PublicKeyHeader) - if err != nil { - return "", nil - } - - return outString, nil -} - -// PublicKeyBinOut gets a public key from a private key -func PublicKeyBinOut(privateKey string) ([]byte, error) { - privKeyReader := strings.NewReader(privateKey) - entries, err := openpgp.ReadArmoredKeyRing(privKeyReader) - if err != nil { - return nil, err - } - - var outBuf bytes.Buffer - for _, e := range entries { - if err := e.Serialize(&outBuf); err != nil { - return nil, err - } - } - - return outBuf.Bytes(), nil -}