/
verifier.go
103 lines (83 loc) · 2.4 KB
/
verifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package aws
import (
"crypto/subtle"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"time"
"github.com/fullsailor/pkcs7"
"github.com/manifoldco/torus-cli/data"
)
const (
// BootstrapTime indicates the amount of time that a machine
// is able to register with Torus after being booted
BootstrapTime = 5 // minutes
)
// Verifier verifies the AWS instance metadata
type Verifier struct {
// Identity is the []bytes of the identity metadata document
Identity []byte
// Signature is the []bytes of the identity signature
Signature []byte
}
// Verify verifies the instance metadata and instance identity documents to provide a safe way
// to provision a machine
// TODO: Verify further that the instance actually exists in AWS
func (v *Verifier) Verify() error {
if v.Identity == nil {
return fmt.Errorf("no identity document to verify")
}
if v.Signature == nil {
return fmt.Errorf("no signature found for verification")
}
signature := fmt.Sprintf("-----BEGIN PKCS7-----\n%s\n-----END PKCS7-----", v.Signature)
block, rest := pem.Decode([]byte(signature))
if len(rest) != 0 {
return fmt.Errorf("Unable to decode signature")
}
sigData, err := pkcs7.Parse(block.Bytes)
if err != nil {
return fmt.Errorf("Unable to parse signature")
}
awsCert, err := awsPublicCert()
if err != nil {
return nil
}
sigData.Certificates = []*x509.Certificate{awsCert}
if err := sigData.Verify(); err != nil {
return fmt.Errorf("failed to verify")
}
if subtle.ConstantTimeCompare(v.Identity, sigData.Content) != 1 {
return fmt.Errorf("failed to validate instance metadata")
}
var signedIdentityDoc identityDocument
json.Unmarshal(sigData.Content, &signedIdentityDoc)
elapsed := time.Since(signedIdentityDoc.PendingTime)
if elapsed.Minutes() > BootstrapTime {
return fmt.Errorf(
"failed validation - this server has beens started more than %d minutes ago",
BootstrapTime,
)
}
return nil
}
func awsPublicCert() (*x509.Certificate, error) {
awsCert, err := data.Asset("data/aws_identity_cert.pem")
if err != nil {
return nil, fmt.Errorf("unable to find AWS Public certificate")
}
block, rest := pem.Decode(awsCert)
if len(rest) != 0 {
return nil, fmt.Errorf("unable to decode AWS Public certificate")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
if cert == nil {
return nil, errors.New("invalid cerficicate")
}
return cert, nil
}