Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: go build -v ./...

- name: Test
run: go test -race -v ./...
run: go test -v ./...

- name: Benchmark
run: go test -bench=. -run=XXX -benchmem -v ./...
Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# Age Key Derivation - Go
# Age (Deterministic) Key Derivation

[![Go Reference](https://pkg.go.dev/badge/github.com/awnumar/agekd.svg)](https://pkg.go.dev/github.com/awnumar/agekd) [![Go workflow](https://github.com/awnumar/agekd/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/awnumar/agekd/actions/workflows/go.yml)



`agekd` is a Go library that can be used to derive [`age`](https://github.com/FiloSottile/age) X25519 identities deterministically from keys or passwords.
AgeKD is a Go library that can be used to derive [`age`](https://github.com/FiloSottile/age) X25519 identities deterministically from keys or passwords.

This package **does not** provide a CLI. If you need that functionality, check out [age-keygen-deterministic](https://github.com/keisentraut/age-keygen-deterministic).

See the upstream `age` [documentation](https://pkg.go.dev/filippo.io/age) for further guidance on working with `age` identities and recipients.

### **This package is currently pre-v1 and is therefore subject to breaking changes.**

## When would you use this?

- You already have key material and want to use it for age operations.
- Your execution environment has the capability to generate cryptographically secure keys, but it prevents your program from persisting custom keys (such as a Kubernetes pod using Hashicorp Vault).
- You want to programmatically derive age identities from passwords.

## Installation

Inside your project folder, run:
Expand Down Expand Up @@ -44,20 +48,22 @@ To generate an age identity from a password:
identity, err := agekd.X25519IdentityFromPassword(key, nil)
```

The default Argon2id settings are:
The default Argon2id parameters are:

```go
DefaultArgon2idTime uint32 = 8
DefaultArgon2idMemory uint32 = 500000 // KiB = 512 MB
DefaultArgon2idTime uint32 = 4
DefaultArgon2idMemory uint32 = 6291456 // KiB = 6 GiB
DefaultArgon2idThreads uint8 = 8
```

but you can provide your own with:
which takes ~3s per hash on an AMD 5800X3D 8-Core CPU. You can select your own parameters with:

```go
identity, err := agekd.X25519IdentityFromPasswordWithParameters(key, nil, time, memory, threads)
```

For guidance on Argon2id parameter selection, refer to [rfc9106](https://www.rfc-editor.org/rfc/rfc9106.html#name-parameter-choice).

## Licensing

Unless otherwise specified within a file, this code is distributed under the [MIT license](/LICENSE).
Expand Down
4 changes: 2 additions & 2 deletions keygen.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
)

const (
DefaultArgon2idTime uint32 = 8
DefaultArgon2idMemory uint32 = 500000 // KiB = 512 MB
DefaultArgon2idTime uint32 = 4
DefaultArgon2idMemory uint32 = 6291456 // KiB = 6 GiB
DefaultArgon2idThreads uint8 = 8

kdfLabel = "github.com/awnumar/agekd"
Expand Down
28 changes: 14 additions & 14 deletions keygen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ func TestX25519IdentityFromKey(t *testing.T) {
t.Fatalf("failed to create age identity: %v", err)
}
if id.String() != c.expID {
t.Fatalf("age identity mismatch: expected '%s' got '%s'", c.expID, id.String())
t.Errorf("age identity mismatch: expected '%s' got '%s'", c.expID, id.String())
}
if id.Recipient().String() != c.expRcp {
t.Fatalf("age recipient mismatch: expected '%s' got '%s'", c.expRcp, id.Recipient().String())
t.Errorf("age recipient mismatch: expected '%s' got '%s'", c.expRcp, id.Recipient().String())
}
}
}
Expand All @@ -73,32 +73,32 @@ func TestX25519IdentityFromPassword(t *testing.T) {
{
key: []byte{},
salt: []byte{},
expID: "AGE-SECRET-KEY-1P4J8RZE9G8EQ559XYDX024NV57DMXH0YAJUFJLH87FVNFXAWPUVQVGGSK8",
expRcp: "age15mehx5d4xvxfmfygc8ndx5acvy294d5j77dlwfc7ylty8hdm5uws8gfam5",
expID: "AGE-SECRET-KEY-1XXCW4ETUT4MSM92U4HG54ULW2E44ZQQSPUJAN4HUDZLHA70GS3RQL7TCTC",
expRcp: "age12c2ewvhx08mjf6d3hs5d7afqv7w36mddfspe69dzam4fu2g04vnsd0t96t",
},
{
key: nil,
salt: nil,
expID: "AGE-SECRET-KEY-1P4J8RZE9G8EQ559XYDX024NV57DMXH0YAJUFJLH87FVNFXAWPUVQVGGSK8",
expRcp: "age15mehx5d4xvxfmfygc8ndx5acvy294d5j77dlwfc7ylty8hdm5uws8gfam5",
expID: "AGE-SECRET-KEY-1XXCW4ETUT4MSM92U4HG54ULW2E44ZQQSPUJAN4HUDZLHA70GS3RQL7TCTC",
expRcp: "age12c2ewvhx08mjf6d3hs5d7afqv7w36mddfspe69dzam4fu2g04vnsd0t96t",
},
{
key: []byte("hello"),
salt: nil,
expID: "AGE-SECRET-KEY-1CW8DLMQEKF4E7KZ7DS4EZFHRRXKRYU0LM3JG4DZCYAC8W34DLLXQ84HR66",
expRcp: "age1vp667dwd3m49hvg2dzczgnj4ht6cx9rzualmlgkycglh70z4uexqp33cnm",
expID: "AGE-SECRET-KEY-1K7WJRLRK7K5P5V9W5G97TJMHJPNSKKA5AC24P0CTGZ95F5F4SUVSRSL68A",
expRcp: "age1lczf85u63y4r66638spjwgkrjv6smk2zejxwuzfy39sunvgqkaqqss52s0",
},
{
key: []byte("hello"),
salt: []byte("bye"),
expID: "AGE-SECRET-KEY-1SR0LU44D700Q7SNH9XQX4V626N69VJZ275NZ6R98NRQYKRAKUYNS453D3Y",
expRcp: "age1stylxkt70m49q2n0vxarxqx9ncmvu5zswuddja6wfet9r8me0c5s225387",
expID: "AGE-SECRET-KEY-10UGH6AS5STGZ8XMT8AVD2HF25A2K429PE45N66H5CZ3ADCLFD6JQL2G4VR",
expRcp: "age12cq55rlgnwu6arszt6nde2uawsm8enndeuey2ku89vn5tp6mvd0sxf86xw",
},
{
key: []byte{125, 231, 97, 121, 25, 36, 248, 109, 22, 245, 220, 7, 19, 151, 123, 246, 40, 27, 194, 4, 133, 222, 108, 216, 32, 162, 132, 16, 142, 151, 22, 104},
salt: []byte{62, 98, 62, 226, 73, 49, 93, 5, 172, 234, 232, 145, 139, 78, 172, 4, 139, 156, 74, 57, 215, 32, 72, 216, 17, 74, 220, 250, 146, 3, 190, 254},
expID: "AGE-SECRET-KEY-1SK248UN253DWHNRQQR63A0C652V387ER95A5Q50F5HZEW8EHTR7STWH8EN",
expRcp: "age12rxldhqtm073ee845rgvencv79dyy4aykd4qc7a7tnex8m33jvqq494kpa",
expID: "AGE-SECRET-KEY-1M4MPJEN0HX2XA8TSCS7LP03NG5KWKELEEKZSCSCXLGZKYRZVNK8STKWQMQ",
expRcp: "age1m5mpdh9jact30wcfujykt7fad405vhzwdjcx2nrxj3ljh8n2kcgs9e92fs",
},
}
for _, c := range testCases {
Expand All @@ -107,10 +107,10 @@ func TestX25519IdentityFromPassword(t *testing.T) {
t.Fatalf("failed to create age identity: %v", err)
}
if id.String() != c.expID {
t.Fatalf("age identity mismatch: expected '%s' got '%s'", c.expID, id.String())
t.Errorf("age identity mismatch: expected '%s' got '%s'", c.expID, id.String())
}
if id.Recipient().String() != c.expRcp {
t.Fatalf("age recipient mismatch: expected '%s' got '%s'", c.expRcp, id.Recipient().String())
t.Errorf("age recipient mismatch: expected '%s' got '%s'", c.expRcp, id.Recipient().String())
}
id2, err := X25519IdentityFromPasswordWithParameters(c.key, c.salt, DefaultArgon2idTime, DefaultArgon2idMemory, DefaultArgon2idThreads)
if err != nil {
Expand Down