Upgrade gopengpg

This commit is contained in:
Mustafa Gezen 2024-10-16 13:40:38 +02:00
parent 3d804027b2
commit ba81d5d550
33 changed files with 1147 additions and 279 deletions

View File

@ -57,8 +57,8 @@ http_archive(
],
)
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("//:repositories.bzl", "go_repositories")
go_rules_dependencies()

4
go.mod
View File

@ -8,7 +8,7 @@ require (
bazel.build/protobuf v0.0.0-00010101000000-000000000000
cirello.io/dynamolock v1.4.0
github.com/ProtonMail/go-crypto v1.0.0
github.com/ProtonMail/gopenpgp/v2 v2.4.7
github.com/ProtonMail/gopenpgp/v2 v2.7.5
github.com/authzed/authzed-go v0.3.0
github.com/authzed/grpcutil v0.0.0-20240123194739-2ea1e3d2d98b
github.com/aws/aws-sdk-go v1.54.19
@ -65,7 +65,7 @@ require (
cloud.google.com/go/storage v1.43.0 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/beorn7/perks v1.0.1 // indirect

15
go.sum
View File

@ -57,13 +57,13 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
github.com/ProtonMail/gopenpgp/v2 v2.4.7 h1:V3xeelvXgJiZXZuPtSSE+uYbtPw4RmbmyPqXDAESPhg=
github.com/ProtonMail/gopenpgp/v2 v2.4.7/go.mod h1:ZW1KxHNG6q5LMgFKf9Ap/d2eVYeyGf5+fAUEAjJWtmo=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA=
github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
@ -552,7 +552,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
@ -562,7 +561,6 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@ -587,12 +585,10 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -775,7 +771,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

View File

@ -171,9 +171,9 @@ def go_repositories():
)
go_repository(
name = "com_github_cloudflare_circl",
importpath = "github.com/cloudflare/circl",
patch_args = ["-p1"],
patches = ["//patches:circl.patch"],
importpath = "github.com/cloudflare/circl",
sum = "h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=",
version = "v1.3.9",
)
@ -1078,14 +1078,14 @@ def go_repositories():
go_repository(
name = "com_github_protonmail_go_mime",
importpath = "github.com/ProtonMail/go-mime",
sum = "h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=",
version = "v0.0.0-20220302105931-303f85f7fe0f",
sum = "h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=",
version = "v0.0.0-20230322103455-7d82a3887f2f",
)
go_repository(
name = "com_github_protonmail_gopenpgp_v2",
importpath = "github.com/ProtonMail/gopenpgp/v2",
sum = "h1:V3xeelvXgJiZXZuPtSSE+uYbtPw4RmbmyPqXDAESPhg=",
version = "v2.4.7",
sum = "h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA=",
version = "v2.7.5",
)
go_repository(
name = "com_github_rivo_uniseg",
@ -2285,8 +2285,8 @@ def go_repositories():
go_repository(
name = "org_golang_x_mobile",
importpath = "golang.org/x/mobile",
sum = "h1:OVJ6QQUBAesB8CZijKDSsXX7xYVtUhrkY0gwMfbi4p4=",
version = "v0.0.0-20200801112145-973feb4309de",
sum = "h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=",
version = "v0.0.0-20190719004257-d2bd2a29d028",
)
go_repository(
name = "org_golang_x_mod",

View File

@ -11,7 +11,6 @@ go_library(
importpath = "github.com/ProtonMail/go-mime",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/sirupsen/logrus",
"//vendor/golang.org/x/text/encoding",
"//vendor/golang.org/x/text/encoding/htmlindex",
"//vendor/golang.org/x/text/transform",

View File

@ -1,6 +1,25 @@
# Go Mime Wrapper Library
Provides a parser for MIME messages
## Download/Install
Run `go get -u github.com/ProtonMail/go-mime`, or manually `git clone` the
repository into `$GOPATH/src/github.com/ProtonMail/go-mime`.
## Usage
The library can be used to extract the body and attachments from a MIME message
Example:
```go
printAccepter := gomime.NewMIMEPrinter()
bodyCollector := gomime.NewBodyCollector(printAccepter)
attachmentsCollector := gomime.NewAttachmentsCollector(bodyCollector)
mimeVisitor := gomime.NewMimeVisitor(attachmentsCollector)
err := gomime.VisitAll(bytes.NewReader(mmBodyData), h, mimeVisitor)
attachments := attachmentsCollector.GetAttachments(),
attachmentsHeaders := attachmentsCollector.GetAttHeaders()
bodyContent, bodyMimeType := bodyCollector.GetBody()
```

View File

@ -1,7 +1,6 @@
package gomime
import (
"bytes"
"fmt"
"io"
"mime"
@ -11,9 +10,9 @@ import (
"unicode/utf8"
"encoding/base64"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/htmlindex"
"golang.org/x/text/transform"
)
var wordDec = &mime.WordDecoder{
@ -189,21 +188,13 @@ func DecodeCharset(original []byte, mediaType string, contentTypeParams map[stri
}
err = fmt.Errorf("non-utf8 content without charset specification")
}
if err != nil {
return original, err
}
utf8 := make([]byte, len(original))
nDst, nSrc, err := decoder.Transform(utf8, original, false)
for err == transform.ErrShortDst {
utf8 = make([]byte, (nDst/nSrc+1)*len(original))
nDst, nSrc, err = decoder.Transform(utf8, original, false)
}
utf8, err := decoder.Bytes(original)
if err != nil {
return original, err
}
utf8 = bytes.Trim(utf8, "\x00")
return utf8, nil
}

View File

@ -5,6 +5,7 @@ import (
"bytes"
"io"
"io/ioutil"
"log"
"mime"
"mime/multipart"
"net/http"
@ -12,8 +13,6 @@ import (
"net/textproto"
"regexp"
"strings"
log "github.com/sirupsen/logrus"
)
// VisitAcceptor decidest what to do with part which is processed
@ -181,7 +180,7 @@ func GetMultipartParts(r io.Reader, params map[string]string) (parts []io.Reader
headers = []textproto.MIMEHeader{}
var p *multipart.Part
for {
p, err = mr.NextPart()
p, err = mr.NextRawPart()
if err == io.EOF {
err = nil
break
@ -275,7 +274,7 @@ func checkHeaders(headers []textproto.MIMEHeader) bool {
func decodePart(partReader io.Reader, header textproto.MIMEHeader) (decodedPart io.Reader) {
decodedPart = DecodeContentEncoding(partReader, header.Get("Content-Transfer-Encoding"))
if decodedPart == nil {
log.Warnf("Unsupported Content-Transfer-Encoding '%v'", header.Get("Content-Transfer-Encoding"))
log.Printf("Unsupported Content-Transfer-Encoding '%v'", header.Get("Content-Transfer-Encoding"))
decodedPart = partReader
}
return
@ -376,7 +375,7 @@ func (ptc *PlainTextCollector) Accept(partReader io.Reader, header textproto.MIM
if buffer, err := ioutil.ReadAll(decodedPart); err == nil {
buffer, err = DecodeCharset(buffer, mediaType, params)
if err != nil {
log.Warnln("Decode charset error:", err)
log.Println("Decode charset error:", err)
err = nil // Don't fail parsing on decoding errors, use original
}
ptc.plainTextContents.Write(buffer)
@ -431,7 +430,7 @@ func (bc *BodyCollector) Accept(partReader io.Reader, header textproto.MIMEHeade
if buffer, err := ioutil.ReadAll(decodedPart); err == nil {
buffer, err = DecodeCharset(buffer, mediaType, params)
if err != nil {
log.Warnln("Decode charset error:", err)
log.Println("Decode charset error:", err)
err = nil // Don't fail parsing on decoding errors, use original
}
if mediaType == "text/html" {
@ -500,7 +499,7 @@ func (ac *AttachmentsCollector) Accept(partReader io.Reader, header textproto.MI
if buffer, err := ioutil.ReadAll(decodedPart); err == nil {
buffer, err = DecodeCharset(buffer, mediaType, params)
if err != nil {
log.Warnln("Decode charset error:", err)
log.Println("Decode charset error:", err)
err = nil // Don't fail parsing on decoding errors, use original
}
headerBuf := new(bytes.Buffer)

View File

@ -5,6 +5,7 @@ go_library(
srcs = [
"armor.go",
"cipher.go",
"context.go",
"version.go",
],
importmap = "peridot.resf.org/vendor/github.com/ProtonMail/gopenpgp/v2/constants",

View File

@ -3,7 +3,7 @@ package constants
// Constants for armored data.
const (
ArmorHeaderVersion = "GopenPGP 2.4.7"
ArmorHeaderVersion = "GopenPGP 2.7.5"
ArmorHeaderComment = "https://gopenpgp.org"
PGPMessageHeader = "PGP MESSAGE"
PGPSignatureHeader = "PGP SIGNATURE"

View File

@ -15,6 +15,7 @@ const (
SIGNATURE_NOT_SIGNED int = 1
SIGNATURE_NO_VERIFIER int = 2
SIGNATURE_FAILED int = 3
SIGNATURE_BAD_CONTEXT int = 4
)
const DefaultCompression = 2 // ZLIB

View File

@ -0,0 +1,3 @@
package constants
const SignatureContextName = "context@proton.ch"

View File

@ -1,3 +1,3 @@
package constants
const Version = "2.4.7"
const Version = "2.7.5"

View File

@ -16,6 +16,7 @@ go_library(
"message_getters.go",
"mime.go",
"password.go",
"sanitize_string.go",
"sessionkey.go",
"sessionkey_streaming.go",
"signature.go",
@ -34,6 +35,8 @@ go_library(
"@com_github_protonmail_go_crypto//openpgp",
"@com_github_protonmail_go_crypto//openpgp/clearsign",
"@com_github_protonmail_go_crypto//openpgp/ecdh",
"@com_github_protonmail_go_crypto//openpgp/ecdsa",
"@com_github_protonmail_go_crypto//openpgp/eddsa",
"@com_github_protonmail_go_crypto//openpgp/elgamal",
"@com_github_protonmail_go_crypto//openpgp/errors",
"@com_github_protonmail_go_crypto//openpgp/packet",

View File

@ -75,7 +75,7 @@ func (keyRing *KeyRing) NewManualAttachmentProcessor(
estimatedSize int, filename string, dataBuffer []byte,
) (*ManualAttachmentProcessor, error) {
if len(dataBuffer) == 0 {
return nil, errors.New("gopenpgp: can't give a nil or empty buffer to process the attachement")
return nil, errors.New("gopenpgp: can't give a nil or empty buffer to process the attachment")
}
// forces the gc to be called often
@ -148,7 +148,7 @@ func (keyRing *KeyRing) NewManualAttachmentProcessor(
return attachmentProc, nil
}
// readAll works a bit like io.ReadAll
// readAll works a bit like ioutil.ReadAll
// but we can choose the buffer to write to
// and we don't grow the slice in case of overflow.
func readAll(buffer []byte, reader io.Reader) (int, error) {

View File

@ -2,13 +2,13 @@ package crypto
import (
"crypto/dsa" //nolint:staticcheck
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"errors"
"math/big"
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
)
@ -57,7 +57,7 @@ func clearPrivateKey(privateKey interface{}) error {
return clearElGamalPrivateKey(priv)
case *ecdsa.PrivateKey:
return clearECDSAPrivateKey(priv)
case *ed25519.PrivateKey:
case *eddsa.PrivateKey:
return clearEdDSAPrivateKey(priv)
case *ecdh.PrivateKey:
return clearECDHPrivateKey(priv)
@ -115,8 +115,8 @@ func clearECDSAPrivateKey(priv *ecdsa.PrivateKey) error {
return nil
}
func clearEdDSAPrivateKey(priv *ed25519.PrivateKey) error {
clearMem(*priv)
func clearEdDSAPrivateKey(priv *eddsa.PrivateKey) error {
clearMem(priv.D)
return nil
}

View File

@ -2,7 +2,6 @@ package crypto
import (
"bytes"
"crypto"
"io"
"io/ioutil"
"time"
@ -18,13 +17,16 @@ import (
// * message : The plaintext input as a PlainMessage.
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
func (keyRing *KeyRing) Encrypt(message *PlainMessage, privateKey *KeyRing) (*PGPMessage, error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
encrypted, err := asymmetricEncrypt(message, keyRing, privateKey, config)
if err != nil {
return nil, err
return asymmetricEncrypt(message, keyRing, privateKey, false, nil)
}
return NewPGPMessage(encrypted), nil
// EncryptWithContext encrypts a PlainMessage, outputs a PGPMessage.
// If an unlocked private key is also provided it will also sign the message.
// * message : The plaintext input as a PlainMessage.
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
// * signingContext : (optional) the context for the signature.
func (keyRing *KeyRing) EncryptWithContext(message *PlainMessage, privateKey *KeyRing, signingContext *SigningContext) (*PGPMessage, error) {
return asymmetricEncrypt(message, keyRing, privateKey, false, signingContext)
}
// EncryptWithCompression encrypts with compression support a PlainMessage to PGPMessage using public/private keys.
@ -32,60 +34,92 @@ func (keyRing *KeyRing) Encrypt(message *PlainMessage, privateKey *KeyRing) (*PG
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
// * output : The encrypted data as PGPMessage.
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},
return asymmetricEncrypt(message, keyRing, privateKey, true, nil)
}
encrypted, err := asymmetricEncrypt(message, keyRing, privateKey, config)
if err != nil {
return nil, err
}
return NewPGPMessage(encrypted), nil
// EncryptWithContextAndCompression encrypts with compression support a PlainMessage to PGPMessage using public/private keys.
// * message : The plain data as a PlainMessage.
// * privateKey : (optional) an unlocked private keyring to include signature in the message.
// * signingContext : (optional) the context for the signature.
// * output : The encrypted data as PGPMessage.
func (keyRing *KeyRing) EncryptWithContextAndCompression(message *PlainMessage, privateKey *KeyRing, signingContext *SigningContext) (*PGPMessage, error) {
return asymmetricEncrypt(message, keyRing, privateKey, true, signingContext)
}
// Decrypt decrypts encrypted string using pgp keys, returning a PlainMessage
// * message : The encrypted input as a PGPMessage
// * verifyKey : Public key for signature verification (optional)
// * verifyTime : Time at verification (necessary only if verifyKey is not nil)
// * verificationContext : (optional) the context for the signature verification.
//
// When verifyKey is not provided, then verifyTime should be zero, and
// signature verification will be ignored.
func (keyRing *KeyRing) Decrypt(
message *PGPMessage, verifyKey *KeyRing, verifyTime int64,
) (*PlainMessage, error) {
return asymmetricDecrypt(message.NewReader(), keyRing, verifyKey, verifyTime)
return asymmetricDecrypt(message.NewReader(), keyRing, verifyKey, verifyTime, nil)
}
// DecryptWithContext decrypts encrypted string using pgp keys, returning a PlainMessage
// * message : The encrypted input as a PGPMessage
// * verifyKey : Public key for signature verification (optional)
// * verifyTime : Time at verification (necessary only if verifyKey is not nil)
// * verificationContext : (optional) the context for the signature verification.
//
// When verifyKey is not provided, then verifyTime should be zero, and
// signature verification will be ignored.
func (keyRing *KeyRing) DecryptWithContext(
message *PGPMessage,
verifyKey *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (*PlainMessage, error) {
return asymmetricDecrypt(message.NewReader(), keyRing, verifyKey, verifyTime, verificationContext)
}
// SignDetached generates and returns a PGPSignature for a given PlainMessage.
func (keyRing *KeyRing) SignDetached(message *PlainMessage) (*PGPSignature, error) {
signEntity, err := keyRing.getSigningEntity()
if err != nil {
return nil, err
return keyRing.SignDetachedWithContext(message, nil)
}
config := &packet.Config{DefaultHash: crypto.SHA512, Time: getTimeGenerator()}
var outBuf bytes.Buffer
// sign bin
if err := openpgp.DetachSign(&outBuf, signEntity, message.NewReader(), config); err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in signing")
}
return NewPGPSignature(outBuf.Bytes()), nil
// SignDetachedWithContext generates and returns a PGPSignature for a given PlainMessage.
// If a context is provided, it is added to the signature as notation data
// with the name set in `constants.SignatureContextName`.
func (keyRing *KeyRing) SignDetachedWithContext(message *PlainMessage, context *SigningContext) (*PGPSignature, error) {
return signMessageDetached(
keyRing,
message.NewReader(),
message.IsBinary(),
context,
)
}
// VerifyDetached verifies a PlainMessage with a detached PGPSignature
// and returns a SignatureVerificationError if fails.
func (keyRing *KeyRing) VerifyDetached(message *PlainMessage, signature *PGPSignature, verifyTime int64) error {
return verifySignature(
_, err := verifySignature(
keyRing.entities,
message.NewReader(),
signature.GetBinary(),
verifyTime,
nil,
)
return err
}
// VerifyDetachedWithContext verifies a PlainMessage with a detached PGPSignature
// and returns a SignatureVerificationError if fails.
// If a context is provided, it verifies that the signature is valid in the given context, using
// the signature notation with name the name set in `constants.SignatureContextName`.
func (keyRing *KeyRing) VerifyDetachedWithContext(message *PlainMessage, signature *PGPSignature, verifyTime int64, verificationContext *VerificationContext) error {
_, err := verifySignature(
keyRing.entities,
message.NewReader(),
signature.GetBinary(),
verifyTime,
verificationContext,
)
return err
}
// SignDetachedEncrypted generates and returns a PGPMessage
@ -122,38 +156,41 @@ func (keyRing *KeyRing) VerifyDetachedEncrypted(message *PlainMessage, encrypted
// returns the creation time of the signature if it succeeds
// and returns a SignatureVerificationError if fails.
func (keyRing *KeyRing) GetVerifiedSignatureTimestamp(message *PlainMessage, signature *PGPSignature, verifyTime int64) (int64, error) {
packets := packet.NewReader(bytes.NewReader(signature.Data))
var err error
var p packet.Packet
for {
p, err = packets.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
continue
}
sigPacket, ok := p.(*packet.Signature)
if !ok {
continue
}
var outBuf bytes.Buffer
err = sigPacket.Serialize(&outBuf)
if err != nil {
continue
}
err = verifySignature(
sigPacket, err := verifySignature(
keyRing.entities,
message.NewReader(),
outBuf.Bytes(),
signature.GetBinary(),
verifyTime,
nil,
)
if err != nil {
continue
return 0, err
}
return sigPacket.CreationTime.Unix(), nil
}
return 0, errors.Wrap(err, "gopenpgp: can't verify any signature packets")
// GetVerifiedSignatureTimestampWithContext verifies a PlainMessage with a detached PGPSignature
// returns the creation time of the signature if it succeeds
// and returns a SignatureVerificationError if fails.
// If a context is provided, it verifies that the signature is valid in the given context, using
// the signature notation with name the name set in `constants.SignatureContextName`.
func (keyRing *KeyRing) GetVerifiedSignatureTimestampWithContext(
message *PlainMessage,
signature *PGPSignature,
verifyTime int64,
verificationContext *VerificationContext,
) (int64, error) {
sigPacket, err := verifySignature(
keyRing.entities,
message.NewReader(),
signature.GetBinary(),
verifyTime,
verificationContext,
)
if err != nil {
return 0, err
}
return sigPacket.CreationTime.Unix(), nil
}
// ------ INTERNAL FUNCTIONS -------
@ -162,8 +199,9 @@ func (keyRing *KeyRing) GetVerifiedSignatureTimestamp(message *PlainMessage, sig
func asymmetricEncrypt(
plainMessage *PlainMessage,
publicKey, privateKey *KeyRing,
config *packet.Config,
) ([]byte, error) {
compress bool,
signingContext *SigningContext,
) (*PGPMessage, error) {
var outBuf bytes.Buffer
var encryptWriter io.WriteCloser
var err error
@ -174,7 +212,7 @@ func asymmetricEncrypt(
ModTime: plainMessage.getFormattedTime(),
}
encryptWriter, err = asymmetricEncryptStream(hints, &outBuf, &outBuf, publicKey, privateKey, config)
encryptWriter, err = asymmetricEncryptStream(hints, &outBuf, &outBuf, publicKey, privateKey, compress, signingContext)
if err != nil {
return nil, err
}
@ -189,7 +227,7 @@ func asymmetricEncrypt(
return nil, errors.Wrap(err, "gopenpgp: error in closing message")
}
return outBuf.Bytes(), nil
return &PGPMessage{outBuf.Bytes()}, nil
}
// Core for encryption+signature (all) functions.
@ -198,10 +236,24 @@ func asymmetricEncryptStream(
keyPacketWriter io.Writer,
dataPacketWriter io.Writer,
publicKey, privateKey *KeyRing,
config *packet.Config,
compress bool,
signingContext *SigningContext,
) (encryptWriter io.WriteCloser, err error) {
var signEntity *openpgp.Entity
config := &packet.Config{
DefaultCipher: packet.CipherAES256,
Time: getTimeGenerator(),
}
if compress {
config.DefaultCompressionAlgo = constants.DefaultCompression
config.CompressionConfig = &packet.CompressionConfig{Level: constants.DefaultCompressionLevel}
}
if signingContext != nil {
config.SignatureNotations = append(config.SignatureNotations, signingContext.getNotation())
}
var signEntity *openpgp.Entity
if privateKey != nil && len(privateKey.entities) > 0 {
var err error
signEntity, err = privateKey.getSigningEntity()
@ -223,13 +275,18 @@ func asymmetricEncryptStream(
// Core for decryption+verification (non streaming) functions.
func asymmetricDecrypt(
encryptedIO io.Reader, privateKey *KeyRing, verifyKey *KeyRing, verifyTime int64,
encryptedIO io.Reader,
privateKey *KeyRing,
verifyKey *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (message *PlainMessage, err error) {
messageDetails, err := asymmetricDecryptStream(
encryptedIO,
privateKey,
verifyKey,
verifyTime,
verificationContext,
)
if err != nil {
return nil, err
@ -242,7 +299,7 @@ func asymmetricDecrypt(
if verifyKey != nil {
processSignatureExpiration(messageDetails, verifyTime)
err = verifyDetailsSignature(messageDetails, verifyKey)
err = verifyDetailsSignature(messageDetails, verifyKey, verificationContext)
}
return &PlainMessage{
@ -259,6 +316,7 @@ func asymmetricDecryptStream(
privateKey *KeyRing,
verifyKey *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (messageDetails *openpgp.MessageDetails, err error) {
privKeyEntries := privateKey.entities
var additionalEntries openpgp.EntityList
@ -285,6 +343,10 @@ func asymmetricDecryptStream(
},
}
if verificationContext != nil {
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
}
messageDetails, err = openpgp.ReadMessage(encryptedIO, privKeyEntries, nil, config)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message")

View File

@ -55,7 +55,11 @@ Loop:
}
if !hasPacket {
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: couldn't find a session key packet")
} else {
return nil, errors.New("gopenpgp: couldn't find a session key packet")
}
}
if decryptErr != nil {

View File

@ -2,12 +2,10 @@ package crypto
import (
"bytes"
"crypto"
"io"
"time"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/pkg/errors"
)
@ -42,8 +40,89 @@ func (keyRing *KeyRing) EncryptStream(
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
return encryptStream(
keyRing,
pgpMessageWriter,
pgpMessageWriter,
plainMessageMetadata,
signKeyRing,
false,
nil,
)
}
// EncryptStreamWithContext is used to encrypt data as a Writer.
// It takes a writer for the encrypted data and returns a WriteCloser for the plaintext data
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) a context for the embedded signature.
func (keyRing *KeyRing) EncryptStreamWithContext(
pgpMessageWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
signingContext *SigningContext,
) (plainMessageWriter WriteCloser, err error) {
return encryptStream(
keyRing,
pgpMessageWriter,
pgpMessageWriter,
plainMessageMetadata,
signKeyRing,
false,
signingContext,
)
}
// EncryptStreamWithCompression is used to encrypt data as a Writer.
// The plaintext data is compressed before being encrypted.
// It takes a writer for the encrypted data and returns a WriteCloser for the plaintext data
// If signKeyRing is not nil, it is used to do an embedded signature.
func (keyRing *KeyRing) EncryptStreamWithCompression(
pgpMessageWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
return encryptStream(
keyRing,
pgpMessageWriter,
pgpMessageWriter,
plainMessageMetadata,
signKeyRing,
true,
nil,
)
}
// EncryptStreamWithContextAndCompression is used to encrypt data as a Writer.
// The plaintext data is compressed before being encrypted.
// It takes a writer for the encrypted data and returns a WriteCloser for the plaintext data
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) a context for the embedded signature.
func (keyRing *KeyRing) EncryptStreamWithContextAndCompression(
pgpMessageWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
signingContext *SigningContext,
) (plainMessageWriter WriteCloser, err error) {
return encryptStream(
keyRing,
pgpMessageWriter,
pgpMessageWriter,
plainMessageMetadata,
signKeyRing,
true,
signingContext,
)
}
func encryptStream(
encryptionKeyRing *KeyRing,
keyPacketWriter Writer,
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
compress bool,
signingContext *SigningContext,
) (plainMessageWriter WriteCloser, err error) {
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
@ -59,7 +138,7 @@ func (keyRing *KeyRing) EncryptStream(
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
}
plainMessageWriter, err = asymmetricEncryptStream(hints, pgpMessageWriter, pgpMessageWriter, keyRing, signKeyRing, config)
plainMessageWriter, err = asymmetricEncryptStream(hints, keyPacketWriter, dataPacketWriter, encryptionKeyRing, signKeyRing, compress, signingContext)
if err != nil {
return nil, err
}
@ -107,28 +186,102 @@ func (keyRing *KeyRing) EncryptSplitStream(
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (*EncryptSplitResult, error) {
config := &packet.Config{DefaultCipher: packet.CipherAES256, Time: getTimeGenerator()}
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
return encryptSplitStream(
keyRing,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
false,
nil,
)
}
hints := &openpgp.FileHints{
FileName: plainMessageMetadata.Filename,
IsBinary: plainMessageMetadata.IsBinary,
ModTime: time.Unix(plainMessageMetadata.ModTime, 0),
// EncryptSplitStreamWithContext is used to encrypt data as a stream.
// It takes a writer for the Symmetrically Encrypted Data Packet
// (https://datatracker.ietf.org/doc/html/rfc4880#section-5.7)
// and returns a writer for the plaintext data and the key packet.
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) a context for the embedded signature.
func (keyRing *KeyRing) EncryptSplitStreamWithContext(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
signingContext *SigningContext,
) (*EncryptSplitResult, error) {
return encryptSplitStream(
keyRing,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
false,
signingContext,
)
}
// EncryptSplitStreamWithCompression is used to encrypt data as a stream.
// It takes a writer for the Symmetrically Encrypted Data Packet
// (https://datatracker.ietf.org/doc/html/rfc4880#section-5.7)
// and returns a writer for the plaintext data and the key packet.
// If signKeyRing is not nil, it is used to do an embedded signature.
func (keyRing *KeyRing) EncryptSplitStreamWithCompression(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (*EncryptSplitResult, error) {
return encryptSplitStream(
keyRing,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
true,
nil,
)
}
// EncryptSplitStreamWithContextAndCompression is used to encrypt data as a stream.
// It takes a writer for the Symmetrically Encrypted Data Packet
// (https://datatracker.ietf.org/doc/html/rfc4880#section-5.7)
// and returns a writer for the plaintext data and the key packet.
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) a context for the embedded signature.
func (keyRing *KeyRing) EncryptSplitStreamWithContextAndCompression(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
signingContext *SigningContext,
) (*EncryptSplitResult, error) {
return encryptSplitStream(
keyRing,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
true,
signingContext,
)
}
func encryptSplitStream(
encryptionKeyRing *KeyRing,
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
compress bool,
signingContext *SigningContext,
) (*EncryptSplitResult, error) {
var keyPacketBuf bytes.Buffer
plainMessageWriter, err := asymmetricEncryptStream(hints, &keyPacketBuf, dataPacketWriter, keyRing, signKeyRing, config)
plainMessageWriter, err := encryptStream(
encryptionKeyRing,
&keyPacketBuf,
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
compress,
signingContext,
)
if err != nil {
return nil, err
}
return &EncryptSplitResult{
keyPacketBuf: &keyPacketBuf,
plainMessageWriter: plainMessageWriter,
@ -142,6 +295,7 @@ type PlainMessageReader struct {
verifyKeyRing *KeyRing
verifyTime int64
readAll bool
verificationContext *VerificationContext
}
// GetMetadata returns the metadata of the decrypted message.
@ -173,7 +327,7 @@ func (msg *PlainMessageReader) VerifySignature() (err error) {
}
if msg.verifyKeyRing != nil {
processSignatureExpiration(msg.details, msg.verifyTime)
err = verifyDetailsSignature(msg.details, msg.verifyKeyRing)
err = verifyDetailsSignature(msg.details, msg.verifyKeyRing, msg.verificationContext)
} else {
err = errors.New("gopenpgp: no verify keyring was provided before decryption")
}
@ -190,11 +344,49 @@ func (keyRing *KeyRing) DecryptStream(
verifyKeyRing *KeyRing,
verifyTime int64,
) (plainMessage *PlainMessageReader, err error) {
messageDetails, err := asymmetricDecryptStream(
message,
return decryptStream(
keyRing,
message,
verifyKeyRing,
verifyTime,
nil,
)
}
// DecryptStreamWithContext is used to decrypt a pgp message as a Reader.
// It takes a reader for the message data
// and returns a PlainMessageReader for the plaintext data.
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
// verify the embedded signature with the given key ring and verification time.
// * verificationContext (optional): context for the signature verification.
func (keyRing *KeyRing) DecryptStreamWithContext(
message Reader,
verifyKeyRing *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (plainMessage *PlainMessageReader, err error) {
return decryptStream(
keyRing,
message,
verifyKeyRing,
verifyTime,
verificationContext,
)
}
func decryptStream(
decryptionKeyRing *KeyRing,
message Reader,
verifyKeyRing *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (plainMessage *PlainMessageReader, err error) {
messageDetails, err := asymmetricDecryptStream(
message,
decryptionKeyRing,
verifyKeyRing,
verifyTime,
verificationContext,
)
if err != nil {
return nil, err
@ -205,6 +397,7 @@ func (keyRing *KeyRing) DecryptStream(
verifyKeyRing,
verifyTime,
false,
verificationContext,
}, err
}
@ -229,21 +422,45 @@ func (keyRing *KeyRing) DecryptSplitStream(
)
}
// DecryptSplitStreamWithContext is used to decrypt a split pgp message as a Reader.
// It takes a key packet and a reader for the data packet
// and returns a PlainMessageReader for the plaintext data.
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
// verify the embedded signature with the given key ring and verification time.
// * verificationContext (optional): context for the signature verification.
func (keyRing *KeyRing) DecryptSplitStreamWithContext(
keypacket []byte,
dataPacketReader Reader,
verifyKeyRing *KeyRing, verifyTime int64,
verificationContext *VerificationContext,
) (plainMessage *PlainMessageReader, err error) {
messageReader := io.MultiReader(
bytes.NewReader(keypacket),
dataPacketReader,
)
return keyRing.DecryptStreamWithContext(
messageReader,
verifyKeyRing,
verifyTime,
verificationContext,
)
}
// SignDetachedStream generates and returns a PGPSignature for a given message Reader.
func (keyRing *KeyRing) SignDetachedStream(message Reader) (*PGPSignature, error) {
signEntity, err := keyRing.getSigningEntity()
if err != nil {
return nil, err
return keyRing.SignDetachedStreamWithContext(message, nil)
}
config := &packet.Config{DefaultHash: crypto.SHA512, Time: getTimeGenerator()}
var outBuf bytes.Buffer
// sign bin
if err := openpgp.DetachSign(&outBuf, signEntity, message, config); err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in signing")
}
return NewPGPSignature(outBuf.Bytes()), nil
// SignDetachedStreamWithContext generates and returns a PGPSignature for a given message Reader.
// If a context is provided, it is added to the signature as notation data
// with the name set in `constants.SignatureContextName`.
func (keyRing *KeyRing) SignDetachedStreamWithContext(message Reader, context *SigningContext) (*PGPSignature, error) {
return signMessageDetached(
keyRing,
message,
true,
context,
)
}
// VerifyDetachedStream verifies a message reader with a detached PGPSignature
@ -253,12 +470,34 @@ func (keyRing *KeyRing) VerifyDetachedStream(
signature *PGPSignature,
verifyTime int64,
) error {
return verifySignature(
_, err := verifySignature(
keyRing.entities,
message,
signature.GetBinary(),
verifyTime,
nil,
)
return err
}
// VerifyDetachedStreamWithContext verifies a message reader with a detached PGPSignature
// and returns a SignatureVerificationError if fails.
// If a context is provided, it verifies that the signature is valid in the given context, using
// the signature notations.
func (keyRing *KeyRing) VerifyDetachedStreamWithContext(
message Reader,
signature *PGPSignature,
verifyTime int64,
verificationContext *VerificationContext,
) error {
_, err := verifySignature(
keyRing.entities,
message,
signature.GetBinary(),
verifyTime,
verificationContext,
)
return err
}
// SignDetachedEncryptedStream generates and returns a PGPMessage

View File

@ -3,6 +3,7 @@ package crypto
import (
"bytes"
"encoding/base64"
"encoding/json"
goerrors "errors"
"io"
"io/ioutil"
@ -92,7 +93,7 @@ func NewPlainMessageFromFile(data []byte, filename string, time uint32) *PlainMe
// This allows seamless conversion to clear text signed messages (see RFC 4880 5.2.1 and 7.1).
func NewPlainMessageFromString(text string) *PlainMessage {
return &PlainMessage{
Data: []byte(internal.CanonicalizeAndTrim(text)),
Data: []byte(internal.Canonicalize(text)),
TextType: true,
Filename: "",
Time: uint32(GetUnixTime()),
@ -202,7 +203,7 @@ func (msg *PlainMessage) GetBinary() []byte {
// GetString returns the content of the message as a string.
func (msg *PlainMessage) GetString() string {
return strings.ReplaceAll(string(msg.Data), "\r\n", "\n")
return sanitizeString(strings.ReplaceAll(string(msg.Data), "\r\n", "\n"))
}
// GetBase64 returns the base-64 encoded binary content of the message as a
@ -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) {

View File

@ -49,7 +49,8 @@ func (keyRing *KeyRing) DecryptMIMEMessage(
callbacks.OnVerified(constants.SIGNATURE_OK)
}
bodyContent, bodyMimeType := body.GetBody()
callbacks.OnBody(bodyContent, bodyMimeType)
bodyContentSanitized := sanitizeString(bodyContent)
callbacks.OnBody(bodyContentSanitized, bodyMimeType)
for i := 0; i < len(attachments); i++ {
callbacks.OnAttachment(attachmentHeaders[i], []byte(attachments[i]))
}

View File

@ -0,0 +1,7 @@
package crypto
import "strings"
func sanitizeString(input string) string {
return strings.ToValidUTF8(input, "\ufffd")
}

View File

@ -138,17 +138,7 @@ func newSessionKeyFromEncrypted(ek *packet.EncryptedKey) (*SessionKey, error) {
// * message : The plain data as a PlainMessage.
// * output : The encrypted data as PGPMessage.
func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, error) {
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
}
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
}
return encryptWithSessionKey(message, sk, nil, config)
return encryptWithSessionKey(message, sk, nil, false, nil)
}
// EncryptAndSign encrypts a PlainMessage to PGPMessage with a SessionKey and signs it with a Private key.
@ -156,59 +146,50 @@ func (sk *SessionKey) Encrypt(message *PlainMessage) ([]byte, error) {
// * signKeyRing: The KeyRing to sign the message
// * output : The encrypted data as PGPMessage.
func (sk *SessionKey) EncryptAndSign(message *PlainMessage, signKeyRing *KeyRing) ([]byte, error) {
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
return encryptWithSessionKey(message, sk, signKeyRing, false, nil)
}
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
}
signEntity, err := signKeyRing.getSigningEntity()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to sign")
}
return encryptWithSessionKey(message, sk, signEntity, config)
// EncryptAndSignWithContext encrypts a PlainMessage to PGPMessage with a SessionKey and signs it with a Private key.
// * message : The plain data as a PlainMessage.
// * signKeyRing: The KeyRing to sign the message
// * output : The encrypted data as PGPMessage.
// * signingContext : (optional) the context for the signature.
func (sk *SessionKey) EncryptAndSignWithContext(message *PlainMessage, signKeyRing *KeyRing, signingContext *SigningContext) ([]byte, error) {
return encryptWithSessionKey(message, sk, signKeyRing, false, signingContext)
}
// EncryptWithCompression encrypts with compression support a PlainMessage to PGPMessage with a SessionKey.
// * message : The plain data as a PlainMessage.
// * output : The encrypted data as PGPMessage.
func (sk *SessionKey) EncryptWithCompression(message *PlainMessage) ([]byte, error) {
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
return encryptWithSessionKey(message, sk, nil, true, nil)
}
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
DefaultCompressionAlgo: constants.DefaultCompression,
CompressionConfig: &packet.CompressionConfig{Level: constants.DefaultCompressionLevel},
}
return encryptWithSessionKey(message, sk, nil, config)
}
func encryptWithSessionKey(message *PlainMessage, sk *SessionKey, signEntity *openpgp.Entity, config *packet.Config) ([]byte, error) {
func encryptWithSessionKey(
message *PlainMessage,
sk *SessionKey,
signKeyRing *KeyRing,
compress bool,
signingContext *SigningContext,
) ([]byte, error) {
var encBuf = new(bytes.Buffer)
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
NewPlainMessageMetadata(
message.IsBinary(),
message.Filename,
message.Time,
int64(message.Time),
),
encBuf,
sk,
signEntity,
config,
signKeyRing,
compress,
signingContext,
)
if err != nil {
return nil, err
}
if signEntity != nil {
if signKeyRing != nil {
_, err = signWriter.Write(message.GetBinary())
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in writing signed message")
@ -231,6 +212,61 @@ func encryptWithSessionKey(message *PlainMessage, sk *SessionKey, signEntity *op
}
func encryptStreamWithSessionKey(
plainMessageMetadata *PlainMessageMetadata,
dataPacketWriter io.Writer,
sk *SessionKey,
signKeyRing *KeyRing,
compress bool,
signingContext *SigningContext,
) (encryptWriter, signWriter io.WriteCloser, err error) {
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
}
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
}
var signEntity *openpgp.Entity
if signKeyRing != nil {
signEntity, err = signKeyRing.getSigningEntity()
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: unable to sign")
}
}
if compress {
config.DefaultCompressionAlgo = constants.DefaultCompression
config.CompressionConfig = &packet.CompressionConfig{Level: constants.DefaultCompressionLevel}
}
if signingContext != nil {
config.SignatureNotations = append(config.SignatureNotations, signingContext.getNotation())
}
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
}
return encryptStreamWithSessionKeyAndConfig(
plainMessageMetadata.IsBinary,
plainMessageMetadata.Filename,
uint32(plainMessageMetadata.ModTime),
dataPacketWriter,
sk,
signEntity,
config,
)
}
func encryptStreamWithSessionKeyAndConfig(
isBinary bool,
filename string,
modTime uint32,
@ -239,7 +275,15 @@ func encryptStreamWithSessionKey(
signEntity *openpgp.Entity,
config *packet.Config,
) (encryptWriter, signWriter io.WriteCloser, err error) {
encryptWriter, err = packet.SerializeSymmetricallyEncrypted(dataPacketWriter, config.Cipher(), sk.Key, config)
encryptWriter, err = packet.SerializeSymmetricallyEncrypted(
dataPacketWriter,
config.Cipher(),
config.AEAD() != nil,
packet.CipherSuite{Cipher: config.Cipher(), Mode: config.AEAD().Mode()},
sk.Key,
config,
)
if err != nil {
return nil, nil, errors.Wrap(err, "gopenpgp: unable to encrypt")
}
@ -289,9 +333,41 @@ func (sk *SessionKey) Decrypt(dataPacket []byte) (*PlainMessage, error) {
// * verifyTime: when should the signature be valid, as timestamp. If 0 time verification is disabled.
// * output: PlainMessage.
func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing, verifyTime int64) (*PlainMessage, error) {
return decryptWithSessionKeyAndContext(
sk,
dataPacket,
verifyKeyRing,
verifyTime,
nil,
)
}
// DecryptAndVerifyWithContext decrypts pgp data packets using directly a session key and verifies embedded signatures.
// * encrypted: PGPMessage.
// * verifyKeyRing: KeyRing with verification public keys
// * verifyTime: when should the signature be valid, as timestamp. If 0 time verification is disabled.
// * output: PlainMessage.
// * verificationContext (optional): context for the signature verification.
func (sk *SessionKey) DecryptAndVerifyWithContext(dataPacket []byte, verifyKeyRing *KeyRing, verifyTime int64, verificationContext *VerificationContext) (*PlainMessage, error) {
return decryptWithSessionKeyAndContext(
sk,
dataPacket,
verifyKeyRing,
verifyTime,
verificationContext,
)
}
func decryptWithSessionKeyAndContext(
sk *SessionKey,
dataPacket []byte,
verifyKeyRing *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (*PlainMessage, error) {
var messageReader = bytes.NewReader(dataPacket)
md, err := decryptStreamWithSessionKey(sk, messageReader, verifyKeyRing)
md, err := decryptStreamWithSessionKey(sk, messageReader, verifyKeyRing, verificationContext)
if err != nil {
return nil, err
}
@ -303,7 +379,7 @@ func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing
if verifyKeyRing != nil {
processSignatureExpiration(md, verifyTime)
err = verifyDetailsSignature(md, verifyKeyRing)
err = verifyDetailsSignature(md, verifyKeyRing, verificationContext)
}
return &PlainMessage{
@ -314,7 +390,12 @@ func (sk *SessionKey) DecryptAndVerify(dataPacket []byte, verifyKeyRing *KeyRing
}, err
}
func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verifyKeyRing *KeyRing) (*openpgp.MessageDetails, error) {
func decryptStreamWithSessionKey(
sk *SessionKey,
messageReader io.Reader,
verifyKeyRing *KeyRing,
verificationContext *VerificationContext,
) (*openpgp.MessageDetails, error) {
var decrypted io.ReadCloser
var keyring openpgp.EntityList
@ -327,17 +408,24 @@ func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verify
// Decrypt data packet
switch p := p.(type) {
case *packet.SymmetricallyEncrypted:
case *packet.SymmetricallyEncrypted, *packet.AEADEncrypted:
if symPacket, ok := p.(*packet.SymmetricallyEncrypted); ok {
if !symPacket.IntegrityProtected {
return nil, errors.New("gopenpgp: message is not authenticated")
}
}
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to decrypt with session key")
}
decrypted, err = p.Decrypt(dc, sk.Key)
encryptedDataPacket, isDataPacket := p.(packet.EncryptedDataPacket)
if !isDataPacket {
return nil, errors.Wrap(err, "gopenpgp: unknown data packet")
}
decrypted, err = encryptedDataPacket.Decrypt(dc, sk.Key)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to decrypt symmetric packet")
}
default:
return nil, errors.New("gopenpgp: invalid packet type")
}
@ -346,6 +434,10 @@ func decryptStreamWithSessionKey(sk *SessionKey, messageReader io.Reader, verify
Time: getTimeGenerator(),
}
if verificationContext != nil {
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
}
// Push decrypted packet as literal packet and use openpgp's reader
if verifyKeyRing != nil {
keyring = verifyKeyRing.entities

View File

@ -1,8 +1,6 @@
package crypto
import (
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/pkg/errors"
)
@ -30,40 +28,87 @@ func (sk *SessionKey) EncryptStream(
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
dc, err := sk.GetCipherFunc()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to encrypt with session key")
return sk.encryptStream(
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
false,
nil,
)
}
config := &packet.Config{
Time: getTimeGenerator(),
DefaultCipher: dc,
}
var signEntity *openpgp.Entity
if signKeyRing != nil {
signEntity, err = signKeyRing.getSigningEntity()
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: unable to sign")
}
// EncryptStreamWithContext is used to encrypt data as a Writer.
// It takes a writer for the encrypted data packet and returns a writer for the plaintext data.
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) the context for the signature.
func (sk *SessionKey) EncryptStreamWithContext(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
signingContext *SigningContext,
) (plainMessageWriter WriteCloser, err error) {
return sk.encryptStream(
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
false,
signingContext,
)
}
if plainMessageMetadata == nil {
// Use sensible default metadata
plainMessageMetadata = &PlainMessageMetadata{
IsBinary: true,
Filename: "",
ModTime: GetUnixTime(),
}
// EncryptStreamWithCompression is used to encrypt data as a Writer.
// The plaintext data is compressed before being encrypted.
// It takes a writer for the encrypted data packet and returns a writer for the plaintext data.
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) the context for the signature.
func (sk *SessionKey) EncryptStreamWithCompression(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
) (plainMessageWriter WriteCloser, err error) {
return sk.encryptStream(
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
true,
nil,
)
}
// EncryptStreamWithContextAndCompression is used to encrypt data as a Writer.
// The plaintext data is compressed before being encrypted.
// It takes a writer for the encrypted data packet and returns a writer for the plaintext data.
// If signKeyRing is not nil, it is used to do an embedded signature.
// * signingContext : (optional) the context for the signature.
func (sk *SessionKey) EncryptStreamWithContextAndCompression(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
signingContext *SigningContext,
) (plainMessageWriter WriteCloser, err error) {
return sk.encryptStream(
dataPacketWriter,
plainMessageMetadata,
signKeyRing,
true,
signingContext,
)
}
func (sk *SessionKey) encryptStream(
dataPacketWriter Writer,
plainMessageMetadata *PlainMessageMetadata,
signKeyRing *KeyRing,
compress bool,
signingContext *SigningContext,
) (plainMessageWriter WriteCloser, err error) {
encryptWriter, signWriter, err := encryptStreamWithSessionKey(
plainMessageMetadata.IsBinary,
plainMessageMetadata.Filename,
uint32(plainMessageMetadata.ModTime),
plainMessageMetadata,
dataPacketWriter,
sk,
signEntity,
config,
signKeyRing,
compress,
signingContext,
)
if err != nil {
@ -87,10 +132,48 @@ func (sk *SessionKey) DecryptStream(
verifyKeyRing *KeyRing,
verifyTime int64,
) (plainMessage *PlainMessageReader, err error) {
messageDetails, err := decryptStreamWithSessionKey(
return decryptStreamWithSessionKeyAndContext(
sk,
dataPacketReader,
verifyKeyRing,
verifyTime,
nil,
)
}
// DecryptStreamWithContext is used to decrypt a data packet as a Reader.
// It takes a reader for the data packet
// and returns a PlainMessageReader for the plaintext data.
// If verifyKeyRing is not nil, PlainMessageReader.VerifySignature() will
// verify the embedded signature with the given key ring and verification time.
// * verificationContext (optional): context for the signature verification.
func (sk *SessionKey) DecryptStreamWithContext(
dataPacketReader Reader,
verifyKeyRing *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (plainMessage *PlainMessageReader, err error) {
return decryptStreamWithSessionKeyAndContext(
sk,
dataPacketReader,
verifyKeyRing,
verifyTime,
verificationContext,
)
}
func decryptStreamWithSessionKeyAndContext(
sessionKey *SessionKey,
dataPacketReader Reader,
verifyKeyRing *KeyRing,
verifyTime int64,
verificationContext *VerificationContext,
) (plainMessage *PlainMessageReader, err error) {
messageDetails, err := decryptStreamWithSessionKey(
sessionKey,
dataPacketReader,
verifyKeyRing,
verificationContext,
)
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in reading message")
@ -101,5 +184,6 @@ func (sk *SessionKey) DecryptStream(
verifyKeyRing,
verifyTime,
false,
verificationContext,
}, err
}

View File

@ -3,7 +3,6 @@ package crypto
import (
"bytes"
"crypto"
"errors"
"fmt"
"io"
"math"
@ -12,6 +11,7 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"
pgpErrors "github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/pkg/errors"
"github.com/ProtonMail/gopenpgp/v2/constants"
"github.com/ProtonMail/gopenpgp/v2/internal"
@ -29,23 +29,41 @@ var allowedHashes = []crypto.Hash{
type SignatureVerificationError struct {
Status int
Message string
Cause error
}
// Error is the base method for all errors.
func (e SignatureVerificationError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("Signature Verification Error: %v caused by %v", e.Message, e.Cause)
}
return fmt.Sprintf("Signature Verification Error: %v", e.Message)
}
// Unwrap returns the cause of failure.
func (e SignatureVerificationError) Unwrap() error {
return e.Cause
}
// ------------------
// Internal functions
// ------------------
// newSignatureFailed creates a new SignatureVerificationError, type
// SignatureFailed.
func newSignatureFailed() SignatureVerificationError {
func newSignatureBadContext(cause error) SignatureVerificationError {
return SignatureVerificationError{
Status: constants.SIGNATURE_BAD_CONTEXT,
Message: "Invalid signature context",
Cause: cause,
}
}
func newSignatureFailed(cause error) SignatureVerificationError {
return SignatureVerificationError{
Status: constants.SIGNATURE_FAILED,
Message: "Invalid signature",
Cause: cause,
}
}
@ -98,7 +116,7 @@ func processSignatureExpiration(md *openpgp.MessageDetails, verifyTime int64) {
}
// verifyDetailsSignature verifies signature from message details.
func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) error {
func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing, verificationContext *VerificationContext) error {
if !md.IsSigned {
return newSignatureNotSigned()
}
@ -108,18 +126,113 @@ func verifyDetailsSignature(md *openpgp.MessageDetails, verifierKey *KeyRing) er
return newSignatureNoVerifier()
}
if md.SignatureError != nil {
return newSignatureFailed()
return newSignatureFailed(md.SignatureError)
}
if md.Signature == nil ||
md.Signature.Hash < allowedHashes[0] ||
md.Signature.Hash > allowedHashes[len(allowedHashes)-1] {
return newSignatureInsecure()
}
if verificationContext != nil {
err := verificationContext.verifyContext(md.Signature)
if err != nil {
return newSignatureBadContext(err)
}
}
return nil
}
// SigningContext gives the context that will be
// included in the signature's notation data.
type SigningContext struct {
Value string
IsCritical bool
}
// NewSigningContext creates a new signing context.
// The value is set to the notation data.
// isCritical controls whether the notation is flagged as a critical packet.
func NewSigningContext(value string, isCritical bool) *SigningContext {
return &SigningContext{Value: value, IsCritical: isCritical}
}
func (context *SigningContext) getNotation() *packet.Notation {
return &packet.Notation{
Name: constants.SignatureContextName,
Value: []byte(context.Value),
IsCritical: context.IsCritical,
IsHumanReadable: true,
}
}
// VerificationContext gives the context that will be
// used to verify the signature.
type VerificationContext struct {
Value string
IsRequired bool
RequiredAfter int64
}
// NewVerificationContext creates a new verification context.
// The value is checked against the signature's notation data.
// If isRequired is false, the signature is allowed to have no context set.
// If requiredAfter is != 0, the signature is allowed to have no context set if it
// was created before the unix time set in requiredAfter.
func NewVerificationContext(value string, isRequired bool, requiredAfter int64) *VerificationContext {
return &VerificationContext{
Value: value,
IsRequired: isRequired,
RequiredAfter: requiredAfter,
}
}
func (context *VerificationContext) isRequiredAtTime(signatureTime time.Time) bool {
return context.IsRequired &&
(context.RequiredAfter == 0 || signatureTime.After(time.Unix(context.RequiredAfter, 0)))
}
func findContext(notations []*packet.Notation) (string, error) {
context := ""
for _, notation := range notations {
if notation.Name == constants.SignatureContextName {
if context != "" {
return "", errors.New("gopenpgp: signature has multiple context notations")
}
if !notation.IsHumanReadable {
return "", errors.New("gopenpgp: context notation was not set as human-readable")
}
context = string(notation.Value)
}
}
return context, nil
}
func (context *VerificationContext) verifyContext(sig *packet.Signature) error {
signatureContext, err := findContext(sig.Notations)
if err != nil {
return err
}
if signatureContext != context.Value {
contextRequired := context.isRequiredAtTime(sig.CreationTime)
if contextRequired {
return errors.New("gopenpgp: signature did not have the required context")
} else if signatureContext != "" {
return errors.New("gopenpgp: signature had a wrong context")
}
}
return nil
}
// verifySignature verifies if a signature is valid with the entity list.
func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signature []byte, verifyTime int64) error {
func verifySignature(
pubKeyEntries openpgp.EntityList,
origText io.Reader,
signature []byte,
verifyTime int64,
verificationContext *VerificationContext,
) (*packet.Signature, error) {
config := &packet.Config{}
if verifyTime == 0 {
config.Time = func() time.Time {
@ -130,32 +243,90 @@ func verifySignature(pubKeyEntries openpgp.EntityList, origText io.Reader, signa
return time.Unix(verifyTime+internal.CreationTimeOffset, 0)
}
}
if verificationContext != nil {
config.KnownNotations = map[string]bool{constants.SignatureContextName: true}
}
signatureReader := bytes.NewReader(signature)
signer, err := openpgp.CheckDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
sig, signer, err := openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
if errors.Is(err, pgpErrors.ErrSignatureExpired) && signer != nil && verifyTime > 0 {
// if verifyTime = 0: time check disabled, everything is okay
if sig != nil && signer != nil && (errors.Is(err, pgpErrors.ErrSignatureExpired) || errors.Is(err, pgpErrors.ErrKeyExpired)) { //nolint:nestif
if verifyTime == 0 { // Expiration check disabled
err = nil
} else {
// Maybe the creation time offset pushed it over the edge
// Retry with the actual verification time
config.Time = func() time.Time {
return time.Unix(verifyTime, 0)
}
seeker, ok := origText.(io.ReadSeeker)
if !ok {
return nil, errors.Wrap(err, "gopenpgp: message reader do not support seeking, cannot retry signature verification")
}
_, err = seeker.Seek(0, io.SeekStart)
if err != nil {
return nil, newSignatureFailed(errors.Wrap(err, "gopenpgp: could not rewind the data reader."))
}
_, err = signatureReader.Seek(0, io.SeekStart)
if err != nil {
return newSignatureFailed()
return nil, newSignatureFailed(err)
}
sig, signer, err = openpgp.VerifyDetachedSignatureAndHash(pubKeyEntries, seeker, signatureReader, allowedHashes, config)
}
}
signer, err = openpgp.CheckDetachedSignatureAndHash(pubKeyEntries, origText, signatureReader, allowedHashes, config)
if err != nil {
return newSignatureFailed()
return nil, newSignatureFailed(err)
}
if sig == nil || signer == nil {
return nil, newSignatureFailed(errors.New("gopenpgp: no signer or valid signature"))
}
if verificationContext != nil {
err := verificationContext.verifyContext(sig)
if err != nil {
return nil, newSignatureBadContext(err)
}
}
if signer == nil {
return newSignatureFailed()
return sig, nil
}
return nil
func signMessageDetached(
signKeyRing *KeyRing,
messageReader io.Reader,
isBinary bool,
context *SigningContext,
) (*PGPSignature, error) {
config := &packet.Config{
DefaultHash: crypto.SHA512,
Time: getTimeGenerator(),
}
signEntity, err := signKeyRing.getSigningEntity()
if err != nil {
return nil, err
}
if context != nil {
config.SignatureNotations = append(config.SignatureNotations, context.getNotation())
}
var outBuf bytes.Buffer
if isBinary {
err = openpgp.DetachSign(&outBuf, signEntity, messageReader, config)
} else {
err = openpgp.DetachSignText(&outBuf, signEntity, messageReader, config)
}
if err != nil {
return nil, errors.Wrap(err, "gopenpgp: error in signing")
}
return NewPGPSignature(outBuf.Bytes()), nil
}

View File

@ -99,7 +99,7 @@ func (sc *SignatureCollector) Accept(
}
sc.signature = string(buffer)
str, _ := ioutil.ReadAll(rawBody)
canonicalizedBody := internal.CanonicalizeAndTrim(string(str))
canonicalizedBody := internal.Canonicalize(internal.TrimEachLine(string(str)))
rawBody = bytes.NewReader([]byte(canonicalizedBody))
if sc.keyring != nil {
_, err = openpgp.CheckArmoredDetachedSignature(sc.keyring, rawBody, bytes.NewReader(buffer), sc.config)
@ -110,7 +110,7 @@ func (sc *SignatureCollector) Accept(
case errors.Is(err, pgpErrors.ErrUnknownIssuer):
sc.verified = newSignatureNoVerifier()
default:
sc.verified = newSignatureFailed()
sc.verified = newSignatureFailed(err)
}
} else {
sc.verified = newSignatureNoVerifier()

View File

@ -4,8 +4,10 @@ go_library(
name = "helper",
srcs = [
"cleartext.go",
"decrypt_check.go",
"helper.go",
"key.go",
"message.go",
"mobile.go",
"mobile_stream.go",
"sign_detached.go",
@ -15,6 +17,8 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/ProtonMail/gopenpgp/v2/crypto",
"//vendor/github.com/ProtonMail/gopenpgp/v2/internal",
"//vendor/github.com/pkg/errors",
"@com_github_protonmail_go_crypto//openpgp/packet",
],
)

View File

@ -2,6 +2,7 @@ package helper
import (
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/gopenpgp/v2/internal"
"github.com/pkg/errors"
)
@ -48,7 +49,7 @@ func VerifyCleartextMessageArmored(publicKey, armored string, verifyTime int64)
// SignCleartextMessage signs text given a private keyring, canonicalizes and
// trims the newlines, and returns the PGP-compliant special armoring.
func SignCleartextMessage(keyRing *crypto.KeyRing, text string) (string, error) {
message := crypto.NewPlainMessageFromString(text)
message := crypto.NewPlainMessageFromString(internal.TrimEachLine(text))
signature, err := keyRing.SignDetached(message)
if err != nil {
@ -67,7 +68,7 @@ func VerifyCleartextMessage(keyRing *crypto.KeyRing, armored string, verifyTime
return "", errors.Wrap(err, "gopengpp: unable to unarmor cleartext message")
}
message := crypto.NewPlainMessageFromString(clearTextMessage.GetString())
message := crypto.NewPlainMessageFromString(internal.TrimEachLine(clearTextMessage.GetString()))
signature := crypto.NewPGPSignature(clearTextMessage.GetBinarySignature())
err = keyRing.VerifyDetached(message, signature, verifyTime)
if err != nil {

View File

@ -0,0 +1,86 @@
package helper
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"io"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/pkg/errors"
)
const AES_BLOCK_SIZE = 16
func supported(cipher packet.CipherFunction) bool {
switch cipher {
case packet.CipherAES128, packet.CipherAES192, packet.CipherAES256:
return true
case packet.CipherCAST5, packet.Cipher3DES:
return false
}
return false
}
func blockSize(cipher packet.CipherFunction) int {
switch cipher {
case packet.CipherAES128, packet.CipherAES192, packet.CipherAES256:
return AES_BLOCK_SIZE
case packet.CipherCAST5, packet.Cipher3DES:
return 0
}
return 0
}
func blockCipher(cipher packet.CipherFunction, key []byte) (cipher.Block, error) {
switch cipher {
case packet.CipherAES128, packet.CipherAES192, packet.CipherAES256:
return aes.NewCipher(key)
case packet.CipherCAST5, packet.Cipher3DES:
return nil, errors.New("gopenpgp: cipher not supported for quick check")
}
return nil, errors.New("gopenpgp: unknown cipher")
}
// QuickCheckDecryptReader checks with high probability if the provided session key
// can decrypt a data packet given its 24 byte long prefix.
// The method reads up to but not exactly 24 bytes from the prefixReader.
// NOTE: Only works for SEIPDv1 packets with AES.
func QuickCheckDecryptReader(sessionKey *crypto.SessionKey, prefixReader crypto.Reader) (bool, error) {
algo, err := sessionKey.GetCipherFunc()
if err != nil {
return false, errors.New("gopenpgp: cipher algorithm not found")
}
if !supported(algo) {
return false, errors.New("gopenpgp: cipher not supported for quick check")
}
packetParser := packet.NewReader(prefixReader)
_, err = packetParser.Next()
if err != nil {
return false, errors.New("gopenpgp: failed to parse packet prefix")
}
blockSize := blockSize(algo)
encryptedData := make([]byte, blockSize+2)
_, err = io.ReadFull(prefixReader, encryptedData)
if err != nil {
return false, errors.New("gopenpgp: prefix is too short to check")
}
blockCipher, err := blockCipher(algo, sessionKey.Key)
if err != nil {
return false, errors.New("gopenpgp: failed to initialize the cipher")
}
_ = packet.NewOCFBDecrypter(blockCipher, encryptedData, packet.OCFBNoResync)
return encryptedData[blockSize-2] == encryptedData[blockSize] &&
encryptedData[blockSize-1] == encryptedData[blockSize+1], nil
}
// QuickCheckDecrypt checks with high probability if the provided session key
// can decrypt the encrypted data packet given its 24 byte long prefix.
// The method only considers the first 24 bytes of the prefix slice (prefix[:24]).
// NOTE: Only works for SEIPDv1 packets with AES.
func QuickCheckDecrypt(sessionKey *crypto.SessionKey, prefix []byte) (bool, error) {
return QuickCheckDecryptReader(sessionKey, bytes.NewReader(prefix))
}

View 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
}

View File

@ -26,6 +26,20 @@ func DecryptExplicitVerify(
return newExplicitVerifyMessage(message, err)
}
// DecryptExplicitVerifyWithContext decrypts a PGP message given a private keyring
// and a public keyring to verify the embedded signature. Returns the plain
// data and an error on signature verification failure.
// The caller can provide a context that will be used to verify the signature.
func DecryptExplicitVerifyWithContext(
pgpMessage *crypto.PGPMessage,
privateKeyRing, publicKeyRing *crypto.KeyRing,
verifyTime int64,
verificationContext *crypto.VerificationContext,
) (*ExplicitVerifyMessage, error) {
message, err := privateKeyRing.DecryptWithContext(pgpMessage, publicKeyRing, verifyTime, verificationContext)
return newExplicitVerifyMessage(message, err)
}
// DecryptSessionKeyExplicitVerify decrypts a PGP data packet given a session key
// and a public keyring to verify the embedded signature. Returns the plain data and
// an error on signature verification failure.
@ -39,6 +53,21 @@ func DecryptSessionKeyExplicitVerify(
return newExplicitVerifyMessage(message, err)
}
// DecryptSessionKeyExplicitVerifyWithContext decrypts a PGP data packet given a session key
// and a public keyring to verify the embedded signature. Returns the plain data and
// an error on signature verification failure.
// The caller can provide a context that will be used to verify the signature.
func DecryptSessionKeyExplicitVerifyWithContext(
dataPacket []byte,
sessionKey *crypto.SessionKey,
publicKeyRing *crypto.KeyRing,
verifyTime int64,
verificationContext *crypto.VerificationContext,
) (*ExplicitVerifyMessage, error) {
message, err := sessionKey.DecryptAndVerifyWithContext(dataPacket, publicKeyRing, verifyTime, verificationContext)
return newExplicitVerifyMessage(message, err)
}
func newExplicitVerifyMessage(message *crypto.PlainMessage, err error) (*ExplicitVerifyMessage, error) {
var explicitVerify *ExplicitVerifyMessage
if err != nil {

View File

@ -7,14 +7,18 @@ import (
"github.com/ProtonMail/gopenpgp/v2/constants"
)
func CanonicalizeAndTrim(text string) string {
func Canonicalize(text string) string {
return strings.ReplaceAll(strings.ReplaceAll(text, "\r\n", "\n"), "\n", "\r\n")
}
func TrimEachLine(text string) string {
lines := strings.Split(text, "\n")
for i := range lines {
lines[i] = strings.TrimRight(lines[i], " \t\r")
}
return strings.Join(lines, "\r\n")
return strings.Join(lines, "\n")
}
// CreationTimeOffset stores the amount of seconds that a signature may be

4
vendor/modules.txt vendored
View File

@ -76,10 +76,10 @@ github.com/ProtonMail/go-crypto/openpgp/internal/ecc
github.com/ProtonMail/go-crypto/openpgp/internal/encoding
github.com/ProtonMail/go-crypto/openpgp/packet
github.com/ProtonMail/go-crypto/openpgp/s2k
# github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f
# github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f
## explicit; go 1.12
github.com/ProtonMail/go-mime
# github.com/ProtonMail/gopenpgp/v2 v2.4.7
# github.com/ProtonMail/gopenpgp/v2 v2.7.5
## explicit; go 1.15
github.com/ProtonMail/gopenpgp/v2/armor
github.com/ProtonMail/gopenpgp/v2/constants