All checks were successful
check / check (push) Successful in 5s
The module, the script entrypoints and Makefile, Docker-only linting, the mnemonic sources in the specified order with their refusals, the BIP-85 derivation, and keyfunc ssh pub and priv with the README test vectors as tests. Two review rounds; the second passed with no findings. Model: opus-5 (implementation and review); fable-5-1 (landing)
64 lines
1.6 KiB
Go
64 lines
1.6 KiB
Go
// Package sshkey turns derived bytes into an ed25519 SSH key.
|
|
package sshkey
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// Application is the number this key type occupies in the derivation
|
|
// path. It spells SSH the way BIP-85 spells RSA, as the ASCII codes of
|
|
// the letters written out.
|
|
const Application = 838372
|
|
|
|
// ErrSize is returned when the derived bytes are not the length an
|
|
// ed25519 seed has to be.
|
|
var ErrSize = errors.New("an ed25519 key needs 32 derived bytes")
|
|
|
|
// Key is one ed25519 SSH key.
|
|
type Key struct {
|
|
private ed25519.PrivateKey
|
|
}
|
|
|
|
// New makes a key whose ed25519 seed is the derived bytes.
|
|
func New(derived []byte) (*Key, error) {
|
|
if len(derived) != ed25519.SeedSize {
|
|
return nil, fmt.Errorf("%w, got %d", ErrSize, len(derived))
|
|
}
|
|
|
|
return &Key{private: ed25519.NewKeyFromSeed(derived)}, nil
|
|
}
|
|
|
|
// Line returns the public key as one authorized_keys line, without a
|
|
// trailing newline.
|
|
func (k *Key) Line(comment string) (string, error) {
|
|
public, err := ssh.NewPublicKey(k.private.Public())
|
|
if err != nil {
|
|
return "", fmt.Errorf("encoding the public key: %w", err)
|
|
}
|
|
|
|
line := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(public)))
|
|
|
|
if comment != "" {
|
|
line += " " + comment
|
|
}
|
|
|
|
return line, nil
|
|
}
|
|
|
|
// Block returns the unencrypted private key in the OpenSSH format that
|
|
// ssh reads, ending in a newline.
|
|
func (k *Key) Block(comment string) (string, error) {
|
|
block, err := ssh.MarshalPrivateKey(k.private, comment)
|
|
if err != nil {
|
|
return "", fmt.Errorf("encoding the private key: %w", err)
|
|
}
|
|
|
|
return string(pem.EncodeToMemory(block)), nil
|
|
}
|