ssh

package
v1.0.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 15, 2023 License: Apache-2.0 Imports: 11 Imported by: 0

README

SSH File Signatures

SSH keys can be used to sign files! Unfortunately this is a pretty recent change to the openssh tooling, so it is not supported by golang.org/x/crypto/ssh yet.

This document explains how it works at a high level.

Keys

SSH keys are usually split into public and private files, named id_rsa.pub and id_rsa, respectively. These files are encoded and formatted a little differently than other signing keys.

Public Keys

These are typically in the "known hosts" format. This looks something like:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDw0ZWP4zZLELSJVenQTQsrFJVBnoP64KTg/UWRU6qOb8HEOdtHJDOyTmo9dvN/yJoTFtWAfQEjaTsMVJzTD0gOk6ncTsp0BUtgXawSCfEUiv7v+2VgSVbUfAv/NL+HEGSCdcORnansIyrZaHwAjR3ei3O+pRWvgjRj3pOH1rWGrxaC5IbsELYzS/HvwAG/uwcxgBv4POvaq6eCEHVbqRjIYjjoYsC+c24sgSQxOyXvDS7j2z9TPHPvepDhVr9y6xnnqhLqZEWmidRrbb35aYkVLJxmGTFy/JW1cewyU2Jb3+sKQOiOwL7DAB39tRyec2ed+EHh6QLW4pcMnoXsWuPyi+G595HiUYmIlqXJ5JPo0Cv/rOJrmWSFceWiDjC/SeODp/AcK0EsN/p3wOp6ac7EzAz9Npri0vwSQX4MUYlya/olKiKCx5GIhTZtXioREPd8v4osx2VrVyDxKX99PVVbxw1FXSe4u+PuOawJzUA4vW41mxUY9zoAsb/fvoNPtrrT9HfC+7Pg6ryBdz+445M8Atc8YjjLeYXkTXWD6KMielRzBFFoIwIgi0bMotq3iQ9IwjQSXPMDQLb+UPg8xqsgRsX3wvyZzdBhxO4Bdomv7JYmySysaGgliHktU8qRse1lpDIXMovPtowywcKL4U3seDKrq7saVO0qdsLavy1o0w== lorenc.d@gmail.com

These can be parsed with ParseKnownHosts , NOT ParsePublicKey.

In addition to the key material itself, this can contain the algorithm (ssh-rsa here) and a comment (lorenc.d@gmail.com) here.

Private Keys

These are stored in an "armored" PEM format, resembling PGP or x509 keys:

-----BEGIN SSH PRIVATE KEY-----
<base64 encoded key here>
-----END SSH PRIVATE KEY-----

These can be parsed correctly with ParsePrivateKey.

Wire Format

The wire format is relatively standard.

  • Bytes are laid out in order.
  • Fixed-length fields are laid out at the proper offset with the specified length.
  • Strings are stored with the size as a prefix.

Signature

These can be generated and validated from the command line with the ssh-keygen -Y set of commands: sign, verify, and check-novalidate.

To work with them in Go is a little tricker. The signature is stored using a struct packed using the openssh wire format. The data that is used in the signing function is also packed in another struct before it is signed.

Signature Format

Signatures are formatted on disk in a PEM-encoded format. The header is -----BEGIN SSH SIGNATURE-----, and the end is -----BEGIN SSH SIGNATURE-----. The signature contents are base64-encoded.

The signature contents are wrapped with extra metadata, then encoded as a struct using the openssh wire format. That struct is defined here.

In Go:

type wrappedSig struct {
	MagicHeader   [6]byte
	Version       uint32
	PublicKey     string
	Namespace     string
	Reserved      string
	HashAlgorithm string
	Signature     string
}

The PublicKey and Signature fields are also stored as openssh-wire-formatted structs. The MagicHeader is SSHSIG. The Version is 1. The Namespace is file (for this use-case). Reserved must be empty.

Go can already parse the PublicKey and Signature fields, and the Signature struct contains a Blob with the signature data.

Signed Message

In addition to these wrappers, the message to be signed is wrapped with some metadata before it is passed to the signing function.

That wrapper is defined here.

And in Go:

type messageWrapper struct {
	Namespace     string
	Reserved      string
	HashAlgorithm string
	Hash          string
}

So, the data must first be hashed, then packed in this struct and encoded in the openssh wire format. Then, this resulting data is signed using the desired signature function.

The Namespace field must be file (for this usecase). The Reserved field must be empty.

The output of this signature function (and the hash) becomes the Signature.Blob value, which gets wire-encoded, wrapped, wire-encoded and finally pem-encoded.

Documentation

Overview

Package ssh implements signing with SSH keys

Package ssh contains types and utilities related to SSH signatures.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Armor

func Armor(s *ssh.Signature, p ssh.PublicKey) []byte

Armor generates a PEM armored signature block.

func Sign

func Sign(sshPrivateKey string, data io.Reader) ([]byte, error)

Sign signs the supplied message with the private key.

func Verify

func Verify(message io.Reader, armoredSignature []byte, pubKey ssh.PublicKey) error

Verify verifies the supplied signature against the specified key.

Types

type Signature

type Signature struct {
	// contains filtered or unexported fields
}

Signature encapsulates an SSH Signature object for verification.

func Decode

func Decode(b []byte) (*Signature, error)

Decode parses a PEM armored signature block.

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Signer implements signature.Signer for SSH keys.

func (*Signer) PublicKey

func (s *Signer) PublicKey(_ ...signature.PublicKeyOption) (crypto.PublicKey, error)

PublicKey returns the public key for a Signer.

func (*Signer) SignMessage

func (s *Signer) SignMessage(message io.Reader, _ ...signature.SignOption) ([]byte, error)

SignMessage signs the supplied message.

func (*Signer) VerifySignature

func (s *Signer) VerifySignature(signature, message io.Reader, _ ...signature.VerifyOption) error

VerifySignature verifies a suppled signature.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL