├── .gitignore ├── LICENSE ├── README.md ├── Salsa20Cipher.plgx ├── Salsa20Cipher ├── Properties │ └── AssemblyInfo.cs ├── Salsa20Cipher.csproj ├── Salsa20CipherExt.cs ├── Salsa20CryptoTransform.cs └── Salsa20Engine.cs ├── Salsa20CipherPlugin.sln ├── make-plgx.bat └── version-information.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016, Scott Bennett 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeePass-SalsaCipher 2 | 3 | Enables KeePass to encrypt databases using the Salsa20 cipher. Just download 4 | the released `Salsa20Cipher.plgx` file and put it in the KeePass installation 5 | folder. KeePass should automatically recognize it as an available encryption 6 | algorithm. 7 | 8 | http://keepass.info/plugins.html 9 | 10 | ### Requirements 11 | 12 | * KeePass 2.18 or higher 13 | * .NET framework v3.0 or higher 14 | 15 | ### Build Info 16 | 17 | The released `Salsa20Cipher.plgx` is built using these options: 18 | 19 | `--plgx-prereq-kp:2.18 --plgx-prereq-os:Windows --plgx-prereq-net:3` 20 | 21 | See the [KeePass plugin development page](http://keepass.info/help/v2_dev/plg_index.html#plgx) 22 | for more information. 23 | 24 | ### Notes 25 | 26 | * This project was created with Visual Studio 2010 27 | * The plugin has only been tested on Windows 7 28 | -------------------------------------------------------------------------------- /Salsa20Cipher.plgx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbennett1990/KeePass-SalsaCipher/91207eb901b6580db8e63f72413fa247ef26edd1/Salsa20Cipher.plgx -------------------------------------------------------------------------------- /Salsa20Cipher/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("Salsa20 Cipher")] 9 | [assembly: AssemblyDescription("Enables KeePass to encrypt databases using the Salsa20 algorithm.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Scott Bennett")] 12 | [assembly: AssemblyProduct("KeePass Plugin")] 13 | [assembly: AssemblyCopyright("Copyright (c) 2015 Scott Bennett")] 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("716E1C8A-EE17-4BDC-93AE-A977B882833A")] 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 | [assembly: AssemblyVersion("0.6.1")] 33 | [assembly: AssemblyFileVersion("0.6.1")] 34 | -------------------------------------------------------------------------------- /Salsa20Cipher/Salsa20Cipher.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {A7DEAA9C-9BBB-474F-9467-FF150FC50BED} 9 | Library 10 | Properties 11 | Salsa20Cipher 12 | Salsa20Cipher 13 | v3.0 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | C:\Program Files (x86)\KeePass\KeePass.exe 37 | False 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | -------------------------------------------------------------------------------- /Salsa20Cipher/Salsa20CipherExt.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Scott Bennett 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | using KeePass.Plugins; 28 | 29 | namespace Salsa20Cipher { 30 | public sealed class Salsa20CipherExt : Plugin { 31 | private IPluginHost pluginHost = null; 32 | private const string updateUrl = 33 | "https://raw.githubusercontent.com/sbennett1990/KeePass-SalsaCipher/master/version-information.txt"; 34 | 35 | private static Salsa20Engine salsa20CipherEngine = new Salsa20Engine(); 36 | 37 | /// 38 | /// Access the URL of my version information file. (Read-Only) 39 | /// 40 | public override string UpdateUrl { 41 | get { 42 | return updateUrl; 43 | } 44 | } 45 | 46 | /// 47 | /// Initialize the Salsa20 Cipher plugin. 48 | /// 49 | /// The host application (i.e. KeePass 2.18) 50 | /// True for successful initialization; false otherwise 51 | public override bool Initialize(IPluginHost host) { 52 | if (host == null) { 53 | return false; 54 | } 55 | 56 | pluginHost = host; 57 | 58 | if (salsa20CipherEngine == null) { 59 | return false; 60 | } 61 | 62 | pluginHost.CipherPool.AddCipher(salsa20CipherEngine); 63 | 64 | return true; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Salsa20Cipher/Salsa20CryptoTransform.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2013 Logos Bible Software 3 | * 4 | * This code is derived from software originally written by 5 | * Logos Bible Software. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | using System; 27 | using System.Security.Cryptography; 28 | using System.Text; 29 | 30 | namespace Salsa20Cipher { 31 | /// 32 | /// Salsa20 Stream Cipher 33 | /// 34 | public sealed class Salsa20CryptoTransform : ICryptoTransform { 35 | /// 36 | /// The Salsa20 state (aka "context") 37 | /// 38 | private uint[] state; 39 | 40 | /// 41 | /// The number of Salsa rounds to perform 42 | /// 43 | private readonly int numRounds; 44 | 45 | /// 46 | /// Set up a new Salsa20 state. The lengths of the given parameters are 47 | /// checked before encryption happens. A 32-byte (256-bit) key is required. 48 | /// The nonce (aka IV) must be at least 8-bytes (64-bits) long. If it is 49 | /// any larger, only the first 64 bits will be used. 50 | /// 51 | /// 52 | /// A 32-byte (256-bit) key, treated as a concatenation of eight 32-bit 53 | /// little-endian integers 54 | /// 55 | /// 56 | /// An 8-byte (64-bit) nonce, treated as a concatenation of two 32-bit 57 | /// little-endian integers 58 | /// 59 | public Salsa20CryptoTransform(byte[] key, byte[] iv) { 60 | if (key == null) { 61 | throw new ArgumentNullException("key"); 62 | } 63 | if (iv == null) { 64 | throw new ArgumentNullException("iv"); 65 | } 66 | if (key.Length != 32) { 67 | throw new ArgumentException( 68 | "Key length must be 32 bytes. Actual is " + key.Length.ToString() 69 | ); 70 | } 71 | if (iv.Length < 8) { 72 | throw new ArgumentException( 73 | "Nonce should have 8 bytes. Actual is " + iv.Length.ToString() 74 | ); 75 | } 76 | 77 | Initialize(key, iv); 78 | numRounds = 20; 79 | } 80 | 81 | /// 82 | /// Determine whether the current transform can be reused (Read-Only) 83 | /// 84 | public bool CanReuseTransform { 85 | get { 86 | return false; 87 | } 88 | } 89 | 90 | /// 91 | /// Determine whether multiple blocks can be transformed (Read-Only) 92 | /// 93 | public bool CanTransformMultipleBlocks { 94 | get { 95 | return true; 96 | } 97 | } 98 | 99 | /// 100 | /// Get the input block size, in bytes (Read-Only) 101 | /// 102 | public int InputBlockSize { 103 | get { 104 | return 64; 105 | } 106 | } 107 | 108 | /// 109 | /// Get the output block size, in bytes (Read-Only) 110 | /// 111 | public int OutputBlockSize { 112 | get { 113 | return 64; 114 | } 115 | } 116 | 117 | /// 118 | /// Initialize the Salsa state with the given key and nonce (aka 119 | /// Initialization Vector or IV). A 32-byte (256-bit) key is required. The 120 | /// nonce must be at least 8-bytes (64-bits) long. If it is any larger, 121 | /// only the first 64 bits will be used. 122 | /// 123 | /// 124 | /// A 32-byte (256-bit) key, treated as a concatenation of eight 32-bit 125 | /// little-endian integers 126 | /// 127 | /// 128 | /// An 8-byte (64-bit) nonce, treated as a concatenation of two 32-bit 129 | /// little-endian integers 130 | /// 131 | private void Initialize(byte[] key, byte[] iv) { 132 | state = new uint[16]; 133 | 134 | state[1] = ToUInt32(key, 0); 135 | state[2] = ToUInt32(key, 4); 136 | state[3] = ToUInt32(key, 8); 137 | state[4] = ToUInt32(key, 12); 138 | 139 | // These are the same constants defined in the reference implementation 140 | // see http://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c 141 | byte[] sigma = Encoding.ASCII.GetBytes("expand 32-byte k"); 142 | byte[] tau = Encoding.ASCII.GetBytes("expand 16-byte k"); 143 | 144 | byte[] constants = (key.Length == 32) ? sigma : tau; 145 | int keyIndex = key.Length - 16; 146 | 147 | state[11] = ToUInt32(key, keyIndex + 0); 148 | state[12] = ToUInt32(key, keyIndex + 4); 149 | state[13] = ToUInt32(key, keyIndex + 8); 150 | state[14] = ToUInt32(key, keyIndex + 12); 151 | 152 | state[0] = ToUInt32(constants, 0); 153 | state[5] = ToUInt32(constants, 4); 154 | state[10] = ToUInt32(constants, 8); 155 | state[15] = ToUInt32(constants, 12); 156 | 157 | state[6] = ToUInt32(iv, 0); 158 | state[7] = ToUInt32(iv, 4); 159 | state[8] = 0; 160 | state[9] = 0; 161 | } 162 | 163 | /// 164 | /// Encrypt an arbitrary-length plaintext message (inputBuffer), writing the 165 | /// resulting ciphertext to the outputBuffer. The number of bytes to read 166 | /// from the input buffer is determined by inputCount. 167 | /// 168 | /// 169 | /// 170 | /// 171 | /// 172 | /// 173 | /// The number of bytes written 174 | public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { 175 | /* Check the parameters */ 176 | if (inputBuffer == null) { 177 | throw new ArgumentNullException("inputBuffer"); 178 | } 179 | if (inputOffset < 0 || inputOffset >= inputBuffer.Length) { 180 | throw new ArgumentOutOfRangeException("inputOffset"); 181 | } 182 | if (inputCount < 0 || (inputOffset + inputCount) > inputBuffer.Length) { 183 | throw new ArgumentOutOfRangeException("inputCount"); 184 | } 185 | if (outputBuffer == null) { 186 | throw new ArgumentNullException("outputBuffer"); 187 | } 188 | if (outputOffset < 0 || (outputOffset + inputCount) > outputBuffer.Length) { 189 | throw new ArgumentOutOfRangeException("outputOffset"); 190 | } 191 | if (state == null) { 192 | throw new ObjectDisposedException(GetType().Name); 193 | } 194 | 195 | byte[] output = new byte[64]; 196 | int bytesTransformed = 0; 197 | 198 | while (inputCount > 0) { 199 | Salsa20Core(output, state); 200 | 201 | state[8] = AddOne(state[8]); 202 | if (state[8] == 0) { 203 | /* Stopping at 2^70 bytes per nonce is the user's responsibility */ 204 | state[9] = AddOne(state[9]); 205 | } 206 | 207 | int blockSize = Math.Min(64, inputCount); 208 | 209 | for (int i = 0; i < blockSize; i++) { 210 | outputBuffer[outputOffset + i] = (byte) (inputBuffer[inputOffset + i] ^ output[i]); 211 | } 212 | 213 | bytesTransformed += blockSize; 214 | 215 | inputCount -= 64; 216 | outputOffset += 64; 217 | inputOffset += 64; 218 | } 219 | 220 | return bytesTransformed; 221 | } 222 | 223 | /// 224 | /// Transforms the specified region of the specified byte array. 225 | /// 226 | /// 227 | /// 228 | /// 229 | /// The computed transform 230 | public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { 231 | // No parameter checking needed as that is handled in TransformBlock() 232 | 233 | byte[] output = new byte[inputCount]; 234 | TransformBlock(inputBuffer, inputOffset, inputCount, output, 0); 235 | 236 | return output; 237 | } 238 | 239 | /// 240 | /// Clear and dispose of the internal Salsa state. 241 | /// 242 | public void Dispose() { 243 | if (state != null) { 244 | Array.Clear(state, 0, state.Length); 245 | } 246 | 247 | state = null; 248 | } 249 | 250 | /// 251 | /// The Salsa20 Core Function reads a 64-byte vector x and produces a 64-byte 252 | /// vector Salsa20(x). This is the basis of the Salsa20 Stream Cipher. 253 | /// 254 | /// 255 | /// See the Salsa20 core page 256 | /// for more detailed information about the Salsa20 core function. 257 | /// 258 | /// 259 | /// 260 | private void Salsa20Core(byte[] output, uint[] input) { 261 | uint[] tmp = (uint[]) input.Clone(); 262 | 263 | for (int i = numRounds; i > 0; i -= 2) { 264 | tmp[4] ^= Rotate(Add(tmp[0], tmp[12]), 7); 265 | tmp[8] ^= Rotate(Add(tmp[4], tmp[0]), 9); 266 | tmp[12] ^= Rotate(Add(tmp[8], tmp[4]), 13); 267 | tmp[0] ^= Rotate(Add(tmp[12], tmp[8]), 18); 268 | tmp[9] ^= Rotate(Add(tmp[5], tmp[1]), 7); 269 | tmp[13] ^= Rotate(Add(tmp[9], tmp[5]), 9); 270 | tmp[1] ^= Rotate(Add(tmp[13], tmp[9]), 13); 271 | tmp[5] ^= Rotate(Add(tmp[1], tmp[13]), 18); 272 | tmp[14] ^= Rotate(Add(tmp[10], tmp[6]), 7); 273 | tmp[2] ^= Rotate(Add(tmp[14], tmp[10]), 9); 274 | tmp[6] ^= Rotate(Add(tmp[2], tmp[14]), 13); 275 | tmp[10] ^= Rotate(Add(tmp[6], tmp[2]), 18); 276 | tmp[3] ^= Rotate(Add(tmp[15], tmp[11]), 7); 277 | tmp[7] ^= Rotate(Add(tmp[3], tmp[15]), 9); 278 | tmp[11] ^= Rotate(Add(tmp[7], tmp[3]), 13); 279 | tmp[15] ^= Rotate(Add(tmp[11], tmp[7]), 18); 280 | tmp[1] ^= Rotate(Add(tmp[0], tmp[3]), 7); 281 | tmp[2] ^= Rotate(Add(tmp[1], tmp[0]), 9); 282 | tmp[3] ^= Rotate(Add(tmp[2], tmp[1]), 13); 283 | tmp[0] ^= Rotate(Add(tmp[3], tmp[2]), 18); 284 | tmp[6] ^= Rotate(Add(tmp[5], tmp[4]), 7); 285 | tmp[7] ^= Rotate(Add(tmp[6], tmp[5]), 9); 286 | tmp[4] ^= Rotate(Add(tmp[7], tmp[6]), 13); 287 | tmp[5] ^= Rotate(Add(tmp[4], tmp[7]), 18); 288 | tmp[11] ^= Rotate(Add(tmp[10], tmp[9]), 7); 289 | tmp[8] ^= Rotate(Add(tmp[11], tmp[10]), 9); 290 | tmp[9] ^= Rotate(Add(tmp[8], tmp[11]), 13); 291 | tmp[10] ^= Rotate(Add(tmp[9], tmp[8]), 18); 292 | tmp[12] ^= Rotate(Add(tmp[15], tmp[14]), 7); 293 | tmp[13] ^= Rotate(Add(tmp[12], tmp[15]), 9); 294 | tmp[14] ^= Rotate(Add(tmp[13], tmp[12]), 13); 295 | tmp[15] ^= Rotate(Add(tmp[14], tmp[13]), 18); 296 | } 297 | 298 | for (int i = 0; i < 16; i++) { 299 | ToBytes(Add(tmp[i], input[i]), output, 4 * i); 300 | } 301 | } 302 | 303 | /// 304 | /// n-bit left rotation operation (towards the high bits) for 32-bit 305 | /// integers. 306 | /// 307 | /// 308 | /// 309 | /// The result of (v LEFTSHIFT c) 310 | private static uint Rotate(uint v, int c) { 311 | return (v << c) | (v >> (32 - c)); 312 | } 313 | 314 | /// 315 | /// Unchecked integer addition. The Salsa spec defines certain operations 316 | /// to use 32-bit unsigned integer addition modulo 2^32. 317 | /// 318 | /// 319 | /// 320 | /// The result of (v + w) modulo 2^32 321 | private static uint Add(uint v, uint w) { 322 | return unchecked(v + w); 323 | } 324 | 325 | /// 326 | /// Add 1 to the input parameter using unchecked integer addition. The 327 | /// Salsa spec defines certain operations to use 32-bit unsigned integer 328 | /// addition modulo 2^32. 329 | /// 330 | /// 331 | /// The result of (v + 1) modulo 2^32 332 | private static uint AddOne(uint v) { 333 | return unchecked(v + 1); 334 | } 335 | 336 | /// 337 | /// Convert four bytes of the input buffer into an unsigned 338 | /// 32-bit integer, beginning at the inputOffset. 339 | /// 340 | /// 341 | /// 342 | /// An unsigned 32-bit integer 343 | private static uint ToUInt32(byte[] input, int inputOffset) { 344 | unchecked { 345 | return (uint) (((input[inputOffset] | 346 | (input[inputOffset + 1] << 8)) | 347 | (input[inputOffset + 2] << 16)) | 348 | (input[inputOffset + 3] << 24)); 349 | } 350 | } 351 | 352 | /// 353 | /// Serialize the input integer into the output buffer. The input integer 354 | /// will be split into 4 bytes and put into four sequential places in the 355 | /// output buffer, starting at the outputOffset. 356 | /// 357 | /// 358 | /// 359 | /// 360 | private static void ToBytes(uint input, byte[] output, int outputOffset) { 361 | unchecked { 362 | output[outputOffset] = (byte) input; 363 | output[outputOffset + 1] = (byte) (input >> 8); 364 | output[outputOffset + 2] = (byte) (input >> 16); 365 | output[outputOffset + 3] = (byte) (input >> 24); 366 | } 367 | } 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /Salsa20Cipher/Salsa20Engine.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Scott Bennett 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | using System.IO; 28 | using System.Security.Cryptography; 29 | 30 | using KeePassLib; 31 | using KeePassLib.Cryptography.Cipher; 32 | 33 | namespace Salsa20Cipher { 34 | public sealed class Salsa20Engine : ICipherEngine { 35 | private PwUuid uuidCipher; 36 | private const string cipherName = "Salsa20 Cipher"; 37 | 38 | private static readonly byte[] Salsa20UuidBytes = new byte[] { 39 | 0x71, 0x6E, 0x1C, 0x8A, 0xEE, 0x17, 0x4B, 0xDC, 40 | 0x93, 0xAE, 0xA9, 0x77, 0xB8, 0x82, 0x83, 0x3A 41 | }; 42 | 43 | /// 44 | /// Basic constructor 45 | /// 46 | public Salsa20Engine() { 47 | uuidCipher = new PwUuid(Salsa20UuidBytes); 48 | } 49 | 50 | /// 51 | /// Access this Cipher's UUID. 52 | /// 53 | public PwUuid CipherUuid { 54 | get { 55 | return uuidCipher; 56 | } 57 | } 58 | 59 | /// 60 | /// Access this Cipher's name. 61 | /// 62 | public string DisplayName { 63 | get { 64 | return cipherName; 65 | } 66 | } 67 | 68 | /// 69 | /// Encrypt the incoming plainTextStream. 70 | /// 71 | /// 72 | /// 73 | /// 74 | /// An encrypted stream of bytes 75 | public Stream EncryptStream(Stream plainTextStream, byte[] key, byte[] iv) { 76 | ICryptoTransform iTransform = new Salsa20CryptoTransform(key, iv); 77 | 78 | return new CryptoStream(plainTextStream, iTransform, CryptoStreamMode.Write); 79 | } 80 | 81 | /// 82 | /// Decrypt the incoming encryptedStream. 83 | /// 84 | /// 85 | /// 86 | /// 87 | /// The plaintext stream derived from encrypted stream 88 | public Stream DecryptStream(Stream encryptedStream, byte[] key, byte[] iv) { 89 | ICryptoTransform iTransform = new Salsa20CryptoTransform(key, iv); 90 | 91 | return new CryptoStream(encryptedStream, iTransform, CryptoStreamMode.Read); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Salsa20CipherPlugin.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Salsa20Cipher", "Salsa20Cipher\Salsa20Cipher.csproj", "{A7DEAA9C-9BBB-474F-9467-FF150FC50BED}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {A7DEAA9C-9BBB-474F-9467-FF150FC50BED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {A7DEAA9C-9BBB-474F-9467-FF150FC50BED}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {A7DEAA9C-9BBB-474F-9467-FF150FC50BED}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {A7DEAA9C-9BBB-474F-9467-FF150FC50BED}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /make-plgx.bat: -------------------------------------------------------------------------------- 1 | :: This build file is very rigid and highly un-portable. I use is as a convenience on my own computer. 2 | :: Note to self: Run this file within the KeePass directory! 3 | 4 | SET basefolder=C:\Users\%username%\Documents\Code\GitHub\KeePass-SalsaCipher 5 | SET srcfolder=%basefolder%\Salsa20Cipher 6 | SET plgx=%basefolder%\Salsa20Cipher.plgx 7 | 8 | rd %srcfolder%\bin /S /Q 9 | rd %srcfolder%\obj /S /Q 10 | 11 | .\KeePass.exe --plgx-create %srcfolder% --plgx-prereq-kp:2.18 --plgx-prereq-os:Windows --plgx-prereq-net:3 12 | 13 | xcopy %plgx% /Y 14 | -------------------------------------------------------------------------------- /version-information.txt: -------------------------------------------------------------------------------- 1 | : 2 | Salsa20 Cipher:0.6.0 3 | : --------------------------------------------------------------------------------