/// <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); }