Prepare release 2.7.5 with packet API (#269)
Adds the following API functions: - API to get signature key IDs for mobile: func (msg *PGPMessage) GetHexSignatureKeyIDsJson() []byte - API to get encryption key IDs for mobile: func (msg *PGPMessage) GetHexEncryptionKeyIDsJson() []byte - API to get the number of key packets in a PGP message: func (msg *PGPSplitMessage) GetNumberOfKeyPackets() (int, error) - API in package `helper` to encrypt a PGP message to an additional key: func EncryptPGPMessageToAdditionalKey(messageToModify *crypto.PGPSplitMessage, keyRing *crypto.KeyRing, additionalKey *crypto.KeyRing) error
This commit is contained in:
parent
02a4599829
commit
c6a3058e2e
7 changed files with 157 additions and 2 deletions
6
.github/workflows/android.yml
vendored
6
.github/workflows/android.yml
vendored
|
|
@ -23,6 +23,12 @@ jobs:
|
||||||
go-version: ^1.16
|
go-version: ^1.16
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
|
- name: Install NDK
|
||||||
|
uses: nttld/setup-ndk@v1
|
||||||
|
with:
|
||||||
|
ndk-version: r23c
|
||||||
|
link-to-sdk: true
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
|
|
||||||
20
CHANGELOG.md
20
CHANGELOG.md
|
|
@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2.7.5] 2023-31-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- API to get signature key IDs for mobile:
|
||||||
|
```go
|
||||||
|
func (msg *PGPMessage) GetHexSignatureKeyIDsJson() []byte
|
||||||
|
```
|
||||||
|
- API to get encryption key IDs for mobile:
|
||||||
|
```go
|
||||||
|
func (msg *PGPMessage) GetHexEncryptionKeyIDsJson() []byte
|
||||||
|
```
|
||||||
|
- API to get the number of key packets in a PGP message:
|
||||||
|
```go
|
||||||
|
func (msg *PGPSplitMessage) GetNumberOfKeyPackets() (int, error)
|
||||||
|
```
|
||||||
|
- API in package `helper` to encrypt a PGP message to an additional key:
|
||||||
|
```go
|
||||||
|
func EncryptPGPMessageToAdditionalKey(messageToModify *crypto.PGPSplitMessage, keyRing *crypto.KeyRing, additionalKey *crypto.KeyRing) error
|
||||||
|
```
|
||||||
|
|
||||||
## [2.7.4] 2023-10-27
|
## [2.7.4] 2023-10-27
|
||||||
### Fixed
|
### Fixed
|
||||||
- Ensure that `(SessionKey).Decrypt` functions return an error if no integrity protection is present in the encrypted input. To protect SEIPDv1 encrypted messages, SED packets must not be allowed in decryption.
|
- Ensure that `(SessionKey).Decrypt` functions return an error if no integrity protection is present in the encrypted input. To protect SEIPDv1 encrypted messages, SED packets must not be allowed in decryption.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package constants
|
||||||
|
|
||||||
// Constants for armored data.
|
// Constants for armored data.
|
||||||
const (
|
const (
|
||||||
ArmorHeaderVersion = "GopenPGP 2.7.4"
|
ArmorHeaderVersion = "GopenPGP 2.7.5"
|
||||||
ArmorHeaderComment = "https://gopenpgp.org"
|
ArmorHeaderComment = "https://gopenpgp.org"
|
||||||
PGPMessageHeader = "PGP MESSAGE"
|
PGPMessageHeader = "PGP MESSAGE"
|
||||||
PGPSignatureHeader = "PGP SIGNATURE"
|
PGPSignatureHeader = "PGP SIGNATURE"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
package constants
|
package constants
|
||||||
|
|
||||||
const Version = "2.7.4"
|
const Version = "2.7.5"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
goerrors "errors"
|
goerrors "errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -287,6 +288,21 @@ func (msg *PGPMessage) GetHexEncryptionKeyIDs() ([]string, bool) {
|
||||||
return getHexKeyIDs(msg.GetEncryptionKeyIDs())
|
return getHexKeyIDs(msg.GetEncryptionKeyIDs())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHexEncryptionKeyIDsJson returns the key IDs of the keys to which the session key is encrypted as a JSON array.
|
||||||
|
// If an error occurs it returns nil.
|
||||||
|
// Helper function for go-mobile clients.
|
||||||
|
func (msg *PGPMessage) GetHexEncryptionKeyIDsJson() []byte {
|
||||||
|
hexIds, ok := msg.GetHexEncryptionKeyIDs()
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
hexIdsJson, err := json.Marshal(hexIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return hexIdsJson
|
||||||
|
}
|
||||||
|
|
||||||
// GetSignatureKeyIDs Returns the key IDs of the keys to which the (readable) signature packets are encrypted to.
|
// GetSignatureKeyIDs Returns the key IDs of the keys to which the (readable) signature packets are encrypted to.
|
||||||
func (msg *PGPMessage) GetSignatureKeyIDs() ([]uint64, bool) {
|
func (msg *PGPMessage) GetSignatureKeyIDs() ([]uint64, bool) {
|
||||||
return getSignatureKeyIDs(msg.Data)
|
return getSignatureKeyIDs(msg.Data)
|
||||||
|
|
@ -297,6 +313,20 @@ func (msg *PGPMessage) GetHexSignatureKeyIDs() ([]string, bool) {
|
||||||
return getHexKeyIDs(msg.GetSignatureKeyIDs())
|
return getHexKeyIDs(msg.GetSignatureKeyIDs())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHexSignatureKeyIDsJson returns the key IDs of the keys to which the (readable) signature packets
|
||||||
|
// are encrypted to as a JSON array. Helper function for go-mobile clients.
|
||||||
|
func (msg *PGPMessage) GetHexSignatureKeyIDsJson() []byte {
|
||||||
|
sigHexSigIds, ok := msg.GetHexSignatureKeyIDs()
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sigHexKeyIdsJSON, err := json.Marshal(sigHexSigIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return sigHexKeyIdsJSON
|
||||||
|
}
|
||||||
|
|
||||||
// GetBinaryDataPacket returns the unarmored binary datapacket as a []byte.
|
// GetBinaryDataPacket returns the unarmored binary datapacket as a []byte.
|
||||||
func (msg *PGPSplitMessage) GetBinaryDataPacket() []byte {
|
func (msg *PGPSplitMessage) GetBinaryDataPacket() []byte {
|
||||||
return msg.DataPacket
|
return msg.DataPacket
|
||||||
|
|
@ -324,6 +354,27 @@ func (msg *PGPSplitMessage) GetPGPMessage() *PGPMessage {
|
||||||
return NewPGPMessage(append(msg.KeyPacket, msg.DataPacket...))
|
return NewPGPMessage(append(msg.KeyPacket, msg.DataPacket...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetNumberOfKeyPackets returns the number of keys packets in this message.
|
||||||
|
func (msg *PGPSplitMessage) GetNumberOfKeyPackets() (int, error) {
|
||||||
|
bytesReader := bytes.NewReader(msg.KeyPacket)
|
||||||
|
packets := packet.NewReader(bytesReader)
|
||||||
|
var keyPacketCount int
|
||||||
|
for {
|
||||||
|
p, err := packets.Next()
|
||||||
|
if goerrors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
switch p.(type) {
|
||||||
|
case *packet.SymmetricKeyEncrypted, *packet.EncryptedKey:
|
||||||
|
keyPacketCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keyPacketCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SplitMessage splits the message into key and data packet(s).
|
// SplitMessage splits the message into key and data packet(s).
|
||||||
// Parameters are for backwards compatibility and are unused.
|
// Parameters are for backwards compatibility and are unused.
|
||||||
func (msg *PGPMessage) SplitMessage() (*PGPSplitMessage, error) {
|
func (msg *PGPMessage) SplitMessage() (*PGPSplitMessage, error) {
|
||||||
|
|
|
||||||
22
helper/message.go
Normal file
22
helper/message.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import "github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
|
|
||||||
|
// EncryptPGPMessageToAdditionalKey decrypts the session key of the input PGPSplitMessage with a private key in keyRing
|
||||||
|
// and encrypts it towards the additionalKeys by adding the additional key packets to the input PGPSplitMessage.
|
||||||
|
// If successful, new key packets are added to message.
|
||||||
|
// * messageToModify : The encrypted pgp message that should be modified
|
||||||
|
// * keyRing : The private keys to decrypt the session key in the messageToModify.
|
||||||
|
// * additionalKey : The public keys the message should be additionally encrypted to.
|
||||||
|
func EncryptPGPMessageToAdditionalKey(messageToModify *crypto.PGPSplitMessage, keyRing *crypto.KeyRing, additionalKey *crypto.KeyRing) error {
|
||||||
|
sessionKey, err := keyRing.DecryptSessionKey(messageToModify.KeyPacket)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
additionalKeyPacket, err := additionalKey.EncryptSessionKey(sessionKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
messageToModify.KeyPacket = append(messageToModify.KeyPacket, additionalKeyPacket...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
56
helper/message_test.go
Normal file
56
helper/message_test.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package helper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
|
"github.com/ProtonMail/gopenpgp/v2/helper"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncryptPGPMessageToAdditionalKey(t *testing.T) {
|
||||||
|
keyA, err := crypto.GenerateKey("A", "a@a.a", "x25519", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when generating key, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyB, err := crypto.GenerateKey("B", "b@b.b", "x25519", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when generating key, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyRingA, err := crypto.NewKeyRing(keyA)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when creating keyring, got:", err)
|
||||||
|
}
|
||||||
|
keyRingB, err := crypto.NewKeyRing(keyB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when creating keyring, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := crypto.NewPlainMessageFromString("plain text")
|
||||||
|
// Encrypt towards A
|
||||||
|
ciphertext, err := keyRingA.Encrypt(message, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when encrypting, got:", err)
|
||||||
|
}
|
||||||
|
ciphertextSplit, err := ciphertext.SplitMessage()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when splitting message, got:", err)
|
||||||
|
}
|
||||||
|
// Also encrypt the message towards B
|
||||||
|
if err := helper.EncryptPGPMessageToAdditionalKey(ciphertextSplit, keyRingA, keyRingB); err != nil {
|
||||||
|
t.Fatal("Expected no error when modifying the message, got:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test decrypt with B
|
||||||
|
decrypted, err := keyRingB.Decrypt(
|
||||||
|
ciphertextSplit.GetPGPMessage(),
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected no error when decrypting, got:", err)
|
||||||
|
}
|
||||||
|
assert.Exactly(t, message.GetString(), decrypted.GetString())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue