Skeleton, mnemonic input, derivation, and the ssh commands #5

Merged
clawbot merged 2 commits from issue-1-skeleton into next 2026-09-07 17:34:54 +02:00
4 changed files with 53 additions and 4 deletions
Showing only changes of commit 731ffffb5f - Show all commits

View File

@@ -6,6 +6,7 @@ import (
"testing"
"git.eeqj.de/sneak/keyfunc/internal/cli"
"git.eeqj.de/sneak/keyfunc/internal/derive"
"git.eeqj.de/sneak/keyfunc/internal/mnemonic"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
@@ -62,6 +63,14 @@ func TestThePrivateKeyMatchesThePublicOne(t *testing.T) {
require.Equal(t, vectorOne, back)
}
func TestAnIndexWithNoHardenedChildIsRefused(t *testing.T) {
t.Setenv(mnemonic.Variable, example())
out, err := execute(t, "ssh", "pub", "-n", "2147483648")
require.ErrorIs(t, err, derive.ErrIndexTooLarge)
require.Empty(t, out)
}
func TestTheMnemonicCommandIsUsed(t *testing.T) {
t.Setenv(mnemonic.Variable, "")
@@ -77,6 +86,17 @@ func TestTheMnemonicCommandIsUsed(t *testing.T) {
func run(t *testing.T, args ...string) string {
t.Helper()
out, err := execute(t, args...)
require.NoError(t, err)
return out
}
// execute runs the tool and returns both what it wrote and how it
// ended.
func execute(t *testing.T, args ...string) (string, error) {
t.Helper()
var out bytes.Buffer
root := cli.Root()
@@ -84,7 +104,7 @@ func run(t *testing.T, args ...string) string {
root.SetErr(&out)
root.SetArgs(args)
require.NoError(t, root.ExecuteContext(t.Context()))
err := root.ExecuteContext(t.Context())
return out.String()
return out.String(), err
}

View File

@@ -2,6 +2,7 @@
package derive
import (
"errors"
"fmt"
"git.eeqj.de/sneak/secret/pkg/bip85"
@@ -16,8 +17,18 @@ const (
// Size is how many bytes every key type is given.
Size = 32
// MaxIndex is the largest key index there is. Every element of
// the path is hardened, and a hardened BIP-32 child index stops
// here.
MaxIndex = 1<<31 - 1
)
// ErrIndexTooLarge is returned for a key index above MaxIndex. Such an
// index has no hardened child to derive, so there is no key to give
// back rather than a key nobody else would reproduce.
var ErrIndexTooLarge = errors.New("the key index is too large")
// Path returns the derivation path for an application number and a key
// index.
func Path(application, index uint32) string {
@@ -28,7 +39,15 @@ func Path(application, index uint32) string {
// The mnemonic becomes a seed with an empty passphrase, the seed
// becomes a master key, the master key gives BIP-85 entropy at the
// path, and the entropy seeds the generator the bytes are read from.
// An index above MaxIndex is refused before any of that happens.
func Bytes(words string, application, index uint32) ([]byte, error) {
if index > MaxIndex {
return nil, fmt.Errorf(
"%w: %d is above %d",
ErrIndexTooLarge, index, MaxIndex,
)
}
seed := bip39.NewSeed(words, "")
master, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)

View File

@@ -38,6 +38,16 @@ func TestEveryIndexGivesItsOwnBytes(t *testing.T) {
require.False(t, bytes.Equal(first, second))
}
func TestAnIndexWithNoHardenedChildIsRefused(t *testing.T) {
t.Parallel()
_, err := derive.Bytes(example(), application, derive.MaxIndex+1)
require.ErrorIs(t, err, derive.ErrIndexTooLarge)
_, err = derive.Bytes(example(), application, derive.MaxIndex)
require.NoError(t, err)
}
func TestTheSameInputAlwaysGivesTheSameBytes(t *testing.T) {
t.Parallel()

View File

@@ -8,8 +8,8 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
hook=".git/hooks/pre-commit"
printf '#!/bin/sh\nset -e\nscript/precommit\n' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
printf '#!/bin/sh\nset -e\nscript/precommit\n' > "$hook"
chmod +x "$hook"
echo "pre-commit hook installed: runs script/precommit"
}