Source File
pss.go
Belonging Package
crypto/rsa
// Copyright 2013 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
// This file implements the RSASSA-PSS signature scheme according to RFC 8017.
import (
)
// Per RFC 8017, Section 9.1
//
// EM = MGF1 xor DB || H( 8*0x00 || mHash || salt ) || 0xbc
//
// where
//
// DB = PS || 0x01 || salt
//
// and PS can be empty so
//
// emLen = dbLen + hLen + 1 = psLen + sLen + hLen + 2
//
func emsaPSSEncode( []byte, int, []byte, hash.Hash) ([]byte, error) {
// See RFC 8017, Section 9.1.1.
:= .Size()
:= len()
:= ( + 7) / 8
// 1. If the length of M is greater than the input limitation for the
// hash function (2^61 - 1 octets for SHA-1), output "message too
// long" and stop.
//
// 2. Let mHash = Hash(M), an octet string of length hLen.
if len() != {
return nil, errors.New("crypto/rsa: input must be hashed with given hash")
}
// 3. If emLen < hLen + sLen + 2, output "encoding error" and stop.
if < ++2 {
return nil, ErrMessageTooLong
}
:= make([]byte, )
:= - - - 2
:= [:+1+]
:= [+1+ : -1]
// 4. Generate a random octet string salt of length sLen; if sLen = 0,
// then salt is the empty string.
//
// 5. Let
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
//
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets.
//
// 6. Let H = Hash(M'), an octet string of length hLen.
var [8]byte
.Write([:])
.Write()
.Write()
= .Sum([:0])
.Reset()
// 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
// zero octets. The length of PS may be 0.
//
// 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
// emLen - hLen - 1.
[] = 0x01
copy([+1:], )
// 9. Let dbMask = MGF(H, emLen - hLen - 1).
//
// 10. Let maskedDB = DB \xor dbMask.
mgf1XOR(, , )
// 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in
// maskedDB to zero.
[0] &= 0xff >> (8* - )
// 12. Let EM = maskedDB || H || 0xbc.
[-1] = 0xbc
// 13. Output EM.
return , nil
}
func emsaPSSVerify(, []byte, , int, hash.Hash) error {
// See RFC 8017, Section 9.1.2.
:= .Size()
if == PSSSaltLengthEqualsHash {
=
}
:= ( + 7) / 8
if != len() {
return errors.New("rsa: internal error: inconsistent length")
}
// 1. If the length of M is greater than the input limitation for the
// hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
// and stop.
//
// 2. Let mHash = Hash(M), an octet string of length hLen.
if != len() {
return ErrVerification
}
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
if < ++2 {
return ErrVerification
}
// 4. If the rightmost octet of EM does not have hexadecimal value
// 0xbc, output "inconsistent" and stop.
if [-1] != 0xbc {
return ErrVerification
}
// 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
// let H be the next hLen octets.
:= [:--1]
:= [--1 : -1]
// 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in
// maskedDB are not all equal to zero, output "inconsistent" and
// stop.
var byte = 0xff >> (8* - )
if [0] & ^ != 0 {
return ErrVerification
}
// 7. Let dbMask = MGF(H, emLen - hLen - 1).
//
// 8. Let DB = maskedDB \xor dbMask.
mgf1XOR(, , )
// 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
// to zero.
[0] &=
// If we don't know the salt length, look for the 0x01 delimiter.
if == PSSSaltLengthAuto {
:= bytes.IndexByte(, 0x01)
if < 0 {
return ErrVerification
}
= len() - - 1
}
// 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
// or if the octet at position emLen - hLen - sLen - 1 (the leftmost
// position is "position 1") does not have hexadecimal value 0x01,
// output "inconsistent" and stop.
:= - - - 2
for , := range [:] {
if != 0x00 {
return ErrVerification
}
}
if [] != 0x01 {
return ErrVerification
}
// 11. Let salt be the last sLen octets of DB.
:= [len()-:]
// 12. Let
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets.
//
// 13. Let H' = Hash(M'), an octet string of length hLen.
var [8]byte
.Write([:])
.Write()
.Write()
:= .Sum(nil)
// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
if !bytes.Equal(, ) { // TODO: constant time?
return ErrVerification
}
return nil
}
// signPSSWithSalt calculates the signature of hashed using PSS with specified salt.
// Note that hashed must be the result of hashing the input message using the
// given hash function. salt is a random sequence of bytes whose length will be
// later used to verify the signature.
func signPSSWithSalt( *PrivateKey, crypto.Hash, , []byte) ([]byte, error) {
:= .N.BitLen() - 1
, := emsaPSSEncode(, , , .New())
if != nil {
return nil,
}
if boring.Enabled {
, := boringPrivateKey()
if != nil {
return nil,
}
// Note: BoringCrypto always does decrypt "withCheck".
// (It's not just decrypt.)
, := boring.DecryptRSANoPadding(, )
if != nil {
return nil,
}
return , nil
}
// RFC 8017: "Note that the octet length of EM will be one less than k if
// modBits - 1 is divisible by 8 and equal to k otherwise, where k is the
// length in octets of the RSA modulus n." 🙄
//
// This is extremely annoying, as all other encrypt and decrypt inputs are
// always the exact same size as the modulus. Since it only happens for
// weird modulus sizes, fix it by padding inefficiently.
if , := len(), .Size(); < {
:= make([]byte, )
copy([-:], )
=
}
return decrypt(, , withCheck)
}
const (
// PSSSaltLengthAuto causes the salt in a PSS signature to be as large
// as possible when signing, and to be auto-detected when verifying.
PSSSaltLengthAuto = 0
// PSSSaltLengthEqualsHash causes the salt length to equal the length
// of the hash used in the signature.
PSSSaltLengthEqualsHash = -1
)
// PSSOptions contains options for creating and verifying PSS signatures.
type PSSOptions struct {
// SaltLength controls the length of the salt used in the PSS signature. It
// can either be a positive number of bytes, or one of the special
// PSSSaltLength constants.
SaltLength int
// Hash is the hash function used to generate the message digest. If not
// zero, it overrides the hash function passed to SignPSS. It's required
// when using PrivateKey.Sign.
Hash crypto.Hash
}
// HashFunc returns opts.Hash so that [PSSOptions] implements [crypto.SignerOpts].
func ( *PSSOptions) () crypto.Hash {
return .Hash
}
func ( *PSSOptions) () int {
if == nil {
return PSSSaltLengthAuto
}
return .SaltLength
}
var invalidSaltLenErr = errors.New("crypto/rsa: PSSOptions.SaltLength cannot be negative")
// SignPSS calculates the signature of digest using PSS.
//
// digest must be the result of hashing the input message using the given hash
// function. The opts argument may be nil, in which case sensible defaults are
// used. If opts.Hash is set, it overrides hash.
//
// The signature is randomized depending on the message, key, and salt size,
// using bytes from rand. Most applications should use [crypto/rand.Reader] as
// rand.
func ( io.Reader, *PrivateKey, crypto.Hash, []byte, *PSSOptions) ([]byte, error) {
// Note that while we don't commit to deterministic execution with respect
// to the rand stream, we also don't apply MaybeReadByte, so per Hyrum's Law
// it's probably relied upon by some. It's a tolerable promise because a
// well-specified number of random bytes is included in the signature, in a
// well-specified way.
if boring.Enabled && == boring.RandReader {
, := boringPrivateKey()
if != nil {
return nil,
}
return boring.SignRSAPSS(, , , .saltLength())
}
boring.UnreachableExceptTests()
if != nil && .Hash != 0 {
= .Hash
}
:= .saltLength()
switch {
case PSSSaltLengthAuto:
= (.N.BitLen()-1+7)/8 - 2 - .Size()
if < 0 {
return nil, ErrMessageTooLong
}
case PSSSaltLengthEqualsHash:
= .Size()
default:
// If we get here saltLength is either > 0 or < -1, in the
// latter case we fail out.
if <= 0 {
return nil, invalidSaltLenErr
}
}
:= make([]byte, )
if , := io.ReadFull(, ); != nil {
return nil,
}
return signPSSWithSalt(, , , )
}
// VerifyPSS verifies a PSS signature.
//
// A valid signature is indicated by returning a nil error. digest must be the
// result of hashing the input message using the given hash function. The opts
// argument may be nil, in which case sensible defaults are used. opts.Hash is
// ignored.
//
// The inputs are not considered confidential, and may leak through timing side
// channels, or if an attacker has control of part of the inputs.
func ( *PublicKey, crypto.Hash, []byte, []byte, *PSSOptions) error {
if boring.Enabled {
, := boringPublicKey()
if != nil {
return
}
if := boring.VerifyRSAPSS(, , , , .saltLength()); != nil {
return ErrVerification
}
return nil
}
if len() != .Size() {
return ErrVerification
}
// Salt length must be either one of the special constants (-1 or 0)
// or otherwise positive. If it is < PSSSaltLengthEqualsHash (-1)
// we return an error.
if .saltLength() < PSSSaltLengthEqualsHash {
return invalidSaltLenErr
}
:= .N.BitLen() - 1
:= ( + 7) / 8
, := encrypt(, )
if != nil {
return ErrVerification
}
// Like in signPSSWithSalt, deal with mismatches between emLen and the size
// of the modulus. The spec would have us wire emLen into the encoding
// function, but we'd rather always encode to the size of the modulus and
// then strip leading zeroes if necessary. This only happens for weird
// modulus sizes anyway.
for len() > && len() > 0 {
if [0] != 0 {
return ErrVerification
}
= [1:]
}
return emsaPSSVerify(, , , .saltLength(), .New())
}
The pages are generated with Golds v0.7.0-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |