Example #1
0
        /// <summary>
        /// Decrypts a cipher with an authentication tag and additional data.
        /// </summary>
        /// <param name="cipher">The cipher to be decrypted.</param>
        /// <param name="nonce">The 8 byte nonce.</param>
        /// <param name="key">The 32 byte key.</param>
        /// <param name="additionalData">The additional data; may be null, otherwise between 0 and 16 bytes.</param>
        /// <returns>The decrypted cipher.</returns>
        /// <exception cref="KeyOutOfRangeException"></exception>
        /// <exception cref="NonceOutOfRangeException"></exception>
        /// <exception cref="AdditionalDataOutOfRangeException"></exception>
        /// <exception cref="CryptographicException"></exception>
        public ArraySegment <byte> Decrypt(ArraySegment <byte> cipher, byte[] nonce, byte[] key, ArraySegment <byte> additionalData)
        {
            //validate the length of the key
            if (key == null || key.Length != KEYBYTES)
            {
                throw new KeyOutOfRangeException("key", (key == null) ? 0 : key.Length,
                                                 string.Format("key must be {0} bytes in length.", KEYBYTES));
            }

            //validate the length of the nonce
            if (nonce == null || nonce.Length != NPUBBYTES)
            {
                throw new NonceOutOfRangeException("nonce", (nonce == null) ? 0 : nonce.Length,
                                                   string.Format("nonce must be {0} bytes in length.", NPUBBYTES));
            }

            //additionalData can be null
            GCHandle hAddData;
            IntPtr   pAddData;
            int      adataLength;

            if (additionalData.Array == null)
            {
                hAddData    = GCHandle.Alloc(Array.Empty <byte>(), GCHandleType.Pinned);
                pAddData    = hAddData.AddrOfPinnedObject();
                adataLength = 0;
            }
            else
            {
                hAddData    = GCHandle.Alloc(additionalData.Array, GCHandleType.Pinned);
                pAddData    = hAddData.AddrOfPinnedObject() + additionalData.Offset;
                adataLength = additionalData.Count;
            }



            //      //validate the length of the additionalData
            //      if (additionalData.Length > ABYTES || additionalData.Length < 0)
            //        throw new AdditionalDataOutOfRangeException(
            //          string.Format("additionalData must be between {0} and {1} bytes in length.", 0, ABYTES));

            var message = _bufferPool.Rent(cipher.Count - ABYTES);
            var hMsg    = GCHandle.Alloc(message, GCHandleType.Pinned);
            var hCipher = GCHandle.Alloc(cipher.Array, GCHandleType.Pinned);

            var ret = SodiumLibrary.crypto_aead_xchacha20poly1305_ietf_decrypt(hMsg.AddrOfPinnedObject(),
                                                                               out long messageLength, null, hCipher.AddrOfPinnedObject() + cipher.Offset, cipher.Count,
                                                                               pAddData, adataLength, nonce, key);

            hMsg.Free();
            hAddData.Free();
            hCipher.Free();

            if (ret != 0)
            {
                throw new CryptographicException("Error decrypting message.");
            }

            return(new ArraySegment <byte>(message, 0, (int)messageLength));
        }
        /// <summary>
        /// Decrypts a cipher with an authentication tag and additional data using XChaCha20-Poly1305.
        /// </summary>
        /// <param name="cipher">The cipher to be decrypted.</param>
        /// <param name="nonce">The 24 byte nonce.</param>
        /// <param name="key">The 32 byte key.</param>
        /// <param name="additionalData">The additional data; may be null, otherwise between 0 and 16 bytes.</param>
        /// <returns>The decrypted cipher.</returns>
        /// <exception cref="KeyOutOfRangeException"></exception>
        /// <exception cref="NonceOutOfRangeException"></exception>
        /// <exception cref="AdditionalDataOutOfRangeException"></exception>
        /// <exception cref="CryptographicException"></exception>
        public static byte[] Decrypt(byte[] cipher, byte[] nonce, byte[] key, byte[] additionalData = null)
        {
            //additionalData can be null
            if (additionalData == null)
            {
                additionalData = new byte[0x00];
            }

            //validate the length of the key
            if (key == null || key.Length != KEYBYTES)
            {
                throw new KeyOutOfRangeException("key", (key == null) ? 0 : key.Length,
                                                 string.Format("key must be {0} bytes in length.", KEYBYTES));
            }

            //validate the length of the nonce
            if (nonce == null || nonce.Length != NPUBBYTES)
            {
                throw new NonceOutOfRangeException("nonce", (nonce == null) ? 0 : nonce.Length,
                                                   string.Format("nonce must be {0} bytes in length.", NPUBBYTES));
            }

            //validate the length of the additionalData
            if (additionalData.Length > ABYTES || additionalData.Length < 0)
            {
                throw new AdditionalDataOutOfRangeException(
                          string.Format("additionalData must be between {0} and {1} bytes in length.", 0, ABYTES));
            }

            var  message = new byte[cipher.Length - ABYTES];
            var  bin     = Marshal.AllocHGlobal(message.Length);
            long messageLength;

            var ret = SodiumLibrary.crypto_aead_xchacha20poly1305_ietf_decrypt(bin, out messageLength, null, cipher, cipher.Length,
                                                                               additionalData, additionalData.Length, nonce, key);

            Marshal.Copy(bin, message, 0, (int)messageLength);
            Marshal.FreeHGlobal(bin);

            if (ret != 0)
            {
                throw new CryptographicException("Error decrypting message.");
            }

            if (message.Length == messageLength)
            {
                return(message);
            }

            //remove the trailing nulls from the array
            var tmp = new byte[messageLength];

            Array.Copy(message, 0, tmp, 0, (int)messageLength);

            return(tmp);
        }