public override bool VerifyHash(ReadOnlySpan <byte> hash, ReadOnlySpan <byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) { if (string.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } if (padding == RSASignaturePadding.Pkcs1 && padding == RSASignaturePadding.Pss) { throw PaddingModeNotSupported(); } RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm); SafeRsaHandle rsa = GetKey(); int requiredBytes = Interop.AndroidCrypto.RsaSize(rsa); if (signature.Length != requiredBytes) { return(false); } if (hash.Length != processor.HashLength) { return(false); } byte[] rented = CryptoPool.Rent(requiredBytes); Span <byte> unwrapped = new Span <byte>(rented, 0, requiredBytes); try { int ret = Interop.AndroidCrypto.RsaVerificationPrimitive(signature, unwrapped, rsa); CheckReturn(ret); if (ret == 0) { // Return value of 0 from RsaVerificationPrimitive indicates the signature could not be decrypted. return(false); } Debug.Assert( ret == requiredBytes, $"RsaVerificationPrimitive returned {ret} when {requiredBytes} was expected"); if (padding == RSASignaturePadding.Pkcs1) { byte[] repadRent = CryptoPool.Rent(unwrapped.Length); Span <byte> repadded = repadRent.AsSpan(0, requiredBytes); processor.PadPkcs1Signature(hash, repadded); bool valid = CryptographicOperations.FixedTimeEquals(repadded, unwrapped); CryptoPool.Return(repadRent, requiredBytes); return(valid); } else if (padding == RSASignaturePadding.Pss) { return(processor.VerifyPss(hash, unwrapped, KeySize)); } else { Debug.Fail("Padding mode should be checked prior to this point."); throw PaddingModeNotSupported(); } } finally { CryptoPool.Return(rented, requiredBytes); } throw PaddingModeNotSupported(); }
internal bool DepadOaep( ReadOnlySpan <byte> source, Span <byte> destination, out int bytesWritten) { // https://tools.ietf.org/html/rfc3447#section-7.1.2 using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName)) { Span <byte> lHash = stackalloc byte[_hLen]; if (!hasher.TryGetHashAndReset(lHash, out int hLen2) || hLen2 != _hLen) { Debug.Fail("TryGetHashAndReset failed with exact-size destination"); throw new CryptographicException(); } int y = source[0]; ReadOnlySpan <byte> maskedSeed = source.Slice(1, _hLen); ReadOnlySpan <byte> maskedDB = source.Slice(1 + _hLen); Span <byte> seed = stackalloc byte[_hLen]; // seedMask = MGF(maskedDB, hLen) Mgf1(hasher, maskedDB, seed); // seed = seedMask XOR maskedSeed Xor(seed, maskedSeed); byte[] tmp = CryptoPool.Rent(source.Length); try { Span <byte> dbMask = new Span <byte>(tmp, 0, maskedDB.Length); // dbMask = MGF(seed, k - hLen - 1) Mgf1(hasher, seed, dbMask); // DB = dbMask XOR maskedDB Xor(dbMask, maskedDB); ReadOnlySpan <byte> lHashPrime = dbMask.Slice(0, _hLen); int separatorPos = int.MaxValue; for (int i = dbMask.Length - 1; i >= _hLen; i--) { // if dbMask[i] is 1, val is 0. otherwise val is [01,FF] byte dbMinus1 = (byte)(dbMask[i] - 1); int val = dbMinus1; // if val is 0: FFFFFFFF & FFFFFFFF => FFFFFFFF // if val is any other byte value, val-1 will be in the range 00000000 to 000000FE, // and so the high bit will not be set. val = (~val & (val - 1)) >> 31; // if val is 0: separator = (0 & i) | (~0 & separator) => separator // else: separator = (~0 & i) | (0 & separator) => i // // Net result: non-branching "if (dbMask[i] == 1) separatorPos = i;" separatorPos = (val & i) | (~val & separatorPos); } bool lHashMatches = CryptographicOperations.FixedTimeEquals(lHash, lHashPrime); bool yIsZero = y == 0; bool separatorMadeSense = separatorPos < dbMask.Length; // This intentionally uses non-short-circuiting operations to hide the timing // differential between the three failure cases bool shouldContinue = lHashMatches & yIsZero & separatorMadeSense; if (!shouldContinue) { throw new CryptographicException(SR.Cryptography_OAEP_Decryption_Failed); } Span <byte> message = dbMask.Slice(separatorPos + 1); if (message.Length <= destination.Length) { message.CopyTo(destination); bytesWritten = message.Length; return(true); } else { bytesWritten = 0; return(false); } } finally { CryptoPool.Return(tmp, source.Length); } } }