Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
8.4 KiB
297 lines
8.4 KiB
// 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()) |
|
}
|
|
|