...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package ssh
17
18 import (
19 "encoding/pem"
20 "errors"
21 "fmt"
22
23 "golang.org/x/crypto/ssh"
24 )
25
26 const (
27 namespace = "file"
28 pemType = "SSH SIGNATURE"
29 )
30
31 func Armor(s *ssh.Signature, p ssh.PublicKey) []byte {
32 sig := WrappedSig{
33 Version: 1,
34 PublicKey: string(p.Marshal()),
35 Namespace: namespace,
36 HashAlgorithm: defaultHashAlgorithm,
37 Signature: string(ssh.Marshal(s)),
38 }
39
40 copy(sig.MagicHeader[:], magicHeader)
41
42 enc := pem.EncodeToMemory(&pem.Block{
43 Type: pemType,
44 Bytes: ssh.Marshal(sig),
45 })
46 return enc
47 }
48
49 func Decode(b []byte) (*Signature, error) {
50 pemBlock, _ := pem.Decode(b)
51 if pemBlock == nil {
52 return nil, errors.New("unable to decode pem file")
53 }
54
55 if pemBlock.Type != pemType {
56 return nil, fmt.Errorf("wrong pem block type: %s. Expected SSH-SIGNATURE", pemBlock.Type)
57 }
58
59
60 sig := WrappedSig{}
61 if err := ssh.Unmarshal(pemBlock.Bytes, &sig); err != nil {
62 return nil, err
63 }
64
65 if sig.Version != 1 {
66 return nil, fmt.Errorf("unsupported signature version: %d", sig.Version)
67 }
68 if string(sig.MagicHeader[:]) != magicHeader {
69 return nil, fmt.Errorf("invalid magic header: %s", sig.MagicHeader[:])
70 }
71 if sig.Namespace != "file" {
72 return nil, fmt.Errorf("invalid signature namespace: %s", sig.Namespace)
73 }
74 if _, ok := supportedHashAlgorithms[sig.HashAlgorithm]; !ok {
75 return nil, fmt.Errorf("unsupported hash algorithm: %s", sig.HashAlgorithm)
76 }
77
78
79 sshSig := ssh.Signature{}
80 if err := ssh.Unmarshal([]byte(sig.Signature), &sshSig); err != nil {
81 return nil, err
82 }
83
84
85 pk, err := ssh.ParsePublicKey([]byte(sig.PublicKey))
86 if err != nil {
87 return nil, err
88 }
89
90 return &Signature{
91 signature: &sshSig,
92 pk: pk,
93 hashAlg: sig.HashAlgorithm,
94 }, nil
95 }
96
View as plain text