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
|
||||
id: go
|
||||
|
||||
- name: Install NDK
|
||||
uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
ndk-version: r23c
|
||||
link-to-sdk: true
|
||||
|
||||
- name: Checkout
|
||||
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/),
|
||||
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
|
||||
### 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.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package constants
|
|||
|
||||
// Constants for armored data.
|
||||
const (
|
||||
ArmorHeaderVersion = "GopenPGP 2.7.4"
|
||||
ArmorHeaderVersion = "GopenPGP 2.7.5"
|
||||
ArmorHeaderComment = "https://gopenpgp.org"
|
||||
PGPMessageHeader = "PGP MESSAGE"
|
||||
PGPSignatureHeader = "PGP SIGNATURE"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package constants
|
||||
|
||||
const Version = "2.7.4"
|
||||
const Version = "2.7.5"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package crypto
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
goerrors "errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
|
@ -287,6 +288,21 @@ func (msg *PGPMessage) GetHexEncryptionKeyIDs() ([]string, bool) {
|
|||
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.
|
||||
func (msg *PGPMessage) GetSignatureKeyIDs() ([]uint64, bool) {
|
||||
return getSignatureKeyIDs(msg.Data)
|
||||
|
|
@ -297,6 +313,20 @@ func (msg *PGPMessage) GetHexSignatureKeyIDs() ([]string, bool) {
|
|||
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.
|
||||
func (msg *PGPSplitMessage) GetBinaryDataPacket() []byte {
|
||||
return msg.DataPacket
|
||||
|
|
@ -324,6 +354,27 @@ func (msg *PGPSplitMessage) GetPGPMessage() *PGPMessage {
|
|||
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).
|
||||
// Parameters are for backwards compatibility and are unused.
|
||||
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