sshsig

package module
v0.0.0-...-841cf5b Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2021 License: Apache-2.0 Imports: 10 Imported by: 5

README

Armored ssh signatures in go

Go Reference

Package sshsig implements signing/verifying armored SSH signatures. You can use this package to sign data and verify signatures using your ssh private keys or your ssh agent. It gives the same output as using ssh-keygen, eg when signing ssh-keygen -Y sign -f keyfile -n namespace data

This code is based upon work by https://github.com/sigstore/rekor

References: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig

You can find some examples on how to use this library on: https://pkg.go.dev/github.com/42wim/sshsig#pkg-examples

Examples

package main

import (
	"bytes"
	"fmt"
	"net"
	"os"

	"github.com/42wim/sshsig"
	"golang.org/x/crypto/ssh/agent"
)

func ExampleSignWithAgent() {
	// This example will panic when you don't have a ssh-agent running.
	conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err != nil {
		panic(err)
	}

	ag := agent.NewClient(conn)

	// This public key must match in your agent (use `ssh-add -L` to get the public key)
	pubkey := []byte(`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAo3D7CGN01tTYY/dLKXEv8RxRyxa32c51X0uKMhnMab wim@localhost`)
	//
	data := []byte("hello world")

	res, err := sshsig.SignWithAgent(pubkey, ag, bytes.NewBuffer(data), "file")
	if err != nil {
		panic(err)
	}

	fmt.Println(string(res))
}

func ExampleSign() {
	privkey := []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCOjP6i4Pm/pYAAmpAMNZ6xrbHl9RW8xdul6kzIWuKMMAAAAIhoQm34aEJt
+AAAAAtzc2gtZWQyNTUxOQAAACCOjP6i4Pm/pYAAmpAMNZ6xrbHl9RW8xdul6kzIWuKMMA
AAAEBfIl93TLj6qHeg37GnPuZ00h8OVv1mzlhy0rhuO4Y0do6M/qLg+b+lgACakAw1nrGt
seX1FbzF26XqTMha4owwAAAAAAECAwQF
-----END OPENSSH PRIVATE KEY-----`)

	data := []byte("hello world")

	res, err := sshsig.Sign(privkey, bytes.NewBuffer(data), "file")
	if err != nil {
		panic(err)
	}

	fmt.Println(string(res))

	// Output:
	// -----BEGIN SSH SIGNATURE-----
	// U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgjoz+ouD5v6WAAJqQDDWesa2x5f
	// UVvMXbpepMyFrijDAAAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
	// OQAAAEBeu9Z+vLxBORysiqEbTzJP0EZKG0/aE5HpTtvimjQS6mHZCAGFg+kimNatBE0Y1j
	// gS4pfD73TlML1SyB5lb/YO
	// -----END SSH SIGNATURE-----
}

func main() {
	ExampleSign()
}

Documentation

Overview

Package sshsig implements signing/verifying armored SSH signatures. You can use this package to sign data and verify signatures using your ssh private keys or your ssh agent. It gives the same output as using `ssh-keygen`, eg when signing `ssh-keygen -Y sign -f keyfile -n namespace data`

This code is based upon work by https://github.com/sigstore/rekor

References:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Armor

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

Armored returns the signature in an armored format.

func Sign

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

Sign signs the data with the given private key in PEM format and returns an armored signature. The purpose of the namespace value is to specify a unambiguous interpretation domain for the signature, e.g. file signing. This prevents cross-protocol attacks caused by signatures intended for one intended domain being accepted in another. If empty, the default is "file". This can be compared with `ssh-keygen -Y sign -f keyfile -n namespace data`

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/42wim/sshsig"
)

func main() {
	privkey := []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCOjP6i4Pm/pYAAmpAMNZ6xrbHl9RW8xdul6kzIWuKMMAAAAIhoQm34aEJt
+AAAAAtzc2gtZWQyNTUxOQAAACCOjP6i4Pm/pYAAmpAMNZ6xrbHl9RW8xdul6kzIWuKMMA
AAAEBfIl93TLj6qHeg37GnPuZ00h8OVv1mzlhy0rhuO4Y0do6M/qLg+b+lgACakAw1nrGt
seX1FbzF26XqTMha4owwAAAAAAECAwQF
-----END OPENSSH PRIVATE KEY-----`)

	data := []byte("hello world")

	res, err := sshsig.Sign(privkey, bytes.NewBuffer(data), "file")
	if err != nil {
		panic(err)
	}

	fmt.Println(string(res))

}
Output:

-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgjoz+ouD5v6WAAJqQDDWesa2x5f
UVvMXbpepMyFrijDAAAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
OQAAAEBeu9Z+vLxBORysiqEbTzJP0EZKG0/aE5HpTtvimjQS6mHZCAGFg+kimNatBE0Y1j
gS4pfD73TlML1SyB5lb/YO
-----END SSH SIGNATURE-----

func SignWithAgent

func SignWithAgent(publicKey []byte, ag agent.Agent, data io.Reader, namespace string) ([]byte, error)

SignWithAgent asks the ssh Agent to sign the data with the signer matching the given publicKey and returns an armored signature. The purpose of the namespace value is to specify a unambiguous interpretation domain for the signature, e.g. file signing. This prevents cross-protocol attacks caused by signatures intended for one intended domain being accepted in another. If empty, the default is "file". This can be compared with `ssh-keygen -Y sign -f keyfile -n namespace data`

Example
package main

import (
	"bytes"
	"fmt"
	"net"
	"os"

	"github.com/42wim/sshsig"
	"golang.org/x/crypto/ssh/agent"
)

func main() {
	// This example will panic when you don't have a ssh-agent running.
	conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err != nil {
		panic(err)
	}

	ag := agent.NewClient(conn)

	// This public key must match in your agent (use `ssh-add -L` to get the public key)
	pubkey := []byte(`ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAo3D7CGN01tTYY/dLKXEv8RxRyxa32c51X0uKMhnMab wim@localhost`)
	//
	data := []byte("hello world")

	res, err := sshsig.SignWithAgent(pubkey, ag, bytes.NewBuffer(data), "file")
	if err != nil {
		panic(err)
	}

	fmt.Println(string(res))
}
Output:

func Verify

func Verify(message io.Reader, armoredSignature []byte, publicKey []byte, namespace string) error

Verify verifies the signature of the given data and the armored signature using the given public key and the namespace. If the namespace is empty, the default namespace (file) is used.

Types

type MessageWrapper

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

https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L81

type Signature

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

func Decode

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

Decode parses an armored signature.

type WrappedSig

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

https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L34

Directories

Path Synopsis
Package pem implements the PEM data encoding, which originated in Privacy Enhanced Mail.
Package pem implements the PEM data encoding, which originated in Privacy Enhanced Mail.

Jump to

Keyboard shortcuts

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