Skip to content

Commit f88a0f0

Browse files
🚀 [Feature]: Add function to create deterministic key pairs (#21)
## Description - Fixes #19 This pull request introduces a new feature to generate deterministic key pairs using a seed in the `New-SodiumKeyPair` PowerShell function. The changes include updates to both the `Sodium` class and the `New-SodiumKeyPair` function to support this new functionality. ### New Feature: Deterministic Key Pair Generation * **`Sodium` class update**: Added a new P/Invoke method `crypto_box_seed_keypair` to generate key pairs using a seed. * **Documentation updates**: Enhanced the comments and examples in `New-SodiumKeyPair.ps1` to include usage information for the new seed parameter. * **Parameter addition**: Added a new `Seed` parameter to the `New-SodiumKeyPair` function to allow for deterministic key pair generation. * **Key pair generation logic**: Updated the key pair generation logic to handle both seeded and non-seeded key pair creation based on the provided parameters. ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [ ] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [x] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas
1 parent b2ae5fa commit f88a0f0

4 files changed

Lines changed: 83 additions & 8 deletions

File tree

PSModule/Sodium/Sodium.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public static class Sodium
1111
[DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
1212
public static extern int crypto_box_keypair(byte[] pk, byte[] sk);
1313

14+
[DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
15+
public static extern int crypto_box_seed_keypair(byte[] pk, byte[] sk, byte[] seed);
16+
1417
[DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
1518
public static extern int crypto_box_seal(byte[] ciphertext, byte[] message, ulong mlen, byte[] pk);
1619

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,21 @@ PublicKey PrivateKey
3838
9fv51aqi00MYN4UR7Ew/DLXMS9t1NapLs7yyo+vegz4= MiJAFUZxZ1UCbQTwKfH7HY6AhIFYQlnok5fBD2K+y/g=
3939
```
4040

41-
## Example 2: Encrypt a message using a public key (Sealed Boxes encryption)
41+
### Example 2: Deterministic Key Pair Generation
42+
43+
Generate a key pair deterministically using a seed. The same seed will always produce the same key pair.
44+
45+
```powershell
46+
New-SodiumKeyPair -Seed 'MySecureSeed'
47+
48+
PublicKey PrivateKey
49+
-------- - ----------
50+
WQakMx2mIAQMwLqiZteHUTwmMP6mUdK2FL0WEybWgB8= ci5/7eZ0IbGXtqQMaNvxhJ2d9qwFxA8Kjx+vivSTXqU=
51+
```
52+
53+
54+
55+
### Example 3: Encrypt a message using a public key (Sealed Boxes encryption)
4256

4357
After generating a key pair, a message can be encrypted using the associated public key with [Sealed Boxes encryption](https://doc.libsodium.org/public-key_cryptography/sealed_boxes).
4458
Below, a message is encrypted using the public key from the previous example.
@@ -53,7 +67,7 @@ ConvertTo-SodiumSealedBox @params
5367
905j4S/JyP9XBBmOIdHSOXiDu7fUtZo9TFIMnAfBMESgcVBwttLnEyxJn4xPEX5OMKQ+Bc4P6Hg=
5468
```
5569

56-
## Example 3: Decrypt a Sodium-encrypted sealed box string
70+
### Example 4: Decrypt a Sodium-encrypted sealed box string
5771

5872
To decrypt a string that was encrypted using [Sealed Boxes encryption](https://doc.libsodium.org/public-key_cryptography/sealed_boxes), both the private and public keys are required.
5973

src/functions/public/New-SodiumKeyPair.ps1

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,35 @@
88
The keys are returned as a PowerShell custom object, with both the public and private keys
99
encoded in base64 format.
1010
11+
If a seed is provided, the key pair is deterministically generated using a SHA-256 derived seed.
12+
This ensures that the same input seed will always produce the same key pair.
13+
1114
.EXAMPLE
1215
New-SodiumKeyPair
1316
17+
Output:
18+
```powershell
19+
PublicKey PrivateKey
20+
--------- ----------
21+
Ac0wdsq6lqLGktckJrasPcTbVRuUCU+OKzVpMno+v0g= PVXI64v00+aT2b2O6Q4l+SfMBUY2R/Nogsl2mp/hXAs=
22+
```
23+
1424
Generates a new key pair and returns a custom object containing the base64-encoded
1525
public and private keys.
1626
27+
.EXAMPLE
28+
New-SodiumKeyPair -Seed "MySecureSeed"
29+
30+
Output:
31+
```powershell
32+
PublicKey PrivateKey
33+
--------- ----------
34+
WQakMx2mIAQMwLqiZteHUTwmMP6mUdK2FL0WEybWgB8= ci5/7eZ0IbGXtqQMaNvxhJ2d9qwFxA8Kjx+vivSTXqU=
35+
```
36+
37+
Generates a deterministic key pair using the given seed string. The same seed will produce
38+
the same key pair every time.
39+
1740
.LINK
1841
https://psmodule.io/Sodium/Functions/New-SodiumKeyPair/
1942
@@ -26,8 +49,12 @@
2649
Justification = 'Does not change state'
2750
)]
2851
[OutputType([pscustomobject])]
29-
[CmdletBinding()]
30-
param()
52+
[CmdletBinding(DefaultParameterSetName = 'NewKeyPair')]
53+
param(
54+
# A seed value to use for key generation.
55+
[Parameter(Mandatory, ParameterSetName = 'SeededKeyPair')]
56+
[string] $Seed
57+
)
3158

3259
begin {
3360
$null = [PSModule.Sodium]::sodium_init()
@@ -36,14 +63,26 @@
3663
process {
3764
$pkSize = [PSModule.Sodium]::crypto_box_publickeybytes().ToUInt32()
3865
$skSize = [PSModule.Sodium]::crypto_box_secretkeybytes().ToUInt32()
39-
4066
$publicKey = New-Object byte[] $pkSize
4167
$privateKey = New-Object byte[] $skSize
4268

43-
# Generate key pair
44-
$null = [PSModule.Sodium]::crypto_box_keypair($publicKey, $privateKey)
69+
switch ($PSCmdlet.ParameterSetName) {
70+
'SeededKeyPair' {
71+
# Derive a 32-byte seed from the provided string seed (using SHA-256)
72+
$seedBytes = [System.Text.Encoding]::UTF8.GetBytes($Seed)
73+
$derivedSeed = [System.Security.Cryptography.SHA256]::Create().ComputeHash($seedBytes)
74+
$result = [PSModule.Sodium]::crypto_box_seed_keypair($publicKey, $privateKey, $derivedSeed)
75+
break
76+
}
77+
default {
78+
$result = [PSModule.Sodium]::crypto_box_keypair($publicKey, $privateKey)
79+
}
80+
}
81+
82+
if ($result -ne 0) {
83+
throw 'Key pair generation failed.'
84+
}
4585

46-
# Convert to Base64 for easy storage/transfer
4786
return [pscustomobject]@{
4887
PublicKey = [Convert]::ToBase64String($publicKey)
4988
PrivateKey = [Convert]::ToBase64String($privateKey)

tests/Sodium.Tests.ps1

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,24 @@
7474
$publicKeyBytes.Length | Should -Be 32
7575
$privateKeyBytes.Length | Should -Be 32
7676
}
77+
78+
It 'Generates deterministic keys when a seed is provided' {
79+
$seed = 'DeterministicSeed'
80+
$keyPair1 = New-SodiumKeyPair -Seed $seed
81+
$keyPair2 = New-SodiumKeyPair -Seed $seed
82+
83+
$keyPair1.PublicKey | Should -Be $keyPair2.PublicKey
84+
$keyPair1.PrivateKey | Should -Be $keyPair2.PrivateKey
85+
}
86+
87+
It 'Generates different keys for different seeds' {
88+
$seed1 = 'SeedOne'
89+
$seed2 = 'SeedTwo'
90+
$keyPair1 = New-SodiumKeyPair -Seed $seed1
91+
$keyPair2 = New-SodiumKeyPair -Seed $seed2
92+
93+
$keyPair1.PublicKey | Should -Not -Be $keyPair2.PublicKey
94+
$keyPair1.PrivateKey | Should -Not -Be $keyPair2.PrivateKey
95+
}
7796
}
7897
}

0 commit comments

Comments
 (0)