Documentation ¶
Overview ¶
Package sshsig provides an API to sign and verify messages using SSH keys. It is an implementation of the SSH Signature format as described in https://github.com/openssh/openssh-portable/blob/V_9_3_P1/PROTOCOL.sshsig.
Index ¶
- Constants
- Variables
- func Armor(s *Signature) []byte
- func Verify(m io.Reader, sig *Signature, pub ssh.PublicKey, h HashAlgorithm, ...) error
- type HashAlgorithm
- type Signature
- func ParseSignature(b []byte) (*Signature, error)
- func Sign(m io.Reader, signer ssh.Signer, h HashAlgorithm, namespace string) (*Signature, error)
- func SignWithRand(m, rand io.Reader, signer ssh.Signer, h HashAlgorithm, namespace string) (*Signature, error)
- func Unarmor(b []byte) (*Signature, error)
Examples ¶
Constants ¶
const PEMType = "SSH SIGNATURE"
PEMType is the PEM type of an armored SSH signature.
Variables ¶
var ( // ErrUnsupportedHashAlgorithm is returned by Sign and Verify if the hash // algorithm is not supported. ErrUnsupportedHashAlgorithm = errors.New("unsupported hash algorithm") // algorithm is not available. ErrUnavailableHashAlgorithm = errors.New("unavailable hash algorithm") )
var ( // ErrPublicKeyMismatch is returned by Verify if the public key in the signature // does not match the public key used to verify the signature. ErrPublicKeyMismatch = errors.New("public key does not match") // ErrNamespaceMismatch is returned by Verify if the namespace in the signature // does not match the namespace used to verify the signature. ErrNamespaceMismatch = errors.New("namespace does not match") )
var ( // ErrUnsupportedSignatureVersion is returned when the signature version is // not supported. ErrUnsupportedSignatureVersion = errors.New("unsupported signature version") // ErrInvalidMagicPreamble is returned when the magic preamble is invalid. ErrInvalidMagicPreamble = errors.New("invalid magic preamble") )
var ErrMissingNamespace = errors.New("missing namespace")
ErrMissingNamespace is returned by Sign if the namespace value is missing.
Functions ¶
func Verify ¶
func Verify(m io.Reader, sig *Signature, pub ssh.PublicKey, h HashAlgorithm, namespace string) error
Verify verifies the message from the io.Reader matches the Signature using the given ssh.PublicKey and HashAlgorithm.
The purpose of the namespace value is to specify an 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. Unlike Sign, the namespace value is not required to allow verification of signatures created by looser implementations.
Verify returns an error if the verification process fails.
Example ¶
// Load a public key to verify with. pub, _, _, _, err := ssh.ParseAuthorizedKey([]byte(ed25519PublicKey)) if err != nil { panic(err) } // Load the armored (PEM) signature to verify. armored := []byte(`-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1 NgAAAEEEOIYuWF0v/w8XVrOLUa30nMhLwiXdsf4aow88kfpnfA/Zn+Xhr9nRh97e tNV1/Kqv1VE/On/YH+094IhlatyELQAAAARmaWxlAAAAAAAAAAZzaGE1MTIAAABk AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABJAAAAIBXp90537Om8Xbv0iTxVwvSy iZmhAca7kPt0uSg0IVtTAAAAIQCbN+co4miAJ7t9XLIQuOaOQCM5P0AxRCdsMG4e BnAL0w== -----END SSH SIGNATURE-----`) sig, err := sshsig.Unarmor(armored) if err != nil { panic(err) } // Verify the signature, using the same hash algorithm and namespace as // used to sign the message. If the signature is valid, no error is // returned. message := []byte("Hello world!") if err := sshsig.Verify(bytes.NewReader(message), sig, pub, sig.HashAlgorithm, sig.Namespace); err != nil { panic(err) } // When more strict verification is required, the hash algorithm and/or // namespace can be checked against the expected values. if err := sshsig.Verify(bytes.NewReader(message), sig, pub, sshsig.HashSHA512, "file"); err != nil { panic(err) }
Output:
Types ¶
type HashAlgorithm ¶
type HashAlgorithm string
HashAlgorithm represents an algorithm used to compute a hash of a message.
const ( // HashSHA256 is the SHA-256 hash algorithm. HashSHA256 HashAlgorithm = "sha256" // HashSHA512 is the SHA-512 hash algorithm. HashSHA512 HashAlgorithm = "sha512" )
func SupportedHashAlgorithms ¶
func SupportedHashAlgorithms() []HashAlgorithm
SupportedHashAlgorithms returns a list of supported hash algorithms.
func (HashAlgorithm) Available ¶
func (h HashAlgorithm) Available() error
Available returns ErrUnsupportedHashAlgorithm if the hash algorithm is not supported, ErrUnavailableHashAlgorithm if the hash algorithm is not available, nil otherwise.
func (HashAlgorithm) Hash ¶
func (h HashAlgorithm) Hash() hash.Hash
Hash returns a hash.Hash for the hash algorithm. If the hash algorithm is not available, it panics. The library itself ensures that the hash algorithm is available before calling this function.
func (HashAlgorithm) String ¶
func (h HashAlgorithm) String() string
String returns the string representation of the hash algorithm.
func (HashAlgorithm) Supported ¶
func (h HashAlgorithm) Supported() error
Supported returns ErrUnsupportedHashAlgorithm if the hash algorithm is not supported, nil otherwise. Use Available if the intention is to make use of the hash algorithm, as it also checks if the hash algorithm is available.
type Signature ¶
type Signature struct { // Version is the version of the signature format. // It currently supports version 1, any other value will be rejected with // ErrUnsupportedSignatureVersion. Version uint32 // PublicKey is the public key used to create the Signature. PublicKey ssh.PublicKey // Namespace is the domain of the signature, and is used to prevent signature // reuse across different applications. Namespace string // HashAlgorithm is the hash algorithm used to hash the Signature message. HashAlgorithm HashAlgorithm // Signature is the SSH signature of the hash of the message. Signature *ssh.Signature }
Signature represents the SSH signature of a message. It can be marshaled into an SSH wire format using Marshal, or into an armored (PEM) format using Armor.
Manually construction of this type is not recommended. Use ParseSignature or Unarmor instead to retrieve a Signature from a wire or armored (PEM) format.
func ParseSignature ¶
ParseSignature parses a signature in SSH wire format into a Signature. It returns an error if the signature is invalid.
func Sign ¶
Sign generates a signature of the message from the io.Reader using the given ssh.Signer private key. The signature hash is computed using the provided HashAlgorithm.
The purpose of the namespace value is to specify an 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. The namespace must not be empty, or ErrMissingNamespace will be returned.
When the signer is an RSA key, the signature algorithm will always be "rsa-sha2-512". This is the same default used by OpenSSH, and is required by the SSH signature wire protocol.
Sign returns a Signature containing the signed message and metadata, or an error if the signing process fails.
Example ¶
// Load the private key to sign with. signer, err := ssh.ParsePrivateKey([]byte(ecdsaPrivateKey)) if err != nil { panic(err) } // Sign a message with the private key, using an SHA-512 hash and the // namespace "file". message := []byte("Hello world!") sig, err := sshsig.Sign(bytes.NewReader(message), signer, sshsig.HashSHA512, "file") if err != nil { panic(err) } // Print the signature in armored (PEM) format. armored := sshsig.Armor(sig) fmt.Printf("%s", armored)
Output:
func SignWithRand ¶
func SignWithRand(m, rand io.Reader, signer ssh.Signer, h HashAlgorithm, namespace string) (*Signature, error)
SignWithRand is like Sign, but uses the provided rand io.Reader to create any necessary random values. Most callers likely want to use Sign instead.