Private/XChaCha20Poly1305.psm1
|
#!/usr/bin/env pwsh using namespace System using module ./Utilities.psm1 using module ./ChaCha20.psm1 class XChaCha20Poly1305 : CryptobaseUtils { static [int] $KeySize = 32 static [int] $NonceSize = 24 static [int] $TagSize = 16 static [byte[]] HChaCha20([byte[]]$key, [byte[]]$nonce) { [uint[]]$state = [uint[]]::new(16) $state[0] = 0x61707865 $state[1] = 0x3320646e $state[2] = 0x79622d32 $state[3] = 0x6b206574 for ($i = 0; $i -lt 8; $i++) { $state[4 + $i] = [System.BitConverter]::ToUInt32($key, $i * 4) } for ($i = 0; $i -lt 4; $i++) { $state[12 + $i] = [System.BitConverter]::ToUInt32($nonce, $i * 4) } for ($i = 0; $i -lt 10; $i++) { [ChaCha20Poly1305Managed]::QuarterRound($state, 0, 4, 8, 12) [ChaCha20Poly1305Managed]::QuarterRound($state, 1, 5, 9, 13) [ChaCha20Poly1305Managed]::QuarterRound($state, 2, 6, 10, 14) [ChaCha20Poly1305Managed]::QuarterRound($state, 3, 7, 11, 15) [ChaCha20Poly1305Managed]::QuarterRound($state, 0, 5, 10, 15) [ChaCha20Poly1305Managed]::QuarterRound($state, 1, 6, 11, 12) [ChaCha20Poly1305Managed]::QuarterRound($state, 2, 7, 8, 13) [ChaCha20Poly1305Managed]::QuarterRound($state, 3, 4, 9, 14) } [byte[]]$result = [byte[]]::new(32) [Array]::Copy([System.BitConverter]::GetBytes($state[0]), 0, $result, 0, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[1]), 0, $result, 4, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[2]), 0, $result, 8, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[3]), 0, $result, 12, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[12]), 0, $result, 16, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[13]), 0, $result, 20, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[14]), 0, $result, 24, 4) [Array]::Copy([System.BitConverter]::GetBytes($state[15]), 0, $result, 28, 4) return $result } static [byte[]] Encrypt([byte[]]$plainbytes, [byte[]]$key, [byte[]]$nonce) { return [XChaCha20Poly1305]::Encrypt($plainbytes, $key, $nonce, $null) } static [byte[]] Encrypt([byte[]]$plainbytes, [byte[]]$key, [byte[]]$nonce, [byte[]]$aad) { if ($null -eq $key -or $key.Length -ne [XChaCha20Poly1305]::KeySize) { throw [ArgumentException]::new('Key must be 32 bytes.') } if ($null -eq $nonce -or $nonce.Length -ne [XChaCha20Poly1305]::NonceSize) { throw [ArgumentException]::new('Nonce must be 24 bytes.') } $subKey = [XChaCha20Poly1305]::HChaCha20($key, $nonce[0..15]) $nonce12 = [byte[]]@(0, 0, 0, 0) + $nonce[16..23] return [ChaCha20Poly1305Managed]::Encrypt($subKey, $nonce12, $plainbytes, $aad) } static [byte[]] Decrypt([byte[]]$inputbytes, [byte[]]$key, [byte[]]$nonce) { return [XChaCha20Poly1305]::Decrypt($inputbytes, $key, $nonce, $null) } static [byte[]] Decrypt([byte[]]$inputbytes, [byte[]]$key, [byte[]]$nonce, [byte[]]$aad) { if ($null -eq $key -or $key.Length -ne [XChaCha20Poly1305]::KeySize) { throw [ArgumentException]::new('Key must be 32 bytes.') } if ($null -eq $nonce -or $nonce.Length -ne [XChaCha20Poly1305]::NonceSize) { throw [ArgumentException]::new('Nonce must be 24 bytes.') } if ($inputbytes.Length -lt [XChaCha20Poly1305]::TagSize) { throw [ArgumentException]::new('Ciphertext too short.') } $subKey = [XChaCha20Poly1305]::HChaCha20($key, $nonce[0..15]) $nonce12 = [byte[]]@(0, 0, 0, 0) + $nonce[16..23] return [ChaCha20Poly1305Managed]::Decrypt($subKey, $nonce12, $inputbytes, $aad) } static [byte[]] Authenticate([byte[]]$plainbytes, [byte[]]$key, [byte[]]$nonce) { return [XChaCha20Poly1305]::Authenticate($plainbytes, $key, $nonce, $null) } static [byte[]] Authenticate([byte[]]$plainbytes, [byte[]]$key, [byte[]]$nonce, [byte[]]$aad) { $ctWithTag = [XChaCha20Poly1305]::Encrypt($plainbytes, $key, $nonce, $aad) return $ctWithTag[($ctWithTag.Length - 16)..($ctWithTag.Length - 1)] } static [bool] Verify([byte[]]$plainbytes, [byte[]]$tag, [byte[]]$key, [byte[]]$nonce) { return [XChaCha20Poly1305]::Verify($plainbytes, $tag, $key, $nonce, $null) } static [bool] Verify([byte[]]$plainbytes, [byte[]]$tag, [byte[]]$key, [byte[]]$nonce, [byte[]]$aad) { try { $computedTag = [XChaCha20Poly1305]::Authenticate($plainbytes, $key, $nonce, $aad) if ($computedTag.Length -ne $tag.Length) { return $false } for ($i = 0; $i -lt $tag.Length; $i++) { if ($computedTag[$i] -ne $tag[$i]) { return $false } } return $true } catch { return $false } } } |