Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions src/functions/public/ConvertFrom-Base64Url.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
function ConvertFrom-Base64Url {
<#
.SYNOPSIS
Decodes a base64url-encoded string or array of strings into UTF-8 strings.

.DESCRIPTION
Converts a base64url-encoded string or array of strings into human-readable UTF-8 strings. Base64url encoding
is a URL-safe variant of base64 that uses '-' instead of '+', '_' instead of '/', and omits padding '=' characters.
The function accepts input from the pipeline and validates the input using the `Test-Base64Url` function before decoding.

.EXAMPLE
"U29tZSBkYXRh" | ConvertFrom-Base64Url

Output:
```powershell
Some data
```

Decodes the base64url-encoded string "U29tZSBkYXRh" into its original UTF-8 representation.

.EXAMPLE
@("SGVsbG8", "V29ybGQ") | ConvertFrom-Base64Url

Output:
```powershell
Hello
World
```

Decodes each base64url-encoded string in the array into its original UTF-8 representation.

.OUTPUTS
System.String

.NOTES
The decoded UTF-8 string(s).

.LINK
https://psmodule.io/Base64/Functions/ConvertFrom-Base64Url/
#>
[Alias('ConvertFrom-Base64UrlString')]
[OutputType([string])]
[CmdletBinding()]
param(
# The base64url-encoded string or array of strings to be decoded.
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[ValidateScript({ Test-Base64Url -Base64UrlString $_ }, ErrorMessage = 'Invalid Base64Url string')]
[string[]] $Base64UrlString,

# The encoding to use when converting the string to bytes.
[Parameter()]
[ValidateSet('UTF8', 'UTF7', 'UTF32', 'ASCII', 'Unicode', 'BigEndianUnicode', 'Latin1')]
[string] $Encoding = 'UTF8'
)

process {
foreach ($item in $Base64UrlString) {
# Convert base64url to base64 by replacing '-' with '+', '_' with '/', and adding padding if needed
$base64 = $item.Replace('-', '+').Replace('_', '/')

# Add padding if needed (base64 length must be multiple of 4)
$padding = $base64.Length % 4
if ($padding -ne 0) {
$base64 += '=' * (4 - $padding)
}

[System.Text.Encoding]::$Encoding.GetString([Convert]::FromBase64String($base64))
}
}
}
67 changes: 67 additions & 0 deletions src/functions/public/ConvertTo-Base64Url.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function ConvertTo-Base64Url {
<#
.SYNOPSIS
Converts a string or array of strings to their base64url encoded representation.

.DESCRIPTION
This function takes a string or array of strings as input and converts them to base64url encoded strings using UTF-8 encoding.
Base64url encoding is a URL-safe variant of base64 that replaces '+' with '-', '/' with '_', and removes padding '=' characters.
It accepts input from the pipeline and can process string values directly or as an array.

.EXAMPLE
"Hello World" | ConvertTo-Base64Url

Output:
```powershell
SGVsbG8gV29ybGQ
```

Converts the string "Hello World" to its base64url encoded equivalent.

.EXAMPLE
@("Hello", "World") | ConvertTo-Base64Url

Output:
```powershell
SGVsbG8
V29ybGQ
```

Converts each string in the array to its base64url encoded equivalent.

.OUTPUTS
System.String

.NOTES
The base64url encoded representation of the input string(s).

.LINK
https://psmodule.io/Base64/Functions/ConvertTo-Base64Url/
#>
[Alias('ConvertTo-Base64UrlString')]
[OutputType([string])]
[CmdletBinding()]
param(
# The input string or array of strings to be converted to base64url encoding.
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[string[]] $String,

# The encoding to use when converting the string to bytes.
[Parameter()]
[ValidateSet('UTF8', 'UTF7', 'UTF32', 'ASCII', 'Unicode', 'BigEndianUnicode', 'Latin1')]
[string] $Encoding = 'UTF8'
)

process {
foreach ($item in $String) {
$base64 = [Convert]::ToBase64String([System.Text.Encoding]::$Encoding.GetBytes($item))
# Convert to base64url by replacing '+' with '-', '/' with '_', and removing padding '='
$base64url = $base64.Replace('+', '-').Replace('/', '_').TrimEnd('=')
$base64url
}
}
}
73 changes: 73 additions & 0 deletions src/functions/public/Test-Base64Url.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
filter Test-Base64Url {
<#
.SYNOPSIS
Determines whether a given string is a valid base64url-encoded string.

.DESCRIPTION
This function checks whether the provided string is a valid base64url-encoded string.
Base64url encoding is a URL-safe variant of base64 that uses '-' instead of '+', '_' instead of '/',
and omits padding '=' characters. It attempts to decode the input by converting it to standard base64
and using `[Convert]::FromBase64String()`. If the decoding succeeds, it returns `$true`; otherwise, it returns `$false`.

.EXAMPLE
Test-Base64Url -Base64UrlString 'U29tZSBkYXRh'

Output:
```powershell
True
```

Returns `$true` as the string is a valid base64url-encoded string.

.EXAMPLE
'U29tZSBkYXRh' | Test-Base64Url

Output:
```powershell
True
```

Returns `$true` as the string is a valid base64url-encoded string.

.OUTPUTS
bool

.NOTES
Returns `$true` if the string is a valid base64url-encoded string, otherwise `$false`.

.LINK
https://psmodule.io/Test/Functions/Test-Base64Url
#>
[OutputType([bool])]
[CmdletBinding()]
param (
# The base64url-encoded string to validate.
[Parameter(
Mandatory,
ValueFromPipeline
)]
[string] $Base64UrlString
)

try {
# Check for invalid characters (base64url should only contain A-Z, a-z, 0-9, -, _)
if ($Base64UrlString -match '[^A-Za-z0-9\-_]') {
return $false
}

# Convert base64url to base64 by replacing '-' with '+', '_' with '/'
$base64 = $Base64UrlString.Replace('-', '+').Replace('_', '/')

# Add padding if needed (base64 length must be multiple of 4)
$padding = $base64.Length % 4
if ($padding -ne 0) {
$base64 += '=' * (4 - $padding)
}

# Try to decode
$null = [Convert]::FromBase64String($base64)
$true
} catch {
$false
}
}
123 changes: 123 additions & 0 deletions tests/Base64.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
. "$PSScriptRoot/../src/functions/public/Test-Base64.ps1"
. "$PSScriptRoot/../src/functions/public/ConvertTo-Base64.ps1"
. "$PSScriptRoot/../src/functions/public/ConvertFrom-Base64.ps1"
. "$PSScriptRoot/../src/functions/public/Test-Base64Url.ps1"
. "$PSScriptRoot/../src/functions/public/ConvertTo-Base64Url.ps1"
. "$PSScriptRoot/../src/functions/public/ConvertFrom-Base64Url.ps1"

# Create test files
$testContentPath = '/tmp/base64_test/test_content.txt'
Expand Down Expand Up @@ -111,4 +114,124 @@ Describe 'Base64' {
$result[2] | Should -Be 'Line 3'
}
}
Context 'Function: Test-Base64Url' {
It "Test-Base64Url -Base64UrlString 'VGhpc0lzQU5pY2VTdHJpbmc' -> true" {
Test-Base64Url -Base64UrlString 'VGhpc0lzQU5pY2VTdHJpbmc' | Should -Be $true
}
It "'SGVsbG8gV29ybGQ' | Test-Base64Url -> true" {
'SGVsbG8gV29ybGQ' | Test-Base64Url | Should -Be $true
}
It "Test-Base64Url with invalid characters should return false" {
'Invalid+String/With=Padding' | Test-Base64Url | Should -Be $false
}
It "Test-Base64Url with URL-safe characters should return true" {
'VGhpcyB3aWxsIGhhdmUgKyBhbmQgLyBjaGFyYWN0ZXJzIHdoZW4gZW5jb2RlZA' | Test-Base64Url | Should -Be $true
}
}
Context 'Function: ConvertTo-Base64Url' {
It "ConvertTo-Base64Url -String 'ThisIsANiceString' -> VGhpc0lzQU5pY2VTdHJpbmc" {
ConvertTo-Base64Url -String 'ThisIsANiceString' | Should -Be 'VGhpc0lzQU5pY2VTdHJpbmc'
}

It "'Hello World' | ConvertTo-Base64Url -> SGVsbG8gV29ybGQ" {
'Hello World' | ConvertTo-Base64Url | Should -Be 'SGVsbG8gV29ybGQ'
}
It "ConvertTo-Base64Url -String @('Hello', 'World') -> @('SGVsbG8', 'V29ybGQ')" {
$result = ConvertTo-Base64Url -String @('Hello', 'World')
$result.Count | Should -Be 2
$result[0] | Should -Be 'SGVsbG8'
$result[1] | Should -Be 'V29ybGQ'
}

It "@('Hello', 'World') | ConvertTo-Base64Url -> @('SGVsbG8', 'V29ybGQ')" {
$result = @('Hello', 'World') | ConvertTo-Base64Url
$result.Count | Should -Be 2
$result[0] | Should -Be 'SGVsbG8'
$result[1] | Should -Be 'V29ybGQ'
}

It "Variable containing multiple strings piped to ConvertTo-Base64Url" {
$strings = @('Hello', 'World')
$result = $strings | ConvertTo-Base64Url
$result.Count | Should -Be 2
$result[0] | Should -Be 'SGVsbG8'
$result[1] | Should -Be 'V29ybGQ'
}

It "File content piped to ConvertTo-Base64Url" {
$testFilePath = '/tmp/base64_test/test_content.txt'
$result = Get-Content -Path $testFilePath | ConvertTo-Base64Url
$result.Count | Should -Be 3
$result[0] | Should -Be 'TGluZSAx'
$result[1] | Should -Be 'TGluZSAy'
$result[2] | Should -Be 'TGluZSAz'
}

It "ConvertTo-Base64Url removes padding characters" {
# Test string that would normally have padding
'sure.' | ConvertTo-Base64Url | Should -Be 'c3VyZS4'
}

It "ConvertTo-Base64Url replaces + and / with URL-safe characters" {
# Test string that contains characters that would result in + and / in base64
'subject?' | ConvertTo-Base64Url | Should -Be 'c3ViamVjdD8'
}
}
Context 'Function: ConvertFrom-Base64Url' {
It "ConvertFrom-Base64Url -Base64UrlString 'VGhpc0lzQU5pY2VTdHJpbmc' -> ThisIsANiceString" {
ConvertFrom-Base64Url -Base64UrlString 'VGhpc0lzQU5pY2VTdHJpbmc' | Should -Be 'ThisIsANiceString'
}

It "'SGVsbG8gV29ybGQ' | ConvertFrom-Base64Url -> Hello World" {
'SGVsbG8gV29ybGQ' | ConvertFrom-Base64Url | Should -Be 'Hello World'
}
It "ConvertFrom-Base64Url -Base64UrlString @('SGVsbG8', 'V29ybGQ') -> @('Hello', 'World')" {
$result = ConvertFrom-Base64Url -Base64UrlString @('SGVsbG8', 'V29ybGQ')
$result.Count | Should -Be 2
$result[0] | Should -Be 'Hello'
$result[1] | Should -Be 'World'
}

It "@('SGVsbG8', 'V29ybGQ') | ConvertFrom-Base64Url -> @('Hello', 'World')" {
$result = @('SGVsbG8', 'V29ybGQ') | ConvertFrom-Base64Url
$result.Count | Should -Be 2
$result[0] | Should -Be 'Hello'
$result[1] | Should -Be 'World'
}

It "Variable containing multiple Base64Url strings piped to ConvertFrom-Base64Url" {
$base64UrlStrings = @('SGVsbG8', 'V29ybGQ')
$result = $base64UrlStrings | ConvertFrom-Base64Url
$result.Count | Should -Be 2
$result[0] | Should -Be 'Hello'
$result[1] | Should -Be 'World'
}

It "File content with Base64Url strings piped to ConvertFrom-Base64Url" {
$testBase64UrlFilePath = '/tmp/base64_test/test_base64url_content.txt'
@('TGluZSAx', 'TGluZSAy', 'TGluZSAz') | Out-File -FilePath $testBase64UrlFilePath
$result = Get-Content -Path $testBase64UrlFilePath | ConvertFrom-Base64Url
$result.Count | Should -Be 3
$result[0] | Should -Be 'Line 1'
$result[1] | Should -Be 'Line 2'
$result[2] | Should -Be 'Line 3'
}

It "ConvertFrom-Base64Url handles missing padding correctly" {
# Test string without padding
'c3VyZS4' | ConvertFrom-Base64Url | Should -Be 'sure.'
}

It "ConvertFrom-Base64Url handles URL-safe characters correctly" {
# Test string with URL-safe characters
'c3ViamVjdD8' | ConvertFrom-Base64Url | Should -Be 'subject?'
}

It "Round-trip conversion maintains data integrity" {
$original = 'Test string with special chars: +/?='
$encoded = $original | ConvertTo-Base64Url
$decoded = $encoded | ConvertFrom-Base64Url
$decoded | Should -Be $original
}
}
}