|
| 1 | +package awsidentity |
| 2 | + |
| 3 | +import ( |
| 4 | + "crypto" |
| 5 | + "crypto/rsa" |
| 6 | + "crypto/sha256" |
| 7 | + "crypto/x509" |
| 8 | + "encoding/base64" |
| 9 | + "encoding/json" |
| 10 | + "encoding/pem" |
| 11 | + |
| 12 | + "golang.org/x/xerrors" |
| 13 | +) |
| 14 | + |
| 15 | +// Region represents the AWS locations a public-key covers. |
| 16 | +type Region string |
| 17 | + |
| 18 | +const ( |
| 19 | + Other Region = "other" |
| 20 | + HongKong Region = "hongkong" |
| 21 | + Bahrain Region = "bahrain" |
| 22 | + CapeTown Region = "capetown" |
| 23 | + Milan Region = "milan" |
| 24 | + China Region = "china" |
| 25 | + GovCloud Region = "govcloud" |
| 26 | +) |
| 27 | + |
| 28 | +var ( |
| 29 | + All = []Region{Other, HongKong, Bahrain, CapeTown, Milan, China, GovCloud} |
| 30 | +) |
| 31 | + |
| 32 | +// Certificates hold public keys for various AWS regions. See: |
| 33 | +type Certificates map[Region]string |
| 34 | + |
| 35 | +// Identity represents a validated document and signature. |
| 36 | +type Identity struct { |
| 37 | + InstanceID string |
| 38 | + Region Region |
| 39 | +} |
| 40 | + |
| 41 | +// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html |
| 42 | +type awsInstanceIdentityDocument struct { |
| 43 | + InstanceID string `json:"instanceId"` |
| 44 | +} |
| 45 | + |
| 46 | +// Validate ensures the document was signed by an AWS public key. |
| 47 | +// Regions that aren't provided in certificates will use defaults. |
| 48 | +func Validate(signature, document string, certificates Certificates) (Identity, error) { |
| 49 | + if certificates == nil { |
| 50 | + certificates = Certificates{} |
| 51 | + } |
| 52 | + for _, region := range All { |
| 53 | + if _, ok := certificates[region]; ok { |
| 54 | + continue |
| 55 | + } |
| 56 | + defaultCertificate, exists := defaultCertificates[region] |
| 57 | + if !exists { |
| 58 | + panic("dev error: no certificate exists for region " + region) |
| 59 | + } |
| 60 | + certificates[region] = defaultCertificate |
| 61 | + } |
| 62 | + |
| 63 | + var instanceIdentity awsInstanceIdentityDocument |
| 64 | + err := json.Unmarshal([]byte(document), &instanceIdentity) |
| 65 | + if err != nil { |
| 66 | + return Identity{}, xerrors.Errorf("parse document: %w", err) |
| 67 | + } |
| 68 | + rawSignature, err := base64.StdEncoding.DecodeString(signature) |
| 69 | + if err != nil { |
| 70 | + return Identity{}, xerrors.Errorf("decode signature: %w", err) |
| 71 | + } |
| 72 | + hashedDocument := sha256.Sum256([]byte(document)) |
| 73 | + |
| 74 | + for region, certificate := range certificates { |
| 75 | + regionBlock, rest := pem.Decode([]byte(certificate)) |
| 76 | + if len(rest) != 0 { |
| 77 | + return Identity{}, xerrors.Errorf("invalid certificate for %q. %d bytes remain", region, len(rest)) |
| 78 | + } |
| 79 | + regionCert, err := x509.ParseCertificate(regionBlock.Bytes) |
| 80 | + if err != nil { |
| 81 | + return Identity{}, xerrors.Errorf("parse certificate: %w", err) |
| 82 | + } |
| 83 | + regionPublicKey, valid := regionCert.PublicKey.(*rsa.PublicKey) |
| 84 | + if !valid { |
| 85 | + return Identity{}, xerrors.Errorf("certificate for %q was not an rsa key", region) |
| 86 | + } |
| 87 | + err = rsa.VerifyPKCS1v15(regionPublicKey, crypto.SHA256, hashedDocument[:], rawSignature) |
| 88 | + if err != nil { |
| 89 | + continue |
| 90 | + } |
| 91 | + return Identity{ |
| 92 | + InstanceID: instanceIdentity.InstanceID, |
| 93 | + Region: region, |
| 94 | + }, nil |
| 95 | + } |
| 96 | + return Identity{}, rsa.ErrVerification |
| 97 | +} |
| 98 | + |
| 99 | +// Default AWS certificates for regions. |
| 100 | +// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/verify-signature.html |
| 101 | +var defaultCertificates = Certificates{ |
| 102 | + Other: `-----BEGIN CERTIFICATE----- |
| 103 | +MIIDIjCCAougAwIBAgIJAKnL4UEDMN/FMA0GCSqGSIb3DQEBBQUAMGoxCzAJBgNV |
| 104 | +BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgw |
| 105 | +FgYDVQQKEw9BbWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3Mu |
| 106 | +Y29tMB4XDTE0MDYwNTE0MjgwMloXDTI0MDYwNTE0MjgwMlowajELMAkGA1UEBhMC |
| 107 | +VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxGDAWBgNV |
| 108 | +BAoTD0FtYXpvbi5jb20gSW5jLjEaMBgGA1UEAxMRZWMyLmFtYXpvbmF3cy5jb20w |
| 109 | +gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAIe9GN//SRK2knbjySG0ho3yqQM3 |
| 110 | +e2TDhWO8D2e8+XZqck754gFSo99AbT2RmXClambI7xsYHZFapbELC4H91ycihvrD |
| 111 | +jbST1ZjkLQgga0NE1q43eS68ZeTDccScXQSNivSlzJZS8HJZjgqzBlXjZftjtdJL |
| 112 | +XeE4hwvo0sD4f3j9AgMBAAGjgc8wgcwwHQYDVR0OBBYEFCXWzAgVyrbwnFncFFIs |
| 113 | +77VBdlE4MIGcBgNVHSMEgZQwgZGAFCXWzAgVyrbwnFncFFIs77VBdlE4oW6kbDBq |
| 114 | +MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2Vh |
| 115 | +dHRsZTEYMBYGA1UEChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1h |
| 116 | +em9uYXdzLmNvbYIJAKnL4UEDMN/FMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF |
| 117 | +BQADgYEAFYcz1OgEhQBXIwIdsgCOS8vEtiJYF+j9uO6jz7VOmJqO+pRlAbRlvY8T |
| 118 | +C1haGgSI/A1uZUKs/Zfnph0oEI0/hu1IIJ/SKBDtN5lvmZ/IzbOPIJWirlsllQIQ |
| 119 | +7zvWbGd9c9+Rm3p04oTvhup99la7kZqevJK0QRdD/6NpCKsqP/0= |
| 120 | +-----END CERTIFICATE-----`, |
| 121 | + HongKong: `-----BEGIN CERTIFICATE----- |
| 122 | +MIICSzCCAbQCCQDtQvkVxRvK9TANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJV |
| 123 | +UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEYMBYGA1UE |
| 124 | +ChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1hem9uYXdzLmNvbTAe |
| 125 | +Fw0xOTAyMDMwMzAwMDZaFw0yOTAyMDIwMzAwMDZaMGoxCzAJBgNVBAYTAlVTMRMw |
| 126 | +EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgwFgYDVQQKEw9B |
| 127 | +bWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3MuY29tMIGfMA0G |
| 128 | +CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1kkHXYTfc7gY5Q55JJhjTieHAgacaQkiR |
| 129 | +Pity9QPDE3b+NXDh4UdP1xdIw73JcIIG3sG9RhWiXVCHh6KkuCTqJfPUknIKk8vs |
| 130 | +M3RXflUpBe8Pf+P92pxqPMCz1Fr2NehS3JhhpkCZVGxxwLC5gaG0Lr4rFORubjYY |
| 131 | +Rh84dK98VwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAA6xV9f0HMqXjPHuGILDyaNN |
| 132 | +dKcvplNFwDTydVg32MNubAGnecoEBtUPtxBsLoVYXCOb+b5/ZMDubPF9tU/vSXuo |
| 133 | +TpYM5Bq57gJzDRaBOntQbX9bgHiUxw6XZWaTS/6xjRJDT5p3S1E0mPI3lP/eJv4o |
| 134 | +Ezk5zb3eIf10/sqt4756 |
| 135 | +-----END CERTIFICATE-----`, |
| 136 | + Bahrain: `-----BEGIN CERTIFICATE----- |
| 137 | +MIIDPDCCAqWgAwIBAgIJAMl6uIV/zqJFMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNV |
| 138 | +BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSAw |
| 139 | +HgYDVQQKDBdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzEaMBgGA1UEAwwRZWMyLmFt |
| 140 | +YXpvbmF3cy5jb20wIBcNMTkwNDI2MTQzMjQ3WhgPMjE5ODA5MjkxNDMyNDdaMHIx |
| 141 | +CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0 |
| 142 | +dGxlMSAwHgYDVQQKDBdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzEaMBgGA1UEAwwR |
| 143 | +ZWMyLmFtYXpvbmF3cy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALVN |
| 144 | +CDTZEnIeoX1SEYqq6k1BV0ZlpY5y3KnoOreCAE589TwS4MX5+8Fzd6AmACmugeBP |
| 145 | +Qk7Hm6b2+g/d4tWycyxLaQlcq81DB1GmXehRkZRgGeRge1ePWd1TUA0I8P/QBT7S |
| 146 | +gUePm/kANSFU+P7s7u1NNl+vynyi0wUUrw7/wIZTAgMBAAGjgdcwgdQwHQYDVR0O |
| 147 | +BBYEFILtMd+T4YgH1cgc+hVsVOV+480FMIGkBgNVHSMEgZwwgZmAFILtMd+T4YgH |
| 148 | +1cgc+hVsVOV+480FoXakdDByMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGlu |
| 149 | +Z3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEgMB4GA1UECgwXQW1hem9uIFdlYiBTZXJ2 |
| 150 | +aWNlcyBMTEMxGjAYBgNVBAMMEWVjMi5hbWF6b25hd3MuY29tggkAyXq4hX/OokUw |
| 151 | +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBhkNTBIFgWFd+ZhC/LhRUY |
| 152 | +4OjEiykmbEp6hlzQ79T0Tfbn5A4NYDI2icBP0+hmf6qSnIhwJF6typyd1yPK5Fqt |
| 153 | +NTpxxcXmUKquX+pHmIkK1LKDO8rNE84jqxrxRsfDi6by82fjVYf2pgjJW8R1FAw+ |
| 154 | +mL5WQRFexbfB5aXhcMo0AA== |
| 155 | +-----END CERTIFICATE-----`, |
| 156 | + CapeTown: `-----BEGIN CERTIFICATE----- |
| 157 | +MIICNjCCAZ+gAwIBAgIJAKumfZiRrNvHMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV |
| 158 | +BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0 |
| 159 | +dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAgFw0xOTExMjcw |
| 160 | +NzE0MDVaGA8yMTk5MDUwMjA3MTQwNVowXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgT |
| 161 | +EFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0Ft |
| 162 | +YXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB |
| 163 | +gQDFd571nUzVtke3rPyRkYfvs3jh0C0EMzzG72boyUNjnfw1+m0TeFraTLKb9T6F |
| 164 | +7TuB/ZEN+vmlYqr2+5Va8U8qLbPF0bRH+FdaKjhgWZdYXxGzQzU3ioy5W5ZM1VyB |
| 165 | +7iUsxEAlxsybC3ziPYaHI42UiTkQNahmoroNeqVyHNnBpQIDAQABMA0GCSqGSIb3 |
| 166 | +DQEBCwUAA4GBAAJLylWyElEgOpW4B1XPyRVD4pAds8Guw2+krgqkY0HxLCdjosuH |
| 167 | +RytGDGN+q75aAoXzW5a7SGpxLxk6Hfv0xp3RjDHsoeP0i1d8MD3hAC5ezxS4oukK |
| 168 | +s5gbPOnokhKTMPXbTdRn5ZifCbWlx+bYN/mTYKvxho7b5SVg2o1La9aK |
| 169 | +-----END CERTIFICATE-----`, |
| 170 | + Milan: `-----BEGIN CERTIFICATE----- |
| 171 | +MIICNjCCAZ+gAwIBAgIJAOZ3GEIaDcugMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV |
| 172 | +BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0 |
| 173 | +dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAgFw0xOTEwMjQx |
| 174 | +NTE5MDlaGA8yMTk5MDMyOTE1MTkwOVowXDELMAkGA1UEBhMCVVMxGTAXBgNVBAgT |
| 175 | +EFdhc2hpbmd0b24gU3RhdGUxEDAOBgNVBAcTB1NlYXR0bGUxIDAeBgNVBAoTF0Ft |
| 176 | +YXpvbiBXZWIgU2VydmljZXMgTExDMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB |
| 177 | +gQCjiPgW3vsXRj4JoA16WQDyoPc/eh3QBARaApJEc4nPIGoUolpAXcjFhWplo2O+ |
| 178 | +ivgfCsc4AU9OpYdAPha3spLey/bhHPRi1JZHRNqScKP0hzsCNmKhfnZTIEQCFvsp |
| 179 | +DRp4zr91/WS06/flJFBYJ6JHhp0KwM81XQG59lV6kkoW7QIDAQABMA0GCSqGSIb3 |
| 180 | +DQEBCwUAA4GBAGLLrY3P+HH6C57dYgtJkuGZGT2+rMkk2n81/abzTJvsqRqGRrWv |
| 181 | +XRKRXlKdM/dfiuYGokDGxiC0Mg6TYy6wvsR2qRhtXW1OtZkiHWcQCnOttz+8vpew |
| 182 | +wx8JGMvowtuKB1iMsbwyRqZkFYLcvH+Opfb/Aayi20/ChQLdI6M2R5VU |
| 183 | +-----END CERTIFICATE-----`, |
| 184 | + China: `-----BEGIN CERTIFICATE----- |
| 185 | +MIICSzCCAbQCCQCQu97teKRD4zANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJV |
| 186 | +UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHU2VhdHRsZTEYMBYGA1UE |
| 187 | +ChMPQW1hem9uLmNvbSBJbmMuMRowGAYDVQQDExFlYzIuYW1hem9uYXdzLmNvbTAe |
| 188 | +Fw0xMzA4MjExMzIyNDNaFw0yMzA4MjExMzIyNDNaMGoxCzAJBgNVBAYTAlVTMRMw |
| 189 | +EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0dGxlMRgwFgYDVQQKEw9B |
| 190 | +bWF6b24uY29tIEluYy4xGjAYBgNVBAMTEWVjMi5hbWF6b25hd3MuY29tMIGfMA0G |
| 191 | +CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GFQ2WoBl1xZYH85INUMaTc4D30QXM6f+ |
| 192 | +YmWZyJD9fC7Z0UlaZIKoQATqCO58KNCre+jECELYIX56Uq0lb8LRLP8tijrQ9Sp3 |
| 193 | +qJcXiH66kH0eQ44a5YdewcFOy+CSAYDUIaB6XhTQJ2r7bd4A2vw3ybbxTOWONKdO |
| 194 | +WtgIe3M3iwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAHzQC5XZVeuD9GTJTsbO5AyH |
| 195 | +ZQvki/jfARNrD9dgBRYZzLC/NOkWG6M9wlrmks9RtdNxc53nLxKq4I2Dd73gI0yQ |
| 196 | +wYu9YYwmM/LMgmPlI33Rg2Ohwq4DVgT3hO170PL6Fsgiq3dMvctSImJvjWktBQaT |
| 197 | +bcAgaZLHGIpXPrWSA2d+ |
| 198 | +-----END CERTIFICATE-----`, |
| 199 | + GovCloud: `-----BEGIN CERTIFICATE----- |
| 200 | +MIIDCzCCAnSgAwIBAgIJAIe9Hnq82O7UMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV |
| 201 | +BAYTAlVTMRkwFwYDVQQIExBXYXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0 |
| 202 | +dGxlMSAwHgYDVQQKExdBbWF6b24gV2ViIFNlcnZpY2VzIExMQzAeFw0yMTA3MTQx |
| 203 | +NDI3NTdaFw0yNDA3MTMxNDI3NTdaMFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQIExBX |
| 204 | +YXNoaW5ndG9uIFN0YXRlMRAwDgYDVQQHEwdTZWF0dGxlMSAwHgYDVQQKExdBbWF6 |
| 205 | +b24gV2ViIFNlcnZpY2VzIExMQzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA |
| 206 | +qaIcGFFTx/SO1W5G91jHvyQdGP25n1Y91aXCuOOWAUTvSvNGpXrI4AXNrQF+CmIO |
| 207 | +C4beBASnHCx082jYudWBBl9Wiza0psYc9flrczSzVLMmN8w/c78F/95NfiQdnUQP |
| 208 | +pvgqcMeJo82cgHkLR7XoFWgMrZJqrcUK0gnsQcb6kakCAwEAAaOB1DCB0TALBgNV |
| 209 | +HQ8EBAMCB4AwHQYDVR0OBBYEFNWV53gWJz72F5B1ZVY4O/dfFYBPMIGOBgNVHSME |
| 210 | +gYYwgYOAFNWV53gWJz72F5B1ZVY4O/dfFYBPoWCkXjBcMQswCQYDVQQGEwJVUzEZ |
| 211 | +MBcGA1UECBMQV2FzaGluZ3RvbiBTdGF0ZTEQMA4GA1UEBxMHU2VhdHRsZTEgMB4G |
| 212 | +A1UEChMXQW1hem9uIFdlYiBTZXJ2aWNlcyBMTEOCCQCHvR56vNju1DASBgNVHRMB |
| 213 | +Af8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBCwUAA4GBACrKjWj460GUPZCGm3/z0dIz |
| 214 | +M2BPuH769wcOsqfFZcMKEysSFK91tVtUb1soFwH4/Lb/T0PqNrvtEwD1Nva5k0h2 |
| 215 | +xZhNNRmDuhOhW1K9wCcnHGRBwY5t4lYL6hNV6hcrqYwGMjTjcAjBG2yMgznSNFle |
| 216 | +Rwi/S3BFXISixNx9cILu |
| 217 | +-----END CERTIFICATE-----`, |
| 218 | +} |
0 commit comments