// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/crypto/rsa/pss.go // Copyright (c) 2009 The Go Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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 pss // This file implements the RSASSA-PSS signature scheme according to RFC 8017. import ( "crypto" "crypto/rsa" "errors" "hash" "io" ) // 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(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) { // See RFC 8017, Section 9.1.1. hLen := hash.Size() sLen := len(salt) emLen := (emBits + 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(mHash) != hLen { 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 emLen < hLen+sLen+2 { return nil, errors.New("crypto/rsa: key size too small for PSS signature") } em := make([]byte, emLen) psLen := emLen - sLen - hLen - 2 db := em[:psLen+1+sLen] h := em[psLen+1+sLen : emLen-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 prefix [8]byte hash.Write(prefix[:]) hash.Write(mHash) hash.Write(salt) h = hash.Sum(h[:0]) hash.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. db[psLen] = 0x01 copy(db[psLen+1:], salt) // 9. Let dbMask = MGF(H, emLen - hLen - 1). // // 10. Let maskedDB = DB \xor dbMask. mgf1XOR(db, hash, h) // 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in // maskedDB to zero. db[0] &= 0xff >> (8*emLen - emBits) // 12. Let EM = maskedDB || H || 0xbc. em[emLen-1] = 0xbc // 13. Output EM. return em, nil } func padPSSWithSalt(pub *rsa.PublicKey, hash crypto.Hash, hashed, salt []byte) ([]byte, error) { emBits := pub.N.BitLen() - 1 em, err := emsaPSSEncode(hashed, emBits, salt, hash.New()) if err != nil { return nil, err } return em, nil } func saltLength(opts *rsa.PSSOptions) int { if opts == nil { return rsa.PSSSaltLengthAuto } return opts.SaltLength } func PadPSS(rand io.Reader, pub *rsa.PublicKey, hash crypto.Hash, digest []byte, opts *rsa.PSSOptions) ([]byte, error) { if opts != nil && opts.Hash != 0 { hash = opts.Hash } saltLength := saltLength(opts) switch saltLength { case rsa.PSSSaltLengthAuto: saltLength = (pub.N.BitLen()-1+7)/8 - 2 - hash.Size() case rsa.PSSSaltLengthEqualsHash: saltLength = hash.Size() } salt := make([]byte, saltLength) if _, err := io.ReadFull(rand, salt); err != nil { return nil, err } return padPSSWithSalt(pub, hash, digest, salt) }