package ssh import ( "fmt" "io" "io/ioutil" "encoding/pem" sshLib "golang.org/x/crypto/ssh" "git.sigsum.org/sigsum-go/pkg/types" ) type Parser struct{} const ( BLOB_PREAMBLE = "SSHSIG" SIG_VERSION = 0x01 HASH_ALGO = "sha256" ) type sshBlob struct { MagicPreamble [6]byte SigVersion uint32 PublicKey string Namespace string Reserved string HashAlgorithm string Signature string } type sshKeypart struct { Type string Key string } func (p *Parser) SignatureSuffix() string { return ".sig" } func (p *Parser) PublicKey(r io.Reader) (*types.PublicKey, error) { b, err := ioutil.ReadAll(r) if err != nil { return nil, fmt.Errorf("ssh: read failed: %v", err) } var pub types.PublicKey if err := parsePub(pub[:], b); err != nil { return nil, fmt.Errorf("ssh: %v", err) } return &pub, nil } func (p *Parser) Signature(r io.Reader) (*types.Signature, error) { b, err := ioutil.ReadAll(r) if err != nil { return nil, fmt.Errorf("ssh: signature read failed: %v", err) } block, rest := pem.Decode(b) if len(rest) > 0 { return nil, fmt.Errorf("ssh: invalid signature rest: %v", rest) } if block == nil { return nil, fmt.Errorf("ssh: invalid signature PEM format") } if block.Type != "SSH SIGNATURE" { return nil, fmt.Errorf("ssh: invalid signature PEM type: %v", block.Type) } var blob sshBlob if err := sshLib.Unmarshal(block.Bytes, &blob); err != nil { return nil, fmt.Errorf("ssh: invalid signature format: %v", err) } if string(blob.MagicPreamble[:]) != BLOB_PREAMBLE { return nil, fmt.Errorf("ssh: invalid signature magic: %s", string(blob.MagicPreamble[:])) } if blob.SigVersion != SIG_VERSION { return nil, fmt.Errorf("ssh: invalid signature version: %d", blob.SigVersion) } if blob.HashAlgorithm != HASH_ALGO { return nil, fmt.Errorf("ssh: invalid signature hash algorithm: %s", blob.HashAlgorithm) } var sshSig sshLib.Signature if err := sshLib.Unmarshal([]byte(blob.Signature), &sshSig); err != nil { return nil, fmt.Errorf("ssh: invalid signature blob: %v", err) } var sig types.Signature copy(sig[:], sshSig.Blob) return &sig, nil } func parsePub(dst, data []byte) error { pubkey, _, _, _, err := sshLib.ParseAuthorizedKey(data) if err != nil { return fmt.Errorf("ssh.ParseAuthorizedKey: %s", err) } var keyPart sshKeypart if err := sshLib.Unmarshal(pubkey.Marshal(), &keyPart); err != nil { return fmt.Errorf("ssh: invalid pubkey: %v", err) } copy(dst[:], keyPart.Key) return nil }