mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 00:11:04 +00:00 
			
		
		
		
	* Switch to keybase go-crypto (for some elliptic curve key) + test
* Use assert.NoError 
and add a little more context to failing test description
* Use assert.(No)Error everywhere 🌈
and assert.Error in place of .Nil/.NotNil
		
	
			
		
			
				
	
	
		
			297 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			297 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
// 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 PSS signature scheme [1].
 | 
						|
//
 | 
						|
// [1] http://www.rsa.com/rsalabs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"errors"
 | 
						|
	"hash"
 | 
						|
	"io"
 | 
						|
	"math/big"
 | 
						|
)
 | 
						|
 | 
						|
func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
 | 
						|
	// See [1], 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 message")
 | 
						|
	}
 | 
						|
 | 
						|
	// 3.  If emLen < hLen + sLen + 2, output "encoding error" and stop.
 | 
						|
 | 
						|
	if emLen < hLen+sLen+2 {
 | 
						|
		return nil, errors.New("crypto/rsa: encoding error")
 | 
						|
	}
 | 
						|
 | 
						|
	em := make([]byte, emLen)
 | 
						|
	db := em[:emLen-sLen-hLen-2+1+sLen]
 | 
						|
	h := em[emLen-sLen-hLen-2+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[emLen-sLen-hLen-2] = 0x01
 | 
						|
	copy(db[emLen-sLen-hLen-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 >> uint(8*emLen-emBits))
 | 
						|
 | 
						|
	// 12. Let EM = maskedDB || H || 0xbc.
 | 
						|
	em[emLen-1] = 0xBC
 | 
						|
 | 
						|
	// 13. Output EM.
 | 
						|
	return em, nil
 | 
						|
}
 | 
						|
 | 
						|
func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
 | 
						|
	// 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.
 | 
						|
	hLen := hash.Size()
 | 
						|
	if hLen != len(mHash) {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
 | 
						|
	// 3.  If emLen < hLen + sLen + 2, output "inconsistent" and stop.
 | 
						|
	emLen := (emBits + 7) / 8
 | 
						|
	if emLen < hLen+sLen+2 {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
 | 
						|
	// 4.  If the rightmost octet of EM does not have hexadecimal value
 | 
						|
	//     0xbc, output "inconsistent" and stop.
 | 
						|
	if em[len(em)-1] != 0xBC {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
 | 
						|
	// 5.  Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
 | 
						|
	//     let H be the next hLen octets.
 | 
						|
	db := em[:emLen-hLen-1]
 | 
						|
	h := em[emLen-hLen-1 : len(em)-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.
 | 
						|
	if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
 | 
						|
	// 7.  Let dbMask = MGF(H, emLen - hLen - 1).
 | 
						|
	//
 | 
						|
	// 8.  Let DB = maskedDB \xor dbMask.
 | 
						|
	mgf1XOR(db, hash, h)
 | 
						|
 | 
						|
	// 9.  Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
 | 
						|
	//     to zero.
 | 
						|
	db[0] &= (0xFF >> uint(8*emLen-emBits))
 | 
						|
 | 
						|
	if sLen == PSSSaltLengthAuto {
 | 
						|
	FindSaltLength:
 | 
						|
		for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- {
 | 
						|
			switch db[emLen-hLen-sLen-2] {
 | 
						|
			case 1:
 | 
						|
				break FindSaltLength
 | 
						|
			case 0:
 | 
						|
				continue
 | 
						|
			default:
 | 
						|
				return ErrVerification
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if sLen < 0 {
 | 
						|
			return ErrVerification
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		// 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.
 | 
						|
		for _, e := range db[:emLen-hLen-sLen-2] {
 | 
						|
			if e != 0x00 {
 | 
						|
				return ErrVerification
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if db[emLen-hLen-sLen-2] != 0x01 {
 | 
						|
			return ErrVerification
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// 11.  Let salt be the last sLen octets of DB.
 | 
						|
	salt := db[len(db)-sLen:]
 | 
						|
 | 
						|
	// 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 prefix [8]byte
 | 
						|
	hash.Write(prefix[:])
 | 
						|
	hash.Write(mHash)
 | 
						|
	hash.Write(salt)
 | 
						|
 | 
						|
	h0 := hash.Sum(nil)
 | 
						|
 | 
						|
	// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
 | 
						|
	if !bytes.Equal(h0, h) {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// signPSSWithSalt calculates the signature of hashed using PSS [1] 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(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
 | 
						|
	nBits := priv.N.BitLen()
 | 
						|
	em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New())
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	m := new(big.Int).SetBytes(em)
 | 
						|
	c, err := decryptAndCheck(rand, priv, m)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	s = make([]byte, (nBits+7)/8)
 | 
						|
	copyWithLeftPad(s, c.Bytes())
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
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 number of bytes, or one of the special
 | 
						|
	// PSSSaltLength constants.
 | 
						|
	SaltLength int
 | 
						|
 | 
						|
	// Hash, if not zero, overrides the hash function passed to SignPSS.
 | 
						|
	// This is the only way to specify the hash function when using the
 | 
						|
	// crypto.Signer interface.
 | 
						|
	Hash crypto.Hash
 | 
						|
}
 | 
						|
 | 
						|
// HashFunc returns pssOpts.Hash so that PSSOptions implements
 | 
						|
// crypto.SignerOpts.
 | 
						|
func (pssOpts *PSSOptions) HashFunc() crypto.Hash {
 | 
						|
	return pssOpts.Hash
 | 
						|
}
 | 
						|
 | 
						|
func (opts *PSSOptions) saltLength() int {
 | 
						|
	if opts == nil {
 | 
						|
		return PSSSaltLengthAuto
 | 
						|
	}
 | 
						|
	return opts.SaltLength
 | 
						|
}
 | 
						|
 | 
						|
// SignPSS calculates the signature of hashed using RSASSA-PSS [1].
 | 
						|
// Note that hashed 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.
 | 
						|
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error) {
 | 
						|
	saltLength := opts.saltLength()
 | 
						|
	switch saltLength {
 | 
						|
	case PSSSaltLengthAuto:
 | 
						|
		saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size()
 | 
						|
	case PSSSaltLengthEqualsHash:
 | 
						|
		saltLength = hash.Size()
 | 
						|
	}
 | 
						|
 | 
						|
	if opts != nil && opts.Hash != 0 {
 | 
						|
		hash = opts.Hash
 | 
						|
	}
 | 
						|
 | 
						|
	salt := make([]byte, saltLength)
 | 
						|
	if _, err = io.ReadFull(rand, salt); err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	return signPSSWithSalt(rand, priv, hash, hashed, salt)
 | 
						|
}
 | 
						|
 | 
						|
// VerifyPSS verifies a PSS signature.
 | 
						|
// hashed is the result of hashing the input message using the given hash
 | 
						|
// function and sig is the signature. A valid signature is indicated by
 | 
						|
// returning a nil error. The opts argument may be nil, in which case sensible
 | 
						|
// defaults are used.
 | 
						|
func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error {
 | 
						|
	return verifyPSS(pub, hash, hashed, sig, opts.saltLength())
 | 
						|
}
 | 
						|
 | 
						|
// verifyPSS verifies a PSS signature with the given salt length.
 | 
						|
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
 | 
						|
	nBits := pub.N.BitLen()
 | 
						|
	if len(sig) != (nBits+7)/8 {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
	s := new(big.Int).SetBytes(sig)
 | 
						|
	m := encrypt(new(big.Int), pub, s)
 | 
						|
	emBits := nBits - 1
 | 
						|
	emLen := (emBits + 7) / 8
 | 
						|
	if emLen < len(m.Bytes()) {
 | 
						|
		return ErrVerification
 | 
						|
	}
 | 
						|
	em := make([]byte, emLen)
 | 
						|
	copyWithLeftPad(em, m.Bytes())
 | 
						|
	if saltLen == PSSSaltLengthEqualsHash {
 | 
						|
		saltLen = hash.Size()
 | 
						|
	}
 | 
						|
	return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New())
 | 
						|
}
 |