GopenPGP V3

repository·main·Indexed 23 days ago

https://github.com/protonmail/gopenpgp

A high-level OpenPGP library for Go built on a fork of the golang crypto library. It provides simplified APIs for encryption, decryption, key generation, and digital signatures, including support for symmetric password-based encryption, asymmetric PGP keys, and streaming APIs for large messages. GopenPGP V3 supports modern security standards via the RFC 9580 profile, utilizing Argon2 and AEAD (AES-256, OCB mode).

Tokens
2.7K
Snippets
9
Records
10
Agent score
29%

What's inside gopenpgp

  1. Install GopenPGP V3 via Go Modules

    main

    To use GopenPGP V3 in your project, use Go Modules by running the go get command in your project folder. Note that GopenPGP V3 is not backward compatible with V2; for V2 support, you must use the v2 branch of the repository.

    go get github.com/ProtonMail/gopenpgp/v3
  2. Setup GopenPGP for Go Mobile

    main

    To use GopenPGP with Android or iOS, use gomobile.

    1. Install/verify gomobile:
      gomobile version
      # or if missing:
      go get -u golang.org/x/mobile/cmd/gomobile
    2. Initialize gomobile:
      export PATH="$PATH:$GOPATH/bin"
      gomobile init
    3. Ensure Android/iOS frameworks are installed and environment variables are set.
    4. Build the application using the provided script:
      sh build.sh
    gomobile init
    sh build.sh
  3. Initialize GopenPGP V3 in Go

    main

    After installing the package, import the github.com/ProtonMail/gopenpgp/v3/crypto package to access the core PGP functionality. You can initialize the PGP interface using crypto.PGP().

    package main
    
    import (
    	"fmt"
    	"github.com/ProtonMail/gopenpgp/v3/crypto"
    )
    
    func main() {
    	pgp := crypto.PGP()
    }
  4. Split encrypted output into key and data packets

    main

    You can separate the encrypted output into key packets and data packets.

    Non-streaming: Use pgpMessage.BinaryKeyPacket() and pgpMessage.BinaryDataPacket() on the resulting pgpMessage.

    Streaming: Use crypto.NewPGPSplitWriterKeyAndData(&keyBuffer, &dataBuffer) to create a split writer, then pass it to encHandle.EncryptingWriter(splitWriter, crypto.Bytes). This writes key packets to the first buffer and data packets to the second.

    // Streaming split
    var keyPackets bytes.Buffer
    var dataPackets bytes.Buffer
    splitWriter := crypto.NewPGPSplitWriterKeyAndData(&keyPackets, &dataPackets)
    ptWriter, _ := encHandle.EncryptingWriter(splitWriter, crypto.Bytes)
  5. Generate PGP keys

    main

    Keys are generated using the KeyGeneration() method on a PGP handle. You must first call .AddUserId(name, email) and .New().

    • RSA Keys: Use .GenerateKey() for default RSA or .GenerateKeyWithSecurity(constants.HighSecurity) for higher bit counts. Note that RFC 9580 discourages RSA.
    • ECC Keys: Use .GenerateKey() for Curve25519 v4 or Curve25519 v6 (with RFC 9580). Use .GenerateKeyWithSecurity(constants.HighSecurity) for Curve448 v6 (RFC 9580).
  6. Lock and unlock secret keys

    main

    You can encrypt (lock) and decrypt (unlock) a private key using a password via the LockKey and Unlock methods on the PGP handle.

    password := []byte("password")
    pgp := crypto.PGP()
    
    // Encrypt key with password
    lockedKey, err := pgp.LockKey(aliceKeyPriv, password)
    
    // Decrypt key with password
    unlockedKey, err := lockedKey.Unlock(password)
  7. Sign messages (Detached, Inline, and Cleartext)

    main

    GopenPGP supports three types of signatures:

    1. Detached Signatures: The signature is separate from the message. Use .Sign().SigningKey(key).Detached().New() and .Sign(message, crypto.Armor). Verify using .VerifyDetached().
    2. Inline Signatures: The signature is embedded within the armored message. Use .Sign().SigningKey(key).New() and .Sign(message, crypto.Armor). Verify using .VerifyInline().
    3. Cleartext Signed Messages: The message remains readable but includes a signature block. Use .Sign().SigningKey(key).New() and .SignCleartext(message). Verify using .VerifyCleartext().
    // Detached Signature Example
    signer, err := pgp.Sign().SigningKey(aliceKeyPriv).Detached().New()
    signature, err := signer.Sign(signingMessage, crypto.Armor)
    
    verifier, err := pgp.Verify().VerificationKey(aliceKeyPub).New()
    verifyResult, err := verifier.VerifyDetached(signingMessage, signature, crypto.Armor)
  8. Encrypt and decrypt with PGP keys

    main

    To use asymmetric encryption, load your public and private keys using crypto.NewKeyFromArmored(pubkey) and crypto.NewPrivateKeyFromArmored(privkey, passphrase).

    Basic Encryption/Decryption

    Use pgp.Encryption().Recipient(publicKey).New() to encrypt for a specific recipient. Use pgp.Decryption().DecryptionKey(privateKey).New() to decrypt. Always call decHandle.ClearPrivateParams() after use to clear sensitive data from memory.

    Signing and Encryption

    To sign a message while encrypting, chain .SigningKey(privateKey) to the encryption handle. During decryption, use .VerificationKey(publicKey) on the decryption handle and check decrypted.SignatureError() to verify the signature.

    Multiple Recipients and Hidden Recipients

    • Multiple Recipients: Use crypto.NewKeyRing(key) to create a keyring and recipients.AddKey(key) to add more, then pass the keyring to .Recipients(recipients).
    • Hidden Recipients: Use .HiddenRecipient(key) to prevent the recipient's fingerprint from being visible in the key packet. When decrypting, you must call .DisableIntendedRecipients() on the decryption handle to avoid signature errors caused by the missing intended recipient list.
    // Encrypt and sign plaintext message from alice to bob
    encHandle, err := pgp.Encryption().
      Recipient(bobKeyPub).
      SigningKey(aliceKeyPriv).
      New()
    pgpMessage, err := encHandle.Encrypt([]byte("my message"))
    armored, err := pgpMessage.ArmorBytes()
    
    // Decrypt armored encrypted message using the private key and obtain the plaintext
    decHandle, err := pgp.Decryption().
      DecryptionKey(bobKeyPriv).
      VerificationKey(aliceKeyPub).
      New()
    decrypted, err := decHandle.Decrypt(armored, crypto.Armor)
    if sigErr := decrypted.SignatureError(); sigErr != nil {
      // Signature verification failed
    }
    myMessage := decrypted.Bytes()
    
    encHandle.ClearPrivateParams()
    decHandle.ClearPrivateParams()
  9. Encrypt and decrypt with a password

    main

    You can perform symmetric encryption and decryption using a passphrase without requiring PGP keys. Use pgp.Encryption().Password(password).New() to create an encryption handle and pgp.Decryption().Password(password).New() for decryption.

    Note that using crypto.PGPWithProfile(profile.RFC9580()) is recommended for modern security standards, as it uses Argon2 for key protection and AEAD (AES-256, OCB mode) for encryption.

    import "github.com/ProtonMail/gopenpgp/v3/crypto"
    import "github.com/ProtonMail/gopenpgp/v3/profile"
    
    password := []byte("hunter2")
    
    // Using RFC 9580 profile
    pgp := crypto.PGPWithProfile(profile.RFC9580())
    
    // Encrypt data with a password
    encHandle, err := pgp.Encryption().Password(password).New()
    pgpMessage, err := encHandle.Encrypt([]byte("my message"))
    armored, err := pgpMessage.ArmorBytes()
    
    // Decrypt data with a password
    decHandle, err := pgp.Decryption().Password(password).New()
    decrypted, err := decHandle.Decrypt(armored, crypto.Armor)
    myMessage := decrypted.Bytes()
  10. Encrypt and decrypt large messages with the streaming API

    main

    For large datasets, use the streaming API to avoid loading entire messages into memory.

    1. Encryption: Use encHandle.EncryptingWriter(writer, crypto.Armor) to get a writer that encrypts data as it is written. Use io.Copy to stream from a source reader to the encryption writer.
    2. Decryption: Use decHandle.DecryptingReader(reader, crypto.Armor) to get a reader. Use ptReader.ReadAllAndVerifySignature() to read the content and verify the signature in one step.
    // Encrypt plain text stream and write the output to a file
    encHandle, err := pgp.Encryption().
      Recipient(bobKeyPub).
      SigningKey(aliceKeyPriv).
      New()
    messageReader, err := os.Open("msg.txt")
    ciphertextWriter, err := os.Create("out.pgp")
    
    ptWriter, err := encHandle.EncryptingWriter(ciphertextWriter, crypto.Armor)
    _, err = io.Copy(ptWriter, messageReader)
    err = ptWriter.Close()
    
    // Decrypt stream and read the result to memory
    decHandle, err := pgp.Decryption().
      DecryptionKey(bobKeyPriv).
      VerificationKey(aliceKeyPub).
      New()
    ptReader, err := decHandle.DecryptingReader(ctFileRead, crypto.Armor)
    decResult, err := ptReader.ReadAllAndVerifySignature()
    if sigErr := decResult.SignatureError(); sigErr != nil {
      // Handle sigErr
    }
    // Access decrypted message with decResult.Bytes()