Giter Site home page Giter Site logo

gopenpgp's Introduction

GopenPGP V2

Build Status

GopenPGP is a high-level OpenPGP library built on top of a fork of the golang crypto library.

Table of Contents

Download/Install

Vendored install

To use this library using Go Modules just edit your go.mod configuration to contain:

require (
    ...
    github.com/ProtonMail/gopenpgp/v2 v2.0.1
)

It can then be installed by running:

go mod vendor

Finally your software can include it in your software as follows:

package main

import (
	"fmt"
	"github.com/ProtonMail/gopenpgp/v2/crypto"
)

func main() {
	fmt.Println(crypto.GetUnixTime())
}

Git-Clone install

To install for development mode, cloning the repository, it can be done in the following way:

cd $GOPATH
mkdir -p src/github.com/ProtonMail/
cd $GOPATH/src/github.com/ProtonMail/
git clone [email protected]:ProtonMail/gopenpgp.git
cd gopenpgp
ln -s . v2
go mod

Documentation

A full overview of the API can be found here: https://godoc.org/gopkg.in/ProtonMail/gopenpgp.v2/crypto

In this document examples are provided and the proper use of (almost) all functions is tested.

Using with Go Mobile

This library can be compiled with Gomobile too. First ensure you have a working installation of gomobile:

gomobile version

In case this fails, install it with:

go get -u golang.org/x/mobile/cmd/gomobile

Then ensure your path env var has gomobile's binary, and it is properly init-ed:

export PATH="$PATH:$GOPATH/bin"
gomobile init

Then you must ensure that the Android or iOS frameworks are installed and the respective env vars set.

Finally, build the application

sh build.sh

This script will build for both android and iOS at the same time, to filter one out you can comment out the line in the corresponding section.

Examples

Encrypt / Decrypt with password

import "github.com/ProtonMail/gopenpgp/v2/helper"

const password = []byte("hunter2")

// Encrypt data with password
armor, err := helper.EncryptMessageWithPassword(password, "my message")

// Decrypt data with password
message, err := helper.DecryptMessageWithPassword(password, armor)

To encrypt binary data or use more advanced modes:

import "github.com/ProtonMail/gopenpgp/v2/constants"

const password = []byte("hunter2")

var message = crypto.NewPlainMessage(data)
// Or
message = crypto.NewPlainMessageFromString(string)

// Encrypt data with password
encrypted, err := EncryptMessageWithPassword(message, password)
// Encrypted message in encrypted.GetBinary() or encrypted.GetArmored()

// Decrypt data with password
decrypted, err := DecryptMessageWithPassword(encrypted, password)

//Original message in decrypted.GetBinary()

Encrypt / Decrypt with PGP keys

import "github.com/ProtonMail/gopenpgp/v2/helper"

// put keys in backtick (``) to avoid errors caused by spaces or tabs
const pubkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----`

const privkey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----` // encrypted private key

const passphrase = []byte(`the passphrase of the private key`) // Passphrase of the privKey

// encrypt plain text message using public key
armor, err := helper.EncryptMessageArmored(pubkey, "plain text")

// decrypt armored encrypted message using the private key and obtain plain text
decrypted, err := helper.DecryptMessageArmored(privkey, passphrase, armor)

// encrypt binary message using public key
armor, err := helper.EncryptBinaryMessageArmored(pubkey, []byte("plain text"))

// decrypt armored encrypted message using the private key expecting binary data
decrypted, err := helper.DecryptBinaryMessageArmored(privkey, passphrase, armor)

With signatures:

// Keys initialization as before (omitted)

// encrypt message using public key, sign with the private key
armor, err := helper.EncryptSignMessageArmored(pubkey, privkey, passphrase, "plain text")

// decrypt armored encrypted message using the private key, verify with the public key
// err != nil if verification fails
decrypted, err := helper.DecryptVerifyMessageArmored(pubkey, privkey, passphrase, armor)

For more advanced modes the full API (i.e. without helpers) can be used:

// Keys initialization as before (omitted)
var binMessage = crypto.NewPlainMessage(data)

publicKeyObj, err := crypto.NewKeyFromArmored(publicKey)
publicKeyRing, err := crypto.NewKeyRing(publicKeyObj)

pgpMessage, err := publicKeyRing.Encrypt(binMessage, privateKeyRing)

// Armored message in pgpMessage.GetArmored()
// pgpMessage can be obtained from NewPGPMessageFromArmored(ciphertext)

//pgpMessage can be obtained from a byte array
var pgpMessage = crypto.NewPGPMessage([]byte)

privateKeyObj, err := crypto.NewKeyFromArmored(privateKey)
unlockedKeyObj = privateKeyObj.Unlock(passphrase)
privateKeyRing, err := crypto.NewKeyRing(unlockedKeyObj)

message, err := privateKeyRing.Decrypt(pgpMessage, publicKeyRing, crypto.GetUnixTime())

privateKeyRing.ClearPrivateParams()

// Original data in message.GetString()
// `err` can be a SignatureVerificationError

Generate key

Keys are generated with the GenerateKey function, that returns the armored key as a string and a potential error. The library supports RSA with different key lengths or Curve25519 keys.

const (
  name = "Max Mustermann"
  email = "[email protected]"
  passphrase = []byte("LongSecret")
  rsaBits = 2048
)

// RSA, string
rsaKey, err := helper.GenerateKey(name, email, passphrase, "rsa", rsaBits)

// Curve25519, string
ecKey, err := helper.GenerateKey(name, email, passphrase, "x25519", 0)

// RSA, Key struct
rsaKey, err := crypto.GenerateKey(name, email, "rsa", rsaBits)

// Curve25519, Key struct
ecKey, err := crypto.GenerateKey(name, email, "x25519", 0)

Detached signatures for plain text messages

To sign plain text data either an unlocked private keyring or a passphrase must be provided. The output is an armored signature.

const privkey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----` // Encrypted private key
const passphrase = []byte("LongSecret") // Private key passphrase

var message = crypto.NewPlaintextMessage("Verified message")

privateKeyObj, err := crypto.NewKeyFromArmored(privkey)
unlockedKeyObj = privateKeyObj.Unlock(passphrase)
signingKeyRing, err := crypto.NewKeyRing(unlockedKeyObj)

pgpSignature, err := signingKeyRing.SignDetached(message, trimNewlines)

// The armored signature is in pgpSignature.GetArmored()
// The signed text is in message.GetString()

To verify a signature either private or public keyring can be provided.

const pubkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----`

const signature = `-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----`

message := crypto.NewPlaintextMessage("Verified message")
pgpSignature, err := crypto.NewPGPSignatureFromArmored(signature)

publicKeyObj, err := crypto.NewKeyFromArmored(pubkey)
signingKeyRing, err := crypto.NewKeyRing(publicKeyObj)

err := signingKeyRing.VerifyDetached(message, pgpSignature, crypto.GetUnixTime())

if err == nil {
  // verification success
}

Detached signatures for binary data

const privkey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----` // encrypted private key
const passphrase = "LongSecret"

var message = crypto.NewPlainMessage(data)

privateKeyObj, err := crypto.NewKeyFromArmored(privkey)
unlockedKeyObj := privateKeyObj.Unlock(passphrase)
signingKeyRing, err := crypto.NewKeyRing(unlockedKeyObj)

pgpSignature, err := signingKeyRing.SignDetached(message)

// The armored signature is in pgpSignature.GetArmored()
// The signed text is in message.GetBinary()

To verify a signature either private or public keyring can be provided.

const pubkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----`

const signature = `-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----`

message := crypto.NewPlainMessage("Verified message")
pgpSignature, err := crypto.NewPGPSignatureFromArmored(signature)

publicKeyObj, err := crypto.NewKeyFromArmored(pubkey)
signingKeyRing, err := crypto.NewKeyRing(publicKeyObj)

err := signingKeyRing.VerifyDetached(message, pgpSignature, crypto.GetUnixTime())

if err == nil {
  // verification success
}

Cleartext signed messages

// Keys initialization as before (omitted)
armored, err := helper.SignCleartextMessageArmored(privateKey, passphrase, plaintext)

To verify the message it has to be provided unseparated to the library. If verification fails an error will be returned.

// Keys initialization as before (omitted)
verifiedPlainText, err := helper.VerifyCleartextMessageArmored(publicKey, armored, crypto.GetUnixTime())

Encrypting and decrypting session Keys

A session key can be generated, encrypted to a Asymmetric/Symmetric key packet and obtained from it

// Keys initialization as before (omitted)

sessionKey, err := crypto.GenerateSessionKey()

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 public key or password.

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 be used to decrypt the corresponding symmetrically encrypted data packets:

var message = crypto.NewPlainMessage(data)

// Encrypt data with session key
dataPacket, err := sessionKey.Encrypt(message)

// Decrypt data with session key
decrypted, err := sessionKey.Decrypt(password, dataPacket)

//Original message in decrypted.GetBinary()

Note that it is not possible to process signatures when using data packets directly. Joining the data packet and a key packet gives us a valid PGP message:

pgpSplitMessage := NewPGPSplitMessage(keyPacket, dataPacket)
pgpMessage := pgpSplitMessage.GetPGPMessage()

// And vice-versa
newPGPSplitMessage, err := pgpMessage.SeparateKeyAndData()
// Key Packet is in newPGPSplitMessage.GetBinaryKeyPacket()
// Data Packet is in newPGPSplitMessage.GetBinaryDataPacket()

Checking keys

Keys are now checked on import and the explicit check via Key#Check() is deprecated and no longer necessary.

gopenpgp's People

Contributors

chesnokovilya avatar cquintana92 avatar cuthix avatar dimitar10 avatar dmitriymv avatar hcmlinj avatar jalehotsky avatar kaylukas avatar klische avatar larabr avatar lubux avatar marinthiercelin avatar mdosch avatar skrilltrax avatar t4cc0re avatar twiss avatar whereswaldon avatar williamgotti avatar wussler avatar zhj4478 avatar zugzwang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gopenpgp's Issues

Specifying encryption to use a subkey rather than the primary.

I am trying to encrypt using a client's public key where the primary key is DSA which I understand is only for signing. It also has an ELG-E subkey. I have spent the morning searching but to no avail. Is there something I have missed in order to be able to get the code to use the subkey rather than the primary key?

Please complete the following checklist (by adding [x]):

  • [x ] I have searched open and closed issues for duplicates
  • [x ] This isn't a feature request
  • [x ] This is not a report about my app not working as expected

Checking passphrase correctness

Hi all

I need some help.

I'm working on a product where we store the private key, and the user keeps the passphrase to the said key. To encrypt/decrypt some data, the user then provides the passphrase. I need to check whether the passphrase the user provided is correct. Currently I do it like this:

key, err = key.Unlock([]byte(input.Passphrase))
if err != nil {
	if strings.Contains(err.Error(), "private key checksum failure") {
		log.Infof("Passphrase probably invalid, PGP Error: %s", err.Error())
		return "", &WrongPassphraseError{}
	}
	return "", errors.Wrap(err, "could not unlock key")
}

Is this the correct way of doing it? Is there some better way? Is there another case in which I can get a checksum failure (maybe if a private key got corrupted during storage)?

GopenPGP does not honor certificate expiration

The following certificate is expired using a key expiration time
subpacket on the primary userid, but GopenPGP does not consider
the certificate expired. As revocation is not well supported in
the OpenPGP ecosystem, liveness through e.g. certificate
expiration is the most important way to retire key material.

There are multiple ways to expire a certificate, and GopenPGP
seems to ignore all of them.

Relevant test: https://tests.sequoia-pgp.org/#Certificate_expiration

-----BEGIN PGP PUBLIC KEY BLOCK-----

xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv
/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz
/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/
5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3
X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv
9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0
qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb
SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb
vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w
bGU+wsEUBBMBCgAnBYJfaL9KBYkBP0xYBQsJCAcCBhUKCQgLAgQWAgMBAheAAhsD
Ah4BACEJEPv8yCoBXnMwFiEE0aZuGiOxgsmYD3iM+/zIKgFeczB+7gv7B+G4pHdV
ITJ4okKyIvliv2+I1FcbJasEQRmlXkeKIAkhRYG81uOoEjtdz8s1YqYt+UW8nhy1
kjSHfAylYmdt+4vMAzEnRQ7wYZBw9GloasBT5CnSmpEJtKbPScnJvE7qpMqhF+QZ
gq2z+JEZ1nsd/SRTG7YunSn8T3G+gBax0BhEwyjqnagIYSuvjX9f9RgaLAZiuUvB
5PtEsT0bu+in55vk9fD+gvCag3/GdjPWtohI02v9ScBbhAIZkSe3DCSJ2aTcwtri
LSibDRB0hLNWuZ6alcDh3OQudTMg+BbXk6alj1gqwg7GJZffeZuLoy5/SSnu25Ka
yVBRAo3zYm9Hb7zTQSE/FzIn7ecGyY/JAx+3YZfHxdQ3Xp9JtjlIFt8ne0AqWaZ0
JGJVrb6LQJA3FCbd8dopBQtQ2+h5nt44VBu7SIAwwrnbYEpXDPFXDtELpabPP2oY
ktVGEhgr8QXG7k4970vdjrPrvp6BpkH1pdqH8K3l4oIgJQLl2Phx9zIbzsDNBF2l
nPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamX
nn9sSXvIDEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMX
SO4uImA+Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6
rrd5y2AObaifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA
0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/
wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+pa
LNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV
8rUnR76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwz
j8sxH48AEQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzy
AhsMAAoJEPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+4
1IL4rVcSKhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZ
QanYmtSxcVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zp
f3u0k14itcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn
3OWjCPHVdTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK
2b0vk/+wqMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFA
ExaEK6VyjP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWi
f9RSK4xjzRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj
5KjhX2PVNEJd3XZRzaXZE2aAMQ==
=2qJE
-----END PGP PUBLIC KEY BLOCK-----

Importing library causes errors

When I import github.com/ProtonMail/gopenpgp/crypto, I get the error module golang.org/x/crypto@latest found (v0.0.0-20191119213627-4f8c1d86b1ba), but does not contain package golang.org/x/crypto/rsa

So I noticed another issue #8, that mentioned having the replace directive from the go.mod file in our go.mod file, but that results in this error:

not enough arguments in call to gomime.DecodeCharset
	have ([]byte, map[string]string)
	want ([]byte, string, map[string]string)

Can someone please help me fix this? How are you importing the library in Go (using modules)?

Signature verification on iOS framework

I followed the instructions to create an iOS framework using gobind. How do I decrypt and verify the signature of an encrypted string? The Decrypt function for struct KeyRing has not been ported: the header file shows

// skipped method KeyRing.Decrypt with unsupported parameter or return types

I tried using GopenPGP's DecryptMessageVerify function, but the result objects verify attribute keeps returning a value of 2 (noVerifier).

Have I missed something? Using v1.0.0

GopenPGP fails to decrypt message with unknown packet versions

Unknown versions of PKESK and SKESK packets, as well as unknown
S2K parameters in known versions of SKESK packets, should be
ignored to allow for a smooth evolution of the OpenPGP message
format. Section 11.3 of RFC4880 specifies what a valid message
looks like, but makes no mention of packet versions.

Reproducer:

This is an encrypted message with a PKESKv3 packet, followed by an
fictitious PKESKv23 packet, and a SEIP packet:

-----BEGIN PGP MESSAGE-----

wcDMA3wvqk35PDeyAQv+IP+QSSYM1Esvodr9+IMab8SVbOajBLsthVAH7fgiAq53
8rj/HTptTL0jZKGLAQD9VmGTlDaoaHOaptgCoVW5kRxfMVcH+EURaJqQ7aJZS/Bq
WO0ut2XvMIiy845lC4sYRCooWPQtamoUMSt/j/IYvvJuTyqv5ay2s55XYyE/5rxc
An+9lBBfDwr5zv3CVSHDMfuAsKCFyEZmgbgU5lcSQQZuQ4U1oIlPaseV77PGWzNB
r/GgZvC53EVTwTaveG/C7Vb1evYoGOtjdFSjpHbTRPE1ju1dj5tZzqjeWe01KE7W
oc34mHEIceSG8JgN/XgDzJG8Ef7ua7bzSQ5wtWyajMz3qDmpPMlJsHgI5bXovMfa
3fsam+cdxTNL9ma7polhpySdpPYjpYl3TxRx4W68C7zSyCaPhLBUiK0KHOlXNk2x
j+4Rr2PgiqsoxzvDp+BNt7iqyhg9mbvLt6aLJnXwY1tdwCH00MLZaTWazVGT1Pvn
aPx9zuNd32TDDlC3h5vBwUoXQUFBQUFBQUEJYWFhYWFhYWFhYWFhYWFhYWFhYWFh
YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYdJMASGN
TOcugNx4CCMJunzRfAmEHnYA7QDxKEtawHPaIouJ0yt+56P0W7WnKHpbqGkkR1LI
OL5XWkx2yj3BhQy4qGNcfPPjERRG4wd04g==
=gF/d
-----END PGP MESSAGE-----

This is the key to decrypt the PKESKv3 packet:

-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: Bob's OpenPGP Transferable Secret Key

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
=miES
-----END PGP PRIVATE KEY BLOCK-----

Building iOS Framework Fails - functions and methods must return either zero or one values, and optionally an error

Running gomobile bind -target ios generates following error:

functions and methods must return either zero or one values, and optionally an error
functions and methods must return either zero or one values, and optionally an error
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1231ee7]

goroutine 1 [running]:
golang.org/x/mobile/bind.(*funcSummary).asMethod(0x0, 0xc000b41f20, 0xd, 0xc002ccaed0)
	/Users/steighallquist/go/src/golang.org/x/mobile/bind/genobjc.go:558 +0x37
golang.org/x/mobile/bind.(*ObjcGen).genStructH(0xc000b41f20, 0xc0011c54f0, 0xc001cd6ea0)
	/Users/steighallquist/go/src/golang.org/x/mobile/bind/genobjc.go:1139 +0xdd3
golang.org/x/mobile/bind.(*ObjcGen).GenH(0xc000b41f20, 0x18, 0x13ab440)
	/Users/steighallquist/go/src/golang.org/x/mobile/bind/genobjc.go:187 +0x1736
main.genPkg(0x7ffeefbffa35, 0x4, 0xc0002ffbd0, 0xc0027a7780, 0xb, 0x10, 0xc0003c4090, 0x5, 0x5, 0x0, ...)
	/Users/steighallquist/go/src/golang.org/x/mobile/cmd/gobind/gen.go:144 +0x183a
main.run()
	/Users/steighallquist/go/src/golang.org/x/mobile/cmd/gobind/main.go:158 +0xa4a
main.main()
	/Users/steighallquist/go/src/golang.org/x/mobile/cmd/gobind/main.go:45 +0x78

"Version" and "Comment" order not consistent when using `ciphertext.GetArmoredWithCustomHeaders`

If you run the code below in a loop, you will see that in some instances the output is:

-----BEGIN PGP MESSAGE-----
Version: foo
Comment: bar
...

And in other instances it is:

-----BEGIN PGP MESSAGE-----
Comment: bar
Version: foo
...

The fact that this isn't deterministic is causing my tests to intermittently fail.

I also note that the parameters seem to be in the wrong order when you compare GetArmoredWithCustomHeaders(comment, version) with ArmorWithTypeAndCustomHeaders(..., version, comment), which is also a little confusing:

func (msg *PGPMessage) GetArmoredWithCustomHeaders(comment, version string) (string, error) {
	return armor.ArmorWithTypeAndCustomHeaders(msg.Data, constants.PGPMessageHeader, version, comment)
}

Example Code

CLICK ME
package main

import (
	"fmt"

	"github.com/ProtonMail/gopenpgp/v2/crypto"
)

func main() {
	message := crypto.NewPlainMessageFromString("foobar")

	key, err := crypto.NewKeyFromArmored(`-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGLoNzcBEACXineYjpC9Am/qiSe1DOiCkf1hWeiGsoqMFToy3/qKmjccLUkc
90Ap5BCkrdl2zEWsYUfegZ6F+XQmlKK5nttVbP9Ezoz+Mn2SwPZgw7u/ZYpGPWv2
fYJbO/yjYTfNjgfCdWnRsYAmE6bcu7mT6MoKQ/dM//plyFEwVlzjlnceVzwekeAl
SpntCnB5nEcKnG/2c9FfD8Z0vgGcHTYkP/HffSPTcpDOADlFSnjENL1v3wi2xDo/
SRyfsTLrndkytNDyRTpVEfCduzOcAsK4/DIygucqj2Lx4kSQGpYO2lD/MV6LWH+H
NVsiOeiCREdczu6JAdnCadYqtslPL7pJ0Hg2Ji+Wo7jq8Km2p7gmFnCXHVY90nfU
z4j3nwP5Z0j3jocYd5sbks29eI0a/i+sq+JwFbfQed4PaB/onpSignyQuofEMdaF
HJeN/ld+MdeE1+xh7arzgCYY2ZVvkoEgJgE6K6N0/+teHSLKZFzTntLoeEFd0Ffa
0oiBUYNdo5di7T8XN8Q5mDa45vFBXe941Q19AaVDEsxFv7g71sZ4YEC511oT0lcQ
D/VVKtYg7kYFFmxlw9TvpKRYc0+j1sGGwxdm8s5eXGtY/eEDIfk8QI5aYnthjFbb
ZL2edcMCBHYElsR92MTb+s2c4/BKUs/q/IOoe3IkYlNUqI1H9cYoHuaLDwARAQAB
tEBJbnRlbCBQcm9kdWN0IFNlY3VyaXR5IEluY2lkZW50IFJlc3BvbnNlIFRlYW0g
PHNlY3VyZUBpbnRlbC5jb20+iQJ4BBABAgBiBQJi6Dc3BQkB3pCAMBSAAAAAACAA
B3ByZWZlcnJlZC1lbWFpbC1lbmNvZGluZ0BwZ3AuY29tcGdwbWltZQgLCQgHAwIB
CgIZAQUbAwAAAAUWAAMCAQUeAQAAAAYVCAkKAwIACgkQwOey70F1YHgnQQ/+L7n2
7cCqUtLMqwH6W1wrQxqRIhqflmSifX8qRiYwBi/EcN1Vf958W9zcqEvX6qMyX16m
SGCKC8KjpPw22XiTKuFocSEzs9pzNRc8p6AGHdISKIMUlpr5JzruvMnraSTlJZ9h
IAuXu+6/U7Y1YG2i9VAg0dSvlpa6YLQWfZ8vB7YS0txSESx4krzF683oWW+BC5jD
01pQgmjuX1fb9KAOoIQ2GLUBZ9gAorz3Dvvas30/JG7mNYI5K6cwOILO3UVu0Hx9
LsF/ut4iFkHNf6uQpcdmaOdcM7HME1K8khddGaop7NozXW7/LKMTwSGOCIRbqBCE
FiPF0NeE+a3tc6W1+ciOU2KFQJIIqplAN5B+kuUSYDH1JobivArtOIrV1tmfhF9H
Bbm30hw5XF0fLtCokpaUZtvGIjVcT9epaSGaEYdzkQJRI8jHsNQlms8E1G8QDDnl
29775JV+EMqQrOn9DdfgLeuRpMNeMpu6XvA6gZVr4OBO5m03ckjfx9uC11e84cNy
oN+5bacZRTQAUw7pOs8mUEYgtzDEomUnKpah/EY8H2QzheogZYwrj+hxGG9+JBm+
L2KRzZ7N6DevcMoGwYFPUOAslzvPlLAr0atm3yNNu2HR9vFXQNkQXDJhusfLMDWQ
q5L5rjiWVDY7CFT4Sl+ULVtO6+Dwt6QERV0rt1C0QUludGVsIFByb2R1Y3QgU2Vj
dXJpdHkgQW5ub3VuY2VtZW50IDxzZWN1cml0eS1hbm5vdW5jZUBpbnRlbC5jb20+
iQJ1BBABAgBfBQJi6DecBQkB3pCAMBSAAAAAACAAB3ByZWZlcnJlZC1lbWFpbC1l
bmNvZGluZ0BwZ3AuY29tcGdwbWltZQgLCQgHAwIBCgUbAwAAAAUWAAMCAQUeAQAA
AAYVCAkKAwIACgkQwOey70F1YHitKQ/8CbtlRKAV0d/2tMK1h1xzH6NAYF//9XsM
jW45Il05c5cIwRSybgl6YQAV+WFG24ucEwxnLpyaYPQDNHGtyM4DNZW1BA+1DE5D
A6WF8juVAw+406Uz1jmS+bg2m+lL57AMGPB1TSNR7QY/dv3McUYxSxltF61SqYvB
M/TcV5v1EirHVAjhHoAJDRCXJW19zn0lxM3ef0w2IXVbSL+1wr0mHouc4aCMHypt
NJF1Lm6qtVCWi5pQU83cqqCSwa41axMONVUKE6CI0a5DuvSHA0fOmARWBu/Wdd5A
MoE3SpiCB9LK5iaZI9rm1bL6bbj+CSnPIwEG5wS8iZNUIDqjA7l8IP34psyHWyc/
OwbHzIkZ0Bbqiyw0+g/LB6sxOjY78PqPoHYr59DTrNflMqMgmMFSt8Ug5AbkirYn
sqnIGJzoNeWfRtN2VAa3KYxdofpnunY8BklONRwMgfgjkWtdQju6x/HJSIqQoX/4
0F9E6fAQt4FrSGNQgNeR7X6rbCXs2z+bBS1gTOV4tlSsKDbycZjAjufSyrmCWD8J
r40Gq8FLY1r85kiwwksW2iZlhble9MlvKXIyvkuGxBrokhMLGGrSbsjPcL8t53TW
NtMo49Vd0o1AM8bLjcLzL0LqN8FKEmM9LnPaysu0XBt04g4piKxw7cgPVugUMP6g
xSQ5vvdcmpq5Ag0EYug3OAEQANP1PkBrX2wi4zrAaZ+1gdZjLwLQ08h+bCGgqxSr
aSShdl7al1sKl8enJwkHzr3Ry7nkJoVkAC0bR9BjnQZuvaZQTWA8QM/sCl/CQT6x
l7oBh3sXax3G+lLlT1XAh65dQYOOj0qYeTYqCcseNeykqv9vtHtxAdb/Cfwua9Hu
6mVWLYxW8pAP6kmG7WUCq6J6jaRby/JFXbkLoKBhRKSrJ6Vud3gq5gpEif3G81db
ApDCaPe/oguf2Sr24QFV4c+Q9JMoE6RN4DjFm5/bYcY3LJDRUsriIAOvL12cDNs6
Tqw5/Dy7ZYtYXN6tTF3QNfpXFUEGMMb25viOsDELV7bzm4m2fFp8tC3b0TbTDGuz
lRlZanbFT7UB7SsGZWzM9limdeuPKZ5Wh25cAfKjCP4BEUM+nga3KU3LKLGd1PXN
S56KrzTQnzVvr92mdTWBy5qb7L+iX6/g67nWLrICvnLwV/9wY37EH1dQ+9Y44PcD
50X7b0CHR3Ug0HNNapnPswZOWgyEJOtPGpBU1v1W/3+uyXE8GJpmroaUTs+ECmPM
DVEszYMALf40r/ZBm1YQ+ruiHetz511Dj0wNUAnBhLfZvU/DKp38eSzkzVNetRQt
I5FauRdAlZV4T3VDeRIz+0Xg2cD9mv/gBeyiI/ybotIcfWDos0GwBvYNMnCcYB78
GNWjABEBAAGJBEcEGAECAjEFAmLoNzkFCQHekIAFGwwAAADBXSAEGQEIAAYFAmLo
NzgACgkQdYwkt+4BKuOIwhAAn/qmTDQTE6nEEYp/u/WTpbSvKyX1G0yrLGZ15mZO
nr2tCaXlFqTMF9nc2ygXFtjyEXrfYsEaA3f6JjXCBnJttxVbiHHKAROGAn4Sod6n
ISptqmeWsMEL1NAxzN9NJJrdh/lFGUCIvr/xmOb2PBaFU0gHAMAnsS70WRfm5ud2
13nv9FEOSDF3IcqFbiHNyk26aTCO4jKErVJkdAYuq5Je3jXi5cU/yso6v+TzCn8Z
YyDP13qBXvNoJEnmItLSc/LGIaSwXhGtI/hTWEV0LgeTRlYVzfG3i5xkS15GWaAP
QE9iVQHSI9uZF/g1sKBUU5bJRZ7I41xFpfeqNB63DlsBst0QX0sR7jgIqI/Vy0SB
yctmdQ6SmG8l8I6PgwmKCV/PqHICA3ZQj04MxIoh65l0bOI5bW3YvAWheMVBf5NB
DrmxBGZd/ineeTCH981m3QbmM+9Z17NksBZMOspXsBxXlvpGioQ9Z2PPTm14PXZN
52KEEJTPKaaUbiyo7SwMyeyAg1JoNGIAn8SmIOnm4i2vLEq5dDNas+L3MG5TbDAh
209BXbYTt4QOD5Fb1UDHYZFUnnDMxv78huVcQrOsHkXSGgosuK91ebTb4M8lw9Te
kyGVJ/vwG/wx4rZSV56TrzynAep+9d2BjwXhSLilmf2kwFYac/IM6Tj6KRmQOXxX
jlUACgkQwOey70F1YHiaMg/8CkX+f/Y0SzD/Tvbj06rM5W9JLIX175PItcoSJqlq
XZ94tCs8rcporX1nmj6moxob9MyYqxQuQ1y6sBnerLw7KmxIe+6D+jsRV+lvl7Qq
DNvoeynMIipVUIzdFlxo1e9fQjgu01wV80U20HjpMc6WTU3QtixHd0fQv1PinbvY
d802JlEYlNOdJRDvu1zt25PM8+4aGbJbd5d4E0qyZcs+V8tKwbtmGchXOaPhMeq7
XpYjwYhlI54vU+HM8mcZTSqmezUUGqk0ySwgHB06I5QuJ6QktALZYbWofAnUpFW7
ZDMBIibCJb8uYzLubWZE/1BPlZxg7jgy5IEKfh9RxYdSkg6tnf5Hae1Eg6V7H8EC
Uaa4E6FUEVDQrg2oujEp0aY3LRX9VaCT62Hw+IBFSF02w28qyl/pohWWf/oGsB/J
tesUKGwv0Mpi0ZqQXGSVDqXLF7UUSbHoQOotGFcZ+J9qLnDnGYO6DAPge7xR9ZKJ
3vFqyaIoi+CdU8UBSTx6GGveuUAM44G8oqQclv6UszVznL9z6CWFY2lgG8VbaS9e
S+KBZ/Z/HZwO/j9+6fdA0di5KVd1EXtBjmmFtnYFzfm0pVF53sMfR2hIu1Jauvbc
8+OnsR0t7fjotTjCAjiBcHIMCpFJjv+AsnXZEib9z206L4I6rieXTQoaazEgJcNm
kss=
=hCPU
-----END PGP PUBLIC KEY BLOCK-----`)
	if err != nil {
		panic(err)
	}

	keyring, err := crypto.NewKeyRing(key)
	if err != nil {
		panic(err)
	}

	ciphertext, err := keyring.Encrypt(message, nil)
	if err != nil {
		panic(err)
	}

	fmt.Println(ciphertext.GetArmoredWithCustomHeaders("foo", "bar"))
}

Enhancement request: OpenPGP smartcard support

Thanks for your great work on gopenpgp.

Please however consider adding OpenPGP smartcard support.

In today's cyber security world, such functionality is almost no longer an option. Even more so when secure storage such as Yubikey or OpenHSM are readily available for readily little financial cost.

Add key expiry

Dear maintainers,

thank you very much for your libraries. While using it one question occurred to me: How do I set the key expiry? I haven't found any way to set an expiry date.

How to encrypt message to multiple recipients

Hi, I have some question about PGP encryption.

Here some recipients public key armors for encrypt message. like this:

publicKeyRing, err := pgp.BuildKeyRingArmored(publicKeysArmorJoinWithLineBreak)
armor, err := helper.EncryptMessageArmored(publicKeyRing, privateKeyRing, ''password", "message")

After build the public key ring and encrypt with it.
Only the private key of first armor public key in publicKeysArmorJoinWithLineBreak could decrypt the ciphertext.

Is there some example of encrypt by multiple public keys. Thanks.

Read private key from filesystem

Hi,

I love what you are doing with GopenPGP, but I miss a functionality.
How can I read a (private) key from the disk, e.g. keys generated with gpg --full-generate-key?

Or is it possible to communicate with the gpg-agent process? Then one could ask the agent to provide a key for a fingerprint or something like this.

Thanks in advance and keep the great work going.

Not possible to specify compression method (or symmetric cipher, digest algorithm)

Currently, even when using the more "advanced" encryption functions, the config cannot be specified. Specifically, it uses non-configurable defaults:

func (keyRing *KeyRing) EncryptWithCompression(message *PlainMessage, privateKey *KeyRing) (*PGPMessage, error) {
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
Time: getTimeGenerator(),
DefaultCompressionAlgo: constants.DefaultCompression,
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
}

Specifically, it forces:

  • AES256
  • SHA256
  • zlib (when calling w/ compression)

There doesn't seem to be an option to allow passing my preferred algorithms into the encrypt call (i.e. packet.Config{})

Would you accept a PR to allow passing an optional config in?

Error when doing symmetric encryption

I am symmetrically encrypting a binary file with gopenpgp and decrypt it with gpg2 on linux. The file can be decrypted by gpg2, but is corrupted. The reason is the way it was encrypted. Specifically in crypto/password.go#L109 the line

encryptWriter, err := openpgp.SymmetricallyEncrypt(&outBuf, password, nil, config)

means that the message is treated as text. I think this would need to be changed to

hints := openpgp.FileHints{IsBinary: true}
encryptWriter, err := openpgp.SymmetricallyEncrypt(&outBuf, password, &hints, config)

Error when signing with unprotected armored key

In cleartext.go#L18 when using SignCleartextMessageArmored with an unprotected armored key an error occurs. This is due to the attempt to unlock the key even when the password is empty.

A fix would be to have a check and only do unlockedKey, err := signingKey.Unlock(passphrase) when the passphrase is not empty.

Fails to build android library

Hi.

I followed instructions for building libraries for mobile (https://github.com/ProtonMail/gopenpgp#using-with-go-mobile), and ios/macos builds without a problem.

But android fails with error:

# runtime/cgo
ld: error: duplicate symbol: x_cgo_inittls
>>> defined at gcc_android.c:90
>>>            $WORK/b108/_x003.o:(x_cgo_inittls)
>>> defined at gcc_linux_arm.c:13
>>>            $WORK/b108/_x006.o:(.bss+0x4)
clang: error: linker command failed with exit code 1 (use -v to see invocation)

go version go version go1.15.6 darwin/amd64

All environment variables set.

I tried with NDK versions:

  • 21.0.6113669
  • 22.0.7026061

[Question] Is this library using a secure PRNG?

Hey!

I'm using this library on mobile (Android and iOS). I would like to confirm if this library is using a secure PRNG? I googled and tried looking at source code but I wasn't able to find anything useful.

If it's using secure PRNG then is it in all cases? Or maybe it sometimes uses non secure as a fallback? Is it documented anywhere?

If it's not using secure PRNG then is it possible to make it using mobile APIs for random numbers? For example is it possible to make it use SecureRandom on Android somehow?

I think it's worth to put this information in readme :)

GopenPGP should ignore unbound or unknown certificate components

When a certificate contains an unbound subkey, GopenPGP refuses
to encrypt for the certificate's bound subkey:

-----BEGIN PGP PUBLIC KEY BLOCK-----

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
=33kX
-----END PGP PUBLIC KEY BLOCK-----

The same is true if the certificate contains a bad signature, a
signature with an odd signature type, a signature with an unknown
version, a key with an unknown version, or a packet with an
unknown tag. See the last five lines of the following test for
test vectors:

https://tests.sequoia-pgp.org/#Perturbed_certificates

This is problematic if a certificate gets slightly mangled (some
version of SKS apparently did that), and dismissing certificates
with unknown packet versions prevents a smooth upgrade path to
newer packet versions.

In contrast, OpenPGP.js handles these cases gracefully.

Issues with 'crypto'

Trying to use the "github.com/ProtonMail/gopenpgp/v2/crypto" v2.1.0 and just calling crypto.GetTime() and getting the below error
go build ./

github.com/ProtonMail/gopenpgp/v2/crypto

vendor/github.com/ProtonMail/gopenpgp/v2/crypto/key.go:331:63: cannot use subKey.PublicKey.Fingerprint (type [20]byte) as type []byte in argument to hex.EncodeToString
vendor/github.com/ProtonMail/gopenpgp/v2/crypto/key.go:334:70: cannot use key.entity.PrimaryKey.Fingerprint (type [20]byte) as type []byte in argument to hex.EncodeToString
vendor/github.com/ProtonMail/gopenpgp/v2/crypto/key.go:349:49: cannot use key.entity.PrimaryKey.Fingerprint (type [20]byte) as type []byte in argument to hex.EncodeToString

same with any functions called from crypto
All works fine with v2.0.1

Failing to decrypt due to missing MDC

Thanks for your great work on this library.

I detected an issue.

If we try to decrypt a PGP file missing MDC using DecryptStream, it always fails because we can't set InsecureAllowUnauthenticatedMessages option in

config := &packet.Config{
Time: getTimeGenerator(),
}
.

It may resolve if DecryptStream has the InsecureAllowUnauthenticatedMessages option.
Can we get this feature? Happy to help with a PR potentially.

Building Android Framework Fails - redefinition of 'Java_com_proton_gopenpgp_crypto_ClearTextMessage_getSignature'

When I try to build Android library I encountered following error:

# gobind
crypto_android.c:338:1: error: redefinition of 'Java_com_proton_gopenpgp_crypto_ClearTextMessage_getSignature'
crypto_android.c:300:1: note: previous definition is here
crypto_android.c:936:1: error: redefinition of 'Java_com_proton_gopenpgp_crypto_PGPSplitMessage_getDataPacket'
crypto_android.c:913:1: note: previous definition is here
crypto_android.c:951:1: error: redefinition of 'Java_com_proton_gopenpgp_crypto_PGPSplitMessage_getKeyPacket'
crypto_android.c:921:1: note: previous definition is here

Root cause seems to be that for structs 'CleartTextMessage' and 'PGPSplitMessage' are defined as:

type PGPSplitMessage struct {
	DataPacket []byte
	KeyPacket  []byte
}

type ClearTextMessage struct {
	Data []byte
	Signature []byte
}

and following getter methods defined as:

func (msg *ClearTextMessage) GetSignature() []byte {
	return msg.Signature
}

func (msg *PGPSplitMessage) GetDataPacket() []byte {
	return msg.DataPacket
}

func (msg *PGPSplitMessage) GetKeyPacket() []byte {
	return msg.KeyPacket
}

While building Android library, gomobile automatically generates Java getter and setter for struct properties like "getKeyPacket()", which has same function signature as above getter methods defined in Go source code. IMO, to keep the go code compatible to iOS & Android, we should either:

  1. Avoid defining getter/setters for struct properties, only using getters/setters as convenience methods when we need modification of properties, and keep mind of the method name, e.g.:
func (msg *ClearTextMessage) GetString() string {
	return string(msg.Data)
}
  1. Use different method name for getters/setters to avoid this issue?

Test fails with latest go-crypto

Dear maintainer,

I prepared an update of the go-crypto Debian package to go-crypto v0.0.0-20220812175011-7fcef0dbe794 but this breaks gopenpgp.
It's not caused by the Debian packaging as I get the same issue when checking out your main branch and updating go-crypto:

martin@schlepptop ~/build/gopenpgp/crypto (git)-[main] % go test
--- FAIL: TestClearPrivateKey (0.00s)
    keyring_test.go:157: 
        	Error Trace:	keyring_test.go:157
        	Error:      	Expected nil, but got: &errors.errorString{s:"gopenpgp: unknown private key"}
        	Test:       	TestClearPrivateKey
panic: interface conversion: interface {} is *eddsa.PrivateKey, not *ed25519.PrivateKey [recovered]
	panic: interface conversion: interface {} is *eddsa.PrivateKey, not *ed25519.PrivateKey

goroutine 31 [running]:
testing.tRunner.func1.2({0x6be380, 0xc00039b590})
	/usr/lib/go-1.18/src/testing/testing.go:1389 +0x24e
testing.tRunner.func1()
	/usr/lib/go-1.18/src/testing/testing.go:1392 +0x39f
panic({0x6be380, 0xc00039b590})
	/usr/lib/go-1.18/src/runtime/panic.go:838 +0x207
github.com/ProtonMail/gopenpgp/v2/crypto.TestClearPrivateKey(0xc000082d00)
	/home/martin/build/gopenpgp/crypto/keyring_test.go:162 +0x47b
testing.tRunner(0xc000082d00, 0x735a50)
	/usr/lib/go-1.18/src/testing/testing.go:1439 +0x102
created by testing.(*T).Run
	/usr/lib/go-1.18/src/testing/testing.go:1486 +0x35f
exit status 2
FAIL	github.com/ProtonMail/gopenpgp/v2/crypto	1.990s

Expiration of primary key binding signatures is not honored

GopenPGP does not honor the expiration time on primary key
binding signatures.

Reproducer:

This is a signature over the string "Hello World :)":

-----BEGIN PGP SIGNATURE-----

wsDzBAABCgAGBYJfLAFsACEJEHwvqk35PDeyFiEEHdzhXwkhfO4vOzdgfC+qTfk8
N7KiqwwAts4QGB7v9bABCC2qkTxJhmStC0wQMcHRcjL/qAiVnmasQWmvE9KVsdm3
AaXd8mIx4a37/RRvr9dYrY2eE4uw72cMqPxNja2tvVXkHQvk1oEUqfkvbXs4ypKI
NyeTWjXNOTZEbg0hbm3nMy+Wv7zgB1CEvAsEboLDJlhGqPcD+X8a6CJGrBGUBUrv
KVmZr3U6vEzClz3DBLpoddCQseJRhT4YM1nKmBlZ5quh2LFgTSpajv5OsZheqt9y
EZAPbqmLhDmWRQwGzkWHKceKS7nZ/ox2WK6OS7Ob8ZGZkM64iPo6/EGj5Yc19vQN
AGiIaPEGszBBWlOpHTPhNm0LB0nMWqqaT87oNYwP8CQuuxDb6rKJ2lffCmZH27Lb
UbQZcH8J+0UhpeaiadPZxH5ATJAcenmVtVVMLVOFnm+eIlxzov9ntpgGYt8hLdXB
ITEG9mMgp3TGS9ZzSifMZ8UGtHdp9QdBg8NEVPFzDOMGxpc/Bftav7RRRuPiAER+
7A5CBid5
=aQkm
-----END PGP SIGNATURE-----

Made by the subkey of this certificate:

-----BEGIN PGP PUBLIC KEY BLOCK-----

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
hW1Hj9AO8lzggBQ=
=Nt+N
-----END PGP PUBLIC KEY BLOCK-----

Relevant test: https://tests.sequoia-pgp.org/#Primary_key_binding_signatures

How do I decrypt a PGP Split message with a session key encrypted with multiple public keys?

I have a PGPSplitMessage with a session-key key-packet encrypted with a keyring containing >1 public key.

At the moment, it seems like on decryption PGPMessage.SeparateKeyAndData only returns the first key package.
This means that I can only decrypt with the private key to the last added key to the keyring with which the message was encrypted.

Is there any way of detecting and extracting all key-packages from a split message, OR use a keyring to decrypt it without first splitting it?

I have attached a unit-test below that demonstrates this end-to-end:

Please complete the following checklist (by adding [x]):

  • I have searched open and closed issues for duplicates
  • This isn't a feature request
  • This is not a report about my app not working as expected
package secrets

import (
	"fmt"
	"testing"

	"github.com/ProtonMail/gopenpgp/v2/crypto"
	"github.com/stretchr/testify/assert"
)

func Test_Something(t *testing.T) {
	// Generate keys
	johnKey, err := crypto.GenerateKey("John Doe", "[email protected]", "rsa", 4096)

	assert.NoError(t, err)
	janeKey, err := crypto.GenerateKey("Jane Doe", "[email protected]", "rsa", 4096)
	assert.NoError(t, err)

	// get Janes keys
	s, err := janeKey.GetArmoredPublicKey()
	assert.NoError(t, err)
	janePK, err := crypto.NewKeyFromArmored(s)
	assert.NoError(t, err)

	// get Johns keys
	tKey, err := johnKey.GetArmoredPublicKey()
	assert.NoError(t, err)
	johnPK, err := crypto.NewKeyFromArmored(tKey)
	assert.NoError(t, err)

	// Create a keyring
	keyRing, err := crypto.NewKeyRing(johnPK)
	assert.NoError(t, err)

	// Add Jane to the keyring
	err = keyRing.AddKey(janePK)
	assert.NoError(t, err)

	fmt.Println(len(keyRing.GetIdentities()))

	sessionKey, err := crypto.GenerateSessionKey()
	assert.NoError(t, err)

	message := crypto.NewPlainMessage([]byte("Hello world, we are a message"))
	dataPacket, err := sessionKey.Encrypt(message)
	assert.NoError(t, err)

	fmt.Println(len(keyRing.GetIdentities()))
	keyPacket, err := keyRing.EncryptSessionKey(sessionKey)
	assert.NoError(t, err)
	fmt.Println(len(keyPacket))

	pgpSplitMessage := crypto.NewPGPSplitMessage(keyPacket, dataPacket)
	pgpMessage := pgpSplitMessage.GetPGPMessage()

	armored, err := pgpMessage.GetArmored()
	assert.NoError(t, err)

	loaded, err := crypto.NewPGPMessageFromArmored(armored)
	assert.NoError(t, err)

	// This only gets the first key packet
	pgpMsg, err := loaded.SeparateKeyAndData(1024, 0)
	assert.NoError(t, err)
	pgpMsg.GetBinaryKeyPacket()

	keyRing, err = crypto.NewKeyRing(janeKey)
	assert.NoError(t, err)
	decodedKeyPacket, err := keyRing.DecryptSessionKey(pgpMsg.GetBinaryKeyPacket())
	assert.NoError(t, err)

	fmt.Println(len(pgpMsg.GetBinaryKeyPacket()))

	plain, err := decodedKeyPacket.Decrypt(pgpMsg.DataPacket)
	assert.NoError(t, err)
	assert.Equal(t, string(plain.Data), "Hello world, we are a message")

	keyRing, err = crypto.NewKeyRing(johnKey)
	assert.NoError(t, err)
	decodedKeyPacket, err = keyRing.DecryptSessionKey(pgpMsg.GetBinaryKeyPacket())
	assert.NoError(t, err) // fails here

}

Questions about functionality

Hi, I have some questions about the current status of the library / functionality:

  1. Gomobile does not convert functions that return arrays, so these appear to be currently unavailable in the iOS framework (approx 14 functions). Is there a workaround for this / are these going to implemented?

  2. How do you get key identifier(s) of key(s) used to encrypt a MIME message on iOS? So that we can then find the associated keyring and decrypt the message.

  3. I don't see a way to get identities or entities from keyrings on iOS - am I missing something?

Thanks.

please add helper.EncryptMessageArmoredFromByte or default use []byte mode

Hi , I got a funny bug , about string and []byte in helper

if I read a binary image data and []byte convert to string and use helper.EncryptMessageArmored + helper.DecryptMessageArmored to encode / decode it , it will be success & source / result checksum verify will be same

but if I use command line gpg (v1.4 , v2.x) to decode it , the binary will lost like \x00\x00\x00\x0d\x00\x00\x00\x0d (\r char) and checksum match will be fail

I tried if I don't use helper.EncryptMessageArmored and use it source code , change to use crypto.NewPlainMessage(rawSourceBytes) , and use gpg command to decode it , source / result checksum verify will success

so I just suggest add a helper.EncryptMessageArmoredFromByte function or use default use []byte , this condition will be okay

and it no matter about gpg encode and golang decode condition

Bad header in armored signature

When creating an armored signature in crypto/message.go#L369, the following header is used:

str := "-----BEGIN PGP SIGNED MESSAGE-----\r\nHash:SHA512\r\n\r\n"

When validating the resulting message with gpg2 on linux an error is shown because it expects a space after the semicolon, i.e. Hash: SHA512.

Allow empty email for key generation

Dear gopenpgp maintainers,

thank you very much for your library as it makes OpenPGP easily usable in Go. I am using it to implement "XEP-0373: OpenPGP for XMPP" but I have an issue: The XEP requires the User ID to be in the format "xmpp:[email protected]" but gopenpgp/crypto requires an email to be set so I can only create "xmpp:[email protected] <xmpp:[email protected]>". Could you add a function to create keys more flexible to make the library also useful outside of the email use case? Or just change

if len(email) == 0 {
to accept empty email or name values.

Error while creating debian package

Dear gopenpgp maintainers,

thank you very much for these tools. They seem to be very handy for one of my projects (implementing OpenPGP for XMPP in go-sendxmpp) and therefore I wanted to package it for debian. But I get the following error when trying to create a debian package:

# github.com/ProtonMail/gopenpgp/crypto
src/github.com/ProtonMail/gopenpgp/crypto/key.go:286:20: key.entity.Revoked undefined (type *openpgp.Entity has no field or method Revoked)
src/github.com/ProtonMail/gopenpgp/crypto/key.go:286:70: key.entity.PrimaryIdentity().Revoked undefined (type *openpgp.Identity has no field or method Revoked)

If I did something wrong there than I feel sorry for bothering you but to me it looks like there is something wrong in your library.

Best regards,
Martin

build issues

Hi

I'm having trouble building a very simple program which mimics the first code example in the readme - simply encrypting and decrypting using a password. I'm using go modules.

Initially, when I built it, I got this:

build github.com/opb/protontest: cannot load golang.org/x/crypto/rsa: cannot find module providing package golang.org/x/crypto/rsa

Your webpage mentions that you forked golang.org/x/crypto but there's no mention in this repo's readme about using the forked version. go mod tries to use the default golang.org/x/crypto, so I added a replace directive to my go.mod:

replace golang.org/x/crypto => github.com/ProtonMail/crypto v2.0.0+incompatible

This removed the previous error, but now I'm seeing:

โžœ  go build                                    
# github.com/ProtonMail/gopenpgp/crypto
../../../go/pkg/mod/github.com/!proton!mail/[email protected]/crypto/key.go:291:22: subkey.PublicKey.KeyExpired undefined (type *packet.PublicKey has no field or method KeyExpired)
../../../go/pkg/mod/github.com/!proton!mail/[email protected]/crypto/key.go:320:18: e.PrimaryKey.KeyExpired undefined (type *packet.PublicKey has no field or method KeyExpired)
../../../go/pkg/mod/github.com/!proton!mail/[email protected]/crypto/keyring.go:626:24: subkey.PublicKey.KeyExpired undefined (type *packet.PublicKey has no field or method KeyExpired)

Any pointers would be much appreciated.

Thanks

GopenPGP successfully verifies signatures using SHA-1

SHA-1 is unsuitable for signatures over data. The first
collision was published in 2017, and attacks have advanced since
to the point that chosen-prefix attacks are feasible now.

Signatures using SHA-1 in signed messages and detached signatures
must be rejected.

Relevant test: https://tests.sequoia-pgp.org/#Signature_over_the_shattered_collision

The case is less clear for self-signatures in OpenPGP
certificates, because we discovered that use in certificates is
still wide-spread. Please follow the discussion on openpgp@:

https://mailarchive.ietf.org/arch/msg/openpgp/Rp-inhYKT8A9H5E34iLTrc9I0gc/

Reproducer

Use this certificate:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Bob's OpenPGP certificate

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
NEJd3XZRzaXZE2aAMQ==
=NXei
-----END PGP PUBLIC KEY BLOCK-----

To verify this signature:

-----BEGIN PGP SIGNATURE-----

wsDzBAABAgAdFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAFAl+uh1wACgkQ+/zIKgFe
czBqzAv9G9v98so++R88YSVMTGbfzq4fSh4C/DkfZCT9+j6H1IpYfjfouFQNtTE7
0ACiusd0cTxBdRKOqsbh9EW3yfv77+8XU+sNY0QZ2UgfbEW1USAP4BgACMkpSu5X
mlcE65jw7bhfdnnNsypOKORpH/uyLSMTAvBj3rrzFyikDjJwmSyLdJocjCawkr0U
aOQfDZfJVXhRCj9oXrRW2LBdB7HsLqwFvt/EIct8FYnTxriV8+xuVaihCbX357uE
NXqbT8x07xMAUws/iwrHptmPZP6sz4+bdekMXAMncY4hBR8J0NDh6vpq8yYRylpa
ebJ8mLGhujJPIcU12VOMimGE7+zpAwmlJgPQqdZsjS1ZuvQ/BS37hRIvdUTZljff
EXqqJq3SwZOvdvyRzTk3vciZhEzJ2aLSj0A4PY/k/6fZEdY3ZHRKdWP3btOU6GNF
b+BXPE/eNerLy0Y5BgMOKfsOaqPGz4bDJTNv83gsbYPyFXvhb0hTESRgyNeGC+rC
WOUcC767
=Ox9v
-----END PGP SIGNATURE-----

Over these two files:

The signature authenticates both files even though they are
different.

Question regarding explictily ignored errors

Hello all,

Thanks for all your work on this package and the ProtonMail suite of products in general. I did have a question surrounding some explicitly ignored errors within the signature collector that I'd like to understand further and point out.

  • Line 40 & 54 seem to be ignoring the errors from mime.ParseMediaType(). It appears mime.ParseMediaType() will still return the media type on error, but it is not converted to lowercase and the white space is not trimmed like it would be if no error was thrown. I don't know if that matters in this specific case, but wanted to bring it to awareness.
parentMediaType, params, _ := mime.ParseMediaType(header.Get("Content-Type"))
  • Line 97 is ignoring the error from ioutil.ReadAll() while other usage of ioutil.ReadAll() within the same package handles those errors.
str, _ := ioutil.ReadAll(rawBody)

I just wanted to point these out and try to understand them further. Is there any reason for these that I am not grasping? Thanks!

Fails to build ios/macos frameworks

I did the steps described in the readme and after invoking build.sh script, Android aar is properly produces but no iOS/MacOS frameworks show up.
In the console I can see invalid flag in go:cgo_ldflag: -fembed-bitcode error. And at the bottom:
error: the path does not point to a valid framework: /Users/me/projects/gopenpgp-master/dist/ios/Gopenpgp.framework

I have XCode and its command line tools installed. What am I missing?

GopenPGP fails to verify detached signatures with unknown packet versions

Unknown versions of Signature packets should be ignored to allow
for a smooth evolution of the OpenPGP message format.

Reproducer:

This is a fictitious v23 signature followed by a v4 signature:

-----BEGIN PGP SIGNATURE-----

wsDzFwABCgAGBYJfM9pFACEJEPv8yCoBXnMwFiEE0aZuGiOxgsmYD3iM+/zIKgFe
czBW7gwAjobe0/8MVxpRNBQc9/OFcpWacD6F8y4f+R9hBZ7aGxZduBOsr35i2I0d
Ujba+EyWKjtnACT5AgDI1iG0n4qMMXKT5s8SIQsJk9u9JZ0A2mdDBQgxgdHSM4X5
Yh42APTj+fHDyWzh5VKWVCMRIvdc+xW5E1nuBA9Oa9pgJSF/+W8DzBixAnX8/BpR
pjHUkoyZDr5BakiWPAWHGM9MAWL/pP7GgiUnWAsWoREWFHzk/q8oxyve1hcf8j6d
1ux7764ynaxLrMXgVHebAYKVBCirnG6BO2FvCTZXy42omokHz/UEXroc+/QT2ul9
pZuYJt+/X9oOAkK55kQ0jh+aa2F9wLHEQ+Gq11pqjfhQidR4iGxB8D+buxjSMuXG
23T2DkQlVZXu4XIZIFEn0MH+2pF8bMhfMyM/gKtnUpgfRI6uyx+1aazKRymuJci6
/QxL4l57Ih/DR4LnA9B0iNr7I3ces0kYbL9ZcPxgv2b3SwwJDTxcrUWqZLl3RKU4
+fzYyy1VwsDzBAABCgAGBYJfM9pFACEJEPv8yCoBXnMwFiEE0aZuGiOxgsmYD3iM
+/zIKgFeczBW7gwAjobe0/8MVxpRNBQc9/OFcpWacD6F8y4f+R9hBZ7aGxZduBOs
r35i2I0dUjba+EyWKjtnACT5AgDI1iG0n4qMMXKT5s8SIQsJk9u9JZ0A2mdDBQgx
gdHSM4X5Yh42APTj+fHDyWzh5VKWVCMRIvdc+xW5E1nuBA9Oa9pgJSF/+W8DzBix
AnX8/BpRpjHUkoyZDr5BakiWPAWHGM9MAWL/pP7GgiUnWAsWoREWFHzk/q8oxyve
1hcf8j6d1ux7764ynaxLrMXgVHebAYKVBCirnG6BO2FvCTZXy42omokHz/UEXroc
+/QT2ul9pZuYJt+/X9oOAkK55kQ0jh+aa2F9wLHEQ+Gq11pqjfhQidR4iGxB8D+b
uxjSMuXG23T2DkQlVZXu4XIZIFEn0MH+2pF8bMhfMyM/gKtnUpgfRI6uyx+1aazK
RymuJci6/QxL4l57Ih/DR4LnA9B0iNr7I3ces0kYbL9ZcPxgv2b3SwwJDTxcrUWq
ZLl3RKU4+fzYyy1V
=pV3Y
-----END PGP SIGNATURE-----

This is the certificate to verify the v4 signature:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Bob's OpenPGP certificate

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
NEJd3XZRzaXZE2aAMQ==
=NXei
-----END PGP PUBLIC KEY BLOCK-----

Interestingly, the verification succeeds if the v4 signature
comes first, so it might just be a problem with detecting the
kind of data:

-----BEGIN PGP SIGNATURE-----

wsDzBAABCgAGBYJfM9pFACEJEPv8yCoBXnMwFiEE0aZuGiOxgsmYD3iM+/zIKgFe
czBW7gwAjobe0/8MVxpRNBQc9/OFcpWacD6F8y4f+R9hBZ7aGxZduBOsr35i2I0d
Ujba+EyWKjtnACT5AgDI1iG0n4qMMXKT5s8SIQsJk9u9JZ0A2mdDBQgxgdHSM4X5
Yh42APTj+fHDyWzh5VKWVCMRIvdc+xW5E1nuBA9Oa9pgJSF/+W8DzBixAnX8/BpR
pjHUkoyZDr5BakiWPAWHGM9MAWL/pP7GgiUnWAsWoREWFHzk/q8oxyve1hcf8j6d
1ux7764ynaxLrMXgVHebAYKVBCirnG6BO2FvCTZXy42omokHz/UEXroc+/QT2ul9
pZuYJt+/X9oOAkK55kQ0jh+aa2F9wLHEQ+Gq11pqjfhQidR4iGxB8D+buxjSMuXG
23T2DkQlVZXu4XIZIFEn0MH+2pF8bMhfMyM/gKtnUpgfRI6uyx+1aazKRymuJci6
/QxL4l57Ih/DR4LnA9B0iNr7I3ces0kYbL9ZcPxgv2b3SwwJDTxcrUWqZLl3RKU4
+fzYyy1VwsDzFwABCgAGBYJfM9pFACEJEPv8yCoBXnMwFiEE0aZuGiOxgsmYD3iM
+/zIKgFeczBW7gwAjobe0/8MVxpRNBQc9/OFcpWacD6F8y4f+R9hBZ7aGxZduBOs
r35i2I0dUjba+EyWKjtnACT5AgDI1iG0n4qMMXKT5s8SIQsJk9u9JZ0A2mdDBQgx
gdHSM4X5Yh42APTj+fHDyWzh5VKWVCMRIvdc+xW5E1nuBA9Oa9pgJSF/+W8DzBix
AnX8/BpRpjHUkoyZDr5BakiWPAWHGM9MAWL/pP7GgiUnWAsWoREWFHzk/q8oxyve
1hcf8j6d1ux7764ynaxLrMXgVHebAYKVBCirnG6BO2FvCTZXy42omokHz/UEXroc
+/QT2ul9pZuYJt+/X9oOAkK55kQ0jh+aa2F9wLHEQ+Gq11pqjfhQidR4iGxB8D+b
uxjSMuXG23T2DkQlVZXu4XIZIFEn0MH+2pF8bMhfMyM/gKtnUpgfRI6uyx+1aazK
RymuJci6/QxL4l57Ih/DR4LnA9B0iNr7I3ces0kYbL9ZcPxgv2b3SwwJDTxcrUWq
ZLl3RKU4+fzYyy1V
=6vh8
-----END PGP SIGNATURE-----

GNU dummy keys in subkeys cause crypto.Key to be considered both locked and unlocked

I am diagnosing a problem why I cannot log in to the ProtonMail Bridge and Import-Export App, and I was able to reduce the problem to weird behavior in gopenpgp.

When I add this patch to this library, I will get the output, that both IsLocked() and IsUnlocked() return false. This inconsistency leads to problems in the downstream app.

diff --git a/crypto/key.go b/crypto/key.go
index 49af11e..5864806 100644
--- a/crypto/key.go
+++ b/crypto/key.go
@@ -135,10 +135,16 @@ func (key *Key) Lock(passphrase []byte) (*Key, error) {
 func (key *Key) Unlock(passphrase []byte) (*Key, error) {
        isLocked, err := key.IsLocked()
        if err != nil {
+               fmt.Printf("(key *Key) Unlock unlocked, err: %+v, %+v\n", isLocked, err)
                return nil, err
        }

        if !isLocked {
+
+               isLocked, err = key.IsLocked()
+               isUnLocked, _ := key.IsUnlocked()
+               fmt.Printf("(key *Key) Unlock isLocked: %t, isUnLocked: %t\n", isUnLocked, isUnLocked)
+
                if passphrase == nil {
                        return key.Copy()
                }

output:

(key *Key) Unlock isLocked: false, isUnLocked: false

I suspect, that (for whatever reason) there might be a subkey, that is considered unlocked(or locked). Due to the nature of this being related to my keys and ProtonMail account, I can not share more details, but I am taking a look if I can identify the issue closer.

In any case, this is with a regular ProtonMail account that had some keys imported into it. So it might be possible there is a corruption introduced there. Regardless, since everything works on the web app and iOS app, I believe there to be an edge-case with regards to this library.

Imports are broken

We have troubles with updating dependencies that imports github.com/ProtonMail/gopenpgp.

A simple example:

package main

import (
	_ "github.com/ProtonMail/gopenpgp/v2/crypto"
)

func main() {
}
 $ go mod init example
 $ go get -u ./...
 example imports
        github.com/ProtonMail/gopenpgp/v2/crypto imports
        github.com/ProtonMail/go-crypto/ed25519: cannot find module providing package github.com/ProtonMail/go-crypto/ed25519
example imports
        github.com/ProtonMail/gopenpgp/v2/crypto imports
        github.com/ProtonMail/go-crypto/rsa: cannot find module providing package github.com/ProtonMail/go-crypto/rsa

No helper.EncryptSignBinaryMessageArmored

I am currently working on a Middleware for encrypting/signing mails with go-mail and was wondering why the helper packages does not provide a EncryptSignBinaryMessageArmored method. Is that intentionally or has it just never been needed? If so, it would be great to have that.

Cannot encrypt with OpenPGP.js by ECC-keys generated by gopenpgp

Hello, I am getting this error:
Error: Error encrypting message: Could not find valid key packet for encryption in key e4a279940d321bf9

Keypair was generated by gopenpgp:

privateKey, err := pgp.GenerateKey(form.ID.String(), "", "", "x25519", 256)
if err != nil {
    panic(err)
}

OpenPGP.js code:

const pubkey = `-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEXPExRRYJKwYBBAHaRw8BAQhArsWCIyCNnavWVwB6eclRyZOlwtNfDVOZSIAt
hwuvkGHNMzk1MDMyNDY2MDBAc292Y29tYmFuay5ydSA8OTUwMzI0NjYwMEBzb3Zj
b21iYW5rLnJ1PsJqBBMWCAAcBQJc8TFFCRDcoyJUXTlk6AIbAwIZAQILCQIVCAAA
sGoBANNN7rgB3irpIK4+iPhl58v1i4sENv8Xb1sUXw8pX66nAQBsIfvEc2WGOnxZ
j44QepQMQHJ1eE/lV1Vj2uaJZe9gCc44BFzxMUUSCisGAQQBl1UBBQEBCEDG0lHB
1je+Kd226ioYKsHwUH/w4ceyxrgkB/7E5IHiFAMBCgnCYQQYFggAEwUCXPExRQkQ
3KMiVF05ZOgCGwwAABl/AQBB/67SutQISLGLVy9AtjzSsb/qRegktxQrskcwkS37
0gEABLsPwyUSFSKibjqIx8hy/6LhKzKVtctFUJhsVHNDKQc=
=1ftC
-----END PGP PUBLIC KEY BLOCK-----`;

const encrypt = async() => {
	const options = {
		message: openpgp.message.fromText("Hello, World!"),
		publicKeys: (await openpgp.key.readArmored(pubkey)).keys,
	};

	openpgp.encrypt(options).then(ciphertext => {
		console.log(ciphertext.data);
	}).catch(err => {
		console.error(err);
	});
};

encrypt();

I see that @twiss also maintains OpenPGP.js library, so hope could help.

Framework not working on iOS

ld: warning: ignoring file /Users/dev/Developer/App/App-iOS/Crypto.framework/Crypto, file is universal (4 slices) but does not contain the arm64 architecture: /Users/dev/Developer/App/App-iOS/Crypto.framework/Crypto
Undefined symbols for architecture arm64:
  "_CryptoNewKey", referenced from:
      -[CanaryCorePGPManager migrateKeys] in CanaryCorePGPManager.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I compiled it using the latest version of go & gomobile with sh build.sh. Any ideas?

Lipo reports all archs are present:

lipo -info Crypto.Framework/Crypto 
Architectures in the fat file: Crypto are: armv7 i386 x86_64 arm64 

Only added 2 lines of code:

CryptoKey *key = CryptoNewKey(data, &readError);
NSLog(@"[GOPENPGP] Loaded: %@", key.getHexKeyID);

Encrypt/Decrypt Attachments

Hi, when encrypting data using EncryptAttachment, the result has the key and data packets split. I'd like to store them both together in one file, but concatenating the 2 packets manually seems to cause data corruption for some reason. Any idea why?

Also is using the attachment functions the recommended way for encrypting/decrypting large (1 MB+) binary data?

Thanks

[Question] CryptoKeyRing :- need help with CryptoKeyRing

Hi ,

I am using this framework with iOS app, I just need to know how can I save multiple keys in CryptoKeyRing and able to use it with individual key if necessary.

Is there a way when decrypting a private key can be automatically fetched from CryptoKeyRing or use key directly to decrypt.

I tried creating key ring as below

let key = CryptoNewKeyFromArmored(privatePGPKey, err)
let kRing = CryptoKeyRing(key)

and tried to add another key as

let key2 = CryptoNewKeyFromArmored(privatePGPKey2, err)
try! kring?.add(key2)

but when I check with countEntities() its coming nil.

Can anyone suggest what I am doing wrong or way to overcome this issue

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.