// 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 and the RSAES-OAEP// encryption scheme according to RFC 8017, aka PKCS #1 v2.2.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//// incCounter increments a four byte, big-endian counter.func incCounter( *[4]byte) {if [3]++; [3] != 0 {return }if [2]++; [2] != 0 {return }if [1]++; [1] != 0 {return } [0]++}// mgf1XOR XORs the bytes in out with a mask generated using the MGF1 function// specified in PKCS #1 v2.1.func mgf1XOR( []byte, fips140.Hash, []byte) {var [4]bytevar []byte := 0for < len() { .Reset() .Write() .Write([0:4]) = .Sum([:0])for := 0; < len() && < len(); ++ { [] ^= [] ++ }incCounter(&) }}func emsaPSSEncode( []byte, int, []byte, fips140.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.iflen() != {returnnil, errors.New("crypto/rsa: input must be hashed with given hash") }// 3. If emLen < hLen + sLen + 2, output "encoding error" and stop.if < ++2 {returnnil, 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 .Reset() .Write([:]) .Write() .Write() = .Sum([:0])// 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. [] = 0x01copy([+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}const pssSaltLengthAutodetect = -1func emsaPSSVerify(, []byte, , int, fips140.Hash) error {// See RFC 8017, Section 9.1.2. := .Size() := ( + 7) / 8if != len() {returnerrors.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() {returnErrVerification }// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.if < ++2 {returnErrVerification }// 4. If the rightmost octet of EM does not have hexadecimal value // 0xbc, output "inconsistent" and stop.if [-1] != 0xbc {returnErrVerification }// 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.varbyte = 0xff >> (8* - )if [0] & ^ != 0 {returnErrVerification }// 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 == pssSaltLengthAutodetect { := bytes.IndexByte(, 0x01)if < 0 {returnErrVerification } = len() - - 1 }// FIPS 186-5, Section 5.4(g): "the length (in bytes) of the salt (sLen) // shall satisfy 0 ≤ sLen ≤ hLen".if > {fips140.RecordNonApproved() }// 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. := - - - 2for , := range [:] {if != 0x00 {returnErrVerification } }if [] != 0x01 {returnErrVerification }// 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. .Reset()var [8]byte .Write([:]) .Write() .Write() := .Sum(nil)// 14. If H = H', output "consistent." Otherwise, output "inconsistent."if !bytes.Equal(, ) { // TODO: constant time?returnErrVerification }returnnil}// PSSMaxSaltLength returns the maximum salt length for a given public key and// hash function.func ( *PublicKey, fips140.Hash) (int, error) { := (.N.BitLen()-1+7)/8 - 2 - .Size()if < 0 {return0, ErrMessageTooLong }// FIPS 186-5, Section 5.4(g): "the length (in bytes) of the salt (sLen) // shall satisfy 0 ≤ sLen ≤ hLen".iffips140.Enabled && > .Size() {return .Size(), nil }return , nil}// SignPSS calculates the signature of hashed using RSASSA-PSS.func ( io.Reader, *PrivateKey, fips140.Hash, []byte, int) ([]byte, error) {fipsSelfTest()fips140.RecordApproved()checkApprovedHash()// 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 < 0 {returnnil, errors.New("crypto/rsa: salt length cannot be negative") }// FIPS 186-5, Section 5.4(g): "the length (in bytes) of the salt (sLen) // shall satisfy 0 ≤ sLen ≤ hLen".if > .Size() {fips140.RecordNonApproved() } := make([]byte, )if := drbg.ReadWithReaderDeterministic(, ); != nil {returnnil, } := .pub.N.BitLen() - 1 , := emsaPSSEncode(, , , )if != nil {returnnil, }// 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(), .pub.Size(); < { := make([]byte, )copy([-:], ) = }returndecrypt(, , withCheck)}// VerifyPSS verifies sig with RSASSA-PSS automatically detecting the salt length.func ( *PublicKey, fips140.Hash, []byte, []byte) error {returnverifyPSS(, , , , pssSaltLengthAutodetect)}// VerifyPSS verifies sig with RSASSA-PSS and an expected salt length.func ( *PublicKey, fips140.Hash, []byte, []byte, int) error {if < 0 {returnerrors.New("crypto/rsa: salt length cannot be negative") }returnverifyPSS(, , , , )}func verifyPSS( *PublicKey, fips140.Hash, []byte, []byte, int) error {fipsSelfTest()fips140.RecordApproved()checkApprovedHash()if , := checkPublicKey(); != nil {return } elseif ! {fips140.RecordNonApproved() }iflen() != .Size() {returnErrVerification } := .N.BitLen() - 1 := ( + 7) / 8 , := encrypt(, )if != nil {returnErrVerification }// 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.forlen() > && len() > 0 {if [0] != 0 {returnErrVerification } = [1:] }returnemsaPSSVerify(, , , , )}func checkApprovedHash( fips140.Hash) {switch .(type) {case *sha256.Digest, *sha512.Digest, *sha3.Digest:default:fips140.RecordNonApproved() }}// EncryptOAEP encrypts the given message with RSAES-OAEP.func (, fips140.Hash, io.Reader, *PublicKey, []byte, []byte) ([]byte, error) {// Note that while we don't commit to deterministic execution with respect // to the random 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 ciphertext, in a // well-specified way.fipsSelfTest()fips140.RecordApproved()checkApprovedHash()if , := checkPublicKey(); != nil {returnnil, } elseif ! {fips140.RecordNonApproved() } := .Size()iflen() > -2*.Size()-2 {returnnil, ErrMessageTooLong } .Reset() .Write() := .Sum(nil) := make([]byte, ) := [1 : 1+.Size()] := [1+.Size():]copy([0:.Size()], ) [len()-len()-1] = 1copy([len()-len():], )if := drbg.ReadWithReaderDeterministic(, ); != nil {returnnil, }mgf1XOR(, , )mgf1XOR(, , )returnencrypt(, )}// DecryptOAEP decrypts ciphertext using RSAES-OAEP.func (, fips140.Hash, *PrivateKey, []byte, []byte) ([]byte, error) {fipsSelfTest()fips140.RecordApproved()checkApprovedHash() := .pub.Size()iflen() > || < .Size()*2+2 {returnnil, ErrDecryption } , := decrypt(, , noCheck)if != nil {returnnil, } .Reset() .Write() := .Sum(nil) := subtle.ConstantTimeByteEq([0], 0) := [1 : .Size()+1] := [.Size()+1:]mgf1XOR(, , )mgf1XOR(, , ) := [0:.Size()]// We have to validate the plaintext in constant time in order to avoid // attacks like: J. Manger. A Chosen Ciphertext Attack on RSA Optimal // Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1 // v2.0. In J. Kilian, editor, Advances in Cryptology. := subtle.ConstantTimeCompare(, )// The remainder of the plaintext must be zero or more 0x00, followed // by 0x01, followed by the message. // lookingForIndex: 1 iff we are still looking for the 0x01 // index: the offset of the first 0x01 byte // invalid: 1 iff we saw a non-zero byte before the 0x01.var , , int = 1 := [.Size():]for := 0; < len(); ++ { := subtle.ConstantTimeByteEq([], 0) := subtle.ConstantTimeByteEq([], 1) = subtle.ConstantTimeSelect(&, , ) = subtle.ConstantTimeSelect(, 0, ) = subtle.ConstantTimeSelect(&^, 1, ) }if &&^&^ != 1 {returnnil, ErrDecryption }return [+1:], nil}
The pages are generated with Goldsv0.7.3-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.