-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpkcs1v15.go
271 lines (240 loc) · 8.94 KB
/
pkcs1v15.go
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rsa
import (
"crypto/internal/boring"
"crypto/internal/fips140/rsa"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/subtle"
"errors"
"io"
)
// This file implements encryption and decryption using PKCS #1 v1.5 padding.
// PKCS1v15DecryptOptions is for passing options to PKCS #1 v1.5 decryption using
// the [crypto.Decrypter] interface.
type PKCS1v15DecryptOptions struct {
// SessionKeyLen is the length of the session key that is being
// decrypted. If not zero, then a padding error during decryption will
// cause a random plaintext of this length to be returned rather than
// an error. These alternatives happen in constant time.
SessionKeyLen int
}
// EncryptPKCS1v15 encrypts the given message with RSA and the padding
// scheme from PKCS #1 v1.5. The message must be no longer than the
// length of the public modulus minus 11 bytes.
//
// The random parameter is used as a source of entropy to ensure that
// encrypting the same message twice doesn't result in the same
// ciphertext. Most applications should use [crypto/rand.Reader]
// as random. Note that the returned ciphertext does not depend
// deterministically on the bytes read from random, and may change
// between calls and/or between versions.
//
// WARNING: use of this function to encrypt plaintexts other than
// session keys is dangerous. Use RSA OAEP in new protocols.
func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
if fips140only.Enabled {
return nil, errors.New("crypto/rsa: use of PKCS#1 v1.5 encryption is not allowed in FIPS 140-only mode")
}
if err := checkPublicKeySize(pub); err != nil {
return nil, err
}
randutil.MaybeReadByte(random)
k := pub.Size()
if len(msg) > k-11 {
return nil, ErrMessageTooLong
}
if boring.Enabled && random == boring.RandReader {
bkey, err := boringPublicKey(pub)
if err != nil {
return nil, err
}
return boring.EncryptRSAPKCS1(bkey, msg)
}
boring.UnreachableExceptTests()
// EM = 0x00 || 0x02 || PS || 0x00 || M
em := make([]byte, k)
em[1] = 2
ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):]
err := nonZeroRandomBytes(ps, random)
if err != nil {
return nil, err
}
em[len(em)-len(msg)-1] = 0
copy(mm, msg)
if boring.Enabled {
var bkey *boring.PublicKeyRSA
bkey, err = boringPublicKey(pub)
if err != nil {
return nil, err
}
return boring.EncryptRSANoPadding(bkey, em)
}
fk, err := fipsPublicKey(pub)
if err != nil {
return nil, err
}
return rsa.Encrypt(fk, em)
}
// DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS #1 v1.5.
// The random parameter is legacy and ignored, and it can be nil.
//
// Note that whether this function returns an error or not discloses secret
// information. If an attacker can cause this function to run repeatedly and
// learn whether each instance returned an error then they can decrypt and
// forge signatures as if they had the private key. See
// DecryptPKCS1v15SessionKey for a way of solving this problem.
func DecryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) ([]byte, error) {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return nil, err
}
if boring.Enabled {
bkey, err := boringPrivateKey(priv)
if err != nil {
return nil, err
}
out, err := boring.DecryptRSAPKCS1(bkey, ciphertext)
if err != nil {
return nil, ErrDecryption
}
return out, nil
}
valid, out, index, err := decryptPKCS1v15(priv, ciphertext)
if err != nil {
return nil, err
}
if valid == 0 {
return nil, ErrDecryption
}
return out[index:], nil
}
// DecryptPKCS1v15SessionKey decrypts a session key using RSA and the padding
// scheme from PKCS #1 v1.5. The random parameter is legacy and ignored, and it
// can be nil.
//
// DecryptPKCS1v15SessionKey returns an error if the ciphertext is the wrong
// length or if the ciphertext is greater than the public modulus. Otherwise, no
// error is returned. If the padding is valid, the resulting plaintext message
// is copied into key. Otherwise, key is unchanged. These alternatives occur in
// constant time. It is intended that the user of this function generate a
// random session key beforehand and continue the protocol with the resulting
// value.
//
// Note that if the session key is too small then it may be possible for an
// attacker to brute-force it. If they can do that then they can learn whether a
// random value was used (because it'll be different for the same ciphertext)
// and thus whether the padding was correct. This also defeats the point of this
// function. Using at least a 16-byte key will protect against this attack.
//
// This method implements protections against Bleichenbacher chosen ciphertext
// attacks [0] described in RFC 3218 Section 2.3.2 [1]. While these protections
// make a Bleichenbacher attack significantly more difficult, the protections
// are only effective if the rest of the protocol which uses
// DecryptPKCS1v15SessionKey is designed with these considerations in mind. In
// particular, if any subsequent operations which use the decrypted session key
// leak any information about the key (e.g. whether it is a static or random
// key) then the mitigations are defeated. This method must be used extremely
// carefully, and typically should only be used when absolutely necessary for
// compatibility with an existing protocol (such as TLS) that is designed with
// these properties in mind.
//
// - [0] “Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption
// Standard PKCS #1”, Daniel Bleichenbacher, Advances in Cryptology (Crypto '98)
// - [1] RFC 3218, Preventing the Million Message Attack on CMS,
// https://www.rfc-editor.org/rfc/rfc3218.html
func DecryptPKCS1v15SessionKey(random io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return err
}
k := priv.Size()
if k-(len(key)+3+8) < 0 {
return ErrDecryption
}
valid, em, index, err := decryptPKCS1v15(priv, ciphertext)
if err != nil {
return err
}
if len(em) != k {
// This should be impossible because decryptPKCS1v15 always
// returns the full slice.
return ErrDecryption
}
valid &= subtle.ConstantTimeEq(int32(len(em)-index), int32(len(key)))
subtle.ConstantTimeCopy(valid, key, em[len(em)-len(key):])
return nil
}
// decryptPKCS1v15 decrypts ciphertext using priv. It returns one or zero in
// valid that indicates whether the plaintext was correctly structured.
// In either case, the plaintext is returned in em so that it may be read
// independently of whether it was valid in order to maintain constant memory
// access patterns. If the plaintext was valid then index contains the index of
// the original message in em, to allow constant time padding removal.
func decryptPKCS1v15(priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) {
if fips140only.Enabled {
return 0, nil, 0, errors.New("crypto/rsa: use of PKCS#1 v1.5 encryption is not allowed in FIPS 140-only mode")
}
k := priv.Size()
if k < 11 {
err = ErrDecryption
return 0, nil, 0, err
}
if boring.Enabled {
var bkey *boring.PrivateKeyRSA
bkey, err = boringPrivateKey(priv)
if err != nil {
return 0, nil, 0, err
}
em, err = boring.DecryptRSANoPadding(bkey, ciphertext)
if err != nil {
return 0, nil, 0, ErrDecryption
}
} else {
fk, err := fipsPrivateKey(priv)
if err != nil {
return 0, nil, 0, err
}
em, err = rsa.DecryptWithoutCheck(fk, ciphertext)
if err != nil {
return 0, nil, 0, ErrDecryption
}
}
firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2)
// The remainder of the plaintext must be a string of non-zero random
// octets, followed by a 0, followed by the message.
// lookingForIndex: 1 iff we are still looking for the zero.
// index: the offset of the first zero byte.
lookingForIndex := 1
for i := 2; i < len(em); i++ {
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
}
// The PS padding must be at least 8 bytes long, and it starts two
// bytes into em.
validPS := subtle.ConstantTimeLessOrEq(2+8, index)
valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
index = subtle.ConstantTimeSelect(valid, index+1, 0)
return valid, em, index, nil
}
// nonZeroRandomBytes fills the given slice with non-zero random octets.
func nonZeroRandomBytes(s []byte, random io.Reader) (err error) {
_, err = io.ReadFull(random, s)
if err != nil {
return
}
for i := 0; i < len(s); i++ {
for s[i] == 0 {
_, err = io.ReadFull(random, s[i:i+1])
if err != nil {
return
}
// In tests, the PRNG may return all zeros so we do
// this to break the loop.
s[i] ^= 0x42
}
}
return
}