diff --git a/.golangci.yml b/.golangci.yml index c9bf546..b5c37a1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -26,3 +26,4 @@ linters: - lll # Reports long lines [fast: true, auto-fix: false] - testpackage # Makes you use a separate _test package [fast: true, auto-fix: false] - wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false] + - gofumpt # Enforce a stricter format than gofmt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9921ae0..8aa2094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved key and message armoring testing +- `EncryptSessionKey` now creates encrypted key packets for each valid encryption key in the provided keyring. + Returns a byte slice with all the concatenated key packets. ### Fixed - Public key armoring headers diff --git a/README.md b/README.md index 1732600..7542052 100644 --- a/README.md +++ b/README.md @@ -331,13 +331,13 @@ A session key can be generated, encrypted to a Asymmetric/Symmetric key packet a sessionKey, err := crypto.GenerateSessionKey() -keyPacket, err := publicKeyRing.EncryptSessionKey(sessionKey) +keyPacket, err := publicKeyRing.EncryptSessionKey(sessionKey) // Will encrypt to all the keys in the keyring keyPacketSymm, err := crypto.EncryptSessionKeyWithPassword(sessionKey, password) ``` `KeyPacket` is a `[]byte` containing the session key encrypted with the private key or password. ```go -decodedKeyPacket, err := privateKeyRing.DecryptSessionKey(keyPacket) +decodedKeyPacket, err := privateKeyRing.DecryptSessionKey(keyPacket) // Will decode with the first valid key found decodedSymmKeyPacket, err := crypto.DecryptSessionKeyWithPassword(keyPacketSymm, password) ``` `decodedKeyPacket` and `decodedSymmKeyPacket` are objects of type `*SymmetricKey` that can diff --git a/crypto/keyring_session.go b/crypto/keyring_session.go index 6f80df7..6d307bf 100644 --- a/crypto/keyring_session.go +++ b/crypto/keyring_session.go @@ -48,26 +48,26 @@ func (keyRing *KeyRing) DecryptSessionKey(keyPacket []byte) (*SessionKey, error) // publicKey and returns a binary public-key encrypted session key packet. func (keyRing *KeyRing) EncryptSessionKey(sk *SessionKey) ([]byte, error) { outbuf := &bytes.Buffer{} - cf, err := sk.GetCipherFunc() if err != nil { return nil, errors.Wrap(err, "gopenpgp: unable to encrypt session key") } - var pub *packet.PublicKey + var pubKeys []*packet.PublicKey for _, e := range keyRing.entities { if encryptionKey, ok := e.EncryptionKey(getNow()); ok { - pub = encryptionKey.PublicKey - break + pubKeys = append(pubKeys, encryptionKey.PublicKey) } } - if pub == nil { + if len(pubKeys) == 0 { return nil, errors.New("cannot set key: no public key available") } - if err := packet.SerializeEncryptedKey(outbuf, pub, cf, sk.Key, nil); err != nil { - err = fmt.Errorf("gopenpgp: cannot set key: %v", err) - return nil, err + for _, pub := range pubKeys { + if err := packet.SerializeEncryptedKey(outbuf, pub, cf, sk.Key, nil); err != nil { + err = fmt.Errorf("gopenpgp: cannot set key: %v", err) + return nil, err + } } return outbuf.Bytes(), nil } diff --git a/crypto/sessionkey_test.go b/crypto/sessionkey_test.go index e496810..42ed26e 100644 --- a/crypto/sessionkey_test.go +++ b/crypto/sessionkey_test.go @@ -44,6 +44,21 @@ func TestAsymmetricKeyPacket(t *testing.T) { assert.Exactly(t, testSessionKey, outputSymmetricKey) } +func TestMultipleAsymmetricKeyPacket(t *testing.T) { + keyPacket, err := keyRingTestMultiple.EncryptSessionKey(testSessionKey) + if err != nil { + t.Fatal("Expected no error while generating key packet, got:", err) + } + + // Password defined in keyring_test + outputSymmetricKey, err := keyRingTestMultiple.DecryptSessionKey(keyPacket) + if err != nil { + t.Fatal("Expected no error while decrypting key packet, got:", err) + } + + assert.Exactly(t, testSessionKey, outputSymmetricKey) +} + func TestSymmetricKeyPacket(t *testing.T) { password := []byte("I like encryption") @@ -90,7 +105,8 @@ func TestDataPacketEncryption(t *testing.T) { assert.Exactly(t, message.GetString(), decrypted.GetString()) // Encrypt session key - keyPacket, err := keyRingTestPublic.EncryptSessionKey(testSessionKey) + assert.Exactly(t, 3, len(keyRingTestMultiple.entities)) + keyPacket, err := keyRingTestMultiple.EncryptSessionKey(testSessionKey) if err != nil { t.Fatal("Unable to encrypt key packet, got:", err) } @@ -108,6 +124,9 @@ func TestDataPacketEncryption(t *testing.T) { if err != nil { t.Fatal("Unable to unarmor pgp message, got:", err) } + ids, ok := pgpMessage.getEncryptionKeyIDs() + assert.True(t, ok) + assert.Exactly(t, 3, len(ids)) // Test if final decryption succeeds finalMessage, err := keyRingTestPrivate.Decrypt(pgpMessage, nil, 0)