├── .gitignore ├── Deps ├── libsodium-32.dll └── libsodium-64.dll ├── Natrium ├── RandomNumberGenerator32.cs ├── RandomNumberGenerator64.cs ├── IRandomNumberGenerator.cs ├── IAuthenticatedPublicKeyCryptoContext.cs ├── IMessageSigner.cs ├── Properties │ └── AssemblyInfo.cs ├── SignerKeyPair.cs ├── PublicKeyCryptoKeyPair.cs ├── MessageSigner64.cs ├── MessageSigner32.cs ├── IAuthenticatedPublicKeyCrypto.cs ├── Natrium.csproj ├── PrimitiveCryptoConstructs.cs ├── PlatformInvoke64.cs ├── PlatformInvoke32.cs ├── AuthenticatedPublicKeyCrypto32.cs └── AuthenticatedPublicKeyCrypto64.cs ├── Natrium.sln ├── TestApp ├── Properties │ └── AssemblyInfo.cs ├── Program.cs └── TestApp.csproj ├── README.md └── LICENSE.md /.gitignore: -------------------------------------------------------------------------------- 1 | obj/ 2 | bin/ 3 | *~ 4 | *.suo -------------------------------------------------------------------------------- /Deps/libsodium-32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XenocodeRCE/natrium/master/Deps/libsodium-32.dll -------------------------------------------------------------------------------- /Deps/libsodium-64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XenocodeRCE/natrium/master/Deps/libsodium-64.dll -------------------------------------------------------------------------------- /Natrium/RandomNumberGenerator32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Natrium 4 | { 5 | internal sealed class RandomNumberGenerator32 : IRandomNumberGenerator 6 | { 7 | public void GetRandomBytes(byte[] buffer) 8 | { 9 | PlatformInvoke32.randombytes_buf(buffer, new UIntPtr((uint)buffer.Length)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Natrium/RandomNumberGenerator64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Natrium 4 | { 5 | internal sealed class RandomNumberGenerator64 : IRandomNumberGenerator 6 | { 7 | public void GetRandomBytes(byte[] buffer) 8 | { 9 | PlatformInvoke64.randombytes_buf(buffer, new UIntPtr((uint)buffer.Length)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Natrium/IRandomNumberGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Natrium 7 | { 8 | /// 9 | /// Provides a cryptographically secure random number generator 10 | /// 11 | public interface IRandomNumberGenerator 12 | { 13 | /// 14 | /// Fills a buffer with cryptographically secure random bytes 15 | /// 16 | /// The buffer to fill 17 | void GetRandomBytes(byte[] buffer); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Natrium/IAuthenticatedPublicKeyCryptoContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | 7 | namespace Natrium 8 | { 9 | public interface IAuthenticatedPublicKeyCryptoContext 10 | { 11 | /// 12 | /// Encrypts and authenticates a message 13 | /// 14 | /// The message to encrypt 15 | /// An unique nonce value 16 | /// The encrypted message 17 | byte[] Encrypt(byte[] message, byte[] nonce); 18 | 19 | /// 20 | /// Verifies and decrypts a message 21 | /// 22 | /// The encrypted message 23 | /// The nonce value used to encrypt the message 24 | /// The decrypted message 25 | /// Thrown if integrity of the decrypted data cannot be confirmed 26 | byte[] VerifyAndDecrypt(byte[] encryptedMessage, byte[] nonce); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Natrium/IMessageSigner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Natrium 7 | { 8 | /// 9 | /// Provides methods to digitally sign and verify messages 10 | /// 11 | public interface IMessageSigner 12 | { 13 | /// 14 | /// Creates a new random key pair 15 | /// 16 | /// The generated key pair 17 | SignerKeyPair CreateRandomKeyPair(); 18 | 19 | /// 20 | /// Signs a message using the specified key pair 21 | /// 22 | /// The message to sign 23 | /// The key pair to use 24 | /// The signed message 25 | byte[] SignMessage(byte[] message, SignerKeyPair key); 26 | 27 | /// 28 | /// Verifies a signed message and returns the original message on success 29 | /// 30 | /// A signed message 31 | /// The public key to use for verification 32 | /// The original message that was signed 33 | byte[] VerifySignedMessage(byte[] signedMessage, SignerPublicKey publicKey); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Natrium.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Natrium", "Natrium\Natrium.csproj", "{F16C829B-C9C9-4968-92DA-711EB0BB1071}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{2F1A30AE-A44D-4C9E-A693-1C29B97E4412}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F16C829B-C9C9-4968-92DA-711EB0BB1071}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F16C829B-C9C9-4968-92DA-711EB0BB1071}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F16C829B-C9C9-4968-92DA-711EB0BB1071}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F16C829B-C9C9-4968-92DA-711EB0BB1071}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {2F1A30AE-A44D-4C9E-A693-1C29B97E4412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {2F1A30AE-A44D-4C9E-A693-1C29B97E4412}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {2F1A30AE-A44D-4C9E-A693-1C29B97E4412}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {2F1A30AE-A44D-4C9E-A693-1C29B97E4412}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Natrium/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NSodium")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NSodium")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b171a7fb-7bda-4b52-a8ed-b2e01f1714df")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TestApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestApp")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("acd2dca0-c549-4b02-b62a-fd8837a9d4ce")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Natrium 2 | ======= 3 | 4 | Natrium is an easy-to-use .NET wrapper for [libsodium](https://github.com/jedisct1/libsodium). Libsodium itself is a new, highly secure cryptographic library based on algorithms used in [NaCl](http://nacl.cr.yp.to/). 5 | 6 | Natrium makes it possible to implement _secure_ cryptography into your .NET applications using carefully selected algorithms chosen by cryptographic experts. The following excerpt is from libsodium web page: 7 | ``` 8 | The design choices, particularly in regard to the Curve25519 Diffie-Hellman function, emphasize security (whereas NIST curves emphasize "performance" at the cost of security), and "magic constants" in NaCl/Sodium have clear rationales. 9 | 10 | The same cannot be said of NIST curves, where the specific origins of certain constants are not described by the standards. 11 | 12 | And despite the emphasis on higher security, primitives are faster across-the-board than most implementations of the NIST standards. 13 | ``` 14 | 15 | ## Usage 16 | 17 | In order to use Natrium, you must build the VS solution to get Natrium.dll. When the build is done, you can reference Natrium.dll from your .NET project and start using the classes it contains. Remember to copy the files 'libsodium-32.dll' and 'libsodium-64.dll' from the Deps directory into same directory with your .NET executable files to make them available for Natrium to load. 18 | 19 | ### License 20 | 21 | Natrium is licensed under MIT license. Check the included file LICENSE.md for more details on how the included third-party libraries are licensed. -------------------------------------------------------------------------------- /Natrium/SignerKeyPair.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Natrium 4 | { 5 | /// 6 | /// Represents a key pair used for digital signatures 7 | /// 8 | public sealed class SignerKeyPair : IDisposable 9 | { 10 | private readonly byte[] _secretKeyBytes; 11 | private readonly SignerPublicKey _publicKey; 12 | 13 | public SignerKeyPair(byte[] secretKeyBytes, byte[] publicKey) 14 | { 15 | _secretKeyBytes = secretKeyBytes; 16 | _publicKey = new SignerPublicKey(publicKey); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | if (_secretKeyBytes != null) Array.Clear(_secretKeyBytes, 0, _secretKeyBytes.Length); 22 | _publicKey.Dispose(); 23 | } 24 | 25 | /// 26 | /// The secret key part of the keypair 27 | /// 28 | public byte[] SecretKeyBytes 29 | { 30 | get { return _secretKeyBytes; } 31 | } 32 | 33 | /// 34 | /// The public key part of the keypair 35 | /// 36 | public SignerPublicKey PublicKey 37 | { 38 | get { return _publicKey; } 39 | } 40 | } 41 | 42 | /// 43 | /// Represents a public key used for digital signatures 44 | /// 45 | public sealed class SignerPublicKey : IDisposable 46 | { 47 | private readonly byte[] _keyBytes; 48 | 49 | public SignerPublicKey(byte[] keyBytes) 50 | { 51 | _keyBytes = keyBytes; 52 | } 53 | 54 | /// 55 | /// Gets the public-key bytes 56 | /// 57 | public byte[] PlainBytes 58 | { 59 | get { return _keyBytes; } 60 | } 61 | 62 | public void Dispose() 63 | { 64 | if (_keyBytes != null) Array.Clear(_keyBytes, 0, _keyBytes.Length); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Natrium/PublicKeyCryptoKeyPair.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Natrium 4 | { 5 | /// 6 | /// Represents a key pair used for authenticated public-key encryption 7 | /// 8 | public sealed class PublicKeyCryptoKeyPair : IDisposable 9 | { 10 | private readonly byte[] _secretKeyBytes; 11 | private readonly PublicKeyCryptoPublicKey _publicKey; 12 | 13 | public PublicKeyCryptoKeyPair(byte[] secretKeyBytes, byte[] publicKey) 14 | { 15 | _secretKeyBytes = secretKeyBytes; 16 | _publicKey = new PublicKeyCryptoPublicKey(publicKey); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | if (_secretKeyBytes != null) Array.Clear(_secretKeyBytes, 0, _secretKeyBytes.Length); 22 | _publicKey.Dispose(); 23 | } 24 | 25 | /// 26 | /// The secret key part of the keypair 27 | /// 28 | public byte[] SecretKeyBytes 29 | { 30 | get { return _secretKeyBytes; } 31 | } 32 | 33 | /// 34 | /// The public key part of the keypair 35 | /// 36 | public PublicKeyCryptoPublicKey PublicKey 37 | { 38 | get { return _publicKey; } 39 | } 40 | } 41 | 42 | /// 43 | /// Represents a public key used for authenticated public-key encryption 44 | /// 45 | public sealed class PublicKeyCryptoPublicKey : IDisposable 46 | { 47 | private readonly byte[] _keyBytes; 48 | 49 | public PublicKeyCryptoPublicKey(byte[] keyBytes) 50 | { 51 | _keyBytes = keyBytes; 52 | } 53 | 54 | /// 55 | /// Gets the public-key bytes 56 | /// 57 | public byte[] PlainBytes 58 | { 59 | get { return _keyBytes; } 60 | } 61 | 62 | public void Dispose() 63 | { 64 | if (_keyBytes != null) Array.Clear(_keyBytes, 0, _keyBytes.Length); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | natrium 2 | ======= 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Dextrey 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | 28 | 29 | 30 | libsodium (https://github.com/jedisct1/libsodium/) 31 | ================================================== 32 | 33 | ``` 34 | /* 35 | * Copyright (c) 2013-2014 36 | * Frank Denis 37 | * 38 | * Permission to use, copy, modify, and distribute this software for any 39 | * purpose with or without fee is hereby granted, provided that the above 40 | * copyright notice and this permission notice appear in all copies. 41 | * 42 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 43 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 44 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 45 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 46 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 47 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 48 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 49 | */ 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /Natrium/MessageSigner64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | 6 | namespace Natrium 7 | { 8 | internal sealed class MessageSigner64 : IMessageSigner 9 | { 10 | private readonly uint _publicKeyBytes; 11 | private readonly uint _secretKeyBytes; 12 | private readonly uint _bytes; 13 | 14 | public MessageSigner64() 15 | { 16 | _publicKeyBytes = PlatformInvoke64.crypto_sign_publickeybytes().ToUInt32(); 17 | _secretKeyBytes = PlatformInvoke64.crypto_sign_secretkeybytes().ToUInt32(); 18 | _bytes = PlatformInvoke64.crypto_sign_bytes().ToUInt32(); 19 | } 20 | 21 | public SignerKeyPair CreateRandomKeyPair() 22 | { 23 | var publicKey = new byte[_publicKeyBytes]; 24 | var secretKey = new byte[_secretKeyBytes]; 25 | var result = PlatformInvoke64.crypto_sign_keypair(publicKey, secretKey); 26 | 27 | if (result != 0) throw new CryptographicException("Failed"); 28 | 29 | return new SignerKeyPair(secretKey, publicKey); 30 | } 31 | 32 | public byte[] SignMessage(byte[] message, SignerKeyPair key) 33 | { 34 | var signedMessage = new byte[message.Length + _bytes]; 35 | long signedMessageRealLength = 0; 36 | 37 | var result = PlatformInvoke64.crypto_sign(signedMessage, ref signedMessageRealLength, message, message.Length, 38 | key.SecretKeyBytes); 39 | 40 | if (result != 0) throw new CryptographicException("Failed"); 41 | 42 | Array.Resize(ref signedMessage, (int)signedMessageRealLength); 43 | return signedMessage; 44 | } 45 | 46 | public byte[] VerifySignedMessage(byte[] signedMessage, SignerPublicKey publicKey) 47 | { 48 | var message = new byte[signedMessage.Length]; 49 | long messageRealLength = 0; 50 | 51 | var result = PlatformInvoke64.crypto_sign_open(message, ref messageRealLength, signedMessage, signedMessage.Length, 52 | publicKey.PlainBytes); 53 | 54 | if (result != 0) throw new CryptographicException("Failed"); 55 | 56 | Array.Resize(ref message, (int)messageRealLength); 57 | return message; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Natrium/MessageSigner32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | 7 | namespace Natrium 8 | { 9 | internal sealed class MessageSigner32 : IMessageSigner 10 | { 11 | private readonly uint _publicKeyBytes; 12 | private readonly uint _secretKeyBytes; 13 | private readonly uint _bytes; 14 | 15 | public MessageSigner32() 16 | { 17 | _publicKeyBytes = PlatformInvoke32.crypto_sign_publickeybytes().ToUInt32(); 18 | _secretKeyBytes = PlatformInvoke32.crypto_sign_secretkeybytes().ToUInt32(); 19 | _bytes = PlatformInvoke32.crypto_sign_bytes().ToUInt32(); 20 | } 21 | 22 | public SignerKeyPair CreateRandomKeyPair() 23 | { 24 | var publicKey = new byte[_publicKeyBytes]; 25 | var secretKey = new byte[_secretKeyBytes]; 26 | var result = PlatformInvoke32.crypto_sign_keypair(publicKey, secretKey); 27 | 28 | if (result != 0) throw new CryptographicException("Failed"); 29 | 30 | return new SignerKeyPair(secretKey, publicKey); 31 | } 32 | 33 | public byte[] SignMessage(byte[] message, SignerKeyPair key) 34 | { 35 | var signedMessage = new byte[message.Length + _bytes]; 36 | long signedMessageRealLength = 0; 37 | 38 | var result = PlatformInvoke32.crypto_sign(signedMessage, ref signedMessageRealLength, message, message.Length, 39 | key.SecretKeyBytes); 40 | 41 | if (result != 0) throw new CryptographicException("Failed"); 42 | 43 | Array.Resize(ref signedMessage, (int)signedMessageRealLength); 44 | return signedMessage; 45 | } 46 | 47 | public byte[] VerifySignedMessage(byte[] signedMessage, SignerPublicKey publicKey) 48 | { 49 | var message = new byte[signedMessage.Length]; 50 | long messageRealLength = 0; 51 | 52 | var result = PlatformInvoke32.crypto_sign_open(message, ref messageRealLength, signedMessage, signedMessage.Length, 53 | publicKey.PlainBytes); 54 | 55 | if (result != 0) throw new CryptographicException("Failed"); 56 | 57 | Array.Resize(ref message, (int)messageRealLength); 58 | return message; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /TestApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using Natrium; 7 | 8 | namespace TestApp 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | var signingKp = PrimitiveCryptoConstructs.Instance.MessageSigner.CreateRandomKeyPair(); 15 | var publicKey = signingKp.PublicKey; 16 | 17 | var messageToSign = Encoding.UTF8.GetBytes("This string should not be tampered with"); 18 | var signedMessage = PrimitiveCryptoConstructs.Instance.MessageSigner.SignMessage(messageToSign, signingKp); 19 | 20 | try 21 | { 22 | var confirmedMessage = PrimitiveCryptoConstructs.Instance.MessageSigner.VerifySignedMessage(signedMessage, 23 | publicKey); 24 | 25 | Console.WriteLine("Verified message: " + Encoding.UTF8.GetString(confirmedMessage)); 26 | } 27 | catch (CryptographicException) 28 | { 29 | Console.WriteLine("Signature invalid!"); 30 | } 31 | 32 | 33 | var aliceKeyPair = PrimitiveCryptoConstructs.Instance.AuthenticatedPublicKeyCrypto.CreateRandomKeyPair(); 34 | var alicePublicKey = aliceKeyPair.PublicKey; 35 | 36 | var bobKeyPair = PrimitiveCryptoConstructs.Instance.AuthenticatedPublicKeyCrypto.CreateRandomKeyPair(); 37 | var bobPublicKey = bobKeyPair.PublicKey; 38 | 39 | var c = PrimitiveCryptoConstructs.Instance.AuthenticatedPublicKeyCrypto.CreateCrypterInstance(aliceKeyPair, 40 | bobPublicKey); 41 | 42 | // define the message and nonce 43 | var nonce = PrimitiveCryptoConstructs.Instance.AuthenticatedPublicKeyCrypto.CreateRandomNonce(); 44 | var message = Encoding.UTF8.GetBytes("This string should not be read by the NSA"); 45 | 46 | // encrypt 47 | var encryptedMessage = c.Encrypt(message, nonce); 48 | 49 | // decrypt on Bob's side 50 | var decryptedMessage = 51 | PrimitiveCryptoConstructs.Instance.AuthenticatedPublicKeyCrypto.VerifyAndDecryptMessage(encryptedMessage, 52 | nonce, bobKeyPair, alicePublicKey); 53 | 54 | Console.WriteLine("The message is: " + Encoding.UTF8.GetString(decryptedMessage)); 55 | 56 | Console.ReadLine(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Natrium/IAuthenticatedPublicKeyCrypto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | 7 | namespace Natrium 8 | { 9 | /// 10 | /// Provides methods to encrypt and authenticate data using a public-key authenticator 11 | /// 12 | public interface IAuthenticatedPublicKeyCrypto 13 | { 14 | /// 15 | /// Gets the size, in bytes, of a nonce used in encryption 16 | /// 17 | uint NonceSize { get; } 18 | 19 | /// 20 | /// Creates a new random key pair 21 | /// 22 | /// The generated key pair 23 | PublicKeyCryptoKeyPair CreateRandomKeyPair(); 24 | 25 | /// 26 | /// Creates a new random nonce 27 | /// 28 | /// The generated nonce 29 | byte[] CreateRandomNonce(); 30 | 31 | /// 32 | /// Encrypts and authenticates a message 33 | /// 34 | /// The message to encrypt 35 | /// An unique nonce value 36 | /// Private key pair 37 | /// The public key of the recipient of the message 38 | /// The encrypted message 39 | byte[] EncryptMessage(byte[] message, byte[] nonce, PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey recipientPublicKey); 40 | 41 | /// 42 | /// Verifies and decrypts a message 43 | /// 44 | /// The encrypted message 45 | /// The nonce value used to encrypt the message 46 | /// Private key pair 47 | /// The public key of the sender 48 | /// The decrypted message 49 | /// Thrown if integrity of the decrypted data cannot be confirmed 50 | byte[] VerifyAndDecryptMessage(byte[] encryptedMessage, byte[] nonce, PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey senderPublicKey); 51 | 52 | /// 53 | /// Creates a multi-use crypter instance using the specified keys 54 | /// 55 | /// Private key pair 56 | /// The public key of the other side 57 | /// A crypter context with the key parameters set 58 | IAuthenticatedPublicKeyCryptoContext CreateCrypterInstance(PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey publicKey); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /TestApp/TestApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2F1A30AE-A44D-4C9E-A693-1C29B97E4412} 8 | Exe 9 | Properties 10 | TestApp 11 | TestApp 12 | v4.0 13 | 512 14 | 15 | 16 | x86 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {f16c829b-c9c9-4968-92da-711eb0bb1071} 50 | NSodium 51 | 52 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /Natrium/Natrium.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F16C829B-C9C9-4968-92DA-711EB0BB1071} 8 | Library 9 | Properties 10 | Natrium 11 | Natrium 12 | v4.0 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | AnyCPU 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /Natrium/PrimitiveCryptoConstructs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Natrium 4 | { 5 | /// 6 | /// Provides access to various cryptographic constructs 7 | /// 8 | public class PrimitiveCryptoConstructs 9 | { 10 | // Singleton instance because sodium_init must be called once 11 | // Lazy-loading is utilized for all members 12 | 13 | private static readonly Lazy _instance = new Lazy(() => new PrimitiveCryptoConstructs()); 14 | 15 | /// 16 | /// Gets the singleton instance of the class 17 | /// 18 | public static PrimitiveCryptoConstructs Instance 19 | { 20 | get { return _instance.Value; } 21 | } 22 | 23 | private PrimitiveCryptoConstructs() 24 | { 25 | if (Environment.Is64BitProcess) 26 | { 27 | PlatformInvoke64.sodium_init(); 28 | } 29 | else 30 | { 31 | PlatformInvoke32.sodium_init(); 32 | } 33 | } 34 | 35 | private readonly Lazy _authenticatedPublicKeyCrypto = 36 | new Lazy(() => 37 | { 38 | if (Environment.Is64BitProcess) 39 | { 40 | return new AuthenticatedPublicKeyCrypto64(); 41 | } 42 | else 43 | { 44 | return new AuthenticatedPublicKeyCrypto32(); 45 | } 46 | }); 47 | 48 | /// 49 | /// Gets an instance of a class for encrypting and decrypting messages using public-key cryptography 50 | /// 51 | public IAuthenticatedPublicKeyCrypto AuthenticatedPublicKeyCrypto 52 | { 53 | get { return _authenticatedPublicKeyCrypto.Value; } 54 | } 55 | 56 | private readonly Lazy _messageSigner = 57 | new Lazy(() => 58 | { 59 | if (Environment.Is64BitProcess) 60 | { 61 | return new MessageSigner64(); 62 | } 63 | else 64 | { 65 | return new MessageSigner32(); 66 | } 67 | }); 68 | 69 | /// 70 | /// Gets an instance of a class for digitally signing and verifying messages 71 | /// 72 | public IMessageSigner MessageSigner 73 | { 74 | get { return _messageSigner.Value; } 75 | } 76 | 77 | private readonly Lazy _rng = 78 | new Lazy(() => 79 | { 80 | if (Environment.Is64BitProcess) 81 | { 82 | return new RandomNumberGenerator64(); 83 | } 84 | else 85 | { 86 | return new RandomNumberGenerator32(); 87 | } 88 | }); 89 | 90 | /// 91 | /// Gets an instance of a cryptographically secure pseudo-random number generator (CSPRNG) 92 | /// 93 | public IRandomNumberGenerator Rng 94 | { 95 | get { return _rng.Value; } 96 | } 97 | 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Natrium/PlatformInvoke64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Natrium 5 | { 6 | internal static class PlatformInvoke64 7 | { 8 | private const string LibraryName = "libsodium-64.dll"; 9 | 10 | 11 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 12 | public static extern void sodium_init(); 13 | 14 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 15 | public static extern UIntPtr crypto_box_publickeybytes(); 16 | 17 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 18 | public static extern UIntPtr crypto_box_secretkeybytes(); 19 | 20 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 21 | public static extern UIntPtr crypto_box_noncebytes(); 22 | 23 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 24 | public static extern UIntPtr crypto_box_zerobytes(); 25 | 26 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 27 | public static extern UIntPtr crypto_box_beforenmbytes(); 28 | 29 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 30 | public static extern int crypto_box_keypair(byte[] publicKey, byte[] secretKey); 31 | 32 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 33 | public static extern int crypto_box(byte[] buffer, byte[] message, long messageLength, byte[] nonce, byte[] publicKey, byte[] secretKey); 34 | 35 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 36 | public static extern int crypto_box_beforenm(byte[] sharedSecret, byte[] publicKey, byte[] secretKey); 37 | 38 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 39 | public static extern int crypto_box_afternm(byte[] buffer, byte[] message, long messageLength, byte[] nonce, byte[] sharedSecret); 40 | 41 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 42 | public static extern int crypto_box_open(byte[] buffer, byte[] cipherText, long cipherTextLength, byte[] nonce, byte[] publicKey, byte[] secretKey); 43 | 44 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 45 | public static extern int crypto_box_open_afternm(byte[] buffer, byte[] cipherText, long cipherTextLength, byte[] nonce, byte[] sharedSecret); 46 | 47 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 48 | public static extern void randombytes_buf(byte[] buffer, UIntPtr size); 49 | 50 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 51 | public static extern UIntPtr crypto_sign_publickeybytes(); 52 | 53 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 54 | public static extern UIntPtr crypto_sign_secretkeybytes(); 55 | 56 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 57 | public static extern UIntPtr crypto_sign_bytes(); 58 | 59 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 60 | public static extern int crypto_sign_keypair(byte[] publicKey, byte[] secretKey); 61 | 62 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 63 | public static extern int crypto_sign(byte[] sig, ref long sigLength, byte[] message, long messageLength, byte[] secretKey); 64 | 65 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 66 | public static extern int crypto_sign_open(byte[] message, ref long messageLength, byte[] sig, long sigLength, byte[] publicKey); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Natrium/PlatformInvoke32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Natrium 5 | { 6 | internal static class PlatformInvoke32 7 | { 8 | private const string LibraryName = "libsodium-32.dll"; 9 | 10 | 11 | 12 | 13 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 14 | public static extern void sodium_init(); 15 | 16 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 17 | public static extern UIntPtr crypto_box_publickeybytes(); 18 | 19 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 20 | public static extern UIntPtr crypto_box_secretkeybytes(); 21 | 22 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 23 | public static extern UIntPtr crypto_box_noncebytes(); 24 | 25 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 26 | public static extern UIntPtr crypto_box_zerobytes(); 27 | 28 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 29 | public static extern UIntPtr crypto_box_beforenmbytes(); 30 | 31 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 32 | public static extern int crypto_box_keypair(byte[] publicKey, byte[] secretKey); 33 | 34 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 35 | public static extern int crypto_box(byte[] buffer, byte[] message, long messageLength, byte[] nonce, byte[] publicKey, byte[] secretKey); 36 | 37 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 38 | public static extern int crypto_box_beforenm(byte[] sharedSecret, byte[] publicKey, byte[] secretKey); 39 | 40 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 41 | public static extern int crypto_box_afternm(byte[] buffer, byte[] message, long messageLength, byte[] nonce, byte[] sharedSecret); 42 | 43 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 44 | public static extern int crypto_box_open(byte[] buffer, byte[] cipherText, long cipherTextLength, byte[] nonce, byte[] publicKey, byte[] secretKey); 45 | 46 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 47 | public static extern int crypto_box_open_afternm(byte[] buffer, byte[] cipherText, long cipherTextLength, byte[] nonce, byte[] sharedSecret); 48 | 49 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 50 | public static extern void randombytes_buf(byte[] buffer, UIntPtr size); 51 | 52 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 53 | public static extern UIntPtr crypto_sign_publickeybytes(); 54 | 55 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 56 | public static extern UIntPtr crypto_sign_secretkeybytes(); 57 | 58 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 59 | public static extern UIntPtr crypto_sign_bytes(); 60 | 61 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 62 | public static extern int crypto_sign_keypair(byte[] publicKey, byte[] secretKey); 63 | 64 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 65 | public static extern int crypto_sign(byte[] sig, ref long sigLength, byte[] message, long messageLength, byte[] secretKey); 66 | 67 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] 68 | public static extern int crypto_sign_open(byte[] message, ref long messageLength, byte[] sig, long sigLength, byte[] publicKey); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Natrium/AuthenticatedPublicKeyCrypto32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Natrium 5 | { 6 | internal sealed class AuthenticatedPublicKeyCrypto32 : IAuthenticatedPublicKeyCrypto 7 | { 8 | private readonly uint _publicKeyBytes; 9 | private readonly uint _secretKeyBytes; 10 | private readonly uint _zeroBytes; 11 | private readonly uint _beforeNmBytes; 12 | 13 | public AuthenticatedPublicKeyCrypto32() 14 | { 15 | _publicKeyBytes = PlatformInvoke32.crypto_box_publickeybytes().ToUInt32(); 16 | _secretKeyBytes = PlatformInvoke32.crypto_box_secretkeybytes().ToUInt32(); 17 | _zeroBytes = PlatformInvoke32.crypto_box_zerobytes().ToUInt32(); 18 | _beforeNmBytes = PlatformInvoke32.crypto_box_beforenmbytes().ToUInt32(); 19 | NonceSize = PlatformInvoke32.crypto_box_noncebytes().ToUInt32(); 20 | } 21 | 22 | public uint NonceSize { get; private set; } 23 | 24 | public PublicKeyCryptoKeyPair CreateRandomKeyPair() 25 | { 26 | var publicKey = new byte[_publicKeyBytes]; 27 | var secretKey = new byte[_secretKeyBytes]; 28 | var result = PlatformInvoke32.crypto_box_keypair(publicKey, secretKey); 29 | 30 | if (result != 0) throw new CryptographicException("Failed"); 31 | 32 | return new PublicKeyCryptoKeyPair(secretKey, publicKey); 33 | } 34 | 35 | public byte[] CreateRandomNonce() 36 | { 37 | var nonce = new byte[NonceSize]; 38 | PlatformInvoke32.randombytes_buf(nonce, new UIntPtr((uint)nonce.Length)); 39 | 40 | return nonce; 41 | } 42 | 43 | public byte[] EncryptMessage(byte[] message, byte[] nonce, PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey recipientPublicKey) 44 | { 45 | // verify arguments 46 | if (nonce == null) throw new ArgumentNullException("nonce"); 47 | if (nonce.Length != NonceSize) 48 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", NonceSize)); 49 | 50 | // pad the message (prepend zero bytes) 51 | var paddedMessage = new byte[_zeroBytes + message.Length]; 52 | Buffer.BlockCopy(message, 0, paddedMessage, (int)_zeroBytes, message.Length); 53 | 54 | // encrypt 55 | var encryptedMessage = new byte[paddedMessage.Length]; 56 | var result = PlatformInvoke32.crypto_box(encryptedMessage, paddedMessage, paddedMessage.Length, nonce, recipientPublicKey.PlainBytes, 57 | key.SecretKeyBytes); 58 | 59 | if (result != 0) throw new CryptographicException("Failed"); 60 | 61 | return encryptedMessage; 62 | } 63 | 64 | public byte[] VerifyAndDecryptMessage(byte[] encryptedMessage, byte[] nonce, PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey senderPublicKey) 65 | { 66 | // verify arguments 67 | if (nonce == null) throw new ArgumentNullException("nonce"); 68 | if (nonce.Length != NonceSize) 69 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", NonceSize)); 70 | 71 | // decrypt 72 | var paddedMessage = new byte[encryptedMessage.Length]; 73 | var result = PlatformInvoke32.crypto_box_open(paddedMessage, encryptedMessage, encryptedMessage.Length, nonce, 74 | senderPublicKey.PlainBytes, key.SecretKeyBytes); 75 | 76 | if (result != 0) throw new CryptographicException("Failed"); 77 | 78 | var message = new byte[paddedMessage.Length - _zeroBytes]; 79 | Buffer.BlockCopy(paddedMessage, (int)_zeroBytes, message, 0, message.Length); 80 | 81 | return message; 82 | } 83 | 84 | public IAuthenticatedPublicKeyCryptoContext CreateCrypterInstance(PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey publicKey) 85 | { 86 | // compute the shared secret 87 | var sharedSecret = new byte[_beforeNmBytes]; 88 | var result = PlatformInvoke32.crypto_box_beforenm(sharedSecret, publicKey.PlainBytes, key.SecretKeyBytes); 89 | 90 | if (result != 0) throw new CryptographicException("Failed"); 91 | 92 | return new AuthenticatedPublicKeyCryptoContext(this, sharedSecret); 93 | } 94 | 95 | private class AuthenticatedPublicKeyCryptoContext : IAuthenticatedPublicKeyCryptoContext, IDisposable 96 | { 97 | private readonly AuthenticatedPublicKeyCrypto32 _parent; 98 | private readonly byte[] _sharedSecret; 99 | 100 | public AuthenticatedPublicKeyCryptoContext(AuthenticatedPublicKeyCrypto32 parent, byte[] sharedSecret) 101 | { 102 | _parent = parent; 103 | _sharedSecret = sharedSecret; 104 | } 105 | 106 | public void Dispose() 107 | { 108 | Array.Clear(_sharedSecret, 0, _sharedSecret.Length); 109 | } 110 | 111 | public byte[] Encrypt(byte[] message, byte[] nonce) 112 | { 113 | // verify arguments 114 | if (nonce == null) throw new ArgumentNullException("nonce"); 115 | if (nonce.Length != _parent.NonceSize) 116 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", _parent.NonceSize)); 117 | 118 | 119 | // pad the message (prepend zero bytes) 120 | var paddedMessage = new byte[_parent._zeroBytes + message.Length]; 121 | Buffer.BlockCopy(message, 0, paddedMessage, (int)_parent._zeroBytes, message.Length); 122 | 123 | // encrypt 124 | var encryptedMessage = new byte[paddedMessage.Length]; 125 | var result = PlatformInvoke32.crypto_box_afternm(encryptedMessage, paddedMessage, paddedMessage.Length, nonce, _sharedSecret); 126 | 127 | if (result != 0) throw new CryptographicException("Failed"); 128 | 129 | return encryptedMessage; 130 | } 131 | 132 | public byte[] VerifyAndDecrypt(byte[] encryptedMessage, byte[] nonce) 133 | { 134 | // verify arguments 135 | if (nonce == null) throw new ArgumentNullException("nonce"); 136 | if (nonce.Length != _parent.NonceSize) 137 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", _parent.NonceSize)); 138 | 139 | 140 | // decrypt 141 | var paddedMessage = new byte[encryptedMessage.Length]; 142 | var result = PlatformInvoke32.crypto_box_open_afternm(paddedMessage, encryptedMessage, encryptedMessage.Length, nonce, _sharedSecret); 143 | 144 | if (result != 0) throw new CryptographicException("Failed"); 145 | 146 | var message = new byte[paddedMessage.Length - _parent._zeroBytes]; 147 | Buffer.BlockCopy(paddedMessage, (int)_parent._zeroBytes, message, 0, message.Length); 148 | 149 | return message; 150 | } 151 | 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Natrium/AuthenticatedPublicKeyCrypto64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Natrium 5 | { 6 | internal sealed class AuthenticatedPublicKeyCrypto64 : IAuthenticatedPublicKeyCrypto 7 | { 8 | private readonly uint _publicKeyBytes; 9 | private readonly uint _secretKeyBytes; 10 | private readonly uint _zeroBytes; 11 | private readonly uint _beforeNmBytes; 12 | 13 | public AuthenticatedPublicKeyCrypto64() 14 | { 15 | _publicKeyBytes = PlatformInvoke64.crypto_box_publickeybytes().ToUInt32(); 16 | _secretKeyBytes = PlatformInvoke64.crypto_box_secretkeybytes().ToUInt32(); 17 | _zeroBytes = PlatformInvoke64.crypto_box_zerobytes().ToUInt32(); 18 | _beforeNmBytes = PlatformInvoke64.crypto_box_beforenmbytes().ToUInt32(); 19 | NonceSize = PlatformInvoke64.crypto_box_noncebytes().ToUInt32(); 20 | } 21 | 22 | public uint NonceSize { get; private set; } 23 | 24 | public PublicKeyCryptoKeyPair CreateRandomKeyPair() 25 | { 26 | var publicKey = new byte[_publicKeyBytes]; 27 | var secretKey = new byte[_secretKeyBytes]; 28 | var result = PlatformInvoke64.crypto_box_keypair(publicKey, secretKey); 29 | 30 | if (result != 0) throw new CryptographicException("Failed"); 31 | 32 | return new PublicKeyCryptoKeyPair(secretKey, publicKey); 33 | } 34 | 35 | public byte[] CreateRandomNonce() 36 | { 37 | var nonce = new byte[NonceSize]; 38 | PlatformInvoke64.randombytes_buf(nonce, new UIntPtr((uint)nonce.Length)); 39 | 40 | return nonce; 41 | } 42 | 43 | public byte[] EncryptMessage(byte[] message, byte[] nonce, PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey recipientPublicKey) 44 | { 45 | // verify arguments 46 | if (nonce == null) throw new ArgumentNullException("nonce"); 47 | if (nonce.Length != NonceSize) 48 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", NonceSize)); 49 | 50 | // pad the message (prepend zero bytes) 51 | var paddedMessage = new byte[_zeroBytes + message.Length]; 52 | Buffer.BlockCopy(message, 0, paddedMessage, (int)_zeroBytes, message.Length); 53 | 54 | // encrypt 55 | var encryptedMessage = new byte[paddedMessage.Length]; 56 | var result = PlatformInvoke64.crypto_box(encryptedMessage, paddedMessage, paddedMessage.Length, nonce, recipientPublicKey.PlainBytes, 57 | key.SecretKeyBytes); 58 | 59 | if (result != 0) throw new CryptographicException("Failed"); 60 | 61 | return encryptedMessage; 62 | } 63 | 64 | public byte[] VerifyAndDecryptMessage(byte[] encryptedMessage, byte[] nonce, PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey senderPublicKey) 65 | { 66 | // verify arguments 67 | if (nonce == null) throw new ArgumentNullException("nonce"); 68 | if (nonce.Length != NonceSize) 69 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", NonceSize)); 70 | 71 | // decrypt 72 | var paddedMessage = new byte[encryptedMessage.Length]; 73 | var result = PlatformInvoke64.crypto_box_open(paddedMessage, encryptedMessage, encryptedMessage.Length, nonce, 74 | senderPublicKey.PlainBytes, key.SecretKeyBytes); 75 | 76 | if (result != 0) throw new CryptographicException("Failed"); 77 | 78 | var message = new byte[paddedMessage.Length - _zeroBytes]; 79 | Buffer.BlockCopy(paddedMessage, (int)_zeroBytes, message, 0, message.Length); 80 | 81 | return message; 82 | } 83 | 84 | public IAuthenticatedPublicKeyCryptoContext CreateCrypterInstance(PublicKeyCryptoKeyPair key, PublicKeyCryptoPublicKey publicKey) 85 | { 86 | // compute the shared secret 87 | var sharedSecret = new byte[_beforeNmBytes]; 88 | var result = PlatformInvoke64.crypto_box_beforenm(sharedSecret, publicKey.PlainBytes, key.SecretKeyBytes); 89 | 90 | if (result != 0) throw new CryptographicException("Failed"); 91 | 92 | return new AuthenticatedPublicKeyCryptoContext(this, sharedSecret); 93 | } 94 | 95 | private class AuthenticatedPublicKeyCryptoContext : IAuthenticatedPublicKeyCryptoContext, IDisposable 96 | { 97 | private readonly AuthenticatedPublicKeyCrypto64 _parent; 98 | private readonly byte[] _sharedSecret; 99 | 100 | public AuthenticatedPublicKeyCryptoContext(AuthenticatedPublicKeyCrypto64 parent, byte[] sharedSecret) 101 | { 102 | _parent = parent; 103 | _sharedSecret = sharedSecret; 104 | } 105 | 106 | public void Dispose() 107 | { 108 | Array.Clear(_sharedSecret, 0, _sharedSecret.Length); 109 | } 110 | 111 | public byte[] Encrypt(byte[] message, byte[] nonce) 112 | { 113 | // verify arguments 114 | if (nonce == null) throw new ArgumentNullException("nonce"); 115 | if (nonce.Length != _parent.NonceSize) 116 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", _parent.NonceSize)); 117 | 118 | 119 | // pad the message (prepend zero bytes) 120 | var paddedMessage = new byte[_parent._zeroBytes + message.Length]; 121 | Buffer.BlockCopy(message, 0, paddedMessage, (int)_parent._zeroBytes, message.Length); 122 | 123 | // encrypt 124 | var encryptedMessage = new byte[paddedMessage.Length]; 125 | var result = PlatformInvoke64.crypto_box_afternm(encryptedMessage, paddedMessage, paddedMessage.Length, nonce, _sharedSecret); 126 | 127 | if (result != 0) throw new CryptographicException("Failed"); 128 | 129 | return encryptedMessage; 130 | } 131 | 132 | public byte[] VerifyAndDecrypt(byte[] encryptedMessage, byte[] nonce) 133 | { 134 | // verify arguments 135 | if (nonce == null) throw new ArgumentNullException("nonce"); 136 | if (nonce.Length != _parent.NonceSize) 137 | throw new ArgumentOutOfRangeException("nonce", string.Format("Nonce must be {0} bytes long", _parent.NonceSize)); 138 | 139 | 140 | // decrypt 141 | var paddedMessage = new byte[encryptedMessage.Length]; 142 | var result = PlatformInvoke64.crypto_box_open_afternm(paddedMessage, encryptedMessage, encryptedMessage.Length, nonce, _sharedSecret); 143 | 144 | if (result != 0) throw new CryptographicException("Failed"); 145 | 146 | var message = new byte[paddedMessage.Length - _parent._zeroBytes]; 147 | Buffer.BlockCopy(paddedMessage, (int)_parent._zeroBytes, message, 0, message.Length); 148 | 149 | return message; 150 | } 151 | 152 | } 153 | } 154 | } 155 | --------------------------------------------------------------------------------