File: decryption_handle.go

package info (click to toggle)
golang-github-protonmail-gopenpgp-v3 3.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,028 kB
  • sloc: sh: 87; makefile: 2
file content (249 lines) | stat: -rw-r--r-- 9,911 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package crypto

import (
	"bytes"
	"errors"
	"fmt"

	"github.com/ProtonMail/go-crypto/openpgp/armor"
	"github.com/ProtonMail/gopenpgp/v3/internal"
)

// decryptionHandle collects the configuration parameters to decrypt a pgp message.
// The fields in the struct allow to customize the decryption.
type decryptionHandle struct {
	// DecryptionKeyRing provides the secret keys for decrypting the pgp message.
	// Assumes that the message was encrypted towards a public key in DecryptionKeyRing.
	// If nil, set another field for the type of decryption: SessionKey or Password
	DecryptionKeyRing *KeyRing
	// SessionKeys provides one or more session keys for decrypting the pgp message.
	// Assumes that the message was encrypted with one of the session keys provided.
	// If nil, set another field for the type of decryption: DecryptionKeyRing or Password
	SessionKeys []*SessionKey
	// Passwords provides passwords for decrypting the pgp message.
	// Assumes that the message was encrypted with on of the keys derived from the passwords.
	// If nil, set another field for the type of decryption: DecryptionKeyRing or SessionKey
	Passwords [][]byte
	// VerifyKeyRing provides a set of public keys to verify the signature of the pgp message, if any.
	// If nil, the signatures are not verified.
	VerifyKeyRing *KeyRing
	// VerificationContext provides a verification context for the signature of the pgp message, if any.
	// Only considered if VerifyKeyRing is not nil.
	VerificationContext *VerificationContext
	// MaxDecompressedSize defines the maximum number of bytes allowed for a message
	// after decompression. An error is thrown if the decompressed data exceeds this limit.
	MaxDecompressedSize int64
	// PlainDetachedSignature indicates that all provided detached signatures are not encrypted.
	PlainDetachedSignature bool
	// DisableIntendedRecipients indicates if the signature verification should not check if
	// the decryption key matches the intended recipients of the message.
	// If disabled, the decryption throws no error in a non-matching case.
	DisableIntendedRecipients                   bool
	DisableVerifyTimeCheck                      bool
	DisableStrictMessageParsing                 bool
	DisableAutomaticTextSanitize                bool
	InsecureDisableUnauthenticatedMessagesCheck bool
	InsecureAllowDecryptionWithSigningKeys      bool
	RetrieveSessionKey                          bool
	IsUTF8                                      bool
	clock                                       Clock
	profile                                     EncryptionProfile
}

// --- Default decryption handle to build from

func defaultDecryptionHandle(profile EncryptionProfile, clock Clock) *decryptionHandle {
	return &decryptionHandle{
		clock:   clock,
		profile: profile,
	}
}

// --- Implements PGPDecryption interface

// DecryptingReader returns a wrapper around underlying encryptedMessage Reader,
// such that any read-operation via the wrapper results in a read from the decrypted pgp message.
// The returned VerifyDataReader has to be fully read before any potential signatures can be verified.
// Either read the message fully end then call VerifySignature or use the helper method ReadAllAndVerifySignature.
// The encoding indicates if the input message should be unarmored or not, i.e., Bytes/Armor/Auto
// where Auto tries to detect automatically.
// If encryptedMessage is of type PGPSplitReader, the method tries to verify an encrypted detached signature
// that is read from the separate reader.
func (dh *decryptionHandle) DecryptingReader(encryptedMessage Reader, encoding int8) (plainMessageReader *VerifyDataReader, err error) {
	err = dh.validate()
	if err != nil {
		return
	}
	pgpSplitReader := isPGPSplitReader(encryptedMessage)
	if pgpSplitReader != nil {
		return dh.decryptingReader(pgpSplitReader, pgpSplitReader.Signature(), encoding)
	}
	return dh.decryptingReader(encryptedMessage, nil, encoding)
}

// Decrypt decrypts an encrypted pgp message.
// Returns a VerifiedDataResult, which can be queried for potential signature verification errors,
// and the plaintext data. Note that on a signature error, the method does not return an error.
// Instead, the signature error is stored within the VerifiedDataResult.
// The encoding indicates if the input message should be unarmored or not, i.e., Bytes/Armor/Auto
// where Auto tries to detect automatically.
func (dh *decryptionHandle) Decrypt(pgpMessage []byte, encoding int8) (*VerifiedDataResult, error) {
	messageReader := bytes.NewReader(pgpMessage)
	plainMessageReader, err := dh.DecryptingReader(messageReader, encoding)
	if err != nil {
		return nil, err
	}
	return plainMessageReader.ReadAllAndVerifySignature()
}

// DecryptDetached provides the same functionality as Decrypt but allows
// to supply an encrypted detached signature that should be decrypted and verified
// against the data in the pgp message. If encDetachedSignature is nil, the behavior is similar
// to Decrypt. The encoding indicates if the input message should be unarmored or not,
// i.e., Bytes/Armor/Auto where Auto tries to detect automatically.
func (dh *decryptionHandle) DecryptDetached(pgpMessage []byte, encryptedDetachedSig []byte, encoding int8) (*VerifiedDataResult, error) {
	reader := &pgpSplitReader{
		encMessage: bytes.NewReader(pgpMessage),
	}
	if encryptedDetachedSig != nil {
		reader.encSignature = bytes.NewReader(encryptedDetachedSig)
	}
	verifier, err := dh.DecryptingReader(reader, encoding)
	if err != nil {
		return nil, err
	}
	return verifier.ReadAllAndVerifySignature()
}

// DecryptSessionKey decrypts an encrypted session key.
// To decrypted a session key, the decryption handle must contain either a decryption key or a password.
func (dh *decryptionHandle) DecryptSessionKey(keyPackets []byte) (sk *SessionKey, err error) {
	switch {
	case len(dh.Passwords) > 0:
		for _, passwordCandidate := range dh.Passwords {
			sk, err = decryptSessionKeyWithPassword(keyPackets, passwordCandidate)
			if err == nil {
				return sk, nil
			}
		}
		return nil, err
	case dh.DecryptionKeyRing != nil:
		return decryptSessionKey(dh.DecryptionKeyRing, keyPackets)
	}
	return nil, errors.New("gopenpgp: no decryption key or password provided")
}

// ClearPrivateParams clears all private key material contained in EncryptionHandle from memory.
func (dh *decryptionHandle) ClearPrivateParams() {
	if dh.DecryptionKeyRing != nil {
		dh.DecryptionKeyRing.ClearPrivateParams()
	}
	if len(dh.SessionKeys) > 0 {
		for _, sk := range dh.SessionKeys {
			sk.Clear()
		}
	}
	if len(dh.Passwords) > 0 {
		for _, password := range dh.Passwords {
			clearMem(password)
		}
	}
}

func (dh *decryptionHandle) validate() error {
	if dh.DecryptionKeyRing == nil && len(dh.Passwords) == 0 && len(dh.SessionKeys) == 0 {
		return errors.New("gopenpgp: no decryption key material provided")
	}
	return nil
}

func (dh *decryptionHandle) decryptingReader(encryptedMessage Reader, encryptedSignature Reader, encoding int8) (plainMessageReader *VerifyDataReader, err error) {
	err = dh.validate()
	if err != nil {
		return nil, err
	}
	var armored bool
	encryptedMessage, armored = unarmorInput(encoding, encryptedMessage)
	var armoredBlock *armor.Block
	if armored {
		// Wrap encryptedMessage with decode armor reader.
		armoredBlock, err = armor.Decode(encryptedMessage)
		if err != nil {
			err = fmt.Errorf("gopenpgp: unarmor failed for pgp message: %w", err)
			return nil, err
		}
		encryptedMessage = armoredBlock.Body
	}
	if encryptedSignature != nil {
		encryptedSignature, armored = unarmorInput(encoding, encryptedSignature)
		if armored {
			// Wrap encryptedSignature with decode armor reader.
			armoredBlock, err = armor.Decode(encryptedSignature)
			if err != nil {
				err = fmt.Errorf("gopenpgp: unarmor failed for pgp encrypted signature message: %w", err)
				return nil, err
			}
			encryptedSignature = armoredBlock.Body
			if dh.PlainDetachedSignature && armoredBlock.Type != "PGP SIGNATURE" {
				err = errors.New("gopenpgp: detached signature is not plaintext")
				return nil, err
			}
			if !dh.PlainDetachedSignature && armoredBlock.Type != "PGP MESSAGE" {
				err = errors.New("gopenpgp: encrypted detached signature is not an encrypted pgp message")
				return nil, err
			}
		}
	}

	var decryptionTried bool
	if len(dh.SessionKeys) > 0 {
		// Decrypt with session key.
		if encryptedSignature != nil {
			plainMessageReader, err = dh.decryptStreamAndVerifyDetached(encryptedMessage, encryptedSignature, dh.PlainDetachedSignature)
		} else {
			plainMessageReader, err = dh.decryptStreamWithSession(encryptedMessage)
		}
		decryptionTried = true
	}
	if (!decryptionTried || err != nil) && dh.DecryptionKeyRing != nil {
		// Decrypt with keyring.
		if encryptedSignature != nil {
			plainMessageReader, err = dh.decryptStreamAndVerifyDetached(encryptedMessage, encryptedSignature, dh.PlainDetachedSignature)
		} else {
			plainMessageReader, err = dh.decryptStream(encryptedMessage)
		}
		decryptionTried = true
	}
	if (!decryptionTried || err != nil) && len(dh.Passwords) > 0 {
		// Decrypt with password.
		if encryptedSignature != nil {
			plainMessageReader, err = dh.decryptStreamAndVerifyDetached(encryptedMessage, encryptedSignature, dh.PlainDetachedSignature)
		} else {
			plainMessageReader, err = dh.decryptStream(encryptedMessage)
		}
		decryptionTried = true
	}
	if !decryptionTried {
		// No decryption material provided.
		err = errors.New("gopenpgp: no decryption key ring, session key, or password provided")
	}
	if err != nil {
		return nil, err
	}
	if dh.IsUTF8 {
		plainMessageReader.internalReader = internal.NewSanitizeReader(plainMessageReader.internalReader)
	}
	return plainMessageReader, nil
}

func isPGPSplitReader(w Reader) PGPSplitReader {
	v, ok := interface{}(w).(PGPSplitReader)
	if ok {
		return v
	}
	v, ok = interface{}(&w).(PGPSplitReader)
	if ok {
		return v
	}
	return nil
}