Skip to content

Commit

Permalink
Minor refactor/re-organization
Browse files Browse the repository at this point in the history
  • Loading branch information
latkin committed Sep 9, 2016
1 parent 395276f commit 0b6c9e3
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 70 deletions.
2 changes: 1 addition & 1 deletion 1poshword.psm1
Expand Up @@ -335,7 +335,7 @@ function Unprotect-1PEntry {
if ($opVault) {
DecryptOPVaultEntry $entry $password
} else {
DecryptEntry $entry $password
DecryptAgileKeychainEntry $entry $password
}
switch -regex ($paramSet) {
'Secure' {
Expand Down
148 changes: 80 additions & 68 deletions lib.ps1
@@ -1,3 +1,6 @@
################
# Shared helpers
################
function epoch([uint64] $Seconds) {
(New-Object DateTime @(1970,1,1,0,0,0,0,'Utc')).AddSeconds($seconds).ToLocalTime()
}
Expand All @@ -22,17 +25,11 @@ function NormalizeEntryType([string] $Type) {
}
}

function DecodeSaltedString([string] $EncodedString) {
$bytes = [System.Convert]::FromBase64String($encodedString)
[PSCustomObject] @{
Salt = $bytes[8 .. 15]
Data = $bytes[16 .. ($bytes.Length - 1)]
}
}

function DeriveKeyPbkdf2([string] $Password, [byte[]] $Salt, [int] $Iterations, [int] $byteCount, [string] $HashName) {
$passBytes = [System.Text.UTF8Encoding]::UTF8.GetBytes($password)
$derivation =
# if hash algorithm is SHA1, can use built-in Rfc2898DeriveBytes
# otherwise, need to use custom code
if ($hashName -eq 'SHA1') {
New-Object System.Security.Cryptography.Rfc2898DeriveBytes @($passBytes, $salt, $iterations)
} else {
Expand Down Expand Up @@ -86,43 +83,6 @@ function AESDecrypt([byte[]] $Data, [byte[]] $Key, [byte[]] $IV) {
$result
}

function DecryptOPVaulOPData([string] $Data, [PSObject] $Key) {
$dataBytes = [Convert]::FromBase64String($data)
$dataLen = 0
$mul = 1
$dataBytes[8..15] |% { $dataLen += $mul * $_; $mul *= 256 }
$padLength = 16 - ($dataLen % 16)
$computedHash = (New-Object System.Security.Cryptography.HMACSHA256 @(,$key.Aux)).ComputeHash(($dataBytes | Select-Object -First (32 + $padLength + $dataLen)))
$declaredHash = $dataBytes | Select-Object -Skip (32 + $padLength + $dataLen)
if (Compare-Object $computedHash $declaredHash) {
Write-Error "Unable to validate master password"
}
$iv = $dataBytes[16..31]
$encryptedBytes = $dataBytes | Select-Object -Skip 32 | Select-Object -First ($dataLen + $padLength)
AESDecrypt $encryptedBytes $key.Key $iv | Select-Object -Skip $padLength
}

function DecryptOPVaultItemKey([string] $Data, [PSObject] $Key) {
$dataBytes = [Convert]::FromBase64String($data)
$iv = $dataBytes[0..15]
$encryptedKey = $dataBytes[16..79]
$computedHash = (New-Object System.Security.Cryptography.HMACSHA256 @(,$key.Aux)).ComputeHash(($dataBytes | Select-Object -First 80))
$declaredHash = $dataBytes | Select-Object -Last 32
if (Compare-Object $computedHash $declaredHash) {
Write-Error "Unable to validate master password"
}

AESDecrypt $encryptedKey $key.Key $iv
}

function GetOPVaultKeyFromBytes([byte[]] $Bytes) {
$keyHash = [System.Security.Cryptography.SHA512]::Create().ComputeHash($bytes)
[PSCustomObject] @{
Key = $keyHash | Select-Object -First 32
Aux = $keyHash | Select-Object -Last 32
}
}

function PickDecryptionKey([Entry] $Entry) {
$keys = Get-Content "$($entry.VaultPath)/data/default/encryptionKeys.js" | ConvertFrom-Json |% List
if ($entry.KeyId) { $keys |? Identifier -eq $entry.KeyId }
Expand Down Expand Up @@ -160,8 +120,19 @@ function GetPayloadFromDecryptedEntry([string] $DecryptedJson, [Entry] $Entry) {
}
}

function Decrypt([string] $Data, [object] $Key, [int] $Iterations, [switch] $MD5, [switch] $Pbkdf2) {
$decoded = DecodeSaltedString $data
#######################
# AgileKeychain helpers
#######################
function DecodeAgileKeychainSaltedString([string] $EncodedString) {
$bytes = [System.Convert]::FromBase64String($encodedString)
[PSCustomObject] @{
Salt = $bytes[8 .. 15]
Data = $bytes[16 .. ($bytes.Length - 1)]
}
}

function DecryptAgileKeychainData([string] $Data, [object] $Key, [int] $Iterations, [switch] $MD5, [switch] $Pbkdf2) {
$decoded = DecodeAgileKeychainSaltedString $data
$finalKey =
if ($md5) {
DeriveKeyMD5 ([byte[]] $key) $decoded.Salt
Expand All @@ -173,37 +144,20 @@ function Decrypt([string] $Data, [object] $Key, [int] $Iterations, [switch] $MD5
AESDecrypt $decoded.Data $finalKey.Key $finalKey.Aux
}

function DecryptEntry([Entry] $Entry, [securestring] $Password) {
function DecryptAgileKeychainEntry([Entry] $Entry, [securestring] $Password) {
$decryptionKey = PickDecryptionKey $entry

$dataKey = Decrypt -Pbkdf2 $decryptionKey.Data $password $decryptionKey.Iterations
$dataKeyCheck = Decrypt -MD5 $decryptionkey.Validation $dataKey
$dataKey = DecryptAgileKeychainData -Pbkdf2 $decryptionKey.Data $password $decryptionKey.Iterations
$dataKeyCheck = DecryptAgileKeychainData -MD5 $decryptionkey.Validation $dataKey
if (Compare-Object $dataKey $dataKeyCheck) {
Write-Error "Unable to validate master password"
}

$entryBytes = Decrypt -MD5 $entry.EncryptedData $dataKey
$entryBytes = DecryptAgileKeychainData -MD5 $entry.EncryptedData $dataKey
$entryString = [System.Text.Encoding]::UTF8.GetString($entryBytes).Trim() -replace '\p{C}+$'
GetPayloadFromDecryptedEntry $entryString $entry
}

function DecryptOPVaultEntry([Entry] $Entry, [securestring] $Password) {
$vaultProfile = ((Get-Content "$($entry.VaultPath)/default/profile.js") -replace '^var profile=(.+);$','$1') | ConvertFrom-Json
$plainPass = SecureString2String $password
$derivedKey = DeriveKeyPbkdf2 $plainPass ([Convert]::FromBase64String($vaultProfile.Salt)) $vaultProfile.Iterations 64 'SHA512'
$encryptionKeyData = DecryptOPVaulOPData $vaultProfile.MasterKey $derivedKey
$encryptionKey = GetOPVaultKeyFromBytes $encryptionKeyData
$itemKeyBytes = DecryptOPVaultItemKey $entry.KeyData $encryptionKey
$itemKey = [PSCustomObject] @{
Key = $itemKeyBytes | Select-Object -First 32
Aux = $itemKeyBytes | Select-Object -Last 32
}

$entryBytes = DecryptOPVaulOPData $entry.EncryptedData $itemKey
$entryString = [System.Text.Encoding]::UTF8.GetString($entryBytes)
GetPayloadFromDecryptedEntry $entryString $entry
}

function GetAgileKeychainEntries([string] $VaultPath, [string] $name) {
$contents = Get-Content "$vaultPath/data/default/contents.js" | ConvertFrom-Json
$entryIds = $contents |? { $_[2] -like $name } |% { $_[0] }
Expand All @@ -225,6 +179,64 @@ function GetAgileKeychainEntries([string] $VaultPath, [string] $name) {
Set-StrictMode -Version 2
}

#################
# OPVault helpers
#################
function DecryptOPVaulOPData([string] $Data, [PSObject] $Key) {
$dataBytes = [Convert]::FromBase64String($data)
$dataLen = 0
$mul = 1
$dataBytes[8..15] |% { $dataLen += $mul * $_; $mul *= 256 }
$padLength = 16 - ($dataLen % 16)
$computedHash = (New-Object System.Security.Cryptography.HMACSHA256 @(,$key.Aux)).ComputeHash(($dataBytes | Select-Object -First (32 + $padLength + $dataLen)))
$declaredHash = $dataBytes | Select-Object -Skip (32 + $padLength + $dataLen)
if (Compare-Object $computedHash $declaredHash) {
Write-Error "Unable to validate master password"
}
$iv = $dataBytes[16..31]
$encryptedBytes = $dataBytes | Select-Object -Skip 32 | Select-Object -First ($dataLen + $padLength)
AESDecrypt $encryptedBytes $key.Key $iv | Select-Object -Skip $padLength
}

function DecryptOPVaultItemKey([string] $Data, [PSObject] $Key) {
$dataBytes = [Convert]::FromBase64String($data)
$iv = $dataBytes[0..15]
$encryptedKey = $dataBytes[16..79]
$computedHash = (New-Object System.Security.Cryptography.HMACSHA256 @(,$key.Aux)).ComputeHash(($dataBytes | Select-Object -First 80))
$declaredHash = $dataBytes | Select-Object -Last 32
if (Compare-Object $computedHash $declaredHash) {
Write-Error "Unable to validate master password"
}

AESDecrypt $encryptedKey $key.Key $iv
}

function GetOPVaultKeyFromBytes([byte[]] $Bytes, [switch] $NoHash) {
$resultBytes = if ($noHash) { $bytes }
else { [System.Security.Cryptography.SHA512]::Create().ComputeHash($bytes) }
[PSCustomObject] @{
Key = $resultBytes | Select-Object -First 32
Aux = $resultBytes | Select-Object -Last 32
}
}

function DecryptOPVaultEntry([Entry] $Entry, [securestring] $Password) {
$vaultProfile = ((Get-Content "$($entry.VaultPath)/default/profile.js") -replace '^var profile=(.+);$','$1') | ConvertFrom-Json
$plainPass = SecureString2String $password
$derivedKey = DeriveKeyPbkdf2 $plainPass ([Convert]::FromBase64String($vaultProfile.Salt)) $vaultProfile.Iterations 64 'SHA512'

$encryptionKeyData = DecryptOPVaulOPData $vaultProfile.MasterKey $derivedKey
$encryptionKey = GetOPVaultKeyFromBytes $encryptionKeyData

$itemKeyBytes = DecryptOPVaultItemKey $entry.KeyData $encryptionKey
$itemKey = GetOPVaultKeyFromBytes $itemKeyBytes -NoHash

$entryBytes = DecryptOPVaulOPData $entry.EncryptedData $itemKey
$entryString = [System.Text.Encoding]::UTF8.GetString($entryBytes)

GetPayloadFromDecryptedEntry $entryString $entry
}

function GetOPVaultEntries([string] $VaultPath, [string] $Name, [securestring] $Password) {
$vaultProfile = ((Get-Content "$vaultPath/default/profile.js") -replace '^var profile=(.+);$','$1') | ConvertFrom-Json
$plainPass = SecureString2String $password
Expand Down
1 change: 0 additions & 1 deletion pbkdf2.cs
Expand Up @@ -76,7 +76,6 @@ public class Pbkdf2 {
/// <summary>
/// Gets salt bytes.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Byte array is proper return value in this case.")]
public Byte[] Salt { get; private set; }

/// <summary>
Expand Down

0 comments on commit 0b6c9e3

Please sign in to comment.