├── AnsibleVault ├── AnsibleVault.psd1 ├── AnsibleVault.psm1 ├── Private │ ├── Add-Pkcs7Padding.ps1 │ ├── Convert-ByteToHex.ps1 │ ├── Convert-HexToByte.ps1 │ ├── Get-HMACValue.ps1 │ ├── Get-VaultHeader.ps1 │ ├── Invoke-AESCTRCycle.ps1 │ ├── Invoke-Win32Api.ps1 │ ├── New-PBKDF2Key.ps1 │ ├── New-VaultKey.ps1 │ ├── Remove-Pkcs7Padding.ps1 │ └── Split-Byte.ps1 └── Public │ ├── Get-DecryptedAnsibleVault.ps1 │ └── Get-EncryptedAnsibleVault.ps1 ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Tests ├── Add-Pkcs7Padding.Tests.ps1 ├── Convert-ByteToHex.Tests.ps1 ├── Convert-HexToByte.Tests.ps1 ├── Get-DecryptedAnsibleVault.Tests.ps1 ├── Get-EncryptedAnsibleVault.Tests.ps1 ├── Get-HMACValue.Tests.ps1 ├── Get-VaultHeader.Tests.ps1 ├── Invoke-AESCTRCycle.Tests.ps1 ├── Invoke-Win32Api.Tests.ps1 ├── New-PBKDF2Key.Tests.ps1 ├── New-VaultKey.Tests.ps1 ├── Remove-Pkcs7Padding.Tests.ps1 ├── Resources │ ├── dev_1.2.vault │ ├── dev_1.2.yml │ ├── large_1.1.vault │ ├── large_1.1.yml │ ├── small_1.1.vault │ ├── small_1.1.yml │ ├── unicode_1.1.vault │ └── unicode_1.1.yml └── Split-Byte.Tests.ps1 ├── appveyor.yml ├── build.ps1 ├── deploy.psdeploy.ps1 └── psake.ps1 /AnsibleVault/AnsibleVault.psd1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | @{ 5 | RootModule = 'AnsibleVault.psm1' 6 | ModuleVersion = '0.3.0' 7 | GUID = '718e9512-c750-40f3-b00e-c94b20910ecb' 8 | Author = 'Jordan Borean' 9 | Copyright = 'Copyright (c) 2018 by Jordan Borean, Red Hat, licensed under MIT.' 10 | Description = "Adds cmdlets that can be used to encrypt and decrypt and Ansible Vault in PowerShell.`nSee https://github.com/jborean93/PowerShell-AnsibleVault for more info" 11 | PowerShellVersion = '3.0' 12 | FunctionsToExport = @( 13 | 'Get-DecryptedAnsibleVault', 14 | 'Get-EncryptedAnsibleVault' 15 | ) 16 | PrivateData = @{ 17 | PSData = @{ 18 | Tags = @( 19 | "Automation", 20 | "DevOps", 21 | "Windows", 22 | "Ansible", 23 | "Vault" 24 | ) 25 | LicenseUri = 'https://github.com/jborean93/PowerShell-AnsibleVault/blob/master/LICENSE' 26 | ProjectUri = 'https://github.com/jborean93/PowerShell-AnsibleVault' 27 | ReleaseNotes = 'See https://github.com/jborean93/PowerShell-AnsibleVault/blob/master/CHANGELOG.md' 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /AnsibleVault/AnsibleVault.psm1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | # get public and private function definition files. 5 | $public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) 6 | $private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) 7 | 8 | # dot source the files 9 | foreach ($import in @($public + $private)) { 10 | try { 11 | . $import.FullName 12 | } catch { 13 | Write-Error -Message "Failed to import function $($import.FullName): $_" 14 | } 15 | } 16 | 17 | Export-ModuleMember -Function $public.Basename 18 | -------------------------------------------------------------------------------- /AnsibleVault/Private/Add-Pkcs7Padding.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Add-Pkcs7Padding { 5 | <# 6 | .SYNOPSIS 7 | Add padding to the byte array based on the PKCS7 padding spec. 8 | 9 | .DESCRIPTION 10 | Will add PKCS7 padding to a byte array. This will always add the padding 11 | even if it has already been padded as there is no real way to determine 12 | if the padding has already been applied. 13 | 14 | .PARAMETER Value 15 | [byte[]] The bytes to add the padding to. 16 | 17 | .PARAMETER BlockSize 18 | [int] The size of the block in bits. 19 | 20 | .OUTPUTS 21 | [byte[]] The input bytes after being padded to the BlockSize. 22 | 23 | .EXAMPLE 24 | Add-Pkcs7Padding -Value @([byte]1, [byte]2) -BlockSize 128 25 | 26 | .NOTES 27 | Usually this is done as part of a crypto provider but because we use 28 | Invoke-AESCTRCycle (AES in CTR mode/stream cipher) we need to manually 29 | pad the bytes as this is done in the Ansible Vault implementation. 30 | #> 31 | [CmdletBinding()] 32 | [OutputType([byte[]])] 33 | param( 34 | [Parameter(Mandatory=$true)] [byte[]]$Value, 35 | [Parameter(Mandatory=$true)] [int]$BlockSize 36 | ) 37 | $block_size_bytes = $BlockSize / 8 38 | $padding_length = $block_size_bytes - ($Value.Length % $block_size_bytes) 39 | 40 | if ($padding_length -eq 0) { 41 | $padding_length = $block_size_bytes 42 | } 43 | 44 | $padded_bytes = New-Object -TypeName byte[] -ArgumentList ($Value.Length + $padding_length) 45 | $Value.CopyTo($padded_bytes, 0) 46 | for ($i = $Value.Length; $i -lt $padded_bytes.Length; $i++) { 47 | $padded_bytes[$i] = [byte]$padding_length 48 | } 49 | return $padded_bytes 50 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Convert-ByteToHex.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Convert-ByteToHex { 5 | <# 6 | .SYNOPSIS 7 | Converts a byte array to a string of hex characters. 8 | 9 | .DESCRIPTION 10 | Takes in a byte array and returns the hex string representation of each 11 | byte. 12 | 13 | .PARAMETER Value 14 | [byte[]] The byte array to create the hex string from. 15 | 16 | .RETURNS HexString 17 | [String] The hex string of the byte array. 18 | 19 | .EXAMPLE 20 | Convert-BytesToHex -Value [byte[]]@(72, 101, 108, 108, 111) 21 | 22 | .NOTES 23 | No special notes. 24 | #> 25 | [CmdletBinding()] 26 | [OutputType([String])] 27 | param( 28 | [Parameter(Mandatory=$true)] [byte[]]$Value 29 | ) 30 | $hex_string = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($Value.Length * 2) 31 | foreach ($byte in $Value) { 32 | $hex_string.AppendFormat("{0:x2}", $byte) > $null 33 | } 34 | 35 | return $hex_string.ToString() 36 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Convert-HexToByte.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Convert-HexToByte { 5 | <# 6 | .SYNOPSIS 7 | Converts a string of hex characters to a byte array. 8 | 9 | .DESCRIPTION 10 | Takes in a string of hex characters and returns the byte array that the 11 | hex represents. 12 | 13 | .PARAMETER Value 14 | [String] The hex string to convert. 15 | 16 | .OUTPUTS 17 | [byte[]] The byte array based on the converted hex string. 18 | 19 | .EXAMPLE 20 | Convert-HexToBytes -Value "48656c6c6f20576f726c64" 21 | 22 | .NOTES 23 | The hex string should have no spaces that separate each hex char. 24 | #> 25 | [CmdletBinding()] 26 | [OutputType([byte[]])] 27 | param( 28 | [Parameter(Mandatory=$true)] [String]$Value 29 | ) 30 | $bytes = New-Object -TypeName byte[] -ArgumentList ($Value.Length / 2) 31 | for ($i = 0; $i -lt $Value.Length; $i += 2) { 32 | $bytes[$i / 2] = [Convert]::ToByte($Value.Substring($i, 2), 16) 33 | } 34 | 35 | return [byte[]]$bytes 36 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Get-HMACValue.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Get-HMACValue { 5 | <# 6 | .SYNOPSIS 7 | Generates the HMAC hex string of a byte array. 8 | 9 | .DESCRIPTION 10 | Generates the HMAC hex string of a byte array using the SHA256 algorithm 11 | and the Key specified. 12 | 13 | .PARAMETER Value 14 | [byte[]] The byte array to compute the hash from. 15 | 16 | .PARAMETER Key 17 | [byte[]] The key to use as part of the HMAC function. 18 | 19 | .OUTPUTS 20 | [String] The hex string of the HMAC output. 21 | 22 | .EXAMPLE 23 | Get-HMACValue -Value $bytes -Key $key 24 | 25 | .NOTES 26 | This is locked in to use the SHA256 algorithm, in the future this may 27 | change and be configurable but right now Ansible Vault only uses this. 28 | #> 29 | [CmdletBinding()] 30 | [OutputType([byte[]])] 31 | param( 32 | [Parameter(Mandatory=$true)] [byte[]]$Value, 33 | [Parameter(Mandatory=$true)] [byte[]]$Key 34 | ) 35 | $hmac_sha256 = New-Object -TypeName System.Security.Cryptography.HMACSHA256 -ArgumentList @(,$Key) 36 | $actual_hmac = $hmac_sha256.ComputeHash($Value) 37 | $actual_hmac_hex = Convert-ByteToHex -Value $actual_hmac 38 | 39 | return $actual_hmac_hex 40 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Get-VaultHeader.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Get-VaultHeader { 5 | <# 6 | .SYNOPSIS 7 | Parses the vault text and get's the header information. 8 | 9 | .DESCRIPTION 10 | Takes in the full vault string and returns the header information as well 11 | as the byte array of the encrypted bytes. 12 | 13 | .PARAMETER Value 14 | [String] The Ansible vault contents as a string. 15 | 16 | .OUTPUTS 17 | [Version] The version of the vault. 18 | 19 | [String] The string identifying the cipher type. 20 | 21 | [String] The ID of the vault. 22 | 23 | [byte[]] The byte array of the encrypted vault contents. 24 | 25 | .EXAMPLE 26 | Get-VaultHeader -Value $vault_text 27 | 28 | .NOTES 29 | Currently only the 1.1 and 1.2 versions of Ansible Vault is supported, 30 | as of writting this, they are the latest and only supported versions in 31 | Ansible but that may change in the future. 32 | #> 33 | [CmdletBinding()] 34 | [OutputType([Object[]])] 35 | param( 36 | [Parameter(Mandatory = $true)] [String]$Value 37 | ) 38 | $vault_lines = $Value -split "[\r\n]" | Where-Object {$_} 39 | $header = $vault_lines[0].Trim().Split(";") 40 | 41 | $version = [Version]$header[1].Trim() 42 | if ($version -lt [Version]"1.1" -or $version -gt [Version]"1.2") { 43 | throw [System.NotSupportedException]"Cannot parse vault version $version, currently only 1.1 and 1.2 is supported by this tool" 44 | } 45 | 46 | $cipher = $header[2].Trim() 47 | $id = $null 48 | if ($header.Length -ge 4) { 49 | $id = $header[3].Trim() 50 | } 51 | 52 | $cipher_text = $vault_lines[1..($vault_lines.Length - 1)] -join "" 53 | $cipher_bytes = Convert-HexToByte -Value $cipher_text 54 | 55 | return $version, $cipher, $id, $cipher_bytes 56 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Invoke-AESCTRCycle.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Invoke-AESCTRCycle { 5 | <# 6 | .SYNOPSIS 7 | Uses AES in CTR mode to encrypt/decrypt a byte array. 8 | 9 | .DESCRIPTION 10 | Uses the AES encryption mechanism in CTR block mode to transform input 11 | bytes. This function can be used to encrypt and decrypt the bytes in the 12 | Ansible Vault with relative ease. Because AES in CTR mode is a stream 13 | cipher, the input bytes does not have to be the same as the AES block size. 14 | 15 | .PARAMETER Value 16 | [byte[]] The input bytes to transform. 17 | 18 | .PARAMETER Key 19 | [byte[]] The key used to increment the nonce/counter used as part of the 20 | byte transformation process. 21 | 22 | .PARAMETER Nonce 23 | [byte[]] The nonce/counter used to XOR the input bytes and transform it to 24 | the output bytes 25 | 26 | .OUTPUTS 27 | [byte[]] The encrypted/decrypted bytes after running through a cycle. 28 | 29 | .NOTES 30 | The .NET class AesCryptoServiceProvider does not have a native 31 | CTR mode so this must be done manually. Thanks to Hans Wolff at 32 | https://gist.github.com/hanswolff/8809275, I've been able to use that code 33 | as a reference and create a PowerShell function to do the same. 34 | #> 35 | [CmdletBinding()] 36 | [OutputType([byte[]])] 37 | param( 38 | [Parameter(Mandatory=$true)] [byte[]]$Value, 39 | [Parameter(Mandatory=$true)] [byte[]]$Key, 40 | [Parameter(Mandatory=$true)] [byte[]]$Nonce 41 | ) 42 | 43 | $counter_cipher = New-Object System.Security.Cryptography.AesCryptoServiceProvider 44 | $counter_cipher.Mode = [System.Security.Cryptography.CipherMode]::ECB 45 | $counter_cipher.Padding = [System.Security.Cryptography.PaddingMode]::None 46 | $counter_encryptor = $counter_cipher.CreateEncryptor($Key, (New-Object -TypeName byte[] -ArgumentList($counter_cipher.BlockSize / 8))) 47 | 48 | $xor_mask = New-Object -TypeName System.Collections.Queue 49 | $output = New-Object -TypeName byte[] -ArgumentList $Value.Length 50 | for ($i = 0; $i -lt $Value.Length; $i++) { 51 | if ($xor_mask.Count -eq 0) { 52 | $counter_mode_block = New-Object -TypeName byte[] -ArgumentList ($counter_cipher.BlockSize / 8) 53 | $counter_encryptor.TransformBlock($Nonce, 0, $Nonce.Length, $counter_mode_block, 0) > $null 54 | 55 | for ($j = $Nonce.Length - 1; $j -ge 0; $j--) { 56 | $current_nonce_value = $Nonce[$j] 57 | if ($current_nonce_value -eq 255) { 58 | $Nonce[$j] = 0 59 | } else { 60 | $Nonce[$j] += 1 61 | } 62 | 63 | if ($Nonce[$j] -ne 0) { 64 | break 65 | } 66 | } 67 | 68 | foreach ($counter_byte in $counter_mode_block) { 69 | $xor_mask.Enqueue($counter_byte) 70 | } 71 | } 72 | 73 | $current_mask = $xor_mask.Dequeue() 74 | $output[$i] = [byte]($Value[$i] -bxor $current_mask) 75 | } 76 | 77 | return [byte[]]$output 78 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Invoke-Win32Api.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | 5 | Function Invoke-Win32Api { 6 | <# 7 | .SYNOPSIS 8 | Call a native Win32 API or a function exported in a DLL. 9 | 10 | .DESCRIPTION 11 | This method allows you to call a native Win32 API or DLL function 12 | without compiling C# code using Add-Type. The advantages of this over 13 | using Add-Type is that this is all generated in memory and no temporary 14 | files are created. 15 | 16 | The code has been created with great help from various sources. The main 17 | sources I used were; 18 | # http://www.leeholmes.com/blog/2007/10/02/managing-ini-files-with-powershell/ 19 | # https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/27/use-powershell-to-interact-with-the-windows-api-part-3/ 20 | 21 | .PARAMETER DllName 22 | [String] The DLL to import the method from. 23 | 24 | .PARAMETER MethodName 25 | [String] The name of the method. 26 | 27 | .PARAMETER ReturnType 28 | [Type] The type of the return object returned by the method. 29 | 30 | .PARAMETER ParameterTypes 31 | [Type[]] Array of types that define the parameter types required by the 32 | method. The type index should match the index of the value in the 33 | Parameters parameter. 34 | 35 | If the parameter is a reference or an out parameter, use [Ref] as the type 36 | for that parameter. 37 | 38 | .PARAMETER Parameters 39 | [Object[]] Array of objects to supply for the parameter values required by 40 | the method. The value index should match the index of the value in the 41 | ParameterTypes parameter. 42 | 43 | If the parameter is a reference or an out parameter, the object should be a 44 | [Ref] of the parameter. 45 | 46 | .PARAMETER SetLastError 47 | [Bool] Whether to apply the SetLastError Dll attribute on the method, 48 | default is $false 49 | 50 | .PARAMETER CharSet 51 | [Runtime.InteropServices.CharSet] The charset to apply to the CharSet Dll 52 | attribute on the method, default is [Runtime.InteropServices.CharSet]::Auto 53 | 54 | .OUTPUTS 55 | [Object] The return result from the method, the type of this value is based 56 | on the ReturnType parameter. 57 | 58 | .EXAMPLE 59 | # Use the Win32 APIs to open a file handle 60 | $handle = Invoke-Win32Api -DllName kernel32.dll ` 61 | -MethodName CreateFileW ` 62 | -ReturnType Microsoft.Win32.SafeHandles.SafeFileHandle ` 63 | -ParameterTypes @([String], [System.Security.AccessControl.FileSystemRights], [System.IO.FileShare], [IntPtr], [System.IO.FileMode], [UInt32], [IntPtr]) ` 64 | -Parameters @( 65 | "\\?\C:\temp\test.txt", 66 | [System.Security.AccessControl.FileSystemRights]::FullControl, 67 | [System.IO.FileShare]::ReadWrite, 68 | [IntPtr]::Zero, 69 | [System.IO.FileMode]::OpenOrCreate, 70 | 0, 71 | [IntPtr]::Zero) ` 72 | -SetLastError $true ` 73 | -CharSet Unicode 74 | if ($handle.IsInvalid) { 75 | $last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 76 | throw [System.ComponentModel.Win32Exception]$last_err 77 | } 78 | $handle.Close() 79 | 80 | # Lookup the account name from a SID 81 | $sid_string = "S-1-5-18" 82 | $sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $sid_string 83 | $sid_bytes = New-Object -TypeName byte[] -ArgumentList $sid.BinaryLength 84 | $sid.GetBinaryForm($sid_bytes, 0) 85 | 86 | $name = New-Object -TypeName System.Text.StringBuilder 87 | $name_length = 0 88 | $domain_name = New-Object -TypeName System.Text.StringBuilder 89 | $domain_name_length = 0 90 | 91 | $invoke_args = @{ 92 | DllName = "Advapi32.dll" 93 | MethodName = "LookupAccountSidW" 94 | ReturnType = [bool] 95 | ParameterTypes = @([String], [byte[]], [System.Text.StringBuilder], [Ref], [System.Text.StringBuilder], [Ref], [Ref]) 96 | Parameters = @( 97 | $null, 98 | $sid_bytes, 99 | $name, 100 | [Ref]$name_length, 101 | $domain_name, 102 | [Ref]$domain_name_length, 103 | [Ref][IntPtr]::Zero 104 | ) 105 | SetLastError = $true 106 | CharSet = "Unicode" 107 | } 108 | 109 | $res = Invoke-Win32Api @invoke_args 110 | $name.EnsureCapacity($name_length) 111 | $domain_name.EnsureCapacity($domain_name_length) 112 | $res = Invoke-Win32Api @invoke_args 113 | if (-not $res) { 114 | $last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 115 | throw [System.ComponentModel.Win32Exception]$last_err 116 | } 117 | Write-Output "SID: $sid_string, Domain: $($domain_name.ToString()), Name: $($name.ToString())" 118 | 119 | .NOTES 120 | The parameters to use for a method dynamically based on the method that is 121 | called. There is no cut and fast way to automatically convert the interface 122 | listed on the Microsoft docs. There are great resources to help you create 123 | the "P/Invoke" definition like pinvoke.net. 124 | #> 125 | [CmdletBinding()] 126 | [OutputType([Object])] 127 | param( 128 | [Parameter(Position = 0, Mandatory = $true)] [String]$DllName, 129 | [Parameter(Position = 1, Mandatory = $true)] [String]$MethodName, 130 | [Parameter(Position = 2, Mandatory = $true)] [Type]$ReturnType, 131 | [Parameter(Position = 3)] [Type[]]$ParameterTypes = [Type[]]@(), 132 | [Parameter(Position = 4)] [Object[]]$Parameters = [Object[]]@(), 133 | [Parameter()] [Bool]$SetLastError = $false, 134 | [Parameter()] [Runtime.InteropServices.CharSet]$CharSet = [Runtime.InteropServices.CharSet]::Auto 135 | ) 136 | if ($ParameterTypes.Length -ne $Parameters.Length) { 137 | throw [System.ArgumentException]"ParameterType Count $($ParameterTypes.Length) not equal to Parameter Count $($Parameters.Length)" 138 | } 139 | 140 | # First step is to define the dynamic assembly in the current AppDomain 141 | $assembly = New-Object -TypeName System.Reflection.AssemblyName -ArgumentList "Win32ApiAssembly" 142 | $AssemblyBuilder = [System.Reflection.Assembly].Assembly.GetTypes() | Where-Object { $_.Name -eq 'AssemblyBuilder' } 143 | $dynamic_assembly = $AssemblyBuilder::DefineDynamicAssembly($assembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) 144 | 145 | 146 | # Second step is to create the dynamic module and type/class that contains 147 | # the P/Invoke definition 148 | $dynamic_module = $dynamic_assembly.DefineDynamicModule("Win32Module", $false) 149 | $dynamic_type = $dynamic_module.DefineType("Win32Type", [Reflection.TypeAttributes]"Public, Class") 150 | 151 | # Need to manually get the reference type if the ParameterType is [Ref], we 152 | # define this based on the Parameter type at the same index 153 | $parameter_types = $ParameterTypes.Clone() 154 | for ($i = 0; $i -lt $ParameterTypes.Length; $i++) { 155 | if ($ParameterTypes[$i] -eq [Ref]) { 156 | $parameter_types[$i] = $Parameters[$i].Value.GetType().MakeByRefType() 157 | } 158 | } 159 | 160 | # Next, the method is created where we specify the name, parameters and 161 | # return type that is expected 162 | $dynamic_method = $dynamic_type.DefineMethod( 163 | $MethodName, 164 | [Reflection.MethodAttributes]"Public, Static", 165 | $ReturnType, 166 | $parameter_types 167 | ) 168 | 169 | # Build the attributes (DllImport) part of the method where the DLL 170 | # SetLastError and CharSet are applied 171 | $constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String]) 172 | $method_fields = [Reflection.FieldInfo[]]@( 173 | [Runtime.InteropServices.DllImportAttribute].GetField("SetLastError"), 174 | [Runtime.InteropServices.DllImportAttribute].GetField("CharSet") 175 | ) 176 | $method_fields_values = [Object[]]@($SetLastError, $CharSet) 177 | $custom_attributes = New-Object -TypeName Reflection.Emit.CustomAttributeBuilder -ArgumentList @( 178 | $constructor, 179 | $DllName, 180 | $method_fields, 181 | $method_fields_values 182 | ) 183 | $dynamic_method.SetCustomAttribute($custom_attributes) 184 | 185 | # Create the custom type/class based on what was configured above 186 | $win32_type = $dynamic_type.CreateType() 187 | 188 | # Invoke the method with the parameters supplied and return the result 189 | $result = $win32_type::$MethodName.Invoke($Parameters) 190 | return $result 191 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/New-PBKDF2Key.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function New-PBKDF2Key { 5 | <# 6 | .SYNOPSIS 7 | Uses the PBKDF2 functiion to derive a key used in cryptographic functions. 8 | 9 | .DESCRIPTION 10 | This function can be used to generated a cryptographically secure key based 11 | on the PBKDF2 function. This function calls some native Win32 APIs as the 12 | .NET class Rfc2898DeriveBytes that generates these keys does not allow the 13 | hash algorithm to be changed until .NET 4.7.2. 14 | 15 | Because we want this script to run on older versions on Windows and Ansible 16 | Vault uses the SHA256 algorithm we have to resort to using the native 17 | function BCryptDeriveKeyPBKDF2. 18 | 19 | .PARAMETER Algorithm 20 | [String] Specifies the algorithm to use for the HMAC calculation. This must 21 | be one of the algorithm identifiers specified in 22 | https://msdn.microsoft.com/en-us/library/windows/desktop/aa375534.aspx. 23 | 24 | .PARAMETER Password 25 | [SecureString] The password used as the part of the PBKDF2 function. 26 | 27 | .PARAMETER Salt 28 | [byte[]] The salt used as part of the PBKDF2 function. 29 | 30 | .PARAMETER Length 31 | [UInt32] The length of the derived key. 32 | 33 | .PARAMETER Iterations 34 | [UInt64] The number of iterations for the PBKDF2 function. 35 | 36 | .OUTPUTS 37 | [byte[]] The derived key of the PBKDF2 function run. 38 | 39 | .EXAMPLE 40 | $salt = New-Object -TypeName byte[] -ArgumentList 32 41 | $random_gen = New-Object -TypeName System.Security.Cryptography.RNGCryptoServiceProvider 42 | $random_gen.GetBytes($salt) 43 | 44 | New-PBKDF2Key -Algorithm SHA256 -Password $sec_string -Salt $salt -Length 32 -Iterations 10000 45 | 46 | .NOTES 47 | As Windows has no automatic marshalling for a SecureString to a P/Invoke 48 | call, the SecureString is decrypted to a string. While attempts to clear it 49 | from memory by running the Garbage Collector this isn't a guarantee. We 50 | can only try our best. 51 | #> 52 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Does not adjust system state, creates a new key that is in memory")] 53 | [CmdletBinding()] 54 | [OutputType([byte[]])] 55 | param( 56 | [Parameter(Mandatory=$true)] [String]$Algorithm, 57 | [Parameter(Mandatory=$true)] [SecureString]$Password, 58 | [Parameter(Mandatory=$true)] [byte[]]$Salt, 59 | [Parameter(Mandatory=$true)] [UInt32]$Length, 60 | [Parameter(Mandatory=$true)] [UInt64]$Iterations 61 | ) 62 | 63 | # Rfc2898DeriveBytes only allowed a custom hash algorithm in 4.6 or newer. We only use the .NET Method if running 64 | # on PowerShell Core and falling back to PInvoke for PowerShell Desktop. 65 | $is_core_clr = Get-Variable -Name IsCoreCLR -ErrorAction Ignore 66 | if ($null -ne $is_core_clr -and $is_core_clr.Value -eq $true) { 67 | $algo = [System.Security.Cryptography.HashAlgorithmName]$Algorithm 68 | $pass_ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Password) 69 | try { 70 | $pass_str = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($pass_ptr, $Password.Length) 71 | try { 72 | $provider = New-Object -TypeName System.Security.Cryptography.Rfc2898DeriveBytes -ArgumentList @( 73 | $pass_str, 74 | $Salt, 75 | $Iterations, 76 | $algo 77 | ) 78 | try { 79 | return $provider.GetBytes($Length) 80 | } finally { 81 | $provider.Dispose() 82 | } 83 | } finally { 84 | $pass_str = $null 85 | [System.GC]::Collect() 86 | } 87 | } finally { 88 | [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($pass_ptr) 89 | } 90 | } 91 | 92 | # Rfc2898DeriveBytes not available on older platforms, rely on PInvoke for this step. 93 | $return_codes = @{ 94 | "3221225485" = "An invalid parameter was passed to a service or function (STATUS_INVALID_PARAMETER 0xC0000000D)" 95 | "3221225480" = "An invalid HANDLE was specified (STATUS_INVALID_HANDLE 0xC0000008)" 96 | "3221225495" = "A memory allocation failure occurred (STATUS_NO_MEMORY 0xC0000017)" 97 | "3221226021" = "The object was not found (STATUS_NOT_FOUND 0xC0000225)" 98 | } 99 | 100 | $algo = [IntPtr]::Zero 101 | $open_flags = 0x00000008 # BCRYPT_ALG_HANDLE_HMAC_FLAG 102 | 103 | $res = Invoke-Win32Api -DllName Bcrypt.dll ` 104 | -MethodName BCryptOpenAlgorithmProvider ` 105 | -ReturnType UInt32 ` 106 | -ParameterTypes @([Ref], [String], [String], [UInt32]) ` 107 | -Parameters @([Ref]$algo, $Algorithm, $null, $open_flags) 108 | if ($res -ne 0) { 109 | if ($return_codes.ContainsKey($res.ToString())) { 110 | $exception_msg = $return_codes.$($res.ToString()) 111 | } else { 112 | $hex_code = ("{0:x8}" -f $res).ToUpper() 113 | $exception_msg = "Unknown error (0x$hex_code)" 114 | } 115 | throw "Failed to open algorithm provider with ID '$Algorithm': $exception_msg" 116 | } 117 | 118 | try { 119 | $key = New-Object -TypeName byte[] -ArgumentList $Length 120 | $pass = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Password) 121 | try { 122 | $pass_str = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($pass, $Password.Length) 123 | $pass_bytes = [System.Text.Encoding]::UTF8.GetBytes($pass_str) 124 | $res = Invoke-Win32Api -DllName Bcrypt.dll ` 125 | -MethodName BCryptDeriveKeyPBKDF2 ` 126 | -ReturnType UInt32 ` 127 | -ParameterTypes @([IntPtr], [Byte[]], [UInt32], [byte[]], [UInt32], [UInt64], [byte[]], [UInt32], [UInt32]) ` 128 | -Parameters @($algo, $pass_bytes, $pass_bytes.Length, $Salt, $Salt.Length, $Iterations, $key, $key.Length, 0) 129 | } finally { 130 | [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($pass) 131 | $pass_str = $null 132 | $pass_bytes = $null 133 | [System.GC]::Collect() 134 | } 135 | 136 | if ($res -ne 0) { 137 | if ($return_codes.ContainsKey($res.ToString())) { 138 | $exception_msg = $return_codes.$($res.ToString()) 139 | } else { 140 | $hex_code = ("{0:x8}" -f $res).ToUpper() 141 | $exception_msg = "Unknown error (0x$hex_code)" 142 | } 143 | 144 | throw "Failed to derive key: $exception_msg" 145 | } 146 | } finally { 147 | $res = Invoke-Win32Api -DllName Bcrypt.dll ` 148 | -MethodName BCryptCloseAlgorithmProvider ` 149 | -ReturnType UInt32 ` 150 | -ParameterTypes @([IntPtr], [UInt32]) ` 151 | -Parameters @($algo, 0) 152 | if ($res -ne 0) { 153 | if ($return_codes.ContainsKey($res.ToString())) { 154 | $exception_msg = $return_codes.$($res.ToString()) 155 | } else { 156 | $hex_code = ("{0:x8}" -f $res).ToUpper() 157 | $exception_msg = "Unknown error (0x$hex_code)" 158 | } 159 | 160 | throw "Failed to close algorithm provider: $exception_msg" 161 | } 162 | } 163 | 164 | return [byte[]]$key 165 | } 166 | -------------------------------------------------------------------------------- /AnsibleVault/Private/New-VaultKey.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function New-VaultKey { 5 | <# 6 | .SYNOPSIS 7 | Generates the various keys required for vault encryption/decryption. 8 | 9 | .DESCRIPTION 10 | Generates the Cipher, HMAC, and AES CTR Nonce used as part of the Vault 11 | operations. 12 | 13 | .PARAMETER Password 14 | [SecureString] The password used to derive the key. 15 | 16 | .PARAMETER Salt 17 | [byte[]] The salt used to derive the key. 18 | 19 | .OUTPUTS 20 | [byte[]] The key used in the AES cipher. 21 | 22 | [byte[]] The used as part of the HMAC calculation. 23 | 24 | [byte[]] The nonce/counter used in the AES CTR cipher. 25 | 26 | .EXAMPLE 27 | $salt = New-Object -TypeName byte[] -ArgumentList 32 28 | $random_gen = New-Object -TypeName System.Security.Cryptography.RNGCryptoServiceProvider 29 | $random_gen.GetBytes($salt) 30 | New-VaultKeys -Password $sec_string -Salt $salt 31 | 32 | .NOTES 33 | On decryption, the salt is stored in the cipher bytes whiile the salt must 34 | be randomly generated when creating a vault. 35 | #> 36 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Does not adjust system state, creates a new key that is in memory")] 37 | [CmdletBinding()] 38 | [OutputType([Object[]])] 39 | param( 40 | [Parameter(Mandatory=$true)] [SecureString]$Password, 41 | [Parameter(Mandatory=$true)] [byte[]]$Salt 42 | ) 43 | $derived_key = New-PBKDF2Key -Algorithm SHA256 -Password $password -Salt $Salt -Length 80 -Iterations 10000 44 | $cipher_key = $derived_key[0..31] 45 | $hmac_key = $derived_key[32..63] 46 | $nonce = $derived_key[64..79] 47 | 48 | return $cipher_key, $hmac_key, $nonce 49 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Remove-Pkcs7Padding.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Remove-Pkcs7Padding { 5 | <# 6 | .SYNOPSIS 7 | Removes PKCS7 padding on a paddded byte array. 8 | 9 | .DESCRIPTION 10 | Will remove any PKCS7 padding on a byte array. This can be run multiple 11 | times and the result will always be the same. 12 | 13 | .PARAMETER Value 14 | [byte[]] The bytes to add the remove from. 15 | 16 | .PARAMETER BlockSize 17 | [int] The size of the block in bits. 18 | 19 | .OUTPUTS 20 | [byte[]] The input byte array that has been unpadded. 21 | 22 | .EXAMPLE 23 | Remove-Pkcs7Padding -Bytes [byte[]]@(1, 2, 3, 5, 5, 5, 5 ,5) -BlockSize 64 24 | 25 | .NOTES 26 | Usually this is done as part of a crypto provider but because we use 27 | Invoke-AESCTRCycle (AES in CTR mode/stream cipher) we need to manually 28 | unpad the bytes as this is done in the Ansible Vault implementation. 29 | #> 30 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "", Justification="Does not adjust system state, removes the padding in a byte array")] 31 | [CmdletBinding()] 32 | [OutputType([byte[]])] 33 | param( 34 | [Parameter(Mandatory=$true)] [byte[]]$Value, 35 | [Parameter(Mandatory=$true)] [int]$BlockSize 36 | ) 37 | 38 | $last_byte = [int]$Value[$Value.Length - 1] 39 | if ($last_byte -gt ($BlockSize / 8)) { 40 | return $Value 41 | } elseif ($Value.Length -eq 1) { 42 | return $Value 43 | } 44 | 45 | for ($i = $Value.Length - 1; $i -ge $Value.Length - $last_byte; $i--) { 46 | if ([int]$Value[$i] -ne $last_byte) { 47 | return $Value 48 | } 49 | } 50 | 51 | $unpadded_size = $Value.Length - $last_byte 52 | $unpadded_bytes = New-Object -TypeName byte[] -ArgumentList $unpadded_size 53 | [System.Buffer]::BlockCopy($Value, 0, $unpadded_bytes, 0, $unpadded_size) 54 | return [byte[]]$unpadded_bytes 55 | } -------------------------------------------------------------------------------- /AnsibleVault/Private/Split-Byte.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Split-Byte { 5 | <# 6 | .SYNOPSIS 7 | Splits a byte array based on the character specified. 8 | 9 | .DESCRIPTION 10 | Takes in a byte array and splits into an ArrayList based on the character 11 | specified by Char. 12 | 13 | .PARAMETER Value 14 | [byte[]] The byte array to split. 15 | 16 | .PARAMETER Char 17 | [char] The char to split the byte array with. 18 | 19 | .PARAMETER MaxSplit 20 | [int] If specified, this is maximum number of splits that will occur. 21 | 22 | .OUTPUTS 23 | [System.Collections.ArrayList] The array list that contains each split. 24 | 25 | .EXAMPLE 26 | Split-Bytes -Value -Char ([char]"`n") 27 | 28 | Split-Bytes -Value $byte_array -Char ([char]"a") -MaxSplit 2 29 | 30 | .NOTES 31 | The MaxSplit parameter is the number of times a split will occur and not 32 | number of entries in the returned ArrayList. For example -MaxSplit of 2 but 33 | the char appears more than 2 times, the output object will contain 3 entries. 34 | #> 35 | [CmdletBinding()] 36 | [OutputType([System.Collections.ArrayList])] 37 | param( 38 | [Parameter(Mandatory=$true)] [byte[]]$Value, 39 | [Parameter(Mandatory=$true)] [char]$Char, 40 | [Parameter()] $MaxSplit = $null 41 | ) 42 | $previous_index = 0 43 | $byte_split = New-Object -TypeName System.Collections.ArrayList 44 | while ($true) { 45 | if (($null -ne $MaxSplit) -and ($byte_split.Count -ge $MaxSplit)) { 46 | $new_entry = New-Object -TypeName byte[] -ArgumentList ($Value.Length - $previous_index) 47 | [System.Array]::Copy($Value, $previous_index, $new_entry, 0, $Value.Length - $previous_index) 48 | $byte_split.Add($new_entry) > $null 49 | break 50 | } 51 | 52 | $newline_index = [System.Array]::IndexOf($Value, [byte]$Char, $previous_index) 53 | if ($newline_index -eq -1) { 54 | $new_entry = New-Object -TypeName byte[] -ArgumentList ($Value.Length - $previous_index) 55 | [System.Array]::Copy($Value, $previous_index, $new_entry, 0, $Value.Length - $previous_index) 56 | $byte_split.Add($new_entry) > $null 57 | break 58 | } 59 | 60 | $new_entry = New-Object -TypeName byte[] -ArgumentList ($newline_index - $previous_index) 61 | [System.Array]::Copy($Value, $previous_index, $new_entry, 0, $newline_index - $previous_index) 62 | $byte_split.Add($new_entry) > $null 63 | $previous_index = $newline_index + 1 64 | } 65 | 66 | # remove any empty arrays (2 chars were next to each other) 67 | for ($i = 0; $i -lt $byte_split.Count; $i++) { 68 | if ($byte_split[$i].Count -eq 0) { 69 | $byte_split.RemoveAt($i) > $null 70 | } 71 | } 72 | 73 | return $byte_split 74 | } -------------------------------------------------------------------------------- /AnsibleVault/Public/Get-DecryptedAnsibleVault.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Get-DecryptedAnsibleVault { 5 | <# 6 | .SYNOPSIS 7 | Decrypt an Ansible Vault string and return the plaintext as a string. 8 | 9 | .DESCRIPTION 10 | This cmdlet will take in a Ansible Vault string and return the decrypted 11 | value. 12 | 13 | .PARAMETER Path 14 | [String] The path to a file whose contents will be decrypted. This is 15 | mutually exclusive to the Value parameter. 16 | 17 | .PARAMETER Value 18 | [String] The string value to decrypt. This is mutually exclusive to the 19 | Path parameter. 20 | 21 | .PARAMETER Password 22 | [SecureString] The password used to decrypt the value with 23 | 24 | .PARAMETER Encoding 25 | [System.Text.Encoding] The string encoding of the decrypted bytes returned 26 | by this cmdlet. By default will be UTF8 but if the original plaintext was 27 | read as a different encoding type then this can override the encoding to 28 | what is needed. 29 | 30 | .INPUTS 31 | [String] You can pipe the encrypted vault string to decrypt. 32 | 33 | .OUTPUTS 34 | [String] The decrypted vault string. 35 | 36 | .EXAMPLE 37 | # Create the secure string that stores the vault password 38 | $password = Read-Host -AsSecureString 39 | 40 | # get the decrypted vault string from file contents 41 | Get-DecryptedAnsibleVault -Path C:\temp\vault.txt -Password $password 42 | 43 | # create a vault string from a string 44 | Get-DecryptedAnsibleVault -Value $vault_text -Password $password 45 | 46 | # send the string to encrypt as a pipeline input 47 | $vault_Text | Get-DecryptedAnsibleVault -Password $password 48 | 49 | # decrypt vault that had the original plaintext encoded as UTF-16 50 | Get-DecryptedAnsibleVault -Value $vault_text -Password $password -Encoding ([System.Text.Encoding]::Unicode) 51 | 52 | .NOTES 53 | This only supports the vault versions 1.1 and 1.2. These version are mostly 54 | identical but 1.2 is used when the Id parameter is specified. This should 55 | be interoperable with the ansible-vault code used by Ansible itself. 56 | #> 57 | [CmdletBinding(DefaultParameterSetName="ByPath")] 58 | [OutputType([String])] 59 | param( 60 | [Parameter(Position=0, Mandatory=$true, ParameterSetName="ByPath")] [String]$Path, 61 | [Parameter(Position=0, Mandatory=$true, ParameterSetName="ByValue", ValueFromPipeline, ValueFromPipelineByPropertyName)] [String]$Value, 62 | [Parameter(Position=1, Mandatory=$true)] [SecureString]$Password, 63 | [Parameter()] [System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8 64 | ) 65 | 66 | $vault_text = switch ($PSCmdlet.ParameterSetName) { 67 | ByPath { 68 | $pwd_path = Join-Path -Path $pwd -ChildPath $Path 69 | if (Test-Path -Path $pwd_path -PathType Leaf) { 70 | [System.IO.File]::ReadAllText($pwd_path) 71 | } else { 72 | [System.IO.File]::ReadAllText($Path) 73 | } 74 | } 75 | ByValue { $Value } 76 | } 77 | 78 | if ($null -eq $vault_text) { 79 | throw [System.ArgumentException]"Failed to get vault text to decrypt" 80 | } 81 | if (-not $vault_text.StartsWith('$ANSIBLE_VAULT;')) { 82 | throw [System.ArgumentException]"Vault text does not start with the header `$ANSIBLE_VAULT;" 83 | } 84 | 85 | $version, $cipher, $id, $cipher_bytes = Get-VaultHeader -Value $vault_text 86 | 87 | # The salt, hmac and encrypted bytes value are split by \n, we need to 88 | # split by that char to get the actual values 89 | $salt, $hmac, $encrypted_bytes = Split-Byte -Value $cipher_bytes -Char ([char]"`n") -MaxSplit 2 90 | 91 | $salt = Convert-HexToByte -Value ([System.Text.Encoding]::UTF8.GetString($salt)) 92 | $expected_hmac = [System.Text.Encoding]::UTF8.GetString($hmac) 93 | $encrypted_bytes = Convert-HexToByte -Value ([System.Text.Encoding]::UTF8.GetString($encrypted_bytes)) 94 | 95 | $cipher_key, $hmac_key, $nonce = New-VaultKey -Password $password -Salt $salt 96 | 97 | $actual_hmac = Get-HMACValue -Value $encrypted_bytes -Key $hmac_key 98 | if ($actual_hmac -ne $expected_hmac) { 99 | throw [System.ArgumentException]"HMAC verification failed, was the wrong password entered?" 100 | } 101 | 102 | $decrypted_bytes = Invoke-AESCTRCycle -Value $encrypted_bytes -Key $cipher_key -Nonce $nonce 103 | 104 | # Need to manually remove the padding as AES CTR has no concept of padding 105 | # it is a stream mode 106 | $unpadded_bytes = Remove-Pkcs7Padding -Value $decrypted_bytes -BlockSize 128 107 | $decrypted_string = $Encoding.GetString($unpadded_bytes) 108 | 109 | return $decrypted_string 110 | } 111 | -------------------------------------------------------------------------------- /AnsibleVault/Public/Get-EncryptedAnsibleVault.ps1: -------------------------------------------------------------------------------- 1 | # Copyright: (c) 2018, Jordan Borean (@jborean93) 2 | # MIT License (see LICENSE or https://opensource.org/licenses/MIT) 3 | 4 | Function Get-EncryptedAnsibleVault { 5 | <# 6 | .SYNOPSIS 7 | Create an encrypted string the is compatible with Ansible Vault. 8 | 9 | .DESCRIPTION 10 | This cmdlet will take in a string or path to a file to encrypt and then 11 | return the encrypted Ansible Vault text. 12 | 13 | .PARAMETER Path 14 | [String] The path to a file whose contents will be encrypted. This is 15 | mutually exclusive to the Value parameter. 16 | 17 | .PARAMETER Value 18 | [String] The string value to encrypt. This is mutually exclusive to the 19 | Path parameter. 20 | 21 | .PARAMETER Password 22 | [SecureString] The password used to encrypt the value with 23 | 24 | .PARAMETER Id 25 | [String] The ID to specify for the created vault. If not specified then no 26 | ID will be applied. This is only supported in Ansible since the 2.4 27 | version. 28 | 29 | .INPUTS 30 | [String] You can pipe a string to encrypt to this cmdlet. 31 | 32 | .OUTPUTS 33 | [String] The encrypted vault string. 34 | 35 | .EXAMPLE 36 | # Create the secure string that stores the vault password 37 | $password = Read-Host -AsSecureString 38 | 39 | # create a vault string from a file 40 | Get-EncryptedAnsibleVault -Path C:\temp\vault.txt -Password $password 41 | 42 | # create a vault string from a string 43 | Get-EncryptedAnsibleVault -Value "variable: abc`nvariable2: def" -Password $password 44 | 45 | # send the string to encrypt as a pipeline input 46 | "variable: abc`nvariable2: def" | Get-EncryptedAnsibleVault -Password $password 47 | 48 | # create a vault string with a specific ID 49 | Get-EncryptedAnsibleVault -Value "variable: abc`nvariable2: def" -Password $password -Id Prod 50 | 51 | .NOTES 52 | This only supports the vault versions 1.1 and 1.2. These version are mostly 53 | identical but 1.2 is used when the Id parameter is specified. This should 54 | be interoperable with the ansible-vault code used by Ansible itself. 55 | #> 56 | [CmdletBinding(DefaultParameterSetName="ByPath")] 57 | [OutputType([String])] 58 | param( 59 | [Parameter(Position=0, Mandatory=$true, ParameterSetName="ByPath")] [String]$Path, 60 | [Parameter(Position=0, Mandatory=$true, ParameterSetName="ByValue", ValueFromPipeline, ValueFromPipelineByPropertyName)] [String]$Value, 61 | [Parameter(Position=1, Mandatory=$true)] [SecureString]$Password, 62 | [Parameter()] [String]$Id 63 | ) 64 | 65 | $bytes_to_encrypt = switch($PSCmdlet.ParameterSetName) { 66 | ByPath { 67 | $pwd_path = Join-Path -Path $pwd -ChildPath $Path 68 | if (Test-Path -Path $pwd_path -PathType Leaf) { 69 | [System.IO.File]::ReadAllBytes($pwd_path) 70 | } else { 71 | [System.IO.File]::ReadAllBytes($Path) 72 | } 73 | } 74 | ByValue { [System.Text.Encoding]::UTF8.GetBytes($Value) } 75 | } 76 | if ($null -eq $bytes_to_encrypt) { 77 | throw [System.ArgumentException]"Failed to get bytes for vault to encrypt" 78 | } 79 | 80 | # Generate a secure random salt value 81 | $salt = New-Object -TypeName byte[] -ArgumentList 32 82 | $random_gen = New-Object -TypeName System.Security.Cryptography.RNGCryptoServiceProvider 83 | $random_gen.GetBytes($salt) 84 | 85 | $cipher_key, $hmac_key, $nonce = New-VaultKey -Password $Password -Salt $salt 86 | 87 | # While AES CTR is a stream mode, Ansible still pads the bytes we we need 88 | # to do that here 89 | $padded_bytes = Add-Pkcs7Padding -Value $bytes_to_encrypt -BlockSize 128 90 | $encrypted_bytes = Invoke-AESCTRCycle -Value $padded_bytes -Key $cipher_key -Nonce $nonce 91 | $actual_hmac = Get-HMACValue -Value $encrypted_bytes -Key $hmac_key 92 | 93 | $cipher_text = @((Convert-ByteToHex -Value $salt), $actual_hmac, (Convert-ByteToHex -Value $encrypted_bytes)) -join "`n" 94 | 95 | # Yes the vault cipher text is hexlified twice when it shouldn't be 96 | # necessary 97 | $cipher_text = Convert-ByteToHex -Value ([System.Text.Encoding]::UTF8.GetBytes($cipher_text)) 98 | 99 | # now we need to add a newline every 80 chars 100 | $cipher_text = $cipher_text -replace ".{80}", "$&`n" 101 | 102 | # Finally build the header and cipher text and return the string 103 | $version = "1.1" 104 | $id_suffix = "" 105 | if ($Id) { 106 | $version = "1.2" 107 | $id_suffix = ";$Id" 108 | } 109 | $header = "`$ANSIBLE_VAULT;$version;AES256$id_suffix" 110 | $vault_string = "$header`n$cipher_text" 111 | 112 | return $vault_string 113 | } 114 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for AnsibleVault 2 | 3 | ## v0.3.0 - 2019-12-02 4 | 5 | * Added support for PowerShell Core on both Windows and Linux 6 | 7 | 8 | ## v0.2.0 - 2018-05-25 9 | 10 | * Support getting a vault file based on the pwd when using the `-Path` parameter 11 | * Set the default parameter to `-Path` to better replicate the `ansible-vault` commands 12 | 13 | 14 | ## v0.1.0 - 2018-05-19 15 | 16 | * Initial version for the `AnsibleVault` module 17 | * Adds the `New-EncryptedAnsibleVault` cmdlet to encrypt a string to the format expected by Ansible Vault 18 | * Adds the `Get-DecryptedAnsibleVault` cmdlet to decrypt a vaulted string to plaintext 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jordan Borean 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell-AnsibleVault 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/1jf9wurhryafa47o?svg=true)](https://ci.appveyor.com/project/jborean93/powershell-ansiblevault) 4 | [![PowerShell Gallery](https://img.shields.io/powershellgallery/dt/AnsibleVault.svg)](https://www.powershellgallery.com/packages/AnsibleVault) 5 | 6 | PowerShell module that allows you to encrypt and decrypt Ansible Vault files 7 | natively in Windows. 8 | 9 | ## Info 10 | 11 | This PowerShell module contains 2 PowerShell cmdlets that are used to encrypt 12 | and decrypt and Ansible Vault files without having Ansible installed. The two 13 | cmdlets that are added are 14 | 15 | * `Get-DecryptedAnsibleVault` 16 | * `Get-EncryptedAnsibleVault` 17 | 18 | I've also written a blog post around this at 19 | [Decrypting the secrets of Ansible Vault in PowerShell](https://www.bloggingforlogging.com/2018/05/20/decrypting-the-secrets-of-ansible-vault-in-powershell/). 20 | 21 | ### Get-DecryptedAnsibleVault 22 | 23 | Decrypt an Ansible Vault string and return the plaintext. 24 | 25 | #### Syntax 26 | 27 | ``` 28 | # By Value/String 29 | Get-DecryptedAnsibleVault 30 | -Value 31 | -Password 32 | [[-Encoding] ] 33 | 34 | # From path 35 | Get-DecryptedAnsibleVault 36 | -Path 37 | -Password 38 | [[-Encoding] ] 39 | 40 | # With pipeline input 41 | "`$ANSIBLE_VAULT;1.1;AES256;`n00010203040506070809" | Get-DecryptedAnsibleVault 42 | -Password 43 | [[-Encoding] ] 44 | ``` 45 | 46 | #### Parameters 47 | 48 | * `Value`: The Ansible Vault text as a string to decrypt, this is mutually exclusive to the `Path` parameter 49 | * `Path`: The path to a vault file whose contents will be decrypted, this is mutually exclusive to the `Value` parameter 50 | * `Password`: The password to use when decrypting the contents 51 | 52 | #### Optional Parameters 53 | 54 | * `Encoding`: The string encoding of the decrypted bytes. By default will be `UTF8` but if the original plaintext was encrypted with a different encoding type, this can override the output to what is needed 55 | 56 | #### Input 57 | 58 | * ``: A string can be passed as a pipeline input as the `Value` parameter 59 | 60 | #### Output 61 | 62 | * ``: The decrypted vault contents as a string 63 | 64 | ### Get-EncryptedAnsibleVault 65 | 66 | Create an encrypted string that is compatible with Ansible Vault. 67 | 68 | #### Syntax 69 | 70 | ``` 71 | # By Value/String 72 | Get-EncryptedAnsibleVault 73 | -Value 74 | -Password 75 | [[-Id] ] 76 | 77 | # From Path 78 | Get-EncryptedAnsibleVault 79 | -Path 80 | -Password 81 | [[-Id] ] 82 | 83 | # With pipeline input 84 | "plaintext" | Get-EncryptedAnsibleVault 85 | -Password 86 | [[-Id] ] 87 | ``` 88 | 89 | #### Parameters 90 | 91 | * `Value`: The string to encrypt, this is mutually exclusive to the `Path` parameter 92 | * `Path`: The path to a file whose contents will be encrypted, this is mutually exclusive to the `Value` parameter 93 | * `Password`: The password to use when encrypting the contents 94 | 95 | #### Optional Parameters 96 | 97 | * `Id`: If specified, the vault will be encrypted and this ID will be set in the header 98 | 99 | #### Input 100 | 101 | * ``: A string can be passed as a pipeling input as the `Value` parameter 102 | 103 | #### Output 104 | 105 | * ``: The encrypted vault contents as a string 106 | 107 | 108 | ## Requirements 109 | 110 | These cmdlets have the following requirements 111 | 112 | * PowerShell v3.0 or newer 113 | * Windows PowerShell or PowerShell Core 114 | * Windows Server 2008 R2/Windows 7 or newer, including Linux 115 | 116 | 117 | ## Installing 118 | 119 | The easiest way to install this module is through 120 | [PowerShellGet](https://docs.microsoft.com/en-us/powershell/gallery/overview). 121 | This is installed by default with PowerShell 5 but can be added on PowerShell 122 | 3 or 4 by installing the MSI [here](https://www.microsoft.com/en-us/download/details.aspx?id=51451). 123 | 124 | Once installed, you can install this module by running; 125 | 126 | ``` 127 | # Install for all users 128 | Install-Module -Name AnsibleVault 129 | 130 | # Install for only the current user 131 | Install-Module -Name AnsibleVault -Scope CurrentUser 132 | ``` 133 | 134 | If you wish to remove the module, just run 135 | `Uninstall-Module -Name AnsibleVault`. 136 | 137 | If you cannot use PowerShellGet, you can still install the module manually, 138 | here are some basic steps on how to do this; 139 | 140 | 1. Download the latext zip from GitHub [here](https://github.com/jborean93/PowerShell-AnsibleVault/releases/latest) 141 | 2. Extract the zip 142 | 3. Copy the folder `AnsibleVault` inside the zip to a path that is set in `$env:PSModulePath`. By default this could be `C:\Program Files\WindowsPowerShell\Modules` or `C:\Users\\Documents\WindowsPowerShell\Modules` 143 | 4. Reopen PowerShell and unblock the downloaded files with `$path = (Get-Module -Name AnsibleVault -ListAvailable).ModuleBase; Unblock-File -Path $path\*.psd1; Unblock-File -Path $path\Public\*.ps1; Unblock-File -Path $path\Private\*.ps1` 144 | 5. Reopen PowerShell one more time and you can start using the cmdlets 145 | 146 | _Note: You are not limited to installing the module to those example paths, you can add a new entry to the environment variable `PSModulePath` if you want to use another path._ 147 | 148 | 149 | ## Examples 150 | 151 | Here are some examples that imitate the existing `ansible-vault` commands; 152 | 153 | ``` 154 | # store the password as a secure string 155 | $password = Read-Host -Prompt "Enter the vault password" -AsSecureString 156 | 157 | # ansible-vault encrypt 158 | Get-EncryptedAnsibleVault -Path vault.yml -Password $password | Set-Content -Path vault.yml -NoNewLine 159 | 160 | # ansible-vault encrypt_string --stdin-name 'vault_variable' 161 | $vault_text = Read-Host -Prompt "Enter string to encrypt" | Get-EncryptedAnsibleVault -Password $password 162 | Write-Output -InputObject "vault_variable: !vault |`n $($vault_text.Replace("`n", "`n "))" 163 | 164 | # ansible-vault decrypt 165 | Get-DecryptedAnsibleVault -Path vault.yml -Password $password | Set-Content -Path vault.yml -NoNewLine 166 | 167 | # ansible-vault view 168 | Get-DecryptedAnsibleVault -Path vault.yml -Password $password 169 | 170 | # ansible-vault rekey 171 | $old_pass = Read-Host -Prompt "Enter the original vault password" -AsSecureString 172 | $new_pass = Read-Host -Prompt "Enter the new vault password" -AsSecureString 173 | 174 | Get-DecryptedAnsibleVault -Path vault.yml -Password $old_pass | Get-EncryptedAnsibleVault -Password $new_pass | Set-Content -Path vault.yml -NoNewLine 175 | 176 | # ansible-vault encrypt --vault-id dev@prompt 177 | Get-EncryptedAnsibleVault -Value "some secret" -Id dev -Password (Read-Host -Prompt "Enter the password" -AsSecureString) 178 | ``` 179 | 180 | You are not limited to the above, you can store the outputs in variables and 181 | call these cmdlets in whatever way. 182 | 183 | ## Contributing 184 | 185 | Contributing is quite easy, fork this repo and submit a pull request with the 186 | changes. To test out your changes locally you can just run `.\build.ps1` in 187 | PowerShell. This script will ensure all dependencies are installed before 188 | running the test suite. 189 | 190 | _Note: this requires PowerShellGet or WMF 5 to be installed_ 191 | 192 | 193 | ## Backlog 194 | 195 | * See if it is possible to integrate with vim or some other cli editor if it is installed (`ansible-vault create/edit`) 196 | -------------------------------------------------------------------------------- /Tests/Add-Pkcs7Padding.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | 11 | Describe "$module_name PS$ps_version tests" { 12 | Context 'Strict mode' { 13 | Set-StrictMode -Version latest 14 | 15 | It 'should return correct padding length for block size with input ' -TestCases @( 16 | @{ Size = 32; Value = [byte[]]@(1); Expected = [byte[]]@(1, 3, 3, 3) } 17 | @{ Size = 32; Value = [byte[]]@(1, 2); Expected = [byte[]]@(1, 2, 2, 2) } 18 | @{ Size = 32; Value = [byte[]]@(1, 2, 3); Expected = [byte[]]@(1, 2, 3, 1) } 19 | @{ Size = 32; Value = [byte[]]@(1, 2, 3, 4); Expected = [byte[]]@(1, 2, 3, 4, 4, 4, 4, 4) } 20 | @{ Size = 32; Value = [byte[]]@(1, 2, 3, 4, 5); Expected = [byte[]]@(1, 2, 3, 4, 5, 3, 3, 3) } 21 | @{ Size = 32; Value = [byte[]]@(1, 2, 3, 4, 5, 6); Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 2, 2) } 22 | @{ Size = 32; Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7); Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 1) } 23 | @{ Size = 32; Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8); Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 4, 4, 4, 4) } 24 | @{ Size = 32; Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 9); Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 9, 3, 3, 3) } 25 | @{ Size = 64; Value = [byte[]]@(1, 2, 3, 4); Expected = [byte[]]@(1, 2, 3, 4, 4, 4, 4, 4) } 26 | @{ Size = 64; Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8); Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8) } 27 | ){ 28 | param($Size, $Value, $Expected) 29 | $actual = Add-Pkcs7Padding -Value $Value -BlockSize $Size 30 | $actual.Length | Should -Be $Expected.Length 31 | 32 | for ($i = 0; $i -lt $actual.Length; $i++) { 33 | $actual[$i] | Should -Be $Expected[$i] 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Tests/Convert-ByteToHex.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | 11 | Describe "$module_name PS$ps_version tests" { 12 | Context 'Strict mode' { 13 | Set-StrictMode -Version latest 14 | 15 | It 'should get valid hex string' { 16 | $expected = "48656c6c6f" 17 | $actual = Convert-ByteToHex -Value ([byte[]]@(72, 101, 108, 108, 111)) 18 | 19 | $actual | Should -Be $expected 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Tests/Convert-HexToByte.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | 11 | Describe "$module_name PS$ps_version tests" { 12 | Context 'Strict mode' { 13 | Set-StrictMode -Version latest 14 | 15 | It 'should get valid bytes' { 16 | $expected = [byte[]]@(72, 101, 108, 108, 111) 17 | $actual = Convert-HexToByte -Value "48656c6c6f" 18 | 19 | $actual.Length | Should -Be $expected.Count 20 | for ($i = 0; $i -lt $actual.Count; $i++) { 21 | $actual[$i] | Should -Be $expected[$i] 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Tests/Get-DecryptedAnsibleVault.Tests.ps1: -------------------------------------------------------------------------------- 1 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification="Need to create secure string from samples in tests")] 2 | param() 3 | 4 | $verbose = @{} 5 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 6 | $verbose.Add("Verbose", $true) 7 | } 8 | 9 | $ps_version = $PSVersionTable.PSVersion.Major 10 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 11 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 12 | 13 | Describe "$module_name PS$ps_version tests" { 14 | Context 'Strict mode' { 15 | Set-StrictMode -Version latest 16 | 17 | It "Can decrypt vault with password " -TestCases @( 18 | @{ Vault = "small_1.1"; VaultSecret = 'password' } 19 | @{ Vault = "large_1.1"; VaultSecret = 'Ye^wS##X9qAC4lHDY^ajMvZ*IZrv47Px^hv2#&a8#$KncjKK^T8eJGWcH&Q@yaj4J7rP%ktyMeYTx!ZU2Ce&GeT$$vmSWRq4fqvs' } 20 | @{ Vault = "unicode_1.1"; VaultSecret = '❌➖➕➖➕➖⭕' } 21 | @{ Vault = "dev_1.2"; VaultSecret = 'WsT2Wf!MnHctYXIQbI%xr$L8aid@fLTS6tA*' } 22 | ) { 23 | param ($Vault, $VaultSecret) 24 | 25 | $vault_contents = Get-Content -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', "$($Vault).vault")) -Raw 26 | $expected = (Get-Content -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', "$($Vault).yml")) -Raw).Replace("`r`n", "`n") 27 | $password = ConvertTo-SecureString -String $VaultSecret -AsPlainText -Force 28 | 29 | $actual = Get-DecryptedAnsibleVault -Value $vault_contents -Password $password 30 | $actual | Should -Be $expected 31 | 32 | $actual = Get-DecryptedAnsibleVault -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', "$($Vault).vault")) -Password $password 33 | $actual | Should -Be $expected 34 | 35 | # repeat again and make sure omitting -Path is for the path to a vault file 36 | $actual = Get-DecryptedAnsibleVault ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', "$($Vault).vault")) -Password $password 37 | $actual | Should -Be $expected 38 | 39 | $actual = $vault_contents | Get-DecryptedAnsibleVault -Password $password 40 | $actual | Should -Be $expected 41 | } 42 | It "Can decrypt vault file in pwd not absolute path" { 43 | $password = ConvertTo-SecureString -String "password" -AsPlainText -Force 44 | $previous_pwd = (Get-Location).Path 45 | Set-Location -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources')) 46 | 47 | $expected = (Get-Content -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', 'small_1.1.yml')) -Raw).Replace("`r`n", "`n") 48 | $actual = Get-DecryptedAnsibleVault -Path "small_1.1.vault" -Password $password 49 | Set-Location -Path $previous_pwd 50 | $actual | Should -Be $expected 51 | } 52 | 53 | It "Throw exception on invalid header" { 54 | $password = ConvertTo-SecureString -String "pass" -AsPlainText -Force 55 | { Get-DecryptedAnsibleVault -Value '$FAKE_VAULT;' -Password $password } | 56 | Should -Throw 'Vault text does not start with the header $ANSIBLE_VAULT;' 57 | } 58 | 59 | It "Throw exception on invalid version " -TestCases @( 60 | @{ Version = "1.0" } 61 | @{ Version = "1.3" } 62 | ) { 63 | param ($Version) 64 | 65 | $vault_contents = Get-Content -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', 'small_1.1.vault')) -Raw 66 | $vault_contents = "`$ANSIBLE_VAULT;$Version;AES256`n" + $vault_contents.Substring(31) 67 | 68 | $password = ConvertTo-SecureString -String "pass" -AsPlainText -Force 69 | { Get-DecryptedAnsibleVault -Value $vault_contents -Password $password } | 70 | Should -Throw "Cannot parse vault version $Version, currently only 1.1 and 1.2 is supported by this tool" 71 | } 72 | 73 | It "Throw exception on invalid password" { 74 | $vault_contents = Get-Content -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', 'small_1.1.vault')) -Raw 75 | $password = ConvertTo-SecureString -String "invalid_pass" -AsPlainText -Force 76 | { Get-DecryptedAnsibleVault -Value $vault_contents -Password $password } | 77 | Should -Throw "HMAC verification failed, was the wrong password entered?" 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Tests/Get-EncryptedAnsibleVault.Tests.ps1: -------------------------------------------------------------------------------- 1 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification="Need to create secure string from samples in tests")] 2 | param() 3 | 4 | $verbose = @{} 5 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 6 | $verbose.Add("Verbose", $true) 7 | } 8 | 9 | $ps_version = $PSVersionTable.PSVersion.Major 10 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 11 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 12 | 13 | Describe "$module_name PS$ps_version tests" { 14 | Context 'Strict mode' { 15 | Set-StrictMode -Version latest 16 | 17 | It "Can encrypt vault with password " -TestCases @( 18 | @{ Vault = "small_1.1"; VaultSecret = 'password' } 19 | @{ Vault = "large_1.1"; VaultSecret = 'Ye^wS##X9qAC4lHDY^ajMvZ*IZrv47Px^hv2#&a8#$KncjKK^T8eJGWcH&Q@yaj4J7rP%ktyMeYTx!ZU2Ce&GeT$$vmSWRq4fqvs' } 20 | @{ Vault = "unicode_1.1"; VaultSecret = '❌➖➕➖➕➖⭕' } 21 | @{ Vault = "dev_1.2"; VaultSecret = 'WsT2Wf!MnHctYXIQbI%xr$L8aid@fLTS6tA*' } 22 | ) { 23 | param ($Vault, $VaultSecret) 24 | 25 | $path = [System.IO.Path]::Combine($PSScriptRoot, 'Resources', "$($Vault).yml") 26 | $plaintext = (Get-Content -Path $path -Raw).Replace("`r`n", "`n") 27 | $password = ConvertTo-SecureString -String $VaultSecret -AsPlainText -Force 28 | 29 | $actual = Get-EncryptedAnsibleVault -Value $plaintext -Password $password 30 | $actual2 = Get-EncryptedAnsibleVault -Value $plaintext -Password $password 31 | $actual | Should -Not -Be $plaintext 32 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.1;AES256*' 33 | # verify we used a random salt and the output is different 34 | $actual | Should -Not -Be $actual2 35 | 36 | $actual = $plaintext | Get-EncryptedAnsibleVault -Password $password 37 | $actual2 = $plaintext | Get-EncryptedAnsibleVault -Password $password 38 | $actual | Should -Not -Be $plaintext 39 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.1;AES256*' 40 | $actual | Should -Not -Be $actual2 41 | 42 | $actual = Get-EncryptedAnsibleVault -Path $path -Password $password 43 | $actual2 = Get-EncryptedAnsibleVault $path -Password $password 44 | $actual | Should -Not -Be $plaintext 45 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.1;AES256*' 46 | $actual | Should -Not -Be $actual2 47 | 48 | # we can't assert the vault text as it changes randomly, we can at 49 | # verify we can decrypt using the normal process which is tested 50 | # against known outputs 51 | if ($Vault -eq "unicode_1.1") { 52 | # The original plaintext file is known to be UTF-16, we need to 53 | # override the output encoding for this scenario only 54 | $dec_actual = $actual | Get-DecryptedAnsibleVault -Password $password -Encoding ([System.Text.Encoding]::Unicode) 55 | } else { 56 | $dec_actual = $actual | Get-DecryptedAnsibleVault -Password $password -Encoding ([System.Text.Encoding]::UTF8) 57 | } 58 | # because last actual was from file, the plaintext could have different newlines, just read the file again 59 | $dec_actual | Should -Be (Get-Content -Path $path -Raw) 60 | 61 | # now repeat the above and specify the ID 62 | $actual = Get-EncryptedAnsibleVault -Path $path -Password $password -Id Prod 63 | $actual2 = Get-EncryptedAnsibleVault $path -Password $password -Id Prod 64 | $actual | Should -Not -Be $plaintext 65 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.2;AES256;Prod*' 66 | $actual | Should -Not -Be $actual2 67 | 68 | $actual = Get-EncryptedAnsibleVault -Value $plaintext -Password $password -Id Prod 69 | $actual2 = Get-EncryptedAnsibleVault -Value $plaintext -Password $password -Id Prod 70 | $actual | Should -Not -Be $plaintext 71 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.2;AES256;Prod*' 72 | # verify we used a random salt and the output is different 73 | $actual | Should -Not -Be $actual2 74 | 75 | $actual = $plaintext | Get-EncryptedAnsibleVault -Password $password -Id Prod 76 | $actual2 = $plaintext | Get-EncryptedAnsibleVault -Password $password -Id Prod 77 | $actual | Should -Not -Be $plaintext 78 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.2;AES256;Prod*' 79 | $actual | Should -Not -Be $actual2 80 | 81 | $dec_actual = $actual | Get-DecryptedAnsibleVault -Password $password 82 | # last actual was the string we specified, we can assert with the actual string to make sure 83 | $dec_actual | Should -Be $plaintext 84 | } 85 | It "Can encrypt a vault file in the pwd" { 86 | $password = ConvertTo-SecureString -String "password" -AsPlainText -Force 87 | $previous_pwd = (Get-Location).Path 88 | Set-Location -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources')) 89 | 90 | $actual = Get-EncryptedAnsibleVault -Path "small_1.1.yml" -Password $password 91 | Set-Location -Path $previous_pwd 92 | $actual | Should -BeLike '$ANSIBLE_VAULT;1.1;AES256*' 93 | $dec_actual = $actual | Get-DecryptedAnsibleVault -Password $password 94 | $dec_actual | Should -Be (Get-Content -Path ([System.IO.Path]::Combine($PSScriptRoot, 'Resources', 'small_1.1.yml')) -Raw) 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Tests/Get-HMACValue.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Convert-HexToByte.ps1")) 11 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Convert-ByteToHex.ps1")) 12 | 13 | Describe "$module_name PS$ps_version tests" { 14 | Context 'Strict mode' { 15 | Set-StrictMode -Version latest 16 | 17 | It 'should get valid HMAC256 result for value with key ' -TestCases @( 18 | # Test vectors are from https://tools.ietf.org/html/rfc4231#section-4.1 19 | @{ 20 | Key = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" 21 | Value = "4869205468657265" 22 | Expected = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7" 23 | } 24 | @{ 25 | Key = "4a656665" 26 | Value = "7768617420646f2079612077616e7420666f72206e6f7468696e673f" 27 | Expected = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" 28 | } 29 | @{ 30 | Key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 31 | Value = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" 32 | Expected = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe" 33 | } 34 | @{ 35 | Key = "0102030405060708090a0b0c0d0e0f10111213141516171819" 36 | Value = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" 37 | Expected = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b" 38 | } 39 | @{ 40 | Key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 41 | Value = "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b6579202d2048617368204b6579204669727374" 42 | Expected = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54" 43 | } 44 | @{ 45 | Key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 46 | Value = "5468697320697320612074657374207573696e672061206c6172676572207468616e20626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520686173686564206265666f7265206265696e6720757365642062792074686520484d414320616c676f726974686d2e" 47 | Expected = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2" 48 | } 49 | ) { 50 | param($Key, $Value, $Expected) 51 | $actual = Get-HMACValue -Value (Convert-HexToByte -Value $Value) -Key (Convert-HexToByte -Value $Key) 52 | $actual | Should -Be $Expected 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Tests/Get-VaultHeader.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Convert-HexToByte.ps1")) 11 | 12 | Describe "$module_name PS$ps_version tests" { 13 | Context 'Strict mode' { 14 | Set-StrictMode -Version latest 15 | 16 | It 'should get cipher , version , id for header
' -TestCases @( 17 | @{ Header = "`$ANSIBLE_VAULT;1.1;AES256`n01"; Cipher = 'AES256'; Version = [Version]"1.1"; Id = $null } 18 | @{ Header = "`$ANSIBLE_VAULT;1.2;AES256;dev`r`n01"; Cipher = 'AES256'; Version = [Version]"1.2"; Id = "dev" } 19 | # this is not a real header but it tests the flexibility of the parser 20 | @{ Header = "`$ANSIBLE_VAULT;1.1;AES512`n`r01"; Cipher = 'AES512'; Version = [Version]"1.1"; Id = $null } 21 | ) { 22 | param($Header, $Cipher, $Version, $Id) 23 | 24 | $actual_version, $actual_cipher, $actual_id, $actual_bytes = Get-VaultHeader -Value $Header 25 | $actual_version | Should -Be $Version 26 | $actual_cipher | Should -Be $Cipher 27 | $actual_id | Should -Be $Id 28 | $actual_bytes | Should -Be ([byte[]]@(1)) 29 | } 30 | 31 | It 'should fail with invalid version for version ' -TestCases @( 32 | @{ Version = "1.0" } 33 | @{ Version = "1.3" } 34 | ) { 35 | param($Version) 36 | 37 | $header = "`$ANSIBLE_VAULT;$Version;AES256`n01" 38 | { Get-VaultHeader -Value $header } | Should -Throw "Cannot parse vault version $Version, currently only 1.1 and 1.2 is supported by this tool" 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Tests/Invoke-AESCTRCycle.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Convert-HexToByte.ps1")) 11 | 12 | Describe "$module_name PS$ps_version tests" { 13 | Context 'Strict mode' { 14 | Set-StrictMode -Version latest 15 | 16 | It 'AES CTR cycle for key and counter from input should get ' -TestCases @( 17 | # These test cases are from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf 18 | # F.5.1 CTR-AES127.Encrypt 19 | @{ 20 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 21 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 22 | InputHex = "6bc1bee22e409f96e93d7e117393172a" 23 | OutputHex = "874d6191b620e3261bef6864990db6ce" 24 | } 25 | @{ 26 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 27 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00" 28 | InputHex = "ae2d8a571e03ac9c9eb76fac45af8e51" 29 | OutputHex = "9806f66b7970fdff8617187bb9fffdff" 30 | } 31 | @{ 32 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 33 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01" 34 | InputHex = "30c81c46a35ce411e5fbc1191a0a52ef" 35 | OutputHex = "5ae4df3edbd5d35e5b4f09020db03eab" 36 | } 37 | @{ 38 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 39 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff02" 40 | InputHex = "f69f2445df4f9b17ad2b417be66c3710" 41 | OutputHex = "1e031dda2fbe03d1792170a0f3009cee" 42 | } 43 | # F.5.2 CTR-AES127.Encrypt 44 | @{ 45 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 46 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 47 | InputHex = "874d6191b620e3261bef6864990db6ce" 48 | OutputHex = "6bc1bee22e409f96e93d7e117393172a" 49 | } 50 | @{ 51 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 52 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00" 53 | InputHex = "9806f66b7970fdff8617187bb9fffdff" 54 | OutputHex = "ae2d8a571e03ac9c9eb76fac45af8e51" 55 | } 56 | @{ 57 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 58 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01" 59 | InputHex = "5ae4df3edbd5d35e5b4f09020db03eab" 60 | OutputHex = "30c81c46a35ce411e5fbc1191a0a52ef" 61 | } 62 | @{ 63 | Key = "2b7e151628aed2a6abf7158809cf4f3c" 64 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff02" 65 | InputHex = "1e031dda2fbe03d1792170a0f3009cee" 66 | OutputHex = "f69f2445df4f9b17ad2b417be66c3710" 67 | } 68 | # F.5.3 CTR-AES192.Encrypt 69 | @{ 70 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 71 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 72 | InputHex = "6bc1bee22e409f96e93d7e117393172a" 73 | OutputHex = "1abc932417521ca24f2b0459fe7e6e0b" 74 | } 75 | @{ 76 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 77 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00" 78 | InputHex = "ae2d8a571e03ac9c9eb76fac45af8e51" 79 | OutputHex = "090339ec0aa6faefd5ccc2c6f4ce8e94" 80 | } 81 | @{ 82 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 83 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01" 84 | InputHex = "30c81c46a35ce411e5fbc1191a0a52ef" 85 | OutputHex = "1e36b26bd1ebc670d1bd1d665620abf7" 86 | } 87 | @{ 88 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 89 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff02" 90 | InputHex = "f69f2445df4f9b17ad2b417be66c3710" 91 | OutputHex = "4f78a7f6d29809585a97daec58c6b050" 92 | } 93 | # F.5.4 CTR-AES192.Decrypt 94 | @{ 95 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 96 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 97 | InputHex = "1abc932417521ca24f2b0459fe7e6e0b" 98 | OutputHex = "6bc1bee22e409f96e93d7e117393172a" 99 | } 100 | @{ 101 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 102 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00" 103 | InputHex = "090339ec0aa6faefd5ccc2c6f4ce8e94" 104 | OutputHex = "ae2d8a571e03ac9c9eb76fac45af8e51" 105 | } 106 | @{ 107 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 108 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01" 109 | InputHex = "1e36b26bd1ebc670d1bd1d665620abf7" 110 | OutputHex = "30c81c46a35ce411e5fbc1191a0a52ef" 111 | } 112 | @{ 113 | Key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b" 114 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff02" 115 | InputHex = "4f78a7f6d29809585a97daec58c6b050" 116 | OutputHex = "f69f2445df4f9b17ad2b417be66c3710" 117 | } 118 | # F.5.5 CTR-AES256.Encrypt 119 | @{ 120 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 121 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 122 | InputHex = "6bc1bee22e409f96e93d7e117393172a" 123 | OutputHex = "601ec313775789a5b7a7f504bbf3d228" 124 | } 125 | @{ 126 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 127 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00" 128 | InputHex = "ae2d8a571e03ac9c9eb76fac45af8e51" 129 | OutputHex = "f443e3ca4d62b59aca84e990cacaf5c5" 130 | } 131 | @{ 132 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 133 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01" 134 | InputHex = "30c81c46a35ce411e5fbc1191a0a52ef" 135 | OutputHex = "2b0930daa23de94ce87017ba2d84988d" 136 | } 137 | @{ 138 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 139 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff02" 140 | InputHex = "f69f2445df4f9b17ad2b417be66c3710" 141 | OutputHex = "dfc9c58db67aada613c2dd08457941a6" 142 | } 143 | # F.5.6 CTR-AES256.Decrypt 144 | @{ 145 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 146 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 147 | InputHex = "601ec313775789a5b7a7f504bbf3d228" 148 | OutputHex = "6bc1bee22e409f96e93d7e117393172a" 149 | } 150 | @{ 151 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 152 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff00" 153 | InputHex = "f443e3ca4d62b59aca84e990cacaf5c5" 154 | OutputHex = "ae2d8a571e03ac9c9eb76fac45af8e51" 155 | } 156 | @{ 157 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 158 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff01" 159 | InputHex = "2b0930daa23de94ce87017ba2d84988d" 160 | OutputHex = "30c81c46a35ce411e5fbc1191a0a52ef" 161 | } 162 | @{ 163 | Key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 164 | Counter = "f0f1f2f3f4f5f6f7f8f9fafbfcfdff02" 165 | InputHex = "dfc9c58db67aada613c2dd08457941a6" 166 | OutputHex = "f69f2445df4f9b17ad2b417be66c3710" 167 | } 168 | ) { 169 | param($Key, $Counter, $InputHex, $OutputHex) 170 | $expected = Convert-HexToByte -Value $OutputHex 171 | $actual = Invoke-AESCTRCycle -Value (Convert-HexToByte -Value $InputHex) ` 172 | -Nonce (Convert-HexToByte -Value $Counter) ` 173 | -Key (Convert-HexToByte -Value $Key) 174 | 175 | $actual | Should -Be $expected 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /Tests/Invoke-Win32Api.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | # Tests won't run on Linux so skip 7 | $is_windows = Get-Variable -Name IsWindows -ErrorAction SilentlyContinue 8 | $skip = $null -ne $is_windows -and $is_windows.Value -eq $false 9 | 10 | $ps_version = $PSVersionTable.PSVersion.Major 11 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 12 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 13 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 14 | 15 | Describe "$module_name PS$ps_version tests" { 16 | Context 'Strict mode' { 17 | Set-StrictMode -Version latest 18 | 19 | It 'fail when parameter count mismatch' { 20 | { Invoke-Win32Api -DllName a.dll -MethodName a -ReturnType bool -ParameterTypes @([int]) -Parameters @() } | Should -Throw "ParameterType Count 1 not equal to Parameter Count 0" 21 | } 22 | 23 | It 'invoke API that returns a handle' -Skip:$skip { 24 | $test_file_path = "$PSScriptRoot\Resources\test-deleteme.txt" 25 | if (-not (Test-Path -Path $test_file_path)) { 26 | New-Item -Path $test_file_path -ItemType File > $null 27 | } 28 | Set-Content -Path $test_file_path -Value "abc" 29 | 30 | try { 31 | $handle = Invoke-Win32Api -DllName kernel32.dll ` 32 | -MethodName CreateFileW ` 33 | -ReturnType Microsoft.Win32.SafeHandles.SafeFileHandle ` 34 | -ParameterTypes @([String], [System.Security.AccessControl.FileSystemRights], [System.IO.FileShare], [IntPtr], [System.IO.FileMode], [UInt32], [IntPtr]) ` 35 | -Parameters @( 36 | "\\?\$test_file_path", 37 | [System.Security.AccessControl.FileSystemRights]::Read, 38 | [System.IO.FileShare]::ReadWrite, 39 | [IntPtr]::Zero, 40 | [System.IO.FileMode]::Open, 41 | 0, 42 | [IntPtr]::Zero) ` 43 | -SetLastError $true ` 44 | -CharSet Unicode 45 | 46 | if ($handle.IsInvalid) { 47 | $last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 48 | throw [System.ComponentModel.Win32Exception]$last_err 49 | } 50 | $fs = New-Object -TypeName System.IO.FileStream -ArgumentList $handle, ([System.IO.FileAccess]::Read) 51 | $sr = New-Object -TypeName System.IO.StreamReader -ArgumentList $fs 52 | $actual = $sr.ReadToEnd() 53 | $sr.Close() 54 | 55 | $actual | Should -Be "abc`r`n" 56 | } finally { 57 | Remove-Item -Path $test_file_path -Force > $null 58 | } 59 | } 60 | 61 | It 'invoke API with output strings' -Skip:$skip { 62 | $sid_string = "S-1-5-18" 63 | $sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $sid_string 64 | $sid_bytes = New-Object -TypeName byte[] -ArgumentList $sid.BinaryLength 65 | $sid.GetBinaryForm($sid_bytes, 0) 66 | 67 | $name = New-Object -TypeName System.Text.StringBuilder 68 | $name_length = 0 69 | $domain_name = New-Object -TypeName System.Text.StringBuilder 70 | $domain_name_length = 0 71 | 72 | $invoke_args = @{ 73 | DllName = "Advapi32.dll" 74 | MethodName = "LookupAccountSidW" 75 | ReturnType = [bool] 76 | ParameterTypes = @([String], [byte[]], [System.Text.StringBuilder], [Ref], [System.Text.StringBuilder], [Ref], [Ref]) 77 | Parameters = @( 78 | $null, 79 | $sid_bytes, 80 | $name, 81 | [Ref]$name_length, 82 | $domain_name, 83 | [Ref]$domain_name_length, 84 | [Ref][IntPtr]::Zero 85 | ) 86 | SetLastError = $true 87 | CharSet = "Unicode" 88 | } 89 | 90 | $res = Invoke-Win32Api @invoke_args 91 | $name.EnsureCapacity($name_length) 92 | $domain_name.EnsureCapacity($domain_name_length) 93 | $res = Invoke-Win32Api @invoke_args 94 | if (-not $res) { 95 | $last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 96 | throw [System.ComponentModel.Win32Exception]$last_err 97 | } 98 | $name.ToString() | Should -Be "SYSTEM" 99 | $domain_name.ToString() | Should -Be "NT AUTHORITY" 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /Tests/New-PBKDF2Key.Tests.ps1: -------------------------------------------------------------------------------- 1 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification="Need to create secure string from samples in tests")] 2 | param() 3 | 4 | $verbose = @{} 5 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 6 | $verbose.Add("Verbose", $true) 7 | } 8 | 9 | $ps_version = $PSVersionTable.PSVersion.Major 10 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 11 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 12 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 13 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Convert-ByteToHex.ps1")) 14 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Invoke-Win32Api.ps1")) 15 | 16 | $is_core_clr = Get-Variable -Name IsCoreCLR -ErrorAction Ignore 17 | if ($null -eq $is_core_clr) { 18 | $is_core_clr = $false 19 | } else { 20 | $is_core_clr = $is_core_clr.Value 21 | } 22 | 23 | 24 | Describe "$module_name PS$ps_version tests" { 25 | Context 'Strict mode' { 26 | Set-StrictMode -Version latest 27 | 28 | It 'should get valid KDF output (Algorithm: , Iterations: , Length: ' -TestCases @( 29 | # PBKDF2 with SHA1 are from https://tools.ietf.org/html/rfc6070#section-2 30 | @{ 31 | Algorithm = "SHA1" 32 | Secret = "password" 33 | Salt = "salt" 34 | Iterations = 1 35 | Length = 20 36 | Expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6" 37 | } 38 | @{ 39 | Algorithm = "SHA1" 40 | Secret = "password" 41 | Salt = "salt" 42 | Iterations = 2 43 | Length = 20 44 | Expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957" 45 | } 46 | @{ 47 | Algorithm = "SHA1" 48 | Secret = "password" 49 | Salt = "salt" 50 | Iterations = 4096 51 | Length = 20 52 | Expected = "4b007901b765489abead49d926f721d065a429c1" 53 | } 54 | @{ 55 | Algorithm = "SHA1" 56 | Secret = "password" 57 | Salt = "salt" 58 | Iterations = 16777216 59 | Length = 20 60 | Expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984" 61 | } 62 | @{ 63 | Algorithm = "SHA1" 64 | Secret = "passwordPASSWORDpassword" 65 | Salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt" 66 | Iterations = 4096 67 | Length = 25 68 | Expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038" 69 | } 70 | @{ 71 | Algorithm = "SHA1" 72 | Secret = "pass`0word" 73 | Salt = "sa`0lt" 74 | Iterations = 4096 75 | Length = 16 76 | Expected = "56fa6aa75548099dcc37d7f03425e0c3" 77 | } 78 | # PBKDF2 with SHA256 are from https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors 79 | @{ 80 | Algorithm = "SHA256" 81 | Secret = "password" 82 | Salt = "salt" 83 | Iterations = 1 84 | Length = 32 85 | Expected = "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b" 86 | } 87 | @{ 88 | Algorithm = "SHA256" 89 | Secret = "password" 90 | Salt = "salt" 91 | Iterations = 2 92 | Length = 32 93 | Expected = "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43" 94 | } 95 | @{ 96 | Algorithm = "SHA256" 97 | Secret = "password" 98 | Salt = "salt" 99 | Iterations = 4096 100 | Length = 32 101 | Expected = "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a" 102 | } 103 | @{ 104 | Algorithm = "SHA256" 105 | Secret = "password" 106 | Salt = "salt" 107 | Iterations = 16777216 108 | Length = 32 109 | Expected = "cf81c66fe8cfc04d1f31ecb65dab4089f7f179e89b3b0bcb17ad10e3ac6eba46" 110 | } 111 | @{ 112 | Algorithm = "SHA256" 113 | Secret = "passwordPASSWORDpassword" 114 | Salt = "saltSALTsaltSALTsaltSALTsaltSALTsalt" 115 | Iterations = 4096 116 | Length = 40 117 | Expected = "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9" 118 | } 119 | @{ 120 | Algorithm = "SHA256" 121 | Secret = "pass`0word" 122 | Salt = "sa`0lt" 123 | Iterations = 4096 124 | Length = 16 125 | Expected = "89b69d0516f829893c696226650a8687" 126 | } 127 | ){ 128 | param($Algorithm, $Secret, $Salt, $Iterations, $Length, $Expected) 129 | 130 | if ($is_core_clr) { 131 | # .NET Fails if the Salt is less than 8 chars even though it is valid, just need to skip those tests 132 | return 133 | } 134 | 135 | $sec_pass = ConvertTo-SecureString -String $Secret -AsPlainText -Force 136 | $salt_bytes = [System.Text.Encoding]::UTF8.GetBytes($Salt) 137 | $actual = New-PBKDF2Key -Algorithm $Algorithm ` 138 | -Password $sec_pass ` 139 | -Salt $salt_bytes ` 140 | -Length $Length ` 141 | -Iterations $Iterations 142 | 143 | (Convert-ByteToHex -Value $actual) | Should -Be $Expected 144 | } 145 | 146 | It 'fail with invalid algorithm' { 147 | $sec_pass = ConvertTo-SecureString -String "a" -AsPlainText -Force 148 | if ($is_core_clr) { 149 | $expected = "'fake' is not a known hash algorithm" 150 | } else { 151 | $expected = "Failed to open algorithm provider with ID 'fake': The object was not found (STATUS_NOT_FOUND 0xC0000225)" 152 | } 153 | { New-PBKDF2Key -Algorithm "fake" -Password $sec_pass -Salt ([byte[]]@(1, 2, 3, 4, 5, 6, 7, 8)) -Length 1 -Iterations 1 } | Should -Throw $expected 154 | } 155 | 156 | It 'failed to generate key with invalid parameters' { 157 | $sec_pass = ConvertTo-SecureString -String "a" -AsPlainText -Force 158 | if ($is_core_clr) { 159 | $expected = "Positive number required" 160 | } else { 161 | $expected = "Failed to derive key: An invalid parameter was passed to a service or function (STATUS_INVALID_PARAMETER 0xC0000000D)" 162 | } 163 | { New-PBKDF2Key -Algorithm SHA256 -Password $sec_pass -Salt ([byte[]]@(1, 2, 3, 4, 5, 6, 7, 8)) -Length 1 -Iterations 0 } | Should -Throw $expected 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Tests/New-VaultKey.Tests.ps1: -------------------------------------------------------------------------------- 1 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Justification="Need to create secure string from samples in tests")] 2 | param() 3 | 4 | $verbose = @{} 5 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 6 | $verbose.Add("Verbose", $true) 7 | } 8 | 9 | $ps_version = $PSVersionTable.PSVersion.Major 10 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 11 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 12 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 13 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "New-PBKDF2Key.ps1")) 14 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "Invoke-Win32Api.ps1")) 15 | 16 | Describe "$module_name PS$ps_version tests" { 17 | Context 'Strict mode' { 18 | Set-StrictMode -Version latest 19 | 20 | It 'should get expected return results' { 21 | $sec_pass = ConvertTo-SecureString "pass" -AsPlainText -Force 22 | $actual = New-VaultKey -Password $sec_pass -Salt (New-Object -TypeName byte[] -ArgumentList 32) 23 | $actual.Count | Should -Be 3 24 | 25 | # New-PBKDF2Key returns a random string so let's just make sure the 26 | # count is good 27 | $actual[0].Count | Should -Be 32 28 | $actual[1].Count | Should -Be 32 29 | $actual[2].Count | Should -Be 16 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Tests/Remove-Pkcs7Padding.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | 11 | Describe "$module_name PS$ps_version tests" { 12 | Context 'Strict mode' { 13 | Set-StrictMode -Version latest 14 | 15 | It 'should return correct unpadded length for block size with input ' -TestCases @( 16 | @{ Size = 32; Expected = [byte[]]@(1); Value = [byte[]]@(1, 3, 3, 3) } 17 | @{ Size = 32; Expected = [byte[]]@(1, 2); Value = [byte[]]@(1, 2, 2, 2) } 18 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3); Value = [byte[]]@(1, 2, 3, 1) } 19 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3, 4); Value = [byte[]]@(1, 2, 3, 4, 4, 4, 4, 4) } 20 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3, 4, 5); Value = [byte[]]@(1, 2, 3, 4, 5, 3, 3, 3) } 21 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3, 4, 5, 6); Value = [byte[]]@(1, 2, 3, 4, 5, 6, 2, 2) } 22 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7); Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 1) } 23 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8); Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 4, 4, 4, 4) } 24 | @{ Size = 32; Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 9); Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 9, 3, 3, 3) } 25 | @{ Size = 64; Expected = [byte[]]@(1, 2, 3, 4); Value = [byte[]]@(1, 2, 3, 4, 4, 4, 4, 4) } 26 | @{ Size = 64; Expected = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8); Value = [byte[]]@(1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8) } 27 | ){ 28 | param($Size, $Value, $Expected) 29 | 30 | $actual = [byte[]](Remove-Pkcs7Padding -Value $Value -BlockSize $Size) 31 | # ensure a 2nd run produces the same result 32 | $actual2 = [byte[]](Remove-Pkcs7Padding -Value $actual -BlockSize $Size) 33 | $actual.Length | Should -Be $Expected.Length 34 | $actual2.Length | Should -Be $Expected.Length 35 | 36 | for ($i = 0; $i -lt $actual.Length; $i++) { 37 | $actual[$i] | Should -Be $Expected[$i] 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Tests/Resources/dev_1.2.vault: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.2;AES256;dev 2 | 31306231653333313836353763313035666237353434313734336131323465323035386666616336 3 | 3238313863343161656265353132333937666238333761300a316437383933613164653561316138 4 | 32383233613066663336346661306563306534643462356337386363343964326239393136323963 5 | 3938613431626634310a626365613931663836353233336464313963393730313032386463363334 6 | 3532 -------------------------------------------------------------------------------- /Tests/Resources/dev_1.2.yml: -------------------------------------------------------------------------------- 1 | letmein 2 | -------------------------------------------------------------------------------- /Tests/Resources/large_1.1.yml: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum et turpis ut urna scelerisque scelerisque. Maecenas egestas urna id est volutpat, quis iaculis orci sagittis. Duis ac justo non turpis ullamcorper malesuada. Sed placerat a arcu nec vehicula. Aenean viverra aliquam diam a pretium. Fusce dapibus lacus ac mauris ornare accumsan. Integer egestas lacus erat, sed consectetur tellus condimentum eu. Etiam facilisis accumsan odio, egestas consectetur quam vulputate sit amet. 2 | 3 | Ut at lorem pellentesque, luctus ante non, euismod dui. Mauris vel lorem ullamcorper, accumsan lectus at, eleifend enim. Proin dignissim aliquet varius. Mauris vel scelerisque elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec et auctor odio, sed sodales diam. Sed nec commodo sapien, vitae eleifend est. Suspendisse venenatis odio ut urna tristique volutpat. Morbi ullamcorper volutpat viverra. Suspendisse eget ipsum in nunc tempus finibus. Integer facilisis massa metus, quis pellentesque turpis gravida quis. 4 | 5 | Nulla nulla massa, finibus ac scelerisque ut, blandit non dolor. Aenean faucibus ipsum eu scelerisque rutrum. Donec sed tortor elementum, interdum elit et, eleifend augue. Morbi sed varius nisl, vitae tristique leo. Suspendisse ullamcorper venenatis nibh, a placerat mi varius id. Aliquam sit amet augue nec mauris eleifend blandit non ut erat. Maecenas consectetur ligula a tortor rutrum, non sagittis nisl volutpat. In in nisl a dolor euismod ultricies eget eu nulla. Pellentesque in mi eu orci sodales facilisis. Nulla at nibh dolor. Donec bibendum tellus vitae nunc rhoncus, quis imperdiet neque molestie. 6 | 7 | Sed tincidunt enim et feugiat euismod. Mauris lacinia ipsum nec placerat auctor. Vivamus non enim mauris. Aliquam efficitur dolor neque, non interdum elit scelerisque nec. Quisque ut ultrices velit, id dictum turpis. Nam pharetra arcu vitae risus porta commodo. Cras pretium neque a eleifend vulputate. Ut maximus odio quis sollicitudin ornare. Pellentesque et laoreet sem. Morbi tristique eros sapien, nec dapibus quam dignissim a. Morbi molestie felis sit amet enim sodales mollis id eget purus. Sed ut mauris sed tellus dictum scelerisque a nec orci. In gravida ultricies arcu, ut finibus velit bibendum ac. Donec cursus quam non volutpat tristique. Vivamus non lobortis ligula. 8 | 9 | Cras purus elit, blandit fermentum placerat et, imperdiet suscipit elit. Etiam fringilla ipsum dui, ac ultrices ante varius a. Donec augue dolor, interdum non tortor eu, maximus mattis erat. Aenean vel mauris vitae mauris rutrum auctor. Aliquam varius erat vel nisi cursus, ut sollicitudin diam tempor. Praesent at felis nec augue convallis pellentesque quis at augue. Sed eros mi, dignissim ac pellentesque eget, mollis ut sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce accumsan semper sodales. Etiam malesuada, massa eget placerat sodales, lectus risus dictum ex, ut congue enim nulla a nisi. Vivamus eget mi et enim pharetra posuere eu sed odio. Nunc et laoreet orci. Nunc et neque eget felis consequat ultrices. Mauris dignissim rutrum diam a bibendum. 10 | 11 | Donec luctus imperdiet neque sit amet placerat. In cursus gravida metus vel iaculis. Vestibulum vulputate faucibus mi eu finibus. Pellentesque hendrerit, orci at ullamcorper rutrum, augue urna pellentesque purus, sit amet feugiat nulla nunc in nibh. Aenean tellus ligula, euismod tristique tristique et, scelerisque quis urna. Nullam ut ante ac dui auctor auctor ut id neque. Etiam nisi ipsum, venenatis eget consequat eu, eleifend non lorem. Suspendisse sed mi congue urna finibus pretium. Fusce feugiat nisl est, vitae porttitor elit tincidunt vel. Praesent condimentum erat id orci rhoncus, sit amet dapibus eros varius. 12 | 13 | Etiam vitae tellus ac nisl feugiat malesuada sit amet in nunc. Phasellus finibus lectus nec luctus ornare. Proin porttitor consequat aliquam. Fusce in fringilla nibh. Duis efficitur tellus eu pretium rutrum. Mauris vulputate semper mi. Nam congue, metus non interdum auctor, quam odio mollis ipsum, elementum cursus erat nisi at massa. Vestibulum tempor risus vitae lectus pellentesque consequat. Ut imperdiet aliquet finibus. Suspendisse sodales dui pellentesque diam imperdiet vestibulum. Nam mollis metus vitae nibh pharetra ultrices. 14 | 15 | Donec sollicitudin ultricies risus, vel commodo nisl consectetur ac. Suspendisse aliquam, leo a viverra fringilla, sem felis placerat nunc, et rutrum diam magna a tellus. Sed congue pellentesque tristique. Interdum et malesuada fames ac ante ipsum primis in faucibus. Fusce convallis erat massa, eget luctus libero rhoncus id. Vestibulum viverra, tellus sed ornare efficitur, lorem augue bibendum tortor, ut porta lorem eros eget nunc. Suspendisse eu ligula id eros iaculis tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean porta quam mauris, sit amet iaculis ex efficitur sit amet. Donec in cursus nibh. Sed ullamcorper non libero ut tristique. Quisque ultrices justo sit amet neque auctor, vitae convallis sapien ultrices. Sed gravida, leo vel accumsan consectetur, urna purus porta purus, eget efficitur neque arcu quis felis. 16 | 17 | Sed vulputate, mi fermentum imperdiet lacinia, massa arcu vestibulum elit, vitae accumsan lectus eros vitae arcu. Duis facilisis nulla ac justo mattis, viverra lobortis lectus interdum. Nulla nunc tortor, suscipit eget ligula non, tristique cursus urna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut rutrum tellus et augue lacinia, sed vulputate nibh convallis. Morbi porttitor sagittis nisi, eget aliquam magna cursus sodales. Fusce tortor risus, commodo nec luctus at, fringilla sollicitudin risus. Nam sed mi placerat, finibus tortor sed, placerat turpis. Curabitur non nisi eget ante hendrerit fringilla at ut odio. Ut interdum sapien ipsum, id sollicitudin neque mollis sit amet. Sed auctor vestibulum lacus, nec imperdiet purus vestibulum a. Pellentesque aliquam, quam vel eleifend pellentesque, sapien diam blandit est, quis feugiat metus nisi quis nulla. Pellentesque varius lobortis odio eu pretium. Sed ex eros, porttitor at ante ut, cursus tristique purus. Aliquam a est et tellus pharetra fermentum eu non erat. 18 | 19 | Morbi vitae dolor sit amet arcu malesuada rutrum nec sed nisl. Nullam vestibulum blandit lectus eget suscipit. Integer ut ornare lorem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam id nisi quam. Donec consequat, dui dignissim ultricies suscipit, arcu ligula mollis mauris, vitae rutrum quam eros eu nisl. In dui felis, placerat non enim vel, pellentesque imperdiet orci. In cursus bibendum varius. 20 | 21 | Curabitur in scelerisque diam. Donec rhoncus, libero id facilisis condimentum, ante ipsum sollicitudin sem, quis sollicitudin nisl dolor ut tellus. Vestibulum congue nisl mauris, non fringilla diam elementum in. Proin lorem arcu, scelerisque sit amet semper id, sollicitudin in justo. Nam at nisl quis est fermentum pellentesque. Praesent euismod libero eget eros fringilla, id suscipit enim dignissim. Sed gravida ac magna at commodo. 22 | 23 | Pellentesque eget augue eget ipsum tempus vulputate. Quisque quis vestibulum magna. Pellentesque augue nunc, vestibulum sed eleifend eu, pulvinar a dui. Integer vulputate nisi quam, eu vehicula risus maximus at. Vestibulum eros ipsum, pellentesque ac porttitor nec, rutrum nec odio. In vehicula augue sit amet erat tristique sodales. Donec suscipit justo ac ante condimentum convallis. Maecenas sollicitudin eros in orci interdum, a cursus massa euismod. In non pretium dolor. Sed a urna id enim vehicula egestas ac et elit. Vestibulum congue mollis nunc, eu dignissim elit vehicula vel. Sed id felis ex. Nam lacinia nibh turpis, sit amet ullamcorper nulla efficitur ac. Duis nec arcu consequat dolor pellentesque vulputate. 24 | 25 | Etiam et dolor felis. Nullam a facilisis urna. Maecenas laoreet ipsum non sem bibendum, in eleifend orci accumsan. Morbi congue aliquam nibh at porta. Aliquam gravida mattis sapien sollicitudin porta. Etiam aliquet posuere felis, non vestibulum nisi hendrerit ac. Quisque efficitur consectetur augue, luctus placerat nibh porta quis. In sit amet nisi vel nisl accumsan ultricies. Vivamus augue est, cursus in mollis vel, egestas quis velit. Cras sollicitudin urna ut dui pulvinar tempor quis non elit. Morbi vulputate massa vitae scelerisque mollis. Curabitur euismod pellentesque gravida. Quisque pellentesque nunc libero, at sagittis neque interdum vitae. Vestibulum maximus vestibulum justo a viverra. Suspendisse euismod iaculis mauris, nec vehicula tellus tincidunt eu. Praesent eleifend diam sem, eget fringilla velit aliquet non. 26 | 27 | Duis eget nisl dapibus risus dapibus tristique. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut ullamcorper nunc sed lectus dictum, ut rutrum nisl luctus. In nec pharetra sem. Aenean sodales feugiat arcu nec consectetur. Nulla vitae semper nibh. Vivamus sit amet bibendum augue, et dapibus mi. Morbi efficitur dolor eget fringilla gravida. Pellentesque vel odio vel magna egestas gravida quis sit amet urna. Praesent ultrices velit maximus, sollicitudin nisi non, suscipit leo. Morbi ut leo id nulla suscipit ornare a id enim. 28 | 29 | Sed eu felis eu lacus iaculis scelerisque vitae id tortor. Ut purus metus, porttitor id tempus in, consequat eget lacus. Cras pharetra est eros. Curabitur consectetur pretium nunc vel eleifend. In in consectetur dui. Fusce nec leo sagittis, venenatis arcu at, rhoncus purus. Aenean egestas neque arcu, ac egestas risus tempus vel. Nulla nec augue ipsum. Etiam rhoncus, justo sed sagittis laoreet, lectus urna suscipit velit, nec eleifend nulla eros eget ligula. Nam nec magna ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Proin accumsan accumsan felis, eget porta nunc bibendum quis. Aliquam at mattis erat. Fusce sed elit nisi. 30 | 31 | Donec in enim sit amet elit rhoncus vehicula. Aenean gravida aliquam dolor, vel elementum urna pharetra eu. Fusce ultricies quis ante sit amet mattis. Phasellus non feugiat ante, vel viverra velit. Praesent dictum et tortor vel consequat. Ut gravida feugiat urna vitae pellentesque. Sed ut dapibus est. Curabitur aliquam eget dolor a pulvinar. 32 | 33 | Curabitur pretium odio non dictum pulvinar. Morbi aliquam velit sed aliquet aliquam. Etiam quis neque non quam varius sollicitudin. Cras at rhoncus magna, non tempus libero. Sed pretium leo rhoncus nisi vestibulum imperdiet. Proin imperdiet pretium arcu, sit amet facilisis dui facilisis sit amet. Curabitur suscipit erat vitae neque iaculis, at finibus lorem dictum. 34 | 35 | Mauris commodo quam vitae rhoncus lacinia. Duis quis commodo turpis. Phasellus ultrices orci vitae ipsum fermentum, eu vestibulum orci aliquet. Quisque porttitor in lacus id laoreet. Ut suscipit volutpat lacus vel rhoncus. In hac habitasse platea dictumst. Nullam laoreet ipsum a nisi luctus scelerisque sit amet semper eros. Nullam quis porttitor sapien, in cursus erat. Morbi a sapien fringilla quam consequat vestibulum. Suspendisse auctor scelerisque tortor, non elementum enim gravida ut. Integer in porta nisi. Integer a mi eu risus ornare hendrerit varius id nisi. Aliquam efficitur enim odio, ut feugiat metus rutrum a. 36 | 37 | Fusce elementum, dui at vestibulum rhoncus, dolor diam mollis mi, ac ullamcorper libero nunc in risus. Morbi tincidunt a erat eu fermentum. Nunc ut dui nibh. Proin fermentum bibendum neque sit amet accumsan. Sed porta metus ac maximus rhoncus. Curabitur lobortis iaculis diam. Integer eu mauris non justo laoreet pharetra. Cras suscipit malesuada nisi, sed lobortis erat aliquet ac. Sed eleifend efficitur erat a fermentum. 38 | 39 | Duis vitae lacus quis odio suscipit molestie. Donec sollicitudin sit amet nunc in hendrerit. Maecenas diam sapien, feugiat eu mauris et, eleifend feugiat nisl. Maecenas quis est nec mi faucibus rutrum non at orci. Morbi eget metus urna. Aliquam sodales dapibus orci. Ut tempor, lectus in congue vulputate, justo turpis condimentum elit, in pulvinar magna augue eget elit. Ut id aliquet sem. Phasellus condimentum, justo non dictum porttitor, nunc turpis varius augue, a blandit nisi eros nec lacus. Suspendisse dui ligula, pulvinar at ligula sed, lobortis condimentum neque. Nullam sit amet nunc sagittis, lobortis mauris eu, ultrices purus. Quisque varius bibendum ornare. 40 | 41 | Quisque rhoncus laoreet velit non blandit. Fusce eu efficitur erat. Nullam sit amet turpis vitae diam cursus tempus. Nam eget mi luctus, dignissim felis sed, rutrum sapien. Etiam pellentesque faucibus consectetur. Curabitur eu euismod ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas semper porttitor lorem ultrices vehicula. In mollis lacus sit amet augue tempor varius. Phasellus ullamcorper, mauris non dapibus molestie, ipsum tortor aliquam elit, pharetra tempor tortor nibh eget nunc. Sed sed laoreet neque. Sed at elit nec massa ultricies accumsan nec eu ligula. 42 | 43 | Cras a dapibus lorem. Pellentesque sed tincidunt tellus. Vivamus augue libero, tempus vel mattis quis, suscipit ac felis. Vivamus posuere sapien ac mauris pretium semper. Donec sapien risus, iaculis id tempus quis, bibendum bibendum augue. Proin ac fermentum felis, eu tempus purus. Phasellus luctus maximus consectetur. 44 | 45 | Maecenas aliquam ultrices ligula, et cursus velit tempor sit amet. Aenean mollis diam nec nisl pretium luctus. Aliquam aliquet libero sit amet elementum finibus. Nulla facilisi. Nulla facilisi. Duis eu quam ut nibh auctor mollis vitae sed mi. Mauris risus velit, efficitur vel semper nec, placerat sit amet nulla. Vivamus ornare, leo vitae elementum bibendum, leo orci ultrices elit, at blandit justo enim at arcu. In hac habitasse platea dictumst. Pellentesque laoreet euismod nunc, vel posuere mi dignissim non. Donec eleifend sapien et mauris mollis consequat. 46 | 47 | Aliquam a ipsum ac nisl rhoncus pulvinar quis ut urna. Praesent interdum laoreet feugiat. Cras lacinia gravida sagittis. Mauris rutrum lorem enim, non sagittis risus sagittis eu. Etiam sodales hendrerit leo, vel sodales leo congue eu. Nullam pretium odio id orci blandit, at porttitor mauris imperdiet. Phasellus eget cursus est, eget efficitur ipsum. Etiam feugiat hendrerit tortor, in suscipit nisi accumsan sed. 48 | 49 | Aliquam vehicula, justo sit amet ultrices tempor, elit dui congue magna, et tristique ex arcu eu neque. In mattis risus urna, et dignissim diam egestas semper. Maecenas et ligula a dui ullamcorper placerat id eu diam. Aenean tincidunt fringilla lectus, in maximus nibh placerat eu. Donec a mattis arcu. In egestas rhoncus ante id consequat. Vestibulum non sem ut mauris tincidunt ornare. Sed aliquam, felis ac mattis suscipit, dolor turpis auctor lectus, id congue eros mi vestibulum tortor. Ut pellentesque pharetra velit, non finibus est euismod quis. Fusce in pellentesque justo. 50 | 51 | Praesent ornare tortor in tortor egestas laoreet. Praesent ultricies leo ac consequat dapibus. Vivamus magna leo, feugiat ac mattis in, porttitor ac lorem. Fusce congue imperdiet egestas. Nunc quis imperdiet sapien. Duis facilisis non neque in feugiat. Ut a ornare nisi. Donec sit amet pellentesque mi. Aliquam eros augue, pellentesque sit amet porttitor eu, auctor at diam. Suspendisse eu finibus lacus. Donec vitae nibh et enim ultrices accumsan in quis dui. Mauris id quam id elit hendrerit efficitur. Proin condimentum augue eget massa consequat pulvinar. Phasellus vulputate justo et tortor placerat, a fermentum augue pretium. Aenean non ipsum accumsan, dapibus felis vel, efficitur libero. 52 | 53 | Aenean vestibulum non eros semper facilisis. Curabitur tempor justo nec mauris imperdiet, ac finibus leo mollis. Suspendisse a nulla metus. Aenean ante neque, rutrum eget nisi faucibus, sollicitudin ultricies risus. Phasellus metus risus, condimentum pulvinar commodo luctus, ultricies at nisi. In magna nibh, facilisis non massa id, tempor porttitor elit. Cras augue metus, tincidunt quis efficitur sit amet, semper non dolor. Nullam varius imperdiet lacus, vitae euismod purus rutrum vel. Nulla sit amet nisi ut magna porta rutrum. 54 | 55 | Aenean sodales iaculis quam euismod eleifend. Cras sodales libero velit. Cras metus augue, suscipit sed velit volutpat, tincidunt molestie velit. Aenean tincidunt felis arcu. Aenean a justo magna. Sed ut magna in tortor feugiat molestie vel eget metus. Quisque sit amet velit ac nisl lobortis molestie. Curabitur erat est, dictum varius vulputate ac, fermentum at risus. Sed nunc ex, viverra quis lacinia id, sollicitudin accumsan quam. Maecenas turpis magna, lobortis ac nulla molestie, efficitur commodo quam. Quisque dapibus condimentum augue, eu consequat mauris posuere eget. Suspendisse ac placerat tellus, ut iaculis eros. Donec rhoncus faucibus blandit. Pellentesque vitae quam vel purus hendrerit hendrerit. Fusce vel mauris bibendum, imperdiet quam non, sollicitudin ligula. 56 | 57 | Maecenas congue aliquam sagittis. Maecenas elementum elit quam, vel tristique lectus interdum in. Vestibulum eu nisi nec sem sagittis auctor vulputate congue ex. Mauris consectetur nisi et augue pulvinar, vitae vulputate eros semper. Phasellus consectetur dolor non porta pellentesque. Nulla facilisis turpis enim, vitae ullamcorper nibh vulputate non. Quisque interdum urna non dui eleifend tempus. Curabitur pellentesque venenatis diam. Nam nec mauris id turpis vestibulum mollis. 58 | 59 | In malesuada mauris non nulla varius molestie. Quisque varius felis vitae convallis fringilla. Vestibulum quis egestas augue. Suspendisse tristique tellus tortor, id pulvinar diam fringilla quis. Duis venenatis felis at finibus posuere. Maecenas aliquet accumsan elit, id vulputate ipsum malesuada id. Donec eu porttitor lacus. Sed ut cursus massa. In vitae iaculis tortor. Aliquam aliquet mattis ipsum in placerat. 60 | 61 | Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nullam ac nibh mauris. Nullam eleifend purus eu eros congue feugiat ut ac turpis. Mauris et sagittis risus. Maecenas sit amet lobortis tortor, rhoncus congue turpis. Aenean consequat arcu eu pellentesque congue. Nullam vehicula accumsan mauris nec sollicitudin. Nullam sed odio nec lacus tincidunt tempor eget quis neque. Etiam blandit eu ante in cursus. Suspendisse et nibh ac augue eleifend malesuada at eu dolor. Integer lacus lorem, porttitor iaculis feugiat vel, mollis in elit. Cras accumsan risus ac enim lobortis, a lacinia enim sagittis. Aliquam ex lacus, convallis a gravida at, consectetur cursus libero. Aenean arcu magna, molestie in accumsan eu, euismod eget odio. 62 | 63 | Aenean posuere sagittis risus, eget efficitur felis semper eget. Curabitur ornare ipsum a ex luctus interdum. Donec ullamcorper purus ante, commodo tempus mauris sollicitudin sit amet. Mauris imperdiet lorem ac magna elementum, non elementum ipsum sollicitudin. Ut aliquet lectus eu volutpat mollis. Pellentesque metus nisi, feugiat eget tristique ut, dictum vitae tellus. Suspendisse sapien metus, condimentum in mauris vitae, tincidunt volutpat diam. Donec nec porttitor urna, a fermentum quam. 64 | 65 | Maecenas tincidunt sem consectetur, sollicitudin libero ut, dapibus nisi. Sed eget ullamcorper enim, eu ullamcorper purus. Nam vitae tellus ac ipsum pharetra cursus vitae eu augue. Morbi id turpis leo. In hac habitasse platea dictumst. Praesent eros leo, tempor sit amet euismod et, pellentesque vel libero. Aliquam mattis vulputate nisl, vitae tincidunt eros vulputate id. Vivamus in aliquet enim. Sed eu venenatis purus, a finibus ante. Cras ut quam sed nisl tincidunt hendrerit ac tempor elit. Quisque tortor nibh, posuere sodales vestibulum vitae, porta eu turpis. Morbi laoreet mi et elit tristique tempor. Donec nibh sapien, ornare vel porta vel, efficitur nec velit. Pellentesque aliquet ipsum eget nibh euismod ornare. Sed vulputate nisi sit amet odio tincidunt fermentum. Phasellus efficitur tortor eu magna lobortis faucibus. 66 | 67 | Curabitur sed dignissim lacus. Quisque non ante ut est ultricies tristique quis eu ligula. Suspendisse at eros turpis. Proin tincidunt turpis nunc, at maximus nisi laoreet id. Maecenas fringilla tempor finibus. In hac habitasse platea dictumst. Etiam vestibulum faucibus mi, non cursus justo varius et. Vestibulum turpis nisl, accumsan et aliquam vel, congue a tellus. Proin vitae dictum enim, sit amet convallis quam. Nunc et elit et quam efficitur ultricies. 68 | 69 | Integer eu tortor risus. Maecenas feugiat fringilla nibh facilisis facilisis. Vivamus congue sem eu lorem ullamcorper convallis. Morbi a lobortis tortor. Donec nec aliquet turpis, congue dapibus libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce malesuada magna sed enim tincidunt pretium. 70 | 71 | Phasellus id mattis massa. Duis sit amet nunc nunc. Donec egestas elementum dignissim. Proin blandit maximus tortor ut lacinia. In mattis rutrum erat id efficitur. Aenean aliquet vestibulum ligula quis consectetur. Sed nunc arcu, feugiat bibendum iaculis id, vulputate efficitur lacus. Sed feugiat elit imperdiet, interdum urna non, interdum turpis. 72 | 73 | Vestibulum sodales erat nec mi faucibus imperdiet. In pharetra purus id vulputate euismod. Morbi imperdiet sit amet dolor ac vehicula. In lectus neque, convallis sed vestibulum a, tincidunt quis sapien. Etiam volutpat magna in diam lobortis ultricies. Aliquam erat volutpat. Aenean sed mi id ante maximus ullamcorper. In pulvinar sit amet lacus et consequat. Morbi fermentum vitae tortor eu congue. Suspendisse vitae eleifend elit, a hendrerit ex. Maecenas et bibendum neque. 74 | 75 | Nunc porttitor ultricies nisl, vitae eleifend libero commodo ut. Nulla et dapibus turpis. Etiam bibendum semper risus vitae scelerisque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nunc id ligula blandit, elementum ex nec, facilisis lorem. Donec lobortis pellentesque velit vitae scelerisque. Aenean dictum bibendum faucibus. Aenean lacinia, arcu in tristique feugiat, quam nibh porttitor massa, ut accumsan velit ligula in nisi. Nullam a odio sit amet mauris eleifend laoreet. Nam ut erat est. 76 | 77 | Fusce tincidunt sodales nunc, sit amet mattis metus sagittis vel. Nam molestie, felis vitae convallis maximus, urna odio tristique libero, sit amet dignissim felis velit in enim. Phasellus mattis diam ut est molestie pretium at at tortor. Nam est tortor, consequat ac ex et, euismod euismod dui. Aenean eget efficitur odio. Curabitur eu rutrum lacus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam pulvinar ligula a feugiat euismod. Duis ac eleifend eros, non aliquam urna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec posuere, leo id varius semper, metus sapien gravida risus, eget sodales massa metus quis nisl. Donec leo nisl, interdum nec odio eu, feugiat maximus enim. Maecenas tincidunt venenatis fringilla. Donec eget ultricies mi. 78 | 79 | Maecenas maximus consequat consequat. Aenean consectetur, tortor gravida commodo elementum, nunc ex lacinia justo, vitae ornare tortor nisl eu tellus. Curabitur in ultricies lacus, eget iaculis odio. Sed in posuere arcu. In tempus sodales metus vitae posuere. Aliquam luctus sit amet velit nec posuere. Pellentesque non nibh id leo laoreet imperdiet. Aliquam ut nunc dolor. Proin id ante at mauris rutrum lobortis. 80 | 81 | Fusce lectus lacus, elementum eget sollicitudin nec, sagittis in massa. Donec quis congue mi. Praesent iaculis leo vitae aliquam elementum. Nam risus tellus, mattis sit amet dolor maximus, suscipit commodo sem. Suspendisse sit amet nulla cursus massa lacinia imperdiet. Suspendisse ullamcorper lectus in lorem hendrerit sodales. Mauris sollicitudin, dolor non placerat vestibulum, magna libero aliquet massa, ut vulputate nunc magna at dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis rhoncus rhoncus turpis ut condimentum. In maximus libero vel dictum iaculis. Nulla efficitur et mauris sit amet euismod. Cras feugiat lacus non nisi fringilla, ac elementum quam fringilla. Praesent eget augue erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur sem velit, porta ut ligula vel, maximus convallis nulla. Ut quis nulla sed diam varius mollis quis malesuada mauris. 82 | 83 | Sed sodales purus nisi, vitae varius sem accumsan quis. Nunc rhoncus nisl eu nibh elementum, sed efficitur lorem pretium. Aenean vel mi sem. Aenean at felis ut sem laoreet tempus. Etiam ullamcorper, sapien in aliquet sollicitudin, nisl tellus faucibus eros, eu tempor lectus turpis vitae nulla. Duis dictum leo eros, eget laoreet arcu rhoncus nec. Ut sed diam congue, bibendum purus id, varius eros. Suspendisse finibus ut velit at laoreet. Aliquam viverra, metus ut commodo tristique, leo quam porta ligula, eget tincidunt massa nibh eu velit. Etiam convallis sem vel tellus scelerisque porttitor. In nec ornare quam. 84 | 85 | Nam venenatis tellus et convallis pellentesque. Vivamus pharetra eleifend nulla, non tristique sem. Donec bibendum blandit lorem sit amet congue. Nulla ultrices pellentesque semper. Nunc tincidunt tortor sem, id tempor nibh posuere finibus. Proin placerat ipsum in euismod vehicula. Nullam lacinia venenatis sapien, ac aliquet sapien imperdiet at. Nam feugiat at odio eu dapibus. Quisque aliquam eu sem molestie tincidunt. 86 | 87 | Nunc fermentum felis venenatis odio sollicitudin, a hendrerit urna sollicitudin. Pellentesque sollicitudin sem ac nisl tincidunt lacinia. Proin tincidunt dignissim sapien nec posuere. Praesent elementum auctor ipsum vitae molestie. Mauris enim ligula, venenatis quis sodales ut, tincidunt ut quam. In diam mauris, facilisis in volutpat id, euismod quis elit. Donec posuere vitae tellus vitae sodales. Suspendisse erat risus, semper et metus vel, auctor eleifend nulla. Aliquam tempor mi a turpis rutrum rhoncus eu in dui. Sed rutrum sem a varius ornare. Duis ut tellus nec nunc mollis accumsan. Vestibulum consequat elit vel quam interdum, at volutpat sapien mollis. Pellentesque viverra commodo suscipit. Pellentesque porttitor efficitur felis eu imperdiet. Nulla ac lectus urna. Sed et bibendum lectus. 88 | 89 | Vivamus egestas dui quis risus varius, sed iaculis justo pulvinar. Quisque eu leo aliquet, luctus turpis eget, aliquam lorem. Pellentesque placerat aliquam risus, sit amet condimentum augue fringilla quis. Fusce orci magna, aliquet id leo non, feugiat porttitor metus. Donec fringilla, nunc aliquet tempus egestas, nisi nunc sollicitudin enim, quis lacinia nisi sem at ex. Suspendisse aliquet posuere nisi quis tincidunt. Nullam elementum nisi at pharetra posuere. In pharetra rutrum euismod. Aenean gravida sodales elementum. 90 | 91 | Mauris sit amet nunc in nisi cursus lacinia quis varius tellus. Proin porttitor non quam non lacinia. Curabitur consectetur ullamcorper dignissim. Maecenas nec velit finibus, venenatis velit a, suscipit quam. Donec id fermentum ligula, nec eleifend diam. Ut sit amet mi libero. Fusce elementum, purus aliquam maximus ornare, nunc mi placerat arcu, ac mattis ante ex et nulla. Suspendisse potenti. Aenean rutrum, purus vitae consequat aliquet, nunc nibh aliquet nisl, et tincidunt magna lorem vel neque. Proin semper nisi nec nisl tempus, sit amet mollis lacus lacinia. Curabitur tellus lacus, interdum vitae iaculis sit amet, convallis quis tellus. Vestibulum euismod nunc elit, sed lacinia ex placerat non. 92 | 93 | Etiam et risus luctus, rhoncus nisi ac, mattis justo. Morbi vel nulla tincidunt, egestas leo in, gravida odio. Quisque fermentum neque eu pharetra pharetra. Aenean mattis, dui eget venenatis iaculis, augue nunc blandit est, fermentum viverra urna est eget purus. Suspendisse vitae dolor libero. In commodo sapien quis lorem mattis, non faucibus lacus condimentum. Integer quis risus dignissim, volutpat nunc quis, lacinia libero. Aliquam quis turpis ipsum. 94 | 95 | Quisque ac diam dictum, consectetur quam et, pretium neque. Ut dictum nulla tellus, vitae sodales augue convallis at. Curabitur tempor nisl in blandit pharetra. Donec pretium nisi in ante pellentesque finibus. Praesent posuere elit enim, ac laoreet augue finibus eget. Mauris porttitor finibus ipsum, sed vestibulum dolor fringilla sed. Quisque consectetur facilisis ante, ut ultricies ante sagittis sit amet. Quisque fermentum porttitor nibh, vel facilisis libero tincidunt nec. Vestibulum lorem neque, tincidunt at ligula quis, fringilla interdum nunc. Curabitur bibendum turpis posuere pretium consequat. Etiam vulputate consequat lacinia. 96 | 97 | Ut eu sollicitudin ex. Morbi venenatis felis sit amet ipsum dictum imperdiet. Nulla suscipit facilisis nulla, et mollis sapien elementum sit amet. Vivamus eu vulputate urna. Nam fringilla turpis in enim fringilla suscipit. Morbi consectetur elementum vulputate. Sed tincidunt elit sit amet lectus ornare tincidunt. Donec et urna ultrices, accumsan mi eget, volutpat turpis. Etiam euismod viverra varius. Nunc quis tempor enim. Duis at ullamcorper massa. Nam non rutrum enim, vel blandit nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer porttitor euismod dolor, vel congue nunc auctor tempor. 98 | 99 | Quisque posuere diam sapien, eu maximus nisl pharetra id. In tristique neque vitae massa aliquet laoreet. Praesent sed justo malesuada, rutrum risus vel, dignissim massa. Pellentesque egestas tempor nibh. Suspendisse purus libero, posuere eu mattis sodales, imperdiet sed est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam dictum ex lacinia purus accumsan, eu blandit felis iaculis. Praesent a iaculis eros. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam pharetra nunc metus, eu vulputate magna tempus eget. Proin condimentum accumsan ante sit amet consequat. Sed efficitur neque sed ex pellentesque, ac dictum velit laoreet. 100 | 101 | Sed rutrum lectus ac ante tempus vulputate. Praesent viverra lobortis turpis. Aliquam gravida hendrerit sapien, ac eleifend nulla consequat ut. Fusce sodales, risus ac cursus dignissim, dolor ipsum scelerisque tellus, eu faucibus est nisi ac neque. Nulla auctor velit vel vulputate viverra. Maecenas ac sapien eros. Vivamus molestie auctor tincidunt. Pellentesque et laoreet ligula, non eleifend ante. Praesent scelerisque in orci vel rutrum. Nam vitae dui nec tellus pellentesque dapibus. Proin non auctor risus. Proin auctor, orci eget congue semper, lorem mi accumsan ex, nec accumsan diam est eget felis. Aenean id sodales est, non dignissim ipsum. Proin sit amet aliquet tortor. Sed aliquet, elit nec suscipit sollicitudin, enim felis luctus turpis, eu dictum quam augue eget eros. 102 | 103 | Nunc sem nunc, efficitur sit amet tellus non, cursus porttitor orci. Nunc at nibh purus. Vivamus nec quam maximus, pretium erat sed, sollicitudin ligula. Pellentesque ut ipsum posuere, mollis nisl a, ornare leo. Integer vehicula massa nec erat laoreet, non facilisis ipsum posuere. Duis ac turpis vel sem tempus commodo in eu nisi. Integer a ex risus. 104 | 105 | Donec aliquet blandit tellus non elementum. Pellentesque ultrices finibus consequat. Sed orci dui, vehicula vitae ultricies in, laoreet vitae nisl. Donec nulla dolor, imperdiet vitae rutrum at, ultricies id magna. Sed non venenatis orci. In vulputate justo justo. Etiam ligula odio, iaculis et consequat id, egestas et erat. 106 | 107 | Nulla pharetra lectus eget mauris fermentum elementum. Maecenas pulvinar maximus metus, sit amet facilisis orci iaculis id. Etiam commodo orci non lectus congue, sed finibus arcu luctus. Donec risus odio, porta nec risus vitae, semper ultrices dui. Curabitur rhoncus placerat magna, id luctus arcu malesuada nec. In vulputate dui id lacus molestie, et fringilla justo euismod. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin laoreet eleifend ipsum, vel cursus diam. 108 | 109 | Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus lacinia, diam porta pellentesque hendrerit, dolor ante sodales nulla, in gravida magna mi at sapien. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed et efficitur nulla. Vestibulum aliquam iaculis nisi nec accumsan. Proin eu leo tellus. Donec pharetra sodales odio. Suspendisse quam nulla, varius sit amet turpis vitae, finibus imperdiet magna. Aliquam mollis commodo sem a vehicula. Nunc nibh justo, convallis at tempus nec, sodales at ante. Cras ut ante dui. Proin et dui vitae libero ultricies pharetra a ut elit. Nullam bibendum, erat nec dignissim iaculis, sapien purus mattis est, a lacinia nisi metus et dolor. Donec a blandit nulla. Nunc sagittis luctus interdum. Morbi vitae dui pulvinar, pharetra urna ut, hendrerit augue. 110 | 111 | Curabitur elementum erat diam, in maximus leo fringilla a. Donec venenatis arcu in libero finibus, ac ultricies libero molestie. Praesent commodo lorem at tincidunt pharetra. Sed volutpat quis est quis mattis. Praesent vitae ante dapibus, dignissim tellus eget, feugiat dui. Mauris imperdiet, augue quis imperdiet volutpat, diam elit luctus magna, sed maximus ligula velit eget magna. Phasellus eget vulputate quam. Cras dictum purus lectus, nec imperdiet quam consequat at. In hac habitasse platea dictumst. Nulla porta convallis sapien quis convallis. Proin scelerisque non arcu a sagittis. Sed turpis odio, commodo id nulla ut, rhoncus fringilla nisi. Sed lacinia elit non condimentum vulputate. Pellentesque justo turpis, eleifend ut erat ornare, tempor ornare magna. Sed blandit est nibh, eget dapibus libero semper nec. Aliquam eget purus tempus, finibus magna accumsan, ultricies ex. 112 | 113 | Suspendisse id turpis a mauris suscipit tempor. Proin id eleifend leo. Nam quis scelerisque massa. Sed volutpat mauris eu sapien facilisis vestibulum. Ut lorem lorem, feugiat quis justo non, pharetra mattis lectus. Curabitur in orci ultrices, ultricies dui in, mollis urna. Mauris ut massa enim. Vestibulum in posuere nibh. Quisque sit amet viverra ex, et iaculis urna. Nam egestas ornare purus, efficitur lacinia turpis rhoncus vel. Donec imperdiet venenatis quam, et vehicula tortor suscipit eleifend. Aliquam pharetra neque ut quam consectetur, quis pretium libero blandit. Sed sollicitudin est sem, in ultricies massa eleifend sit amet. Etiam euismod turpis a venenatis tristique. 114 | 115 | Praesent mollis justo nibh, malesuada egestas sapien convallis id. In egestas lacinia arcu tincidunt fermentum. Maecenas sed eros tortor. Mauris ut risus metus. Vestibulum dignissim pulvinar mollis. In accumsan risus vel orci dictum porta. Fusce efficitur egestas turpis id ultrices. Sed eleifend lobortis risus in lobortis. Donec id massa in velit posuere semper eu gravida risus. 116 | 117 | Nunc a turpis dapibus, lacinia felis id, efficitur lacus. Vivamus odio turpis, lacinia vel pharetra a, congue pulvinar orci. Phasellus rutrum turpis nec lorem consequat posuere. Integer ullamcorper cursus tellus non varius. Mauris malesuada massa urna, volutpat egestas erat lacinia sed. Praesent sodales turpis vitae elit tempor mattis. In iaculis pretium magna in dictum. Maecenas gravida ligula nec libero auctor, nec malesuada orci tempor. Maecenas a nisl id lorem semper blandit ac non nibh. Quisque in consectetur nisi. Sed in rutrum diam. Aliquam quis sodales massa, eu volutpat est. Vestibulum accumsan convallis viverra. Suspendisse id bibendum tellus. Pellentesque ut molestie elit. 118 | 119 | Vivamus in ipsum eget risus dapibus varius. Etiam est dui, eleifend vel commodo id, scelerisque eu enim. Nullam vestibulum non neque id laoreet. Vivamus ac venenatis dolor. Donec blandit bibendum metus. Phasellus cursus velit purus, sed mollis lectus hendrerit non. Suspendisse vel gravida lacus. Curabitur elementum odio neque, non volutpat mauris rutrum sed. Donec dignissim, metus et hendrerit convallis, enim massa gravida lacus, eu euismod est mauris eget mauris. Integer scelerisque ex in vehicula volutpat. Aenean dui lorem, suscipit sit amet vehicula id, lobortis eget nulla. Proin nec sollicitudin augue, sed porta diam. 120 | 121 | Vivamus accumsan quam ut risus dictum venenatis. Donec placerat felis id libero porta imperdiet. Ut magna neque, viverra sed nulla ac, dapibus scelerisque purus. Duis rutrum eros nunc, eu consequat lacus auctor a. Donec sed tincidunt mauris. Quisque vitae volutpat magna. Aliquam id iaculis tellus, quis pretium mauris. Maecenas id orci quis eros efficitur lobortis id id ipsum. Phasellus tempor rhoncus felis, a tincidunt tortor faucibus nec. Proin lorem ipsum, ornare sed arcu non, hendrerit commodo nibh. Integer ac urna elit. Nullam sed nisi orci. 122 | 123 | Nunc sed sem aliquet, ultricies erat sed, volutpat nunc. Donec cursus vestibulum volutpat. Nam eget suscipit purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer hendrerit, ex et fringilla porttitor, ipsum justo efficitur ante, eget gravida lacus diam faucibus erat. Duis elementum nunc et ligula iaculis aliquam. Nam sit amet lorem varius libero condimentum ornare. Nam non venenatis dui. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus ultricies bibendum dapibus. Curabitur id varius ipsum. Nunc sed purus nunc. 124 | 125 | Morbi malesuada libero in condimentum maximus. Aenean sed porttitor felis. Maecenas porta, nibh mollis semper ultricies, metus lacus volutpat arcu, consequat gravida nisi lacus sed quam. Mauris blandit volutpat posuere. Pellentesque euismod elit at nunc facilisis, nec euismod orci maximus. Aenean semper finibus pellentesque. Nunc id risus ultricies neque dapibus feugiat. 126 | 127 | Quisque sed tincidunt libero, vel sodales sapien. Donec commodo mi nec tincidunt ullamcorper. Proin malesuada massa quis rhoncus dapibus. Vivamus non ligula nisl. Duis elementum porttitor lacus ac gravida. Donec id pharetra nisi, ac pretium nibh. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis iaculis eget mauris vitae tristique. Donec molestie ac sem non sodales. Vestibulum est tortor, aliquet accumsan elit nec, euismod feugiat metus. Suspendisse metus nunc, molestie vitae lorem elementum, hendrerit rutrum nisl. 128 | 129 | Maecenas urna ligula, mattis vel metus sit amet, molestie ullamcorper eros. Ut sed erat eu arcu congue dictum. Fusce interdum elit purus, et volutpat orci luctus non. Nunc et ante quis nunc viverra malesuada et id dolor. Morbi aliquam purus tellus, vel placerat diam efficitur ac. Quisque sed lectus feugiat, ullamcorper nulla ac, porta neque. In scelerisque volutpat maximus. Praesent velit risus, dignissim in fermentum a, rutrum a massa. Sed sollicitudin in velit sollicitudin tristique. Cras efficitur justo arcu, ac rhoncus justo gravida sed. Sed nisl sapien, mattis in tincidunt eu, laoreet nec augue. Nulla facilisi. Phasellus aliquet nulla lacus, ut viverra velit bibendum quis. 130 | 131 | Aliquam finibus gravida metus in vestibulum. Phasellus tincidunt sapien dapibus eros congue commodo. Aenean mollis varius tellus, non tempus tellus ullamcorper a. Proin mauris mauris, blandit vitae justo sit amet, ullamcorper dignissim velit. In et mattis lorem. Cras a commodo odio, vel molestie arcu. In hac habitasse platea dictumst. Cras aliquet, tortor vel auctor egestas, mauris turpis consequat lectus, nec consequat augue justo at nisl. Donec nec mi luctus enim aliquet iaculis sed sit amet metus. Mauris blandit dolor metus, sed fringilla odio tristique sit amet. In urna lorem, sagittis quis eleifend feugiat, interdum id libero. Vivamus tincidunt scelerisque nunc non congue. Phasellus quis efficitur eros. Fusce consectetur felis id nisi tincidunt, eu ultrices sapien rhoncus. 132 | 133 | Mauris placerat eros sit amet nisi egestas, at congue augue porta. Maecenas commodo purus ut tortor congue ultricies. Morbi nulla metus, ultricies ut metus sodales, dapibus porttitor lectus. Cras iaculis, magna id efficitur accumsan, nisl eros fringilla tortor, et imperdiet nulla leo nec justo. Aliquam erat volutpat. In id leo leo. Donec varius, libero vel maximus rhoncus, magna odio vulputate quam, ac dapibus metus tortor eu massa. Nulla non tristique diam. Quisque at tincidunt eros. Maecenas at vehicula lorem. Nulla erat metus, porttitor ut urna sit amet, eleifend condimentum urna. 134 | 135 | Donec at ligula hendrerit, imperdiet magna quis, gravida tellus. Ut non dignissim justo, at tincidunt lacus. Cras mollis augue vel augue tristique, sit amet luctus quam sodales. Donec mattis malesuada est, non maximus lorem egestas quis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque a est placerat, iaculis diam sed, porta ante. Ut imperdiet vulputate ex, vel commodo elit consequat sit amet. 136 | 137 | Phasellus risus augue, tincidunt vitae nunc ut, egestas fermentum enim. Phasellus sit amet pharetra libero. Integer eu neque mattis, blandit tortor sit amet, bibendum sem. Aliquam sit amet lorem sit amet dolor ultrices ornare sit amet vitae ipsum. Quisque vestibulum tellus eu diam vulputate pellentesque. Proin consectetur, risus eu placerat volutpat, ex lectus rhoncus ipsum, id laoreet risus lectus id arcu. Quisque urna lorem, venenatis at placerat a, ultricies ac dui. Vivamus consequat, velit at lobortis mollis, augue diam condimentum tortor, quis hendrerit diam tellus ut lacus. Nullam nibh orci, euismod eu nibh ac, aliquet scelerisque est. Pellentesque luctus venenatis nibh. Phasellus quis felis lobortis, pretium erat quis, pharetra diam. 138 | 139 | Mauris lobortis fringilla lacus, laoreet varius velit porttitor id. Nam faucibus, velit nec tincidunt placerat, purus nibh mollis neque, et placerat massa ligula et nibh. Aenean justo lorem, venenatis ac massa id, tempor dictum libero. Phasellus aliquam, est in iaculis euismod, dui lectus ultrices lacus, nec feugiat mi nisl nec orci. Mauris pellentesque lacus sit amet leo pulvinar, nec fermentum nisl faucibus. Pellentesque ullamcorper mattis enim. Sed egestas sapien non eros pretium, vitae imperdiet mi eleifend. Fusce sit amet neque aliquam tortor imperdiet eleifend id non quam. Nam egestas ipsum erat, quis fringilla nunc fringilla at. Nunc vehicula rhoncus condimentum. 140 | 141 | Cras ipsum quam, facilisis et rhoncus sit amet, rutrum eget ex. Nam lectus ante, mollis vel interdum at, tristique non ligula. Aenean volutpat ligula lacus, a sagittis neque consectetur in. Etiam non placerat metus, quis sagittis mi. Fusce enim felis, ornare ac auctor ac, condimentum a nisl. In in arcu vitae lectus ultrices euismod. Quisque convallis congue diam volutpat mollis. Quisque et lacinia risus, non tincidunt leo. Maecenas magna velit, auctor vitae aliquet sit amet, lobortis a lectus. Pellentesque tincidunt quis odio vel porttitor. Nullam posuere metus quam, eu lacinia risus porttitor id. Etiam vehicula malesuada eros, a mattis magna posuere in. In sagittis pulvinar nulla, et placerat nisi mollis non. Duis et venenatis metus. Curabitur tempus, magna non dignissim vulputate, tortor lectus congue nisi, vitae commodo dolor purus sed felis. 142 | 143 | Praesent fringilla non ex eu mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis a erat ac lectus pretium fermentum. Quisque ipsum risus, egestas sit amet fringilla vel, hendrerit nec nibh. Integer blandit scelerisque ligula. Etiam ullamcorper, massa quis tristique fermentum, ex massa imperdiet nisl, sit amet rhoncus mi massa sit amet ipsum. Ut fringilla quis mi varius elementum. Aenean ut rhoncus nunc, ac mattis massa. Vivamus auctor turpis eget enim faucibus, et pharetra dolor tempor. Proin sed pulvinar quam. 144 | 145 | Aenean ac mattis risus. Vestibulum pharetra hendrerit nisl et pharetra. Aliquam erat volutpat. Integer rutrum ipsum et massa bibendum congue. Ut facilisis nisl posuere, porttitor purus ut, ullamcorper magna. Fusce enim odio, interdum a eleifend nec, efficitur et eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam vestibulum consectetur posuere. Fusce finibus sem eu tristique laoreet. Aenean feugiat justo in sapien accumsan, vel tristique mi mollis. Praesent tincidunt eros auctor arcu rhoncus, bibendum tincidunt risus lacinia. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. 146 | 147 | Morbi sit amet ipsum in ipsum euismod efficitur quis efficitur velit. In hac habitasse platea dictumst. Etiam finibus egestas urna vel tempus. Quisque fermentum porta libero sit amet consectetur. Phasellus ut dignissim felis, in venenatis odio. Integer auctor, velit eu volutpat hendrerit, ipsum risus dignissim nunc, in imperdiet turpis orci rutrum mauris. Suspendisse semper nunc ac tincidunt lacinia. Nulla facilisi. Aliquam posuere fringilla dolor id fringilla. Quisque volutpat, odio vel facilisis cursus, tortor risus pulvinar nisi, id suscipit elit neque sed quam. Donec sodales nisi dui, vel pulvinar nulla ornare ut. Pellentesque sit amet urna purus. Mauris at neque non eros ultrices euismod vitae vehicula sapien. Nulla faucibus enim vel congue placerat. Integer fermentum dictum massa, eget venenatis velit porta id. 148 | 149 | Vivamus sagittis, nisi consequat condimentum sodales, dui neque iaculis justo, sollicitudin fermentum nulla metus vel risus. Integer hendrerit eleifend lorem, vitae ultricies arcu. Aliquam nulla elit, tincidunt et tempus a, blandit sit amet felis. Donec at maximus felis, at cursus diam. Etiam facilisis, magna at consequat tincidunt, diam sem lacinia enim, sed accumsan mi enim sit amet risus. Donec viverra volutpat dolor sed posuere. Aliquam vitae risus at leo facilisis faucibus. In sed rhoncus purus. Vestibulum volutpat, turpis placerat fringilla placerat, nulla metus imperdiet nisl, non euismod velit leo vel justo. Ut elit velit, congue et arcu et, maximus sodales lectus. Pellentesque sapien eros, tempus sed turpis sed, congue iaculis nisi. Sed feugiat, justo id condimentum aliquam, erat dui dignissim nisi, at finibus odio lectus at mi. 150 | 151 | Nunc gravida dictum mollis. Nunc bibendum tellus augue, at pulvinar risus iaculis eget. Vivamus ut condimentum erat, vel rhoncus risus. Nulla massa nibh, interdum non sagittis ut, ultricies ut nisi. Suspendisse tincidunt imperdiet sem in semper. Donec mattis quam in sollicitudin suscipit. Sed aliquam ex mi, ultrices suscipit lectus mollis non. 152 | 153 | Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque id imperdiet magna, accumsan luctus enim. Etiam quis convallis velit. Donec vitae vulputate nibh. Vivamus a mauris mattis, rutrum turpis id, sollicitudin magna. Etiam posuere, lectus ornare posuere dignissim, diam nunc porta est, nec laoreet libero nisi at ipsum. Vivamus vehicula eleifend nibh, a tincidunt ipsum sodales nec. Cras dapibus eros quis eros pharetra, sed vehicula est pharetra. Donec neque lorem, pretium vel pellentesque at, rhoncus ullamcorper felis. Pellentesque eu est nec orci gravida posuere a eu tellus. Donec lobortis est sit amet purus faucibus posuere. Morbi vitae quam imperdiet nibh ornare gravida vitae sit amet enim. In eu lectus imperdiet, vehicula mauris at, fringilla ante. Etiam vulputate ullamcorper velit eget vestibulum. 154 | 155 | Phasellus pretium gravida ligula, non convallis neque iaculis eu. Quisque iaculis nibh sed justo malesuada vulputate. Etiam dignissim nec sem ac venenatis. Nunc quis pharetra dui. Donec mollis justo ut lorem blandit, vel lobortis risus porttitor. Phasellus eu ultrices dui, eu pharetra nisl. Maecenas scelerisque nibh tortor, ut viverra risus dapibus id. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse potenti. Aenean sollicitudin lectus est, vel semper libero commodo id. Suspendisse sit amet ex accumsan, posuere quam aliquet, viverra tellus. Quisque vestibulum diam et magna ullamcorper, nec posuere mauris rutrum. Ut varius blandit quam sit amet finibus. Aenean et velit eget orci laoreet porta quis at nulla. Ut nec ex fermentum, lacinia ipsum eget, sollicitudin dui. 156 | 157 | Suspendisse vestibulum gravida dignissim. Aliquam at lectus vitae massa ultrices mattis ac eu sem. Cras efficitur cursus ligula, sit amet bibendum felis tempus a. Fusce aliquam vulputate iaculis. Aliquam erat volutpat. Sed eros risus, ornare eu ipsum nec, auctor sagittis nisi. Phasellus vitae feugiat nunc, vitae fermentum lorem. Vivamus semper tempor massa. Sed convallis eu risus vel iaculis. Nunc maximus vitae justo vel semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. 158 | 159 | Nullam imperdiet ex elit. Nunc sodales id magna non maximus. Sed ultrices arcu tellus, sit amet faucibus est efficitur tristique. Aliquam mollis fringilla mi, nec hendrerit ante vehicula ac. Suspendisse est lorem, finibus quis lorem ut, tincidunt consectetur ipsum. Curabitur et nulla in justo imperdiet suscipit et id metus. Fusce laoreet sagittis aliquet. Duis eleifend orci tellus, et elementum felis mollis a. Sed ut justo augue. Vivamus nec enim at mauris vulputate posuere ut sit amet orci. Suspendisse id eros odio. Vivamus eget enim a lorem elementum cursus auctor quis augue. 160 | 161 | Nulla ut blandit arcu, ac gravida neque. Aenean lorem augue, faucibus non auctor id, fringilla sit amet neque. Pellentesque pulvinar odio quis mauris aliquam malesuada. Proin ante justo, cursus in erat eu, faucibus sagittis lacus. Vivamus a hendrerit ligula, non mattis elit. Sed at dignissim turpis. Quisque risus lectus, sagittis in augue non, sagittis facilisis urna. Nunc congue mi et urna blandit auctor. Suspendisse potenti. Maecenas sed pretium magna, vel tincidunt enim. In pretium diam vitae lacinia porttitor. Morbi vel quam ullamcorper, venenatis metus ut, efficitur odio. 162 | 163 | Nullam luctus vehicula nulla, pellentesque congue orci. Maecenas sagittis, lorem vel blandit varius, felis nulla scelerisque lectus, non varius lacus magna eu purus. Vestibulum at tortor ac risus blandit sagittis at vel nisi. Suspendisse augue felis, mollis et massa at, facilisis varius ex. Maecenas mattis elit massa, quis sodales est porttitor a. Morbi non cursus arcu. Ut dignissim lacinia ante. In hac habitasse platea dictumst. Maecenas non sagittis justo. Aliquam efficitur, purus sed pharetra tristique, sem felis aliquam libero, id porta eros arcu et ex. Etiam sapien tortor, finibus sed elementum quis, pulvinar eu tortor. Sed ornare felis non rutrum porttitor. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur a neque ac nisi molestie scelerisque quis blandit libero. 164 | 165 | Phasellus hendrerit nunc ac laoreet euismod. Etiam malesuada, diam a luctus rutrum, ipsum nulla efficitur sapien, vel interdum lacus tortor eget elit. Fusce at tellus feugiat, accumsan ligula vitae, porta lacus. Praesent vulputate vel odio at mollis. Nullam varius, ex vel consectetur condimentum, purus dui dapibus nisi, eget eleifend orci tortor non purus. Phasellus convallis mauris massa, et convallis risus volutpat in. Cras ut arcu sed est semper posuere. Morbi magna mauris, molestie non velit in, pulvinar hendrerit nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. 166 | 167 | Sed laoreet elit id neque tincidunt cursus. Nullam sit amet velit nec ante tristique imperdiet in a eros. Ut id enim eget felis vulputate gravida et in diam. Nunc tristique vulputate mollis. Sed enim risus, aliquet non elementum at, maximus ut tellus. Integer sed egestas nibh, ac euismod sem. In hac habitasse platea dictumst. Integer vitae nunc pellentesque, pellentesque felis at, placerat diam. Nulla placerat tincidunt hendrerit. Nunc imperdiet purus non erat tincidunt, quis sollicitudin leo venenatis. Pellentesque ac tincidunt risus. 168 | 169 | Nullam aliquam, dui id placerat posuere, libero ex iaculis leo, vitae mattis arcu felis sit amet magna. Vestibulum vitae sapien non tellus fringilla pulvinar ut at eros. Phasellus et massa vitae velit vulputate feugiat vitae ac leo. Aenean cursus ut sapien at tempus. Curabitur vitae nulla a elit porttitor dapibus. Donec non velit vel ligula aliquam tincidunt eget ac ex. Nam odio diam, pharetra eu libero id, aliquam faucibus nunc. Maecenas molestie est in justo aliquet, at ullamcorper diam lacinia. Nullam eget nulla nec odio suscipit varius in ut turpis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce in luctus lorem. Maecenas vel ornare dui. Pellentesque eu ex ut ante placerat semper pharetra sit amet elit. 170 | 171 | Phasellus semper vulputate nibh, vitae malesuada mi eleifend sed. Sed ut iaculis urna. Morbi aliquam viverra ultricies. Nulla convallis elit sem, condimentum eleifend tortor facilisis a. Phasellus auctor felis sem, at eleifend sem pharetra sit amet. Ut pulvinar ipsum in convallis eleifend. Sed a aliquam sem. Pellentesque malesuada ullamcorper mi laoreet placerat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed vestibulum pellentesque cursus. Quisque pellentesque sem sagittis nisi imperdiet, ac tristique leo luctus. Vestibulum in lectus eget tellus dictum finibus. Phasellus in nibh in ex suscipit volutpat eget vitae quam. Suspendisse eget nisi non urna ullamcorper scelerisque. 172 | 173 | Pellentesque laoreet viverra lorem, nec efficitur tellus mollis ac. Quisque condimentum interdum mi id scelerisque. Aliquam et sem faucibus leo tincidunt hendrerit at vitae nisl. Aenean a nisl at velit sollicitudin pretium. Aliquam gravida euismod magna, a finibus nibh volutpat non. Curabitur quis ex eu tortor tincidunt sollicitudin. Maecenas sagittis risus sit amet nunc interdum dapibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras dapibus commodo porta. Ut posuere ligula sit amet viverra laoreet. Curabitur ac maximus nibh. Maecenas non eros magna. 174 | 175 | In sit amet ligula tempus, fringilla dolor nec, hendrerit urna. Duis urna nulla, varius quis scelerisque et, dictum vel elit. Fusce cursus lorem imperdiet, malesuada mi et, eleifend nunc. Vestibulum sed metus ac lacus tincidunt volutpat ac vel felis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque vehicula dictum massa non porta. Maecenas ultricies venenatis pellentesque. Duis venenatis et tortor eget sodales. Vivamus non sem sed felis vulputate sollicitudin. Pellentesque pharetra bibendum eleifend. 176 | 177 | Nullam et ipsum eget nisl facilisis molestie. Praesent purus urna, bibendum id felis sed, tincidunt vestibulum velit. Donec gravida euismod interdum. Integer nec tincidunt diam. Sed feugiat non nulla nec facilisis. Pellentesque gravida blandit rutrum. Phasellus id pharetra ipsum. Vivamus tellus nulla, tristique at dictum at, efficitur eget lacus. 178 | 179 | Nulla molestie interdum felis et consequat. Aliquam erat volutpat. Aenean pulvinar non nibh nec suscipit. Vivamus porttitor lobortis ullamcorper. Ut eget lacus id nibh tempus ultricies. Nullam tempor nibh sem, efficitur commodo sem sollicitudin a. Etiam pretium sollicitudin vehicula. Suspendisse a turpis sit amet dolor porttitor mattis. Praesent luctus mauris eu lobortis fermentum. Nunc nisi lorem, pulvinar et elit sed, tempus hendrerit nisl. Pellentesque nisi diam, laoreet nec ex nec, viverra finibus purus. Quisque sed lacus ultricies, consectetur ante ac, tempor sapien. Aliquam aliquam faucibus lorem, vitae posuere turpis viverra vel. Praesent egestas rhoncus lacus, et congue odio ullamcorper et. Nam eu sapien laoreet, maximus mi imperdiet, scelerisque purus. Donec faucibus purus eget ex eleifend, porta viverra lectus varius. 180 | 181 | Nam metus justo, tincidunt id ligula vel, varius congue libero. Vivamus aliquet vel augue eu scelerisque. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris a neque sed eros fermentum congue id eget lorem. Proin sit amet mi ante. Phasellus faucibus ultricies tempor. Fusce bibendum tortor et varius dictum. Integer non risus in leo semper interdum. Proin lobortis elit ac quam porttitor ornare. Aenean eget tortor et ipsum semper molestie. Nunc condimentum semper enim, vitae sollicitudin arcu condimentum sed. Vestibulum quis sapien ullamcorper metus ultricies aliquam et ac mauris. 182 | 183 | Donec vestibulum mattis purus a dignissim. Sed id ligula neque. Phasellus fringilla feugiat nisl sed porta. Praesent sed pulvinar ante. Integer mattis nibh dictum odio interdum sollicitudin. Morbi et ullamcorper eros, sit amet vehicula est. Aenean dapibus turpis quis mi bibendum posuere. Nullam id ligula non massa vestibulum finibus. Praesent aliquam vestibulum nunc tincidunt tincidunt. Vestibulum a mauris a est faucibus cursus. Aliquam erat volutpat. Fusce ut risus nec lectus porttitor varius vel non metus. Aliquam congue lacinia leo, ut posuere lacus porttitor vel. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ut tristique lectus. Mauris lacinia euismod magna, molestie vulputate neque fermentum a. 184 | 185 | Praesent non sapien in odio vulputate vulputate. Sed finibus porta nisi quis consectetur. Aliquam odio magna, scelerisque eu leo sit amet, imperdiet pretium libero. Donec a ante vitae mauris placerat placerat. Duis convallis placerat odio quis dignissim. In tempus sapien orci, eget tincidunt quam congue id. Suspendisse tempus viverra rhoncus. Suspendisse potenti. Nunc justo lorem, imperdiet vel libero non, posuere varius velit. Pellentesque iaculis ut urna sed finibus. Nunc sit amet tempor tellus. 186 | 187 | Duis finibus ornare dui eget sollicitudin. Integer ut facilisis purus. Quisque at purus pharetra, tincidunt ante vel, mollis lorem. Nulla dapibus elit nisi, sit amet vestibulum ligula mollis in. In hac habitasse platea dictumst. Praesent blandit facilisis mi, ac sagittis orci posuere ut. Fusce sed pretium mauris, sed dictum justo. Duis commodo ut augue a molestie. Phasellus at pellentesque purus, sed malesuada odio. 188 | 189 | Sed aliquet varius dolor, non viverra dui malesuada accumsan. Sed vehicula magna ut dignissim pulvinar. Praesent ut justo bibendum, egestas metus a, dignissim turpis. Cras nunc metus, ullamcorper ut lacinia ut, accumsan at nibh. Aenean purus ligula, ultrices sed odio non, efficitur vestibulum sem. Nulla facilisi. Curabitur a elementum ipsum. Cras molestie mauris sed consectetur efficitur. Etiam vestibulum felis eu porta elementum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin eu ligula a nisi venenatis dictum eget vitae eros. Phasellus vitae ultrices odio. Nulla fringilla ligula vitae iaculis fringilla. 190 | 191 | Etiam in efficitur felis. Donec a dapibus lorem, ac molestie metus. Cras aliquam, risus quis finibus mollis, nibh tortor pharetra ante, et condimentum nulla massa id lectus. Pellentesque luctus risus a lobortis placerat. Aenean at ex molestie, commodo elit sit amet, vestibulum leo. Phasellus tincidunt elit tristique ultricies suscipit. Fusce iaculis nisi a massa aliquam consequat. Suspendisse potenti. Suspendisse sed ante pharetra, lobortis risus non, malesuada risus. Ut dui sem, elementum vitae facilisis ac, accumsan nec eros. Etiam imperdiet vulputate lorem ac cursus. Vivamus eu quam maximus nisi tincidunt egestas. 192 | 193 | Aliquam vel sem id nibh cursus ultricies ac vitae tellus. Donec neque ex, finibus id condimentum sed, tempus nec sapien. Duis luctus tortor vel quam rhoncus, sed pulvinar est ultrices. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus finibus libero non dui lacinia, ac pulvinar massa dignissim. Donec sit amet dolor enim. Duis nec turpis leo. Mauris tristique ligula iaculis mauris gravida, at malesuada ante placerat. Integer blandit ex nisl, maximus vestibulum orci cursus vel. Nullam ut orci vitae lacus condimentum condimentum et ac tortor. Donec diam orci, bibendum at sollicitudin ut, egestas at nulla. Etiam libero odio, aliquam sit amet leo sollicitudin, fringilla malesuada dolor. Donec volutpat suscipit elit, in bibendum nulla rhoncus ac. Suspendisse vulputate, lorem vel vulputate tempor, odio dui egestas mi, ac posuere orci turpis sed arcu. Pellentesque rutrum mauris quam, faucibus aliquet mi consequat quis. 194 | 195 | Pellentesque bibendum sed dui id iaculis. Ut eget tristique tellus. Etiam lacinia scelerisque vestibulum. Aliquam erat volutpat. Donec eget scelerisque neque, a sodales velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus at ornare ante. Maecenas nec vulputate ipsum, nec mollis erat. Nam sed lacus vel metus sagittis rutrum. In sodales nisi vitae accumsan finibus. Quisque ut erat commodo, efficitur ex scelerisque, volutpat massa. Vivamus et porta quam. Duis vulputate libero eget risus pulvinar convallis. 196 | 197 | Aenean cursus mattis lorem a pretium. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at scelerisque leo, non rhoncus est. Nam a mauris consectetur, accumsan elit vel, suscipit libero. Donec ut sodales velit. Aliquam a ex auctor, tincidunt purus nec, dapibus massa. Ut tincidunt interdum maximus. Nullam sit amet lacinia massa, bibendum imperdiet eros. Mauris porta rhoncus suscipit. Phasellus volutpat quis urna ut vehicula. In tempor id purus vitae porta. Donec non est interdum, lobortis risus in, lacinia erat. Sed lobortis neque ut tempus convallis. Proin eget neque quis felis fermentum placerat non at mauris. 198 | 199 | Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam a magna mattis, placerat sem id, pulvinar enim. Phasellus maximus nulla eget sagittis sagittis. Quisque tortor tellus, scelerisque ut porta ac, tempor ac dui. Aliquam at elit rutrum, iaculis tellus nec, molestie risus. Morbi semper lacinia erat, sit amet hendrerit sapien vestibulum nec. Fusce vestibulum sed nunc in finibus. 200 | -------------------------------------------------------------------------------- /Tests/Resources/small_1.1.vault: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 39663566303633643332386237316533393763346163363530613661613261303537333466646634 3 | 3233396437626161343066393264316265323065343635340a386465316130643365303365333431 4 | 61666337306435363461306535396164303833366566663036306537313337343262333236653934 5 | 6266373233633661630a333833666536363131653731353532363530313739623838363139613232 6 | 34326330646537323739376636643934653531396139623532366632373231616538 -------------------------------------------------------------------------------- /Tests/Resources/small_1.1.yml: -------------------------------------------------------------------------------- 1 | variable_key: variable 2 | 3 | -------------------------------------------------------------------------------- /Tests/Resources/unicode_1.1.vault: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 32393966306538663736343231363365316565623639346236643131616366653036666265623133 3 | 6634346535623063643166616166363366306139623238630a646230303731353062356263653963 4 | 62616339326232313139386237613530333666393930326564313036643435316662373039333261 5 | 3333663364623665640a346466373136313662346564653631373537373462356434303430343665 6 | 64346235636535616637626364373365373034656433306663353232613638643464363532396234 7 | 66633733336438336664346566666239313934333733316237653130646238666163663861636332 8 | 62363930343562623863663133353035343733346634363765393334646432383136316430656463 9 | 38316332613533393839393062663133333134613232643764386663383334356562303135333433 10 | 62626438383464323833653136666433633665393663386632323038353762633562363036356265 11 | 38376166313932333166613364643265303732663435643436383863343230396638333435636261 12 | 33383630393930393339373264333731646265393031303465353138323630336631653565613431 13 | 33616637633666393737653732313064623936376633306361333131373566383263303230636339 14 | 31333132393738363961663236643035363730373434313236313065393461333539366533656165 15 | 33323334336632653166396565653438623866323434373235363766303133366638616533386435 16 | 64303965663064356633356162366235313638656266623464323961336130303933613561323931 17 | 34343165613365373033366664656665313636373766366633386436663361356162393365333638 18 | 36393362613835396461616364343964366661633634333836656262313462323264663633663865 19 | 63306365626364303339353338333863303563366437303039396230303762393233666131663939 20 | 31626438376265316632613032386132616136613432336237313361663863306339643062346134 21 | 63343663343233323031336130656364366364343939623162396232646363323736313038393033 22 | 64646137623563613365376363373932663164346237393934336432363636353230636130663964 23 | 36643037386637333633653763306563346166393039353039343061396461356235346266323265 24 | 64656566343835386566303161333039366265633534316437663863323362373333313439373864 25 | 35363034623865326565333331336164336463653931376137363333373039323765666166633066 26 | 31363265376234383636323866393138613334386633383635323137323165383939346633313632 27 | 61323231353432643664613361653765306166636238313439663839373862333034626438623962 28 | 64333265383466613430356132633039656539636137336331333735656538306332373835383335 29 | 35373834373632313832636466643630363834323831636261663332353838616435346664363265 30 | 34616362643232323330636539383735636636356331356232646138363765613331356462623465 31 | 32326366636166383331366636383465306261393134663266626664663761656236366564326337 32 | 36393863313031613534373861373936633737316635656163613862333366303562636434316464 33 | 30366363363230323038653964316235356233636132303965643030636533333438636430373364 34 | 34373262363564323230336365633665366432386432633431326533303331636633336365346637 35 | 61383531353434623337643639626231633964636263313861643662313234323439376561666562 36 | 38393937356331653834353365333237663639653564383262393132333932323861623563666130 37 | 64363331353462393635666239356531386361373733313032633637636439323836366133306661 38 | 34306632353832353063313530633266616564396439353839393139353735626562316138386265 39 | 33376666313134353632306331366630373439356532656439336533333565653035366434323432 40 | 37643738383561653534346165616136393135396538656432303638643635346232323939373836 41 | 30633738653961376331376264326632386236633438663566613835373630613462393935623964 42 | 36323734393837366166623839316232626336306433643162393266386134313762313765666438 43 | 35303936323030313764653662353764663432316361376665303539333465623537386339616136 44 | 31666633616231623862626666376630636531636634386435386138643236616638396230353837 45 | 32376133336635386239666461613839306130623738343039356230363631356338636235353037 46 | 38323530633366316565316263663631326136616436336466366262383065326133633536373339 47 | 33396566626539623533656361363164306266356135303634346163333635343462636563616234 48 | 36356664373462326430383135613233643839323831383932623266613666373561383432636339 49 | 64616637333337343962633537343439326638643236346261383662613663376539656635633137 50 | 34373566363832386631643339656539623932643064643261623937366664363162643931666563 51 | 65613634353334353831323266643133626666313334663762633264663935316165616164363164 52 | 36663866623437363064396366633363303037316562643863316262393630643137323862666665 53 | 36393062333463653365623663326332303738333866663362623663646535353135323335613437 54 | 63323737643138646135306635653765663838373664313233323066633731613364336363383034 55 | 39666361393061623963383063643165343331613138323138656232633938326633626336356533 56 | 38346661623931383532393961383433306135323332303431393036396465623239663238666633 57 | 39393462303338363035326435633866633463623733623736336532383461303231343963633266 58 | 61363537643337613962303834353237646531666562363137653339343831613539383237366634 59 | 31616536383634653339623732323635323731373836386331386432393237666235376639313530 60 | 66666635643636323761323761373961613261373431333239366337376463643036616565306239 61 | 31306233653861373963653335346565623364303230343665303234366537626536323732666236 62 | 65393538616538353831616131383437336535366535616265316530633736346432643930313833 63 | 35336664636632643831356636373034313063353666323230383137666162633566373138653732 64 | 383139393738383538383463613239643034 65 | -------------------------------------------------------------------------------- /Tests/Resources/unicode_1.1.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jborean93/PowerShell-AnsibleVault/62928e930d57334caf11c26c401fc50064305576/Tests/Resources/unicode_1.1.yml -------------------------------------------------------------------------------- /Tests/Split-Byte.Tests.ps1: -------------------------------------------------------------------------------- 1 | $verbose = @{} 2 | if ($env:APPVEYOR_REPO_BRANCH -and $env:APPVEYOR_REPO_BRANCH -notlike "master") { 3 | $verbose.Add("Verbose", $true) 4 | } 5 | 6 | $ps_version = $PSVersionTable.PSVersion.Major 7 | $module_name = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") 8 | Import-Module -Name ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault')) -Force 9 | . ([System.IO.Path]::Combine($PSScriptRoot, '..', 'AnsibleVault', 'Private', "$($module_name).ps1")) 10 | 11 | Describe "$module_name PS$ps_version tests" { 12 | Context 'Strict mode' { 13 | Set-StrictMode -Version latest 14 | 15 | It 'should return correct split for split char with bytes with MaxSplit ' -TestCases @( 16 | @{ 17 | Char = [char]"a" 18 | MaxSplit = $null 19 | Value = [byte[]]@(1, 1, 97, 1, 97, 1) 20 | Expected = @( 21 | [byte[]]@(1, 1), 22 | [byte[]]@(1), 23 | [byte[]]@(1) 24 | ) 25 | } 26 | @{ 27 | Char = [char]"a" 28 | MaxSplit = $null 29 | Value = [byte[]]@(1, 1, 97, 1, 97, 1, 97) 30 | Expected = @( 31 | [byte[]]@(1, 1), 32 | [byte[]]@(1), 33 | [byte[]]@(1) 34 | ) 35 | } 36 | @{ 37 | Char = [char]"a" 38 | MaxSplit = 1 39 | Value = [byte[]]@(1, 1, 97, 1, 97, 1, 97) 40 | Expected = @( 41 | [byte[]]@(1, 1), 42 | [byte[]]@(1, 97, 1, 97) 43 | ) 44 | } 45 | @{ 46 | Char = [char]"a" 47 | MaxSplit = 2 48 | Value = [byte[]]@(1, 1, 97, 1, 97, 1, 97) 49 | Expected = @( 50 | [byte[]]@(1, 1), 51 | [byte[]]@(1), 52 | [byte[]]@(1, 97) 53 | ) 54 | } 55 | @{ 56 | Char = [char]"a" 57 | MaxSplit = $null 58 | Value = [byte[]]@(1, 1, 97, 97, 1, 97, 1) 59 | Expected = @( 60 | [byte[]]@(1, 1), 61 | [byte[]]@(1), 62 | [byte[]]@(1) 63 | ) 64 | } 65 | ){ 66 | param($Char, $MaxSplit, $Value, $Expected) 67 | 68 | $invoke_args = @{} 69 | if ($MaxSplit) { 70 | $invoke_args.MaxSplit = $MaxSplit 71 | } 72 | 73 | $actual = Split-Byte -Value $Value -Char $Char @invoke_args 74 | $actual.Count | Should -Be $Expected.Count 75 | 76 | for ($i = 0; $i -lt $actual.Count; $i++) { 77 | $actual[$i].Count | Should -Be $Expected[$i].Count 78 | for ($j = 0; $j -lt $actual[$i].Count; $j++) { 79 | $actual[$i][$j] | Should -Be $Expected[$i][$j] 80 | } 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Publish to PowerShell Gallery with this key 2 | environment: 3 | NuGetApiKey: 4 | secure: VMvVbhuMaUTbI8WgnJH/WF7UBGwj261AgERGm25s21zQErHoIdwacoF3yd6OjIWh 5 | 6 | os: WMF 5 7 | 8 | # Skip on updates to the readme. 9 | skip_commits: 10 | message: /updated readme.*|update readme.*s/ 11 | 12 | build: false 13 | 14 | test_script: 15 | - ps: . .\build.ps1 16 | - pwsh: . .\build.ps1 17 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | # thanks to http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/ 2 | 3 | function Resolve-Module 4 | { 5 | [Cmdletbinding()] 6 | param 7 | ( 8 | [Parameter(Mandatory)] 9 | [string[]]$Name 10 | ) 11 | Process 12 | { 13 | foreach ($ModuleName in $Name) 14 | { 15 | $Module = Get-Module -Name $ModuleName -ListAvailable 16 | Write-Verbose -Message "Resolving Module $($ModuleName)" 17 | 18 | if ($Module) 19 | { 20 | $Version = $Module | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum 21 | $GalleryVersion = Find-Module -Name $ModuleName -Repository PSGallery | Measure-Object -Property Version -Maximum | Select-Object -ExpandProperty Maximum 22 | 23 | if ($Version -lt $GalleryVersion) 24 | { 25 | if ((Get-PSRepository -Name PSGallery).InstallationPolicy -ne 'Trusted') { Set-PSRepository -Name PSGallery -InstallationPolicy Trusted } 26 | 27 | Write-Verbose -Message "$($ModuleName) Installed Version [$($Version.tostring())] is outdated. Installing Gallery Version [$($GalleryVersion.tostring())]" 28 | Install-Module -Name $ModuleName -Force 29 | Import-Module -Name $ModuleName -Force -RequiredVersion $GalleryVersion 30 | } 31 | else 32 | { 33 | Write-Verbose -Message "Module Installed, Importing $($ModuleName)" 34 | Import-Module -Name $ModuleName -Force -RequiredVersion $Version 35 | } 36 | } 37 | else 38 | { 39 | Write-Verbose -Message "$($ModuleName) Missing, installing Module" 40 | if ($ModuleName -eq "PSScriptAnalyzer"){ 41 | # PSScriptAnalyzer v1.18.3 (2019.09.17) is only compatible with pwsh v6.2.0+, 42 | # but pwsh v6.1.0 is installed on Appveyor. 43 | if (($PSVersionTable.PSVersion.Major -ge 6 ) -and ($PSVersionTable.PSVersion -lt "6.2.0")) { 44 | $Version = "1.18.2" 45 | } 46 | } 47 | Install-Module -Name $ModuleName -Force -RequiredVersion $Version 48 | Import-Module -Name $ModuleName -Force -RequiredVersion $Version 49 | } 50 | } 51 | } 52 | } 53 | 54 | # Grab nuget bits, install modules, set build variables, start build. 55 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 56 | 57 | Resolve-Module Psake, PSDeploy, Pester, BuildHelpers, PsScriptAnalyzer 58 | 59 | Get-ChildItem env:BH* | ForEach-Object {[Environment]::SetEnvironmentVariable($_.name, "")} 60 | Set-BuildEnvironment 61 | 62 | Invoke-psake .\psake.ps1 63 | exit ( [int]( -not $psake.build_success ) ) 64 | -------------------------------------------------------------------------------- /deploy.psdeploy.ps1: -------------------------------------------------------------------------------- 1 | # Generic module deployment. 2 | # 3 | # ASSUMPTIONS: 4 | # 5 | # * folder structure either like: 6 | # 7 | # - RepoFolder 8 | # - This PSDeploy file 9 | # - ModuleName 10 | # - ModuleName.psd1 11 | # 12 | # OR the less preferable: 13 | # - RepoFolder 14 | # - RepoFolder.psd1 15 | # 16 | # * Nuget key in $ENV:NugetApiKey 17 | # 18 | # * Set-BuildEnvironment from BuildHelpers module has populated ENV:BHPSModulePath and related variables 19 | 20 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWriteHost", "", Justification="Required in PSDeploy, cannot output to a stream")] 21 | param() 22 | 23 | # Publish to gallery with a few restrictions 24 | $tag_commit = ($env:APPVEYOR_REPO_TAG -eq "true") 25 | if( 26 | $env:BHPSModulePath -and 27 | $env:BHBuildSystem -ne 'Unknown' -and 28 | $env:BHBranchName -eq "master" -and 29 | $tag_commit -and 30 | $PSVersionTable.PSVersion.Major -gt 5 31 | ) 32 | { 33 | Deploy Module { 34 | By PSGalleryModule { 35 | FromSource $ENV:BHPSModulePath 36 | To PSGallery 37 | WithOptions @{ 38 | ApiKey = $ENV:NugetApiKey 39 | } 40 | } 41 | } 42 | } 43 | else 44 | { 45 | "Skipping deployment: To deploy, ensure that...`n" + 46 | "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + 47 | "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + 48 | "`t* The commit is a tagged release from github with APPVEYOR_REPO_TAG=true (Current: $ENV:APPVEYOR_REPO_TAG)" | Write-Host 49 | } 50 | 51 | # Publish to AppVeyor if we're in AppVeyor 52 | if( 53 | $env:BHPSModulePath -and 54 | $env:BHBuildSystem -eq 'AppVeyor' 55 | ) 56 | { 57 | Deploy DeveloperBuild { 58 | By AppVeyorModule { 59 | FromSource $ENV:BHPSModulePath 60 | To AppVeyor 61 | WithOptions @{ 62 | Version = $env:APPVEYOR_BUILD_VERSION 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /psake.ps1: -------------------------------------------------------------------------------- 1 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "", Justification="Global vars are used outside of where they are declared")] 2 | param() 3 | 4 | # PSake makes variables declared here available in other scriptblocks 5 | # Init some things 6 | Properties { 7 | # Find the build folder based on build system 8 | $ProjectRoot = $ENV:BHProjectPath 9 | if(-not $ProjectRoot) 10 | { 11 | $ProjectRoot = $PSScriptRoot 12 | } 13 | 14 | $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" 15 | $PSVersion = $PSVersionTable.PSVersion.Major 16 | $TestFile = "TestResults_PS$PSVersion`_$TimeStamp.xml" 17 | $lines = '----------------------------------------------------------------------' 18 | 19 | $Verbose = @{} 20 | if($ENV:BHCommitMessage -match "!verbose") 21 | { 22 | $Verbose = @{Verbose = $True} 23 | } 24 | } 25 | 26 | Task Default -Depends Deploy 27 | 28 | Task Init { 29 | $lines 30 | Set-Location $ProjectRoot 31 | "Build System Details:" 32 | Get-Item ENV:BH* 33 | "`n" 34 | } 35 | 36 | Task Sanity -Depends Init { 37 | $lines 38 | "`n`tSTATUS: Sanity tests with PsScriptAnalyzer" 39 | 40 | $results = Invoke-ScriptAnalyzer -Path $ProjectRoot\ -Recurse -ErrorAction SilentlyContinue 41 | if ($null -ne $results) { 42 | $results | Out-String 43 | Write-Error "Failed PsScriptAnalyzer tests, build failed" 44 | } 45 | "`n" 46 | } 47 | 48 | Task Test -Depends Sanity { 49 | $lines 50 | "`n`tSTATUS: Testing with PowerShell $PSVersion" 51 | 52 | # Gather test results. Store them in a variable and file 53 | $TestResults = Invoke-Pester -Path $ProjectRoot\Tests -PassThru -OutputFormat NUnitXml -OutputFile "$ProjectRoot\$TestFile" -CodeCoverage $env:BHModulePath\Private\*.ps1, $env:BHModulePath\Public\*.ps1 54 | 55 | # In Appveyor? Upload our tests! #Abstract this into a function? 56 | If($ENV:BHBuildSystem -eq 'AppVeyor') 57 | { 58 | (New-Object 'System.Net.WebClient').UploadFile( 59 | "https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", 60 | "$ProjectRoot\$TestFile" ) 61 | } 62 | 63 | Remove-Item "$ProjectRoot\$TestFile" -Force -ErrorAction SilentlyContinue 64 | 65 | # Failed tests? 66 | # Need to tell psake or it will proceed to the deployment. Danger! 67 | if($TestResults.FailedCount -gt 0) 68 | { 69 | Write-Error "Failed '$($TestResults.FailedCount)' tests, build failed" 70 | } 71 | "`n" 72 | } 73 | 74 | Task Deploy -Depends Test { 75 | $lines 76 | 77 | $Params = @{ 78 | Path = $ProjectRoot 79 | Force = $true 80 | Recurse = $false # We keep psdeploy artifacts, avoid deploying those : ) 81 | } 82 | Invoke-PSDeploy @Verbose @Params 83 | } 84 | --------------------------------------------------------------------------------