private static bool DecryptInternal(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int ciphertextLength, byte[] key, int keyOffset, byte[] nonce, int nonceOffset)
        {
            var plaintextLength = ciphertextLength - MacSizeInBytes;

            PrepareInternalKey(out Array16 <uint> internalKey, key, keyOffset, nonce, nonceOffset);

            Array16 <uint> temp;
            var            tempBytes = new byte[64]; // TODO: remove allocation

            // first iteration
            {
                SalsaCore.Salsa(out temp, ref internalKey, 20);

                //first half is for Poly1305
                Array8 <uint> poly1305Key;
                poly1305Key.x0 = temp.x0;
                poly1305Key.x1 = temp.x1;
                poly1305Key.x2 = temp.x2;
                poly1305Key.x3 = temp.x3;
                poly1305Key.x4 = temp.x4;
                poly1305Key.x5 = temp.x5;
                poly1305Key.x6 = temp.x6;
                poly1305Key.x7 = temp.x7;

                // compute MAC
                Poly1305Donna.poly1305_auth(tempBytes, 0, ciphertext, ciphertextOffset + 16, plaintextLength, ref poly1305Key);
                if (!CryptoBytes.ConstantTimeEquals(tempBytes, 0, ciphertext, ciphertextOffset, MacSizeInBytes))
                {
                    Array.Clear(plaintext, plaintextOffset, plaintextLength);
                    return(false);
                }

                // rest for the message
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 0, temp.x8);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 4, temp.x9);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 8, temp.x10);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 12, temp.x11);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 16, temp.x12);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 20, temp.x13);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 24, temp.x14);
                ByteIntegerConverter.StoreLittleEndian32(tempBytes, 28, temp.x15);
                var count = Math.Min(32, plaintextLength);
                for (var i = 0; i < count; i++)
                {
                    plaintext[plaintextOffset + i] = (byte)(ciphertext[MacSizeInBytes + ciphertextOffset + i] ^ tempBytes[i]);
                }
            }

            // later iterations
            var blockOffset = 32;

            while (blockOffset < plaintextLength)
            {
                internalKey.x8++;
                SalsaCore.Salsa(out temp, ref internalKey, 20);
                ByteIntegerConverter.Array16StoreLittleEndian32(tempBytes, 0, ref temp);
                var count = Math.Min(64, plaintextLength - blockOffset);
                for (var i = 0; i < count; i++)
                {
                    plaintext[plaintextOffset + blockOffset + i] = (byte)(ciphertext[16 + ciphertextOffset + blockOffset + i] ^ tempBytes[i]);
                }
                blockOffset += 64;
            }
            return(true);
        }
Example #2
0
        public void Update(byte[] data, int offset, int count)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            if (data.Length - offset < count)
            {
                throw new ArgumentException("Requires offset + count <= data.Length");
            }

            Array16 <ulong> block;
            var             bytesInBuffer = (int)_totalBytes & (BlockSize - 1);

            _totalBytes += (uint)count;

            if (_totalBytes >= ulong.MaxValue / 8)
            {
                throw new InvalidOperationException("Too much data");
            }

            // Fill existing buffer
            if (bytesInBuffer != 0)
            {
                var toCopy = System.Math.Min(BlockSize - bytesInBuffer, count);
                Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, toCopy);
                offset        += toCopy;
                count         -= toCopy;
                bytesInBuffer += toCopy;
                if (bytesInBuffer == BlockSize)
                {
                    ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0);
                    Sha512Internal.Core(out _state, ref _state, ref block);
                    CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length);
                    bytesInBuffer = 0;
                }
            }

            // Hash complete blocks without copying
            while (count >= BlockSize)
            {
                ByteIntegerConverter.Array16LoadBigEndian64(out block, data, offset);
                Sha512Internal.Core(out _state, ref _state, ref block);
                offset += BlockSize;
                count  -= BlockSize;
            }

            // Copy remainder into buffer
            if (count > 0)
            {
                Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, count);
            }
        }
Example #3
0
 public static byte[] PublicKeyFromSeed(byte[] privateKeySeed)
 {
     KeyPairFromSeed(out byte[] publicKey, out byte[] privateKey, privateKeySeed);
     CryptoBytes.Wipe(privateKey);
     return(publicKey);
 }