// Decrypt implements the crypto.Decrypter operation for the given key. func (key *PrivateKey) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) ([]byte, error) { switch opts := opts.(type) { case *rsa.PKCS1v15DecryptOptions: ptxt, decyptErr := key.execute(gokeyless.OpRSADecrypt, msg) // If opts.SessionKeyLen is set, we must perform a variation of // rsa.DecryptPKCS1v15SessionKey to ensure the entire operation // is performed in constant time regardless of padding errors. if l := opts.SessionKeyLen; l > 0 { plaintext := make([]byte, l) if _, err := io.ReadFull(rand, plaintext); err != nil { return nil, err } valid := subtle.ConstantTimeEq(int32(len(ptxt)), int32(l)) v2 := subtle.ConstantTimeLessOrEq(l, len(ptxt)) l2 := subtle.ConstantTimeSelect(v2, l, len(ptxt)) subtle.ConstantTimeCopy(valid, plaintext[:l2], ptxt[:l2]) return plaintext, nil } // Otherwise, we can just return the error like rsa.DecryptPKCS1v15. return ptxt, decyptErr default: return nil, errors.New("invalid options for Decrypt") } }
// Decrypt implements the crypto.Decrypter operation for the given key. func (key *PrivateKey) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) ([]byte, error) { opts1v15, ok := opts.(*rsa.PKCS1v15DecryptOptions) if opts != nil && !ok { return nil, errors.New("invalid options for Decrypt") } ptxt, err := key.execute(gokeyless.OpRSADecrypt, msg) if err != nil { return nil, err } if ok { // If opts.SessionKeyLen is set, we must perform a variation of // rsa.DecryptPKCS1v15SessionKey to ensure the entire operation // is performed in constant time regardless of padding errors. if l := opts1v15.SessionKeyLen; l > 0 { plaintext := make([]byte, l) if _, err := io.ReadFull(rand, plaintext); err != nil { return nil, err } valid := subtle.ConstantTimeEq(int32(len(ptxt)), int32(l)) v2 := subtle.ConstantTimeLessOrEq(l, len(ptxt)) l2 := subtle.ConstantTimeSelect(v2, l, len(ptxt)) subtle.ConstantTimeCopy(valid, plaintext[:l2], ptxt[:l2]) return plaintext, nil } } return ptxt, nil }
// pkcsUnpad implements PKCS#7 un-padding. If the padding is valid a valid value of 1 is returned. // If the padding is invalid, valid is returned as 0. Any unpaddeddata value should not be used // if pcksUnpad determines the padding is invalid. A logic error returns an error. If you // have not already authenticated the ciphertext, reporting a padding error, even through side channels // (like timing), leaves you open to padding oracle attacks, so beware. // // I am implementing pkcsPad with constant time operations to forestall timing attacks that might // be used to create a padding oracle. Since this package always authenticates first, // timing and padding oracle attacks are ineffective because ciphertexts cannot be // forged or manipulated with more than insignificant probability of success. // In such a case constant time operation is unimportant, but constant timing may be important if this // code is reused elsewhere. func pkcsUnpad(data []byte, blocklen int) (valid int, unpaddeddata []byte, err error) { if blocklen > math.MaxUint8 { err = fmt.Errorf("Unpadding error: Blocklen %d exceeds maximum one byte unsigned integer", blocklen, math.MaxUint8) return } origlen := len(data) if origlen < blocklen { err = fmt.Errorf("Unpadding error: Data length %d is less than blocklen %d", origlen, blocklen) return } if origlen%blocklen != 0 { err = fmt.Errorf("Unpadding error: Data length %d is not a multiple of blocklen %d", origlen, blocklen) return } padchar := data[origlen-1] padcharlen := int(padchar) datalen := origlen - padcharlen valid = subtle.ConstantTimeLessOrEq(padcharlen, blocklen) for i := 1; i <= blocklen; i++ { // valid = (i > padcharlen || data[origlen-i] == padchar) && valid iLePadcharlen := subtle.ConstantTimeLessOrEq(i, padcharlen) isPadChar := subtle.ConstantTimeByteEq(data[origlen-i], padchar) stillvalid := subtle.ConstantTimeSelect(iLePadcharlen, isPadChar, 1) valid &= stillvalid } unpaddeddata = data[:datalen] // This data should not be used if invalid. // Returning it in any case simplifies constant timing return }
func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, msg []byte, err error) { k := (priv.N.BitLen() + 7) / 8 if k < 11 { err = ErrDecryption return } c := new(big.Int).SetBytes(ciphertext) m, err := decrypt(rand, priv, c) if err != nil { return } em := leftPad(m.Bytes(), k) 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. var lookingForIndex, index int 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 msg = em[index+1:] return }