private static SafeSecKeyRefHandle ImportKey(RSAParameters parameters)
            {
                AsnWriter keyWriter;
                bool      hasPrivateKey;

                if (parameters.D != null)
                {
                    keyWriter     = RSAKeyFormatHelper.WritePkcs1PrivateKey(parameters);
                    hasPrivateKey = true;
                }
                else
                {
                    keyWriter     = RSAKeyFormatHelper.WritePkcs1PublicKey(parameters);
                    hasPrivateKey = false;
                }

                byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength());

                if (!keyWriter.TryEncode(rented, out int written))
                {
                    Debug.Fail("TryEncode failed with a pre-allocated buffer");
                    throw new InvalidOperationException();
                }

                // Explicitly clear the inner buffer
                keyWriter.Reset();

                try
                {
                    return(Interop.AppleCrypto.CreateDataKey(
                               rented.AsSpan(0, written),
                               Interop.AppleCrypto.PAL_KeyAlgorithm.RSA,
                               isPublic: !hasPrivateKey));
                }
                finally
                {
                    CryptoPool.Return(rented, written);
                }
            }
Exemple #2
0
        public virtual bool VerifyData(ReadOnlySpan <byte> data, ReadOnlySpan <byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
        {
            ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));
            ArgumentNullException.ThrowIfNull(padding);

            for (int i = 256; ; i = checked (i * 2))
            {
                int    hashLength = 0;
                byte[] hash       = CryptoPool.Rent(i);
                try
                {
                    if (TryHashData(data, hash, hashAlgorithm, out hashLength))
                    {
                        return(VerifyHash(new ReadOnlySpan <byte>(hash, 0, hashLength), signature, hashAlgorithm, padding));
                    }
                }
                finally
                {
                    CryptoPool.Return(hash, hashLength);
                }
            }
        }
        public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }
            if (padding == null)
            {
                throw new ArgumentNullException(nameof(padding));
            }

            Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor);
            SafeRsaHandle             key        = GetKey();

            int rsaSize = Interop.Crypto.RsaSize(key);

            byte[]? buf = null;
            Span <byte> destination = default;

            try
            {
                buf         = CryptoPool.Rent(rsaSize);
                destination = new Span <byte>(buf, 0, rsaSize);

                if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten))
                {
                    Debug.Fail($"{nameof(TryDecrypt)} should not return false for RSA_size buffer");
                    throw new CryptographicException();
                }

                return(destination.Slice(0, bytesWritten).ToArray());
            }
            finally
            {
                CryptographicOperations.ZeroMemory(destination);
                CryptoPool.Return(buf !, clearSize: 0);
            }
        }
Exemple #4
0
        public int TransformFinal(ReadOnlySpan <byte> input, Span <byte> output)
        {
#if DEBUG
            if (_isFinalized)
            {
                Debug.Fail("Cipher was reused without being reset.");
                throw new CryptographicException();
            }

            _isFinalized = true;
#endif

            Debug.Assert((input.Length % PaddingSizeInBytes) == 0);
            Debug.Assert(input.Length <= output.Length);

            int written = 0;

            if (input.Overlaps(output, out int offset) && offset != 0)
            {
                byte[] rented = CryptoPool.Rent(output.Length);

                try
                {
                    written = ProcessFinalBlock(input, rented);
                    rented.AsSpan(0, written).CopyTo(output);
                }
                finally
                {
                    CryptoPool.Return(rented, clearSize: written);
                }
            }
            else
            {
                written = ProcessFinalBlock(input, output);
            }

            return(written);
        }
Exemple #5
0
        public override unsafe bool TryExportSubjectPublicKeyInfo(Span <byte> destination, out int bytesWritten)
        {
            // The PKCS1 RSAPublicKey format is just the modulus (KeySize bits) and Exponent (usually 3 bytes),
            // with each field having up to 7 bytes of overhead and then up to 6 extra bytes of overhead for the
            // SEQUENCE tag.
            //
            // So KeySize / 4 is ideally enough to start.
            int rentSize = KeySize / 4;

            while (true)
            {
                byte[] rented = CryptoPool.Rent(rentSize);
                rentSize = rented.Length;
                int pkcs1Size = 0;

                fixed(byte *rentPtr = rented)
                {
                    try
                    {
                        if (!TryExportRSAPublicKey(rented, out pkcs1Size))
                        {
                            rentSize = checked (rentSize * 2);
                            continue;
                        }

                        using (AsnWriter writer = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(rented.AsSpan(0, pkcs1Size)))
                        {
                            return(writer.TryEncode(destination, out bytesWritten));
                        }
                    }
                    finally
                    {
                        CryptoPool.Return(rented, pkcs1Size);
                    }
                }
            }
        }
        private static SafeSecKeyRefHandle ImportKey(ECParameters parameters)
        {
            AsnWriter keyWriter;
            bool      hasPrivateKey;

            if (parameters.D != null)
            {
                keyWriter     = EccKeyFormatHelper.WriteECPrivateKey(parameters);
                hasPrivateKey = true;
            }
            else
            {
                keyWriter     = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters);
                hasPrivateKey = false;
            }

            byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength());

            if (!keyWriter.TryEncode(rented, out int written))
            {
                Debug.Fail("TryEncode failed with a pre-allocated buffer");
                throw new InvalidOperationException();
            }

            // Explicitly clear the inner buffer
            keyWriter.Reset();

            try
            {
                return(Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey));
            }
            finally
            {
                CryptoPool.Return(rented, written);
            }
        }
Exemple #7
0
        public static unsafe bool TryExportToEncryptedPem <T>(
            T arg,
            ReadOnlySpan <char> password,
            PbeParameters pbeParameters,
            TryExportEncryptedKeyAction <T> exporter,
            Span <char> destination,
            out int charsWritten)
        {
            int bufferSize = 4096;

            while (true)
            {
                byte[] buffer       = CryptoPool.Rent(bufferSize);
                int    bytesWritten = 0;
                bufferSize = buffer.Length;

                // Fixed to prevent GC moves.
                fixed(byte *bufferPtr = buffer)
                {
                    try
                    {
                        if (exporter(arg, password, pbeParameters, buffer, out bytesWritten))
                        {
                            Span <byte> writtenSpan = new Span <byte>(buffer, 0, bytesWritten);
                            return(PemEncoding.TryWrite(PemLabels.EncryptedPkcs8PrivateKey, writtenSpan, destination, out charsWritten));
                        }
                    }
                    finally
                    {
                        CryptoPool.Return(buffer, bytesWritten);
                    }

                    bufferSize = checked (bufferSize * 2);
                }
            }
        }
Exemple #8
0
        private static SafeSecKeyRefHandle ImportLegacyPrivateKey(ref ECParameters parameters)
        {
            AsnWriter keyWriter = EccKeyFormatHelper.WriteECPrivateKey(parameters);

            byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength());

            if (!keyWriter.TryEncode(rented, out int written))
            {
                Debug.Fail("TryEncode failed with a pre-allocated buffer");
                throw new InvalidOperationException();
            }

            // Explicitly clear the inner buffer
            keyWriter.Reset();

            try
            {
                return(Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), true));
            }
            finally
            {
                CryptoPool.Return(rented, written);
            }
        }
Exemple #9
0
            private bool TrySignHash(
                ReadOnlySpan <byte> hash,
                Span <byte> destination,
                HashAlgorithmName hashAlgorithm,
                RSASignaturePadding padding,
                bool allocateSignature,
                out int bytesWritten,
                out byte[]?signature)
            {
                Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name));
                Debug.Assert(padding != null);
                signature = null;

                if (padding == RSASignaturePadding.Pkcs1 && padding == RSASignaturePadding.Pss)
                {
                    throw PaddingModeNotSupported();
                }

                RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
                SafeRsaHandle       rsa       = GetKey();

                int bytesRequired = Interop.AndroidCrypto.RsaSize(rsa);

                if (allocateSignature)
                {
                    Debug.Assert(destination.Length == 0);
                    signature   = new byte[bytesRequired];
                    destination = signature;
                }

                if (destination.Length < bytesRequired)
                {
                    bytesWritten = 0;
                    return(false);
                }

                byte[]      encodedRented = CryptoPool.Rent(bytesRequired);
                Span <byte> encodedBytes  = new Span <byte>(encodedRented, 0, bytesRequired);

                if (padding.Mode == RSASignaturePaddingMode.Pkcs1)
                {
                    processor.PadPkcs1Signature(hash, encodedBytes);
                }
                else if (padding.Mode == RSASignaturePaddingMode.Pss)
                {
                    processor.EncodePss(hash, encodedBytes, KeySize);
                }
                else
                {
                    Debug.Fail("Padding mode should be checked prior to this point.");
                    throw PaddingModeNotSupported();
                }

                int ret = Interop.AndroidCrypto.RsaSignPrimitive(encodedBytes, destination, rsa);

                CryptoPool.Return(encodedRented, bytesRequired);

                CheckReturn(ret);

                Debug.Assert(
                    ret == bytesRequired,
                    $"RsaSignPrimitive returned {ret} when {bytesRequired} was expected");

                bytesWritten = ret;
                return(true);
            }
Exemple #10
0
        private bool TrySignHash(
            ReadOnlySpan <byte> hash,
            Span <byte> destination,
            HashAlgorithmName hashAlgorithm,
            RSASignaturePadding padding,
            bool allocateSignature,
            out int bytesWritten,
            out byte[] signature)
        {
            Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name));
            Debug.Assert(padding != null);

            signature = null;

            // Do not factor out getting _key.Value, since the key creation should not happen on
            // invalid padding modes.

            if (padding.Mode == RSASignaturePaddingMode.Pkcs1)
            {
                int           algorithmNid = GetAlgorithmNid(hashAlgorithm);
                SafeRsaHandle rsa          = _key.Value;

                int bytesRequired = Interop.Crypto.RsaSize(rsa);

                if (allocateSignature)
                {
                    Debug.Assert(destination.Length == 0);
                    signature   = new byte[bytesRequired];
                    destination = signature;
                }

                if (destination.Length < bytesRequired)
                {
                    bytesWritten = 0;
                    return(false);
                }

                if (!Interop.Crypto.RsaSign(algorithmNid, hash, hash.Length, destination, out int signatureSize, rsa))
                {
                    throw Interop.Crypto.CreateOpenSslCryptographicException();
                }

                Debug.Assert(
                    signatureSize == bytesRequired,
                    $"RSA_sign reported signatureSize was {signatureSize}, when {bytesRequired} was expected");

                bytesWritten = signatureSize;
                return(true);
            }
            else if (padding.Mode == RSASignaturePaddingMode.Pss)
            {
                RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
                SafeRsaHandle       rsa       = _key.Value;

                int bytesRequired = Interop.Crypto.RsaSize(rsa);

                if (allocateSignature)
                {
                    Debug.Assert(destination.Length == 0);
                    signature   = new byte[bytesRequired];
                    destination = signature;
                }

                if (destination.Length < bytesRequired)
                {
                    bytesWritten = 0;
                    return(false);
                }

                byte[]      pssRented = CryptoPool.Rent(bytesRequired);
                Span <byte> pssBytes  = new Span <byte>(pssRented, 0, bytesRequired);

                processor.EncodePss(hash, pssBytes, KeySize);

                int ret = Interop.Crypto.RsaSignPrimitive(pssBytes, destination, rsa);

                CryptoPool.Return(pssRented, bytesRequired);

                CheckReturn(ret);

                Debug.Assert(
                    ret == bytesRequired,
                    $"RSA_private_encrypt returned {ret} when {bytesRequired} was expected");

                bytesWritten = ret;
                return(true);
            }

            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);
                }
            }
        }
            public override bool VerifyHash(ReadOnlySpan <byte> hash, ReadOnlySpan <byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
            {
                ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));
                ArgumentNullException.ThrowIfNull(padding);

                ThrowIfDisposed();

                if (padding == RSASignaturePadding.Pkcs1)
                {
                    Interop.AppleCrypto.PAL_HashAlgorithm palAlgId =
                        PalAlgorithmFromAlgorithmName(hashAlgorithm, out _);
                    return(Interop.AppleCrypto.VerifySignature(
                               GetKeys().PublicKey,
                               hash,
                               signature,
                               palAlgId,
                               Interop.AppleCrypto.PAL_SignatureAlgorithm.RsaPkcs1));
                }
                else if (padding.Mode == RSASignaturePaddingMode.Pss)
                {
                    SafeSecKeyRefHandle publicKey = GetKeys().PublicKey;

                    int keySize = KeySize;
                    int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(keySize);

                    if (signature.Length != rsaSize)
                    {
                        return(false);
                    }

                    if (hash.Length != RsaPaddingProcessor.HashLength(hashAlgorithm))
                    {
                        return(false);
                    }

                    byte[]      rented    = CryptoPool.Rent(rsaSize);
                    Span <byte> unwrapped = new Span <byte>(rented, 0, rsaSize);

                    try
                    {
                        if (!Interop.AppleCrypto.TryRsaVerificationPrimitive(
                                publicKey,
                                signature,
                                unwrapped,
                                out int bytesWritten))
                        {
                            Debug.Fail($"TryRsaVerificationPrimitive with a pre-allocated buffer");
                            throw new CryptographicException();
                        }

                        Debug.Assert(bytesWritten == rsaSize);
                        return(RsaPaddingProcessor.VerifyPss(hashAlgorithm, hash, unwrapped, keySize));
                    }
                    finally
                    {
                        CryptographicOperations.ZeroMemory(unwrapped);
                        CryptoPool.Return(rented, clearSize: 0);
                    }
                }

                throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
            }
Exemple #13
0
 public void Dispose()
 {
     CryptoPool.Return(this.memory, this.clearAll ? -1 : 0);
 }
Exemple #14
0
        private void EncryptCore(
            ReadOnlySpan <byte> nonce,
            ReadOnlySpan <byte> plaintext,
            Span <byte> ciphertext,
            Span <byte> tag,
            ReadOnlySpan <byte> associatedData = default)
        {
            if (!Interop.Crypto.CipherSetTagLength(_ctxHandle, tag.Length))
            {
                throw new CryptographicException();
            }

            Interop.Crypto.EvpCipherSetKeyAndIV(
                _ctxHandle,
                Span <byte> .Empty,
                nonce,
                Interop.Crypto.EvpCipherDirection.Encrypt);

            if (associatedData.Length != 0)
            {
                Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData);
            }

            byte[]? rented = null;
            try
            {
                scoped Span <byte> ciphertextAndTag;

                // Arbitrary limit.
                const int StackAllocMax = 128;
                if (checked (ciphertext.Length + tag.Length) <= StackAllocMax)
                {
                    ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length];
                }
                else
                {
                    rented           = CryptoPool.Rent(ciphertext.Length + tag.Length);
                    ciphertextAndTag = new Span <byte>(rented, 0, ciphertext.Length + tag.Length);
                }

                if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, ciphertextAndTag, out int ciphertextBytesWritten, plaintext))
                {
                    throw new CryptographicException();
                }

                if (!Interop.Crypto.EvpCipherFinalEx(
                        _ctxHandle,
                        ciphertextAndTag.Slice(ciphertextBytesWritten),
                        out int bytesWritten))
                {
                    throw new CryptographicException();
                }

                ciphertextBytesWritten += bytesWritten;

                // NOTE: Android appends tag to the end of the ciphertext in case of CCM/GCM and "encryption" mode

                if (ciphertextBytesWritten != ciphertextAndTag.Length)
                {
                    Debug.Fail($"GCM encrypt wrote {ciphertextBytesWritten} of {ciphertextAndTag.Length} bytes.");
                    throw new CryptographicException();
                }

                ciphertextAndTag[..ciphertext.Length].CopyTo(ciphertext);
        internal void PadOaep(
            ReadOnlySpan <byte> source,
            Span <byte> destination)
        {
            // https://tools.ietf.org/html/rfc3447#section-7.1.1

            byte[]? dbMask = null;
            Span <byte> dbMaskSpan = Span <byte> .Empty;

            try
            {
                // Since the biggest known _hLen is 512/8 (64) and destination.Length is 0 or more,
                // this shouldn't underflow without something having severely gone wrong.
                int maxInput = checked (destination.Length - _hLen - _hLen - 2);

                // 1(a) does not apply, we do not allow custom label values.

                // 1(b)
                if (source.Length > maxInput)
                {
                    throw new CryptographicException(
                              SR.Format(SR.Cryptography_Encryption_MessageTooLong, maxInput));
                }

                // The final message (step 2(i)) will be
                // 0x00 || maskedSeed (hLen long) || maskedDB (rest of the buffer)
                Span <byte> seed = destination.Slice(1, _hLen);
                Span <byte> db   = destination.Slice(1 + _hLen);

                using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
                {
                    // DB = lHash || PS || 0x01 || M
                    Span <byte> lHash = db.Slice(0, _hLen);
                    Span <byte> mDest = db.Slice(db.Length - source.Length);
                    Span <byte> ps    = db.Slice(_hLen, db.Length - _hLen - 1 - mDest.Length);
                    Span <byte> psEnd = db.Slice(_hLen + ps.Length, 1);

                    // 2(a) lHash = Hash(L), where L is the empty string.
                    if (!hasher.TryGetHashAndReset(lHash, out int hLen2) || hLen2 != _hLen)
                    {
                        Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                        throw new CryptographicException();
                    }

                    // 2(b) generate a padding string of all zeros equal to the amount of unused space.
                    ps.Clear();

                    // 2(c)
                    psEnd[0] = 0x01;

                    // still 2(c)
                    source.CopyTo(mDest);

                    // 2(d)
                    RandomNumberGenerator.Fill(seed);

                    // 2(e)
                    dbMask     = CryptoPool.Rent(db.Length);
                    dbMaskSpan = new Span <byte>(dbMask, 0, db.Length);
                    Mgf1(hasher, seed, dbMaskSpan);

                    // 2(f)
                    Xor(db, dbMaskSpan);

                    // 2(g)
                    Span <byte> seedMask = stackalloc byte[_hLen];
                    Mgf1(hasher, db, seedMask);

                    // 2(h)
                    Xor(seed, seedMask);

                    // 2(i)
                    destination[0] = 0;
                }
            }
            catch (Exception e) when(!(e is CryptographicException))
            {
                Debug.Fail("Bad exception produced from OAEP padding: " + e);
                throw new CryptographicException();
            }
            finally
            {
                if (dbMask != null)
                {
                    CryptographicOperations.ZeroMemory(dbMaskSpan);
                    CryptoPool.Return(dbMask, clearSize: 0);
                }
            }
        }
        internal bool VerifyPss(ReadOnlySpan <byte> mHash, ReadOnlySpan <byte> em, int keySize)
        {
            // https://tools.ietf.org/html/rfc3447#section-9.1.2

            int emBits = keySize - 1;
            int emLen  = BytesRequiredForBitCount(emBits);

            if (mHash.Length != _hLen)
            {
                return(false);
            }

            Debug.Assert(em.Length >= emLen);

            // In this implementation, sLen is restricted to hLen.
            int sLen = _hLen;

            // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
            if (emLen < _hLen + sLen + 2)
            {
                return(false);
            }

            // 4. If the last byte is not 0xBC, output "inconsistent" and stop.
            if (em[em.Length - 1] != 0xBC)
            {
                return(false);
            }

            // 5. maskedDB is the leftmost emLen - hLen -1 bytes, H is the next hLen bytes.
            int dbLen = emLen - _hLen - 1;

            ReadOnlySpan <byte> maskedDb = em.Slice(0, dbLen);
            ReadOnlySpan <byte> h        = em.Slice(dbLen, _hLen);

            // 6. If the unused bits aren't zero, output "inconsistent" and stop.
            int  unusedBits   = 8 * emLen - emBits;
            byte usedBitsMask = (byte)(0xFF >> unusedBits);

            if ((maskedDb[0] & usedBitsMask) != maskedDb[0])
            {
                return(false);
            }

            // 7. dbMask = MGF(H, emLen - hLen - 1)
            byte[]      dbMaskRented = CryptoPool.Rent(maskedDb.Length);
            Span <byte> dbMask       = new Span <byte>(dbMaskRented, 0, maskedDb.Length);

            try
            {
                using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
                {
                    Mgf1(hasher, h, dbMask);

                    // 8. DB = maskedDB XOR dbMask
                    Xor(dbMask, maskedDb);

                    // 9. Set the unused bits of DB to 0
                    dbMask[0] &= usedBitsMask;

                    // 10 ("a"): If the emLen - hLen - sLen - 2 leftmost bytes are not 0,
                    // output "inconsistent" and stop.
                    //
                    // Since signature verification is a public key operation there's no need to
                    // use fixed time equality checking here.
                    for (int i = emLen - _hLen - sLen - 2 - 1; i >= 0; --i)
                    {
                        if (dbMask[i] != 0)
                        {
                            return(false);
                        }
                    }

                    // 10 ("b") If the octet at position emLen - hLen - sLen - 1 (under a 1-indexed scheme)
                    // is not 0x01, output "inconsistent" and stop.
                    if (dbMask[emLen - _hLen - sLen - 2] != 0x01)
                    {
                        return(false);
                    }

                    // 11. Let salt be the last sLen octets of DB.
                    ReadOnlySpan <byte> salt = dbMask.Slice(dbMask.Length - sLen);

                    // 12/13. Let H' = Hash(eight zeros || mHash || salt)
                    hasher.AppendData(EightZeros);
                    hasher.AppendData(mHash);
                    hasher.AppendData(salt);

                    Span <byte> hPrime = stackalloc byte[_hLen];

                    if (!hasher.TryGetHashAndReset(hPrime, out int hLen2) || hLen2 != _hLen)
                    {
                        Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                        throw new CryptographicException();
                    }

                    // 14. If H = H' output "consistent". Otherwise, output "inconsistent"
                    //
                    // Since this is a public key operation, no need to provide fixed time
                    // checking.
                    return(h.SequenceEqual(hPrime));
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(dbMask);
                CryptoPool.Return(dbMaskRented, clearSize: 0);
            }
        }
        internal void EncodePss(ReadOnlySpan <byte> mHash, Span <byte> destination, int keySize)
        {
            // https://tools.ietf.org/html/rfc3447#section-9.1.1
            int emBits = keySize - 1;
            int emLen  = BytesRequiredForBitCount(emBits);

            if (mHash.Length != _hLen)
            {
                throw new CryptographicException(SR.Cryptography_SignHash_WrongSize);
            }

            // In this implementation, sLen is restricted to the length of the input hash.
            int sLen = _hLen;

            // 3.  if emLen < hLen + sLen + 2, encoding error.
            //
            // sLen = hLen in this implementation.

            if (emLen < 2 + _hLen + sLen)
            {
                throw new CryptographicException(SR.Cryptography_KeyTooSmall);
            }

            // Set any leading bytes to zero, since that will be required for the pending
            // RSA operation.
            destination.Slice(0, destination.Length - emLen).Clear();

            // 12. Let EM = maskedDB || H || 0xbc (H has length hLen)
            Span <byte> em = destination.Slice(destination.Length - emLen, emLen);

            int dbLen = emLen - _hLen - 1;

            Span <byte> db    = em.Slice(0, dbLen);
            Span <byte> hDest = em.Slice(dbLen, _hLen);

            em[emLen - 1] = 0xBC;

            byte[]      dbMaskRented = CryptoPool.Rent(dbLen);
            Span <byte> dbMask       = new Span <byte>(dbMaskRented, 0, dbLen);

            using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
            {
                // 4. Generate a random salt of length sLen
                Span <byte> salt = stackalloc byte[sLen];
                RandomNumberGenerator.Fill(salt);

                // 5. Let M' = an octet string of 8 zeros concat mHash concat salt
                // 6. Let H = Hash(M')

                hasher.AppendData(EightZeros);
                hasher.AppendData(mHash);
                hasher.AppendData(salt);

                if (!hasher.TryGetHashAndReset(hDest, out int hLen2) || hLen2 != _hLen)
                {
                    Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                    throw new CryptographicException();
                }

                // 7. Generate PS as zero-valued bytes of length emLen - sLen - hLen - 2.
                // 8. Let DB = PS || 0x01 || salt
                int psLen = emLen - sLen - _hLen - 2;
                db.Slice(0, psLen).Clear();
                db[psLen] = 0x01;
                salt.CopyTo(db.Slice(psLen + 1));

                // 9. Let dbMask = MGF(H, emLen - hLen - 1)
                Mgf1(hasher, hDest, dbMask);

                // 10. Let maskedDB = DB XOR dbMask
                Xor(db, dbMask);

                // 11. Set the "unused" bits in the leftmost byte of maskedDB to 0.
                int unusedBits = 8 * emLen - emBits;

                if (unusedBits != 0)
                {
                    byte mask = (byte)(0xFF >> unusedBits);
                    db[0] &= mask;
                }
            }

            CryptographicOperations.ZeroMemory(dbMask);
            CryptoPool.Return(dbMaskRented, clearSize: 0);
        }
Exemple #18
0
            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();
            }
            public override bool TryEncrypt(ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten)
            {
                if (padding == null)
                {
                    throw new ArgumentNullException(nameof(padding));
                }

                ThrowIfDisposed();

                int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);

                if (destination.Length < rsaSize)
                {
                    bytesWritten = 0;
                    return(false);
                }

                if (data.Length == 0)
                {
                    RsaPaddingProcessor?processor;

                    switch (padding.Mode)
                    {
                    case RSAEncryptionPaddingMode.Pkcs1:
                        processor = null;
                        break;

                    case RSAEncryptionPaddingMode.Oaep:
                        processor = RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm);
                        break;

                    default:
                        throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
                    }

                    byte[]      rented = CryptoPool.Rent(rsaSize);
                    Span <byte> tmp    = new Span <byte>(rented, 0, rsaSize);

                    try
                    {
                        if (processor != null)
                        {
                            processor.PadOaep(data, tmp);
                        }
                        else
                        {
                            Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Pkcs1);
                            RsaPaddingProcessor.PadPkcs1Encryption(data, tmp);
                        }

                        return(Interop.AppleCrypto.TryRsaEncryptionPrimitive(
                                   GetKeys().PublicKey,
                                   tmp,
                                   destination,
                                   out bytesWritten));
                    }
                    finally
                    {
                        CryptographicOperations.ZeroMemory(tmp);
                        CryptoPool.Return(rented, clearSize: 0);
                    }
                }

                return(Interop.AppleCrypto.TryRsaEncrypt(
                           GetKeys().PublicKey,
                           data,
                           destination,
                           padding,
                           out bytesWritten));
            }
        private void EncryptCore(
            ReadOnlySpan <byte> nonce,
            ReadOnlySpan <byte> plaintext,
            Span <byte> ciphertext,
            Span <byte> tag,
            ReadOnlySpan <byte> associatedData = default)
        {
            Interop.Crypto.EvpCipherSetKeyAndIV(
                _ctxHandle,
                Span <byte> .Empty,
                nonce,
                Interop.Crypto.EvpCipherDirection.Encrypt);

            if (!associatedData.IsEmpty)
            {
                Interop.Crypto.CipherUpdateAAD(_ctxHandle, associatedData);
            }

            byte[]? rented = null;
            int ciphertextAndTagLength = checked (ciphertext.Length + tag.Length);

            try
            {
                // Arbitrary limit.
                const int   StackAllocMax    = 128;
                Span <byte> ciphertextAndTag = stackalloc byte[StackAllocMax];

                if (ciphertextAndTagLength > StackAllocMax)
                {
                    rented           = CryptoPool.Rent(ciphertextAndTagLength);
                    ciphertextAndTag = rented;
                }

                ciphertextAndTag = ciphertextAndTag.Slice(0, ciphertextAndTagLength);

                if (!Interop.Crypto.EvpCipherUpdate(_ctxHandle, ciphertextAndTag, out int ciphertextBytesWritten, plaintext))
                {
                    throw new CryptographicException();
                }

                if (!Interop.Crypto.EvpCipherFinalEx(
                        _ctxHandle,
                        ciphertextAndTag.Slice(ciphertextBytesWritten),
                        out int bytesWritten))
                {
                    throw new CryptographicException();
                }

                ciphertextBytesWritten += bytesWritten;

                // NOTE: Android appends tag to the end of the ciphertext in case of ChaCha20Poly1305 and "encryption" mode

                if (ciphertextBytesWritten != ciphertextAndTagLength)
                {
                    Debug.Fail($"ChaCha20Poly1305 encrypt wrote {ciphertextBytesWritten} of {ciphertextAndTagLength} bytes.");
                    throw new CryptographicException();
                }

                ciphertextAndTag.Slice(0, ciphertext.Length).CopyTo(ciphertext);
                ciphertextAndTag.Slice(ciphertext.Length).CopyTo(tag);
            }
            finally
            {
                if (rented is not null)
                {
                    CryptoPool.Return(rented, clearSize: ciphertextAndTagLength);
                }
            }
        }
Exemple #21
0
        // Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both
        // array-based APIs invoke this common helper with the "encrypt" parameter determining whether encryption or decryption is done.
        private unsafe byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, bool encrypt)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }
            if (padding == null)
            {
                throw new ArgumentNullException(nameof(padding));
            }

            int modulusSizeInBytes = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);

            if (!encrypt && data.Length != modulusSizeInBytes)
            {
                throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize);
            }

            if (encrypt &&
                padding.Mode == RSAEncryptionPaddingMode.Pkcs1 &&
                data.Length > modulusSizeInBytes - Pkcs1PaddingOverhead)
            {
                throw new CryptographicException(
                          SR.Format(SR.Cryptography_Encryption_MessageTooLong, modulusSizeInBytes - Pkcs1PaddingOverhead));
            }

            using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
            {
                if (encrypt && data.Length == 0)
                {
                    byte[]      rented        = CryptoPool.Rent(modulusSizeInBytes);
                    Span <byte> paddedMessage = new Span <byte>(rented, 0, modulusSizeInBytes);

                    try
                    {
                        if (padding == RSAEncryptionPadding.Pkcs1)
                        {
                            RsaPaddingProcessor.PadPkcs1Encryption(data, paddedMessage);
                        }
                        else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
                        {
                            RsaPaddingProcessor processor =
                                RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm);

                            processor.PadOaep(data, paddedMessage);
                        }
                        else
                        {
                            throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode);
                        }

                        return(EncryptOrDecrypt(keyHandle, paddedMessage, AsymmetricPaddingMode.NCRYPT_NO_PADDING_FLAG, null, encrypt));
                    }
                    finally
                    {
                        CryptographicOperations.ZeroMemory(paddedMessage);
                        CryptoPool.Return(rented, clearSize: 0);
                    }
                }

                switch (padding.Mode)
                {
                case RSAEncryptionPaddingMode.Pkcs1:
                    return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encrypt));

                case RSAEncryptionPaddingMode.Oaep:
                    IntPtr namePtr = Marshal.StringToHGlobalUni(padding.OaepHashAlgorithm.Name);
                    try
                    {
                        var paddingInfo = new BCRYPT_OAEP_PADDING_INFO()
                        {
                            pszAlgId = namePtr,

                            // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this.
                            pbLabel = IntPtr.Zero,
                            cbLabel = 0,
                        };
                        return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encrypt));
                    }
                    finally
                    {
                        Marshal.FreeHGlobal(namePtr);
                    }

                default:
                    throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode);
                }
            }
        }
        public static unsafe bool OneShotDecrypt(
            ILiteSymmetricCipher cipher,
            PaddingMode paddingMode,
            ReadOnlySpan <byte> input,
            Span <byte> output,
            out int bytesWritten)
        {
            if (input.Length % cipher.PaddingSizeInBytes != 0)
            {
                throw new CryptographicException(SR.Cryptography_PartialBlock);
            }

            // If there is no padding that needs to be removed, and the output buffer is large enough to hold
            // the resulting plaintext, we can decrypt directly in to the output buffer.
            // We do not do this for modes that require padding removal.
            //
            // This is not done for padded ciphertexts because we don't know if the padding is valid
            // until it's been decrypted. We don't want to decrypt in to a user-supplied buffer and then throw
            // a padding exception after we've already filled the user buffer with plaintext. We should only
            // release the plaintext to the caller once we know the padding is valid.
            if (!SymmetricPadding.DepaddingRequired(paddingMode))
            {
                if (output.Length >= input.Length)
                {
                    bytesWritten = cipher.TransformFinal(input, output);
                    return(true);
                }

                // If no padding is going to be removed, we know the buffer is too small and we can bail out.
                bytesWritten = 0;
                return(false);
            }

            byte[]      rentedBuffer    = CryptoPool.Rent(input.Length);
            Span <byte> buffer          = rentedBuffer.AsSpan(0, input.Length);
            Span <byte> decryptedBuffer = default;

            fixed(byte *pBuffer = buffer)
            {
                try
                {
                    int transformWritten = cipher.TransformFinal(input, buffer);
                    decryptedBuffer = buffer.Slice(0, transformWritten);

                    // This intentionally passes in BlockSizeInBytes instead of PaddingSizeInBytes. This is so that
                    // "extra padded" CFB data can still be decrypted. The .NET Framework always padded CFB8 to the
                    // block size, not the feedback size. We want the one-shot to be able to continue to decrypt
                    // those ciphertexts, so for CFB8 we are more lenient on the number of allowed padding bytes.
                    int unpaddedLength = SymmetricPadding.GetPaddingLength(decryptedBuffer, paddingMode, cipher.BlockSizeInBytes); // validates padding

                    if (unpaddedLength > output.Length)
                    {
                        bytesWritten = 0;
                        return(false);
                    }

                    decryptedBuffer.Slice(0, unpaddedLength).CopyTo(output);
                    bytesWritten = unpaddedLength;
                    return(true);
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(decryptedBuffer);
                    CryptoPool.Return(rentedBuffer, clearSize: 0); // ZeroMemory clears the part of the buffer that was written to.
                }
            }
        }
            public override bool TrySignHash(ReadOnlySpan <byte> hash, Span <byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, out int bytesWritten)
            {
                ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm));
                ArgumentNullException.ThrowIfNull(padding);

                ThrowIfDisposed();

                bool pssPadding = padding.Mode switch
                {
                    RSASignaturePaddingMode.Pss => true,
                    RSASignaturePaddingMode.Pkcs1 => false,
                    _ => throw new CryptographicException(SR.Cryptography_InvalidPaddingMode)
                };

                SecKeyPair keys = GetKeys();

                if (keys.PrivateKey == null)
                {
                    throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
                }

                int keySize = KeySize;
                int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(keySize);

                if (!pssPadding)
                {
                    Interop.AppleCrypto.PAL_HashAlgorithm palAlgId =
                        PalAlgorithmFromAlgorithmName(hashAlgorithm, out int expectedSize);

                    if (hash.Length != expectedSize)
                    {
                        // Windows: NTE_BAD_DATA ("Bad Data.")
                        // OpenSSL: RSA_R_INVALID_MESSAGE_LENGTH ("invalid message length")
                        throw new CryptographicException(
                                  SR.Format(
                                      SR.Cryptography_BadHashSize_ForAlgorithm,
                                      hash.Length,
                                      expectedSize,
                                      hashAlgorithm.Name));
                    }

                    if (destination.Length < rsaSize)
                    {
                        bytesWritten = 0;
                        return(false);
                    }

                    return(Interop.AppleCrypto.TryCreateSignature(
                               keys.PrivateKey,
                               hash,
                               destination,
                               palAlgId,
                               Interop.AppleCrypto.PAL_SignatureAlgorithm.RsaPkcs1,
                               out bytesWritten));
                }

                Debug.Assert(padding.Mode == RSASignaturePaddingMode.Pss);

                if (destination.Length < rsaSize)
                {
                    bytesWritten = 0;
                    return(false);
                }

                byte[]      rented = CryptoPool.Rent(rsaSize);
                Span <byte> buf    = new Span <byte>(rented, 0, rsaSize);

                RsaPaddingProcessor.EncodePss(hashAlgorithm, hash, buf, keySize);

                try
                {
                    return(Interop.AppleCrypto.TryRsaSignaturePrimitive(keys.PrivateKey, buf, destination, out bytesWritten));
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(buf);
                    CryptoPool.Return(rented, clearSize: 0);
                }
            }
Exemple #24
0
        public override bool TryDecrypt(
            ReadOnlySpan <byte> data,
            Span <byte> destination,
            RSAEncryptionPadding padding,
            out int bytesWritten)
        {
            if (padding == null)
            {
                throw new ArgumentNullException(nameof(padding));
            }

            ValidatePadding(padding);
            SafeEvpPKeyHandle key = GetKey();
            int keySizeBytes      = Interop.Crypto.EvpPKeySize(key);

            // OpenSSL requires that the decryption buffer be at least as large as EVP_PKEY_size.
            // So if the destination is too small, use a temporary buffer so we can match
            // Windows behavior of succeeding so long as the buffer can hold the final output.
            if (destination.Length < keySizeBytes)
            {
                // RSA up through 4096 bits use a stackalloc
                Span <byte> tmp = stackalloc byte[512];
                byte[]? rent = null;

                if (keySizeBytes > tmp.Length)
                {
                    rent = CryptoPool.Rent(keySizeBytes);
                    tmp  = rent;
                }

                int  written = Decrypt(key, data, tmp, padding);
                bool ret;

                if (destination.Length < written)
                {
                    bytesWritten = 0;
                    ret          = false;
                }
                else
                {
                    tmp.Slice(0, written).CopyTo(destination);
                    bytesWritten = written;
                    ret          = true;
                }

                // Whether a stackalloc or a rented array, clear our copy of
                // the decrypted content.
                CryptographicOperations.ZeroMemory(tmp.Slice(0, written));

                if (rent != null)
                {
                    // Already cleared.
                    CryptoPool.Return(rent, clearSize: 0);
                }

                return(ret);
            }

            bytesWritten = Decrypt(key, data, destination, padding);
            return(true);
        }
Exemple #25
0
        /// <summary>
        /// Get the secret agreement generated between two parties
        /// </summary>
        private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher)
        {
            Debug.Assert(otherPartyPublicKey != null);

            // Ensure that this ECDH object contains a private key by attempting a parameter export
            // which will throw an OpenSslCryptoException if no private key is available
            ECParameters thisKeyExplicit             = ExportExplicitParameters(true);
            bool         thisIsNamed                 = Interop.Crypto.EcKeyHasCurveName(_key.Value);
            ECDiffieHellmanOpenSslPublicKey?otherKey = otherPartyPublicKey as ECDiffieHellmanOpenSslPublicKey;
            bool disposeOtherKey = false;

            if (otherKey == null)
            {
                disposeOtherKey = true;

                ECParameters otherParameters =
                    thisIsNamed
                        ? otherPartyPublicKey.ExportParameters()
                        : otherPartyPublicKey.ExportExplicitParameters();

                otherKey = new ECDiffieHellmanOpenSslPublicKey(otherParameters);
            }

            bool otherIsNamed = otherKey.HasCurveName;

            SafeEvpPKeyHandle?ourKey   = null;
            SafeEvpPKeyHandle?theirKey = null;

            byte[]? rented = null;
            int secretLength = 0;

            try
            {
                if (otherKey.KeySize != KeySize)
                {
                    throw new ArgumentException(SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey));
                }

                if (otherIsNamed == thisIsNamed)
                {
                    ourKey   = _key.UpRefKeyHandle();
                    theirKey = otherKey.DuplicateKeyHandle();
                }
                else if (otherIsNamed)
                {
                    ourKey = _key.UpRefKeyHandle();

                    using (ECOpenSsl tmp = new ECOpenSsl(otherKey.ExportExplicitParameters()))
                    {
                        theirKey = tmp.UpRefKeyHandle();
                    }
                }
                else
                {
                    using (ECOpenSsl tmp = new ECOpenSsl(thisKeyExplicit))
                    {
                        ourKey = tmp.UpRefKeyHandle();
                    }

                    theirKey = otherKey.DuplicateKeyHandle();
                }

                using (SafeEvpPKeyCtxHandle ctx = Interop.Crypto.EvpPKeyCtxCreate(ourKey, theirKey, out uint secretLengthU))
                {
                    if (ctx == null || ctx.IsInvalid || secretLengthU == 0 || secretLengthU > int.MaxValue)
                    {
                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                    }

                    secretLength = (int)secretLengthU;

                    // Indicate that secret can hold stackallocs from nested scopes
                    Span <byte> secret = stackalloc byte[0];

                    // Arbitrary limit. But it covers secp521r1, which is the biggest common case.
                    const int StackAllocMax = 66;

                    if (secretLength > StackAllocMax)
                    {
                        rented = CryptoPool.Rent(secretLength);
                        secret = new Span <byte>(rented, 0, secretLength);
                    }
                    else
                    {
                        secret = stackalloc byte[secretLength];
                    }

                    Interop.Crypto.EvpPKeyDeriveSecretAgreement(ctx, secret);

                    if (hasher == null)
                    {
                        return(secret.ToArray());
                    }
                    else
                    {
                        hasher.AppendData(secret);
                        return(null);
                    }
                }
            }
            finally
            {
                theirKey?.Dispose();
                ourKey?.Dispose();

                if (disposeOtherKey)
                {
                    otherKey.Dispose();
                }

                if (rented != null)
                {
                    CryptoPool.Return(rented, secretLength);
                }
            }
        }
Exemple #26
0
        internal static unsafe Pkcs8Response ImportEncryptedPkcs8PrivateKey(
            ReadOnlySpan <char> password,
            ReadOnlySpan <byte> source,
            out int bytesRead)
        {
            try
            {
                AsnDecoder.ReadEncodedValue(
                    source,
                    AsnEncodingRules.BER,
                    out _,
                    out _,
                    out int len);

                source = source.Slice(0, len);

                fixed(byte *ptr = &MemoryMarshal.GetReference(source))
                {
                    using (MemoryManager <byte> manager = new PointerMemoryManager <byte>(ptr, source.Length))
                    {
                        try
                        {
                            bytesRead = len;
                            return(ImportPkcs8(source, password));
                        }
                        catch (CryptographicException)
                        {
                        }

                        ArraySegment <byte> decrypted = KeyFormatHelper.DecryptPkcs8(
                            password,
                            manager.Memory.Slice(0, len),
                            out int innerRead);

                        Span <byte> decryptedSpan = decrypted;

                        try
                        {
                            if (innerRead != len)
                            {
                                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                            }

                            bytesRead = len;
                            return(ImportPkcs8(decryptedSpan));
                        }
                        catch (CryptographicException e)
                        {
                            AsnWriter?pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan);

                            if (pkcs8ZeroPublicKey == null)
                            {
                                throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
                            }

                            try
                            {
                                bytesRead = len;
                                return(ImportPkcs8(pkcs8ZeroPublicKey));
                            }
                            catch (CryptographicException)
                            {
                                throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
                            }
                        }
                        finally
                        {
                            CryptoPool.Return(decrypted);
                        }
                    }
                }
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
        }
Exemple #27
0
            private static bool TryDecrypt(
                SafeRsaHandle key,
                ReadOnlySpan <byte> data,
                Span <byte> destination,
                Interop.AndroidCrypto.RsaPadding rsaPadding,
                RsaPaddingProcessor?rsaPaddingProcessor,
                out int bytesWritten)
            {
                // If rsaPadding is PKCS1 or OAEP-SHA1 then no depadding method should be present.
                // If rsaPadding is NoPadding then a depadding method should be present.
                Debug.Assert(
                    (rsaPadding == Interop.AndroidCrypto.RsaPadding.NoPadding) ==
                    (rsaPaddingProcessor != null));

                // Caller should have already checked this.
                Debug.Assert(!key.IsInvalid);

                int rsaSize = Interop.AndroidCrypto.RsaSize(key);

                if (data.Length != rsaSize)
                {
                    throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize);
                }

                if (destination.Length < rsaSize)
                {
                    bytesWritten = 0;
                    return(false);
                }

                Span <byte> decryptBuf = destination;

                byte[]? paddingBuf = null;

                if (rsaPaddingProcessor != null)
                {
                    paddingBuf = CryptoPool.Rent(rsaSize);
                    decryptBuf = paddingBuf;
                }

                try
                {
                    int returnValue = Interop.AndroidCrypto.RsaPrivateDecrypt(data.Length, data, decryptBuf, key, rsaPadding);
                    CheckReturn(returnValue);

                    if (rsaPaddingProcessor != null)
                    {
                        return(rsaPaddingProcessor.DepadOaep(paddingBuf, destination, out bytesWritten));
                    }
                    else
                    {
                        // If the padding mode is RSA_NO_PADDING then the size of the decrypted block
                        // will be RSA_size. If any padding was used, then some amount (determined by the padding algorithm)
                        // will have been reduced, and only returnValue bytes were part of the decrypted
                        // body.  Either way, we can just use returnValue, but some additional bytes may have been overwritten
                        // in the destination span.
                        bytesWritten = returnValue;
                    }

                    return(true);
                }
                finally
                {
                    if (paddingBuf != null)
                    {
                        // DecryptBuf is paddingBuf if paddingBuf is not null, erase it before returning it.
                        // If paddingBuf IS null then decryptBuf was destination, and shouldn't be cleared.
                        CryptographicOperations.ZeroMemory(decryptBuf);
                        CryptoPool.Return(paddingBuf, clearSize: 0);
                    }
                }
            }
        public int Transform(ReadOnlySpan <byte> input, Span <byte> output)
        {
            Debug.Assert(input.Length > 0);
            Debug.Assert((input.Length % PaddingSizeInBytes) == 0);

            int numBytesWritten = 0;

            // NCryptEncrypt and NCryptDecrypt can do in place encryption, but if the buffers overlap
            // the offset must be zero. In that case, we need to copy to a temporary location.
            if (input.Overlaps(output, out int offset) && offset != 0)
            {
                byte[] rented = CryptoPool.Rent(output.Length);

                try
                {
                    numBytesWritten = NCryptTransform(input, rented);
                    rented.AsSpan(0, numBytesWritten).CopyTo(output);
                }
                finally
                {
                    CryptoPool.Return(rented, clearSize: numBytesWritten);
                }
            }
            else
            {
                numBytesWritten = NCryptTransform(input, output);
            }

            if (numBytesWritten != input.Length)
            {
                // CNG gives us no way to tell NCryptDecrypt() that we're decrypting the final block, nor is it performing any
                // padding /depadding for us. So there's no excuse for a provider to hold back output for "future calls." Though
                // this isn't technically our problem to detect, we might as well detect it now for easier diagnosis.
                throw new CryptographicException(SR.Cryptography_UnexpectedTransformTruncation);
            }

            return(numBytesWritten);

            int NCryptTransform(ReadOnlySpan <byte> input, Span <byte> output)
            {
                int bytesWritten;

                // The Handle property duplicates the handle.
                using (SafeNCryptKeyHandle keyHandle = _key.Handle)
                {
                    unsafe
                    {
                        ErrorCode errorCode = _encrypting ?
                                              Interop.NCrypt.NCryptEncrypt(keyHandle, input, input.Length, null, output, output.Length, out bytesWritten, AsymmetricPaddingMode.None) :
                                              Interop.NCrypt.NCryptDecrypt(keyHandle, input, input.Length, null, output, output.Length, out bytesWritten, AsymmetricPaddingMode.None);

                        if (errorCode != ErrorCode.ERROR_SUCCESS)
                        {
                            throw errorCode.ToCryptographicException();
                        }
                    }
                }

                return(bytesWritten);
            }
        }
Exemple #29
0
        private void EncryptCore(
            ReadOnlySpan <byte> nonce,
            ReadOnlySpan <byte> plaintext,
            Span <byte> ciphertext,
            Span <byte> tag,
            ReadOnlySpan <byte> associatedData = default)
        {
            // Convert key length to bits.
            using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8)))
            {
                if (ctx.IsInvalid)
                {
                    throw new CryptographicException();
                }

                if (!Interop.Crypto.CipherSetTagLength(ctx, tag.Length))
                {
                    throw new CryptographicException();
                }

                Interop.Crypto.CipherSetNonceLength(ctx, nonce.Length);
                Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Encrypt);

                if (associatedData.Length != 0)
                {
                    Interop.Crypto.CipherUpdateAAD(ctx, associatedData);
                }

                byte[]? rented = null;
                try
                {
                    Span <byte> ciphertextAndTag = stackalloc byte[0];
                    // Arbitrary limit.
                    const int StackAllocMax = 128;
                    if (checked (ciphertext.Length + tag.Length) <= StackAllocMax)
                    {
                        ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length];
                    }
                    else
                    {
                        rented           = CryptoPool.Rent(ciphertext.Length + tag.Length);
                        ciphertextAndTag = new Span <byte>(rented, 0, ciphertext.Length + tag.Length);
                    }

                    if (!Interop.Crypto.EvpCipherUpdate(ctx, ciphertextAndTag, out int ciphertextBytesWritten, plaintext))
                    {
                        throw new CryptographicException();
                    }

                    if (!Interop.Crypto.EvpCipherFinalEx(
                            ctx,
                            ciphertextAndTag.Slice(ciphertextBytesWritten),
                            out int bytesWritten))
                    {
                        throw new CryptographicException();
                    }

                    ciphertextBytesWritten += bytesWritten;

                    // NOTE: Android appends tag to the end of the ciphertext in case of CCM/GCM and "encryption" mode

                    if (ciphertextBytesWritten != ciphertextAndTag.Length)
                    {
                        Debug.Fail($"CCM encrypt wrote {ciphertextBytesWritten} of {ciphertextAndTag.Length} bytes.");
                        throw new CryptographicException();
                    }

                    ciphertextAndTag[..ciphertext.Length].CopyTo(ciphertext);
Exemple #30
0
            /// <summary>
            /// Get the secret agreement generated between two parties
            /// </summary>
            private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher)
            {
                Debug.Assert(otherPartyPublicKey != null);

                // Ensure that this ECDH object contains a private key by attempting a parameter export
                // which will throw an OpenSslCryptoException if no private key is available
                ECParameters thisKeyExplicit             = ExportExplicitParameters(true);
                bool         thisIsNamed                 = Interop.AndroidCrypto.EcKeyHasCurveName(_key.Value);
                ECDiffieHellmanAndroidPublicKey?otherKey = otherPartyPublicKey as ECDiffieHellmanAndroidPublicKey;
                bool disposeOtherKey = false;

                if (otherKey == null)
                {
                    disposeOtherKey = true;

                    ECParameters otherParameters =
                        thisIsNamed
                            ? otherPartyPublicKey.ExportParameters()
                            : otherPartyPublicKey.ExportExplicitParameters();

                    otherKey = new ECDiffieHellmanAndroidPublicKey(otherParameters);
                }

                bool otherIsNamed = otherKey.HasCurveName;

                SafeEcKeyHandle?ourKey   = null;
                SafeEcKeyHandle?theirKey = null;

                byte[]? rented = null;
                // Calculate secretLength in bytes.
                int secretLength = AsymmetricAlgorithmHelpers.BitsToBytes(KeySize);

                try
                {
                    if (otherKey.KeySize != KeySize)
                    {
                        throw new ArgumentException(SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey));
                    }

                    if (otherIsNamed == thisIsNamed)
                    {
                        ourKey   = _key.UpRefKeyHandle();
                        theirKey = otherKey.DuplicateKeyHandle();
                    }
                    else if (otherIsNamed)
                    {
                        ourKey = _key.UpRefKeyHandle();

                        using (ECAndroid tmp = new ECAndroid(otherKey.ExportExplicitParameters()))
                        {
                            theirKey = tmp.UpRefKeyHandle();
                        }
                    }
                    else
                    {
                        using (ECAndroid tmp = new ECAndroid(thisKeyExplicit))
                        {
                            ourKey = tmp.UpRefKeyHandle();
                        }

                        theirKey = otherKey.DuplicateKeyHandle();
                    }

                    // Indicate that secret can hold stackallocs from nested scopes
                    Span <byte> secret = stackalloc byte[0];

                    // Arbitrary limit. But it covers secp521r1, which is the biggest common case.
                    const int StackAllocMax = 66;

                    if (secretLength > StackAllocMax)
                    {
                        rented = CryptoPool.Rent(secretLength);
                        secret = new Span <byte>(rented, 0, secretLength);
                    }
                    else
                    {
                        secret = stackalloc byte[secretLength];
                    }

                    if (!Interop.AndroidCrypto.EcdhDeriveKey(ourKey, theirKey, secret, out int usedBufferLength))
                    {
                        throw new CryptographicException();
                    }

                    Debug.Assert(secretLength == usedBufferLength, $"Expected secret length {secretLength} does not match actual secret length {usedBufferLength}.");

                    if (hasher == null)
                    {
                        return(secret.ToArray());
                    }
                    else
                    {
                        hasher.AppendData(secret);
                        return(null);
                    }
                }
                finally
                {
                    theirKey?.Dispose();
                    ourKey?.Dispose();

                    if (disposeOtherKey)
                    {
                        otherKey.Dispose();
                    }

                    if (rented != null)
                    {
                        CryptoPool.Return(rented, secretLength);
                    }
                }
            }