diff --git a/bazel/toolchains/go_module_deps.bzl b/bazel/toolchains/go_module_deps.bzl index d1fd24314..716a5900d 100644 --- a/bazel/toolchains/go_module_deps.bzl +++ b/bazel/toolchains/go_module_deps.bzl @@ -1718,6 +1718,15 @@ def go_dependencies(): sum = "h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=", version = "v1.1.0", ) + go_repository( + name = "com_github_edgelesssys_go_azguestattestation", + build_file_generation = "on", + build_file_proto_mode = "disable_global", + importpath = "github.com/edgelesssys/go-azguestattestation", + sum = "h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=", + version = "v0.0.0-20230303085714-62ede861d33f", + ) + go_repository( name = "com_github_edsrzf_mmap_go", build_file_generation = "on", @@ -2724,8 +2733,9 @@ def go_dependencies(): build_file_generation = "on", build_file_proto_mode = "disable_global", importpath = "github.com/google/go-tpm", - sum = "h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=", - version = "v0.3.3", + replace = "github.com/thomasten/go-tpm", + sum = "h1:4/WcA7Q05f5to+jVERKcZjU5NJ89DiRHP3UUZHLfAtw=", + version = "v0.0.0-20230222180349-bb3cc5560299", ) go_repository( name = "com_github_google_go_tpm_tools", diff --git a/bootstrapper/initproto/init.pb.go b/bootstrapper/initproto/init.pb.go index 741aa9a26..04486987a 100644 --- a/bootstrapper/initproto/init.pb.go +++ b/bootstrapper/initproto/init.pb.go @@ -35,9 +35,9 @@ type InitRequest struct { KubernetesVersion string `protobuf:"bytes,8,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"` // repeated SSHUserKey ssh_user_keys = 9; removed // bytes salt = 10; removed - HelmDeployments []byte `protobuf:"bytes,11,opt,name=helm_deployments,json=helmDeployments,proto3" json:"helm_deployments,omitempty"` - EnforcedPcrs []uint32 `protobuf:"varint,12,rep,packed,name=enforced_pcrs,json=enforcedPcrs,proto3" json:"enforced_pcrs,omitempty"` - EnforceIdkeydigest bool `protobuf:"varint,13,opt,name=enforce_idkeydigest,json=enforceIdkeydigest,proto3" json:"enforce_idkeydigest,omitempty"` + HelmDeployments []byte `protobuf:"bytes,11,opt,name=helm_deployments,json=helmDeployments,proto3" json:"helm_deployments,omitempty"` + EnforcedPcrs []uint32 `protobuf:"varint,12,rep,packed,name=enforced_pcrs,json=enforcedPcrs,proto3" json:"enforced_pcrs,omitempty"` + // bool enforce_idkeydigest = 13; removed ConformanceMode bool `protobuf:"varint,14,opt,name=conformance_mode,json=conformanceMode,proto3" json:"conformance_mode,omitempty"` KubernetesComponents []*KubernetesComponent `protobuf:"bytes,15,rep,name=kubernetes_components,json=kubernetesComponents,proto3" json:"kubernetes_components,omitempty"` InitSecret []byte `protobuf:"bytes,16,opt,name=init_secret,json=initSecret,proto3" json:"init_secret,omitempty"` @@ -118,13 +118,6 @@ func (x *InitRequest) GetEnforcedPcrs() []uint32 { return nil } -func (x *InitRequest) GetEnforceIdkeydigest() bool { - if x != nil { - return x.EnforceIdkeydigest - } - return false -} - func (x *InitRequest) GetConformanceMode() bool { if x != nil { return x.ConformanceMode @@ -291,7 +284,7 @@ var File_init_proto protoreflect.FileDescriptor var file_init_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e, - 0x69, 0x74, 0x22, 0xf1, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x69, 0x74, 0x22, 0xc0, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6b, 0x6d, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x6d, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, @@ -307,44 +300,41 @@ var file_init_proto_rawDesc = []byte{ 0x52, 0x0f, 0x68, 0x65, 0x6c, 0x6d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x5f, 0x70, 0x63, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x64, 0x50, 0x63, 0x72, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x6b, 0x65, 0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x49, 0x64, 0x6b, 0x65, - 0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x6f, - 0x64, 0x65, 0x12, 0x4e, 0x0a, 0x15, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, - 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, - 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75, - 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, - 0x22, 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, - 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, - 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, - 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x41, 0x50, - 0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74, - 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69, - 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, - 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x6f, 0x6f, 0x74, - 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x64, 0x50, 0x63, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x64, + 0x65, 0x12, 0x4e, 0x0a, 0x15, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75, 0x62, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, + 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, + 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x41, 0x50, 0x49, + 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, + 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69, 0x6e, + 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, + 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, + 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/bootstrapper/initproto/init.proto b/bootstrapper/initproto/init.proto index 68e0440cd..a7cb178bc 100644 --- a/bootstrapper/initproto/init.proto +++ b/bootstrapper/initproto/init.proto @@ -21,7 +21,7 @@ message InitRequest { // bytes salt = 10; removed bytes helm_deployments = 11; repeated uint32 enforced_pcrs = 12; - bool enforce_idkeydigest = 13; + // bool enforce_idkeydigest = 13; removed bool conformance_mode = 14; repeated KubernetesComponent kubernetes_components = 15; bytes init_secret = 16; diff --git a/bootstrapper/internal/kubernetes/kubernetes.go b/bootstrapper/internal/kubernetes/kubernetes.go index 715c819fa..e8b576396 100644 --- a/bootstrapper/internal/kubernetes/kubernetes.go +++ b/bootstrapper/internal/kubernetes/kubernetes.go @@ -81,9 +81,8 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura // InitCluster initializes a new Kubernetes cluster and applies pod network provider. func (k *KubeWrapper) InitCluster( - ctx context.Context, cloudServiceAccountURI, versionString, clusterName string, - measurementSalt []byte, enforcedPCRs []uint32, helmReleasesRaw []byte, conformanceMode bool, - kubernetesComponents components.Components, log *logger.Logger, + ctx context.Context, cloudServiceAccountURI, versionString, clusterName string, measurementSalt []byte, enforcedPCRs []uint32, + helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger, ) ([]byte, error) { log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components") if err := k.clusterUtil.InstallComponents(ctx, kubernetesComponents); err != nil { diff --git a/cli/internal/cloudcmd/create.go b/cli/internal/cloudcmd/create.go index 6a427dc14..1ff354a09 100644 --- a/cli/internal/cloudcmd/create.go +++ b/cli/internal/cloudcmd/create.go @@ -27,6 +27,7 @@ import ( "github.com/edgelesssys/constellation/v2/cli/internal/image" "github.com/edgelesssys/constellation/v2/cli/internal/libvirt" "github.com/edgelesssys/constellation/v2/cli/internal/terraform" + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/constants" @@ -206,7 +207,7 @@ func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *c ImageID: image, ConfidentialVM: *config.Provider.Azure.ConfidentialVM, SecureBoot: *config.Provider.Azure.SecureBoot, - CreateMAA: *config.Provider.Azure.EnforceIDKeyDigest, + CreateMAA: config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback, Debug: config.IsDebugCluster(), } diff --git a/cli/internal/cloudcmd/validators.go b/cli/internal/cloudcmd/validators.go index 80331ca94..7268c7081 100644 --- a/cli/internal/cloudcmd/validators.go +++ b/cli/internal/cloudcmd/validators.go @@ -26,14 +26,13 @@ import ( type Validator struct { attestationVariant oid.Getter pcrs measurements.M - idkeydigests idkeydigest.IDKeyDigests - enforceIDKeyDigest bool + idKeyConfig idkeydigest.Config validator atls.Validator log debugLog } // NewValidator creates a new Validator. -func NewValidator(conf *config.Config, log debugLog) (*Validator, error) { +func NewValidator(conf *config.Config, maaURL string, log debugLog) (*Validator, error) { v := Validator{log: log} variant, err := oid.FromString(conf.AttestationVariant) if err != nil { @@ -46,8 +45,11 @@ func NewValidator(conf *config.Config, log debugLog) (*Validator, error) { } if v.attestationVariant.OID().Equal(oid.AzureSEVSNP{}.OID()) { - v.enforceIDKeyDigest = conf.EnforcesIDKeyDigest() - v.idkeydigests = conf.IDKeyDigests() + v.idKeyConfig = idkeydigest.Config{ + IDKeyDigests: conf.Provider.Azure.IDKeyDigest, + EnforcementPolicy: conf.IDKeyDigestPolicy(), + MAAURL: maaURL, + } } return &v, nil @@ -138,7 +140,7 @@ func (v *Validator) updateValidator(cmd *cobra.Command) { log := warnLogger{cmd: cmd, log: v.log} // Use of a valid variant has been check in NewValidator so we may drop the error - v.validator, _ = choose.Validator(v.attestationVariant, v.pcrs, v.idkeydigests, v.enforceIDKeyDigest, log) + v.validator, _ = choose.Validator(v.attestationVariant, v.pcrs, v.idKeyConfig, log) } // warnLogger implements logging of warnings for validators. diff --git a/cli/internal/cloudcmd/validators_test.go b/cli/internal/cloudcmd/validators_test.go index 7210452c3..2a4e319dc 100644 --- a/cli/internal/cloudcmd/validators_test.go +++ b/cli/internal/cloudcmd/validators_test.go @@ -110,7 +110,7 @@ func TestNewValidator(t *testing.T) { Azure: &config.AzureConfig{ Measurements: testPCRs, IDKeyDigest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")}, - EnforceIDKeyDigest: &[]bool{true}[0], + EnforceIDKeyDigest: idkeydigest.StrictChecking, }, }, }, @@ -121,7 +121,7 @@ func TestNewValidator(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - validators, err := NewValidator(tc.config, logger.NewTest(t)) + validators, err := NewValidator(tc.config, "https://192.0.2.1:8080/maa", logger.NewTest(t)) if tc.wantErr { assert.Error(err) @@ -168,7 +168,11 @@ func TestValidatorV(t *testing.T) { "azure cvm": { variant: oid.AzureSEVSNP{}, pcrs: newTestPCRs(), - wantVs: snp.NewValidator(newTestPCRs(), idkeydigest.IDKeyDigests{}, false, nil), + wantVs: snp.NewValidator( + newTestPCRs(), + idkeydigest.Config{IDKeyDigests: idkeydigest.IDKeyDigests{}, EnforcementPolicy: idkeydigest.WarnOnly}, + nil, + ), }, "azure trusted launch": { variant: oid.AzureTrustedLaunch{}, diff --git a/cli/internal/cmd/BUILD.bazel b/cli/internal/cmd/BUILD.bazel index 5c4fc089c..0e5e6d865 100644 --- a/cli/internal/cmd/BUILD.bazel +++ b/cli/internal/cmd/BUILD.bazel @@ -43,6 +43,7 @@ go_library( "//cli/internal/terraform", "//disk-mapper/recoverproto", "//internal/atls", + "//internal/attestation/idkeydigest", "//internal/attestation/measurements", "//internal/cloud/azureshared", "//internal/cloud/cloudprovider", diff --git a/cli/internal/cmd/create.go b/cli/internal/cmd/create.go index c7fee54c9..187fb2f46 100644 --- a/cli/internal/cmd/create.go +++ b/cli/internal/cmd/create.go @@ -13,6 +13,7 @@ import ( "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/terraform" + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/constants" @@ -97,8 +98,8 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler if conf.IsAzureNonCVM() { cmd.PrintErrln("Disabling Confidential VMs is insecure. Use only for evaluation purposes.") printedAWarning = true - if conf.EnforcesIDKeyDigest() { - cmd.PrintErrln("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.") + if conf.IDKeyDigestPolicy() == idkeydigest.StrictChecking || conf.IDKeyDigestPolicy() == idkeydigest.MAAFallback { + cmd.PrintErrln("Your config asks for validating the idkeydigest. This is only available on Confidential VMs. It will not be enforced.") } } diff --git a/cli/internal/cmd/init.go b/cli/internal/cmd/init.go index e0bd9dc9a..63074f9e9 100644 --- a/cli/internal/cmd/init.go +++ b/cli/internal/cmd/init.go @@ -137,7 +137,9 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud cmd.PrintErrf("License check failed: %v", err) } i.log.Debugf("Checked license") - validator, err := cloudcmd.NewValidator(conf, i.log) + + i.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant) + validator, err := cloudcmd.NewValidator(conf, idFile.AttestationURL, i.log) if err != nil { return err } @@ -153,7 +155,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud } helmLoader := helm.NewLoader(provider, k8sVersion) i.log.Debugf("Created new Helm loader") - helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt) + helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt, idFile.AttestationURL) i.log.Debugf("Loaded Helm deployments") if err != nil { return fmt.Errorf("loading Helm charts: %w", err) @@ -172,7 +174,6 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud KubernetesComponents: versions.VersionConfigs[k8sVersion].KubernetesComponents.ToInitProto(), HelmDeployments: helmDeployments, EnforcedPcrs: conf.EnforcedPCRs(), - EnforceIdkeydigest: conf.EnforcesIDKeyDigest(), ConformanceMode: flags.conformance, InitSecret: idFile.InitSecret, ClusterName: clusterName, @@ -426,17 +427,6 @@ func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler fi return secret, nil } -func readIPFromIDFile(fileHandler file.Handler) (string, error) { - var idFile clusterid.File - if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil { - return "", err - } - if idFile.IP == "" { - return "", fmt.Errorf("missing IP address in %q", constants.ClusterIDsFileName) - } - return idFile.IP, nil -} - func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, fileHandler file.Handler) (string, error) { i.log.Debugf("Getting service account URI") switch provider { diff --git a/cli/internal/cmd/recover.go b/cli/internal/cmd/recover.go index 0a1de0905..bccb8f562 100644 --- a/cli/internal/cmd/recover.go +++ b/cli/internal/cmd/recover.go @@ -16,6 +16,7 @@ import ( "time" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" + "github.com/edgelesssys/constellation/v2/cli/internal/clusterid" "github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/config" @@ -95,7 +96,8 @@ func (r *recoverCmd) recover( interval = 20 * time.Second // Azure LB takes a while to remove unhealthy instances } - validator, err := cloudcmd.NewValidator(conf, r.log) + r.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant) + validator, err := cloudcmd.NewValidator(conf, flags.maaURL, r.log) if err != nil { return err } @@ -208,20 +210,23 @@ type recoverFlags struct { endpoint string secretPath string configPath string + maaURL string force bool } func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) { + var idFile clusterid.File + if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil && !errors.Is(err, afero.ErrFileNotFound) { + return recoverFlags{}, err + } + endpoint, err := cmd.Flags().GetString("endpoint") r.log.Debugf("Endpoint flag is %s", endpoint) if err != nil { return recoverFlags{}, fmt.Errorf("parsing endpoint argument: %w", err) } if endpoint == "" { - endpoint, err = readIPFromIDFile(fileHandler) - if err != nil { - return recoverFlags{}, fmt.Errorf("getting recovery endpoint: %w", err) - } + endpoint = idFile.IP } endpoint, err = addPortIfMissing(endpoint, constants.RecoveryPort) if err != nil { @@ -248,6 +253,7 @@ func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Hand endpoint: endpoint, secretPath: masterSecretPath, configPath: configPath, + maaURL: idFile.AttestationURL, force: force, }, nil } diff --git a/cli/internal/cmd/verify.go b/cli/internal/cmd/verify.go index 966e1ce8b..f1d0eb197 100644 --- a/cli/internal/cmd/verify.go +++ b/cli/internal/cmd/verify.go @@ -11,7 +11,6 @@ import ( "context" "errors" "fmt" - "io/fs" "net" "strconv" "strings" @@ -84,9 +83,8 @@ func (v *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC return err } - provider := conf.GetProvider() - v.log.Debugf("Creating aTLS Validator for %s", provider) - validators, err := cloudcmd.NewValidator(conf, v.log) + v.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant) + validators, err := cloudcmd.NewValidator(conf, flags.maaURL, v.log) if err != nil { return err } @@ -143,24 +141,24 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle } v.log.Debugf("Flag 'force' set to %t", force) + var idFile clusterid.File + if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil && !errors.Is(err, afero.ErrFileNotFound) { + return verifyFlags{}, fmt.Errorf("reading cluster ID file: %w", err) + } + // Get empty values from ID file emptyEndpoint := endpoint == "" emptyIDs := ownerID == "" && clusterID == "" if emptyEndpoint || emptyIDs { v.log.Debugf("Trying to supplement empty flag values from %q", constants.ClusterIDsFileName) - var idFile clusterid.File - if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err == nil { - if emptyEndpoint { - cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName) - endpoint = idFile.IP - } - if emptyIDs { - cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", constants.ClusterIDsFileName) - ownerID = idFile.OwnerID - clusterID = idFile.ClusterID - } - } else if !errors.Is(err, fs.ErrNotExist) { - return verifyFlags{}, fmt.Errorf("reading cluster ID file: %w", err) + if emptyEndpoint { + cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName) + endpoint = idFile.IP + } + if emptyIDs { + cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", constants.ClusterIDsFileName) + ownerID = idFile.OwnerID + clusterID = idFile.ClusterID } } @@ -178,6 +176,7 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle configPath: configPath, ownerID: ownerID, clusterID: clusterID, + maaURL: idFile.AttestationURL, force: force, }, nil } @@ -187,6 +186,7 @@ type verifyFlags struct { ownerID string clusterID string configPath string + maaURL string force bool } diff --git a/cli/internal/helm/BUILD.bazel b/cli/internal/helm/BUILD.bazel index 06f52a71a..d5b2243e3 100644 --- a/cli/internal/helm/BUILD.bazel +++ b/cli/internal/helm/BUILD.bazel @@ -319,6 +319,7 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/cli/internal/helm", visibility = ["//cli:__subpackages__"], deps = [ + "//internal/attestation/idkeydigest", "//internal/cloud/cloudprovider", "//internal/compatibility", "//internal/config", @@ -352,6 +353,7 @@ go_test( data = glob(["testdata/**"]), embed = [":helm"], deps = [ + "//internal/attestation/idkeydigest", "//internal/attestation/measurements", "//internal/cloud/cloudprovider", "//internal/compatibility", diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/templates/configmap.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/templates/configmap.yaml index bd2db4225..6f7abc449 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/templates/configmap.yaml +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/templates/configmap.yaml @@ -7,9 +7,7 @@ data: {{/* mustToJson is required so the json-strings passed from go are of type string in the rendered yaml. */}} measurements: {{ .Values.measurements | mustToJson }} {{- if eq .Values.csp "Azure" }} - {{/* ConfigMap.data is of type map[string]string. quote will not quote a quoted string. */}} - enforceIdKeyDigest: {{ .Values.enforceIdKeyDigest | quote }} - idkeydigests: {{ .Values.idkeydigests | mustToJson }} + idKeyConfig: {{ .Values.idKeyConfig | mustToJson }} {{- end }} binaryData: measurementSalt: {{ .Values.measurementSalt }} diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.schema.json b/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.schema.json index d3908448e..ecf44a524 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.schema.json +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.schema.json @@ -11,13 +11,13 @@ "examples": ["{'1':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA','warnOnly':true},'15':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=','warnOnly':true}}"] }, "enforceIdKeyDigest": { - "description": "Whether or not idkeydigest should be enforced during attestation on azure.", - "type": "boolean" + "description": "ID Key Digest enforcement policy.", + "enum": ["StrictChecking", "MAAFallback", "WarnOnly"] }, - "idkeydigests": { - "description": "List of expected idkeydigest values for Azure SNP attestation.", + "idKeyConfig": { + "description": "Configuration for validating the ID Key Digest of the SEV-SNP attestation.", "type": "string", - "examples": ["[\"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696\", \"0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3\"]"] + "examples": ["{'EnforcementPolicy': 'MAAFallback', 'MAAURL': 'https://192.0.2.1:8080/maa', 'IDKeyDigests': ['57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696', '0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3'}"] }, "image": { "description": "Container image to use for the spawned pods.", @@ -46,7 +46,7 @@ "properties": { "csp": { "const": "azure" } }, "required": ["csp"] }, - "then": { "required": ["enforceIdKeyDigest", "idkeydigests"] }, + "then": { "required": ["idKeyConfig"] }, "title": "Values", "type": "object" } diff --git a/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.yaml b/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.yaml index cd31be926..9c22ea413 100644 --- a/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.yaml +++ b/cli/internal/helm/charts/edgeless/constellation-services/charts/join-service/values.yaml @@ -1,4 +1,7 @@ csp: "gcp" attestationVariant: "" +measurements: "" +idKeyConfig: "" +measurementSalt: "" joinServicePort: 9090 joinServiceNodePort: 30090 diff --git a/cli/internal/helm/loader.go b/cli/internal/helm/loader.go index ecf68b150..393a89389 100644 --- a/cli/internal/helm/loader.go +++ b/cli/internal/helm/loader.go @@ -17,6 +17,7 @@ import ( "path/filepath" "strings" + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/config" @@ -107,7 +108,7 @@ func AvailableServiceVersions() (string, error) { } // Load the embedded helm charts. -func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSecret, salt []byte) ([]byte, error) { +func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSecret, salt []byte, maaURL string) ([]byte, error) { ciliumRelease, err := i.loadRelease(ciliumInfo) if err != nil { return nil, fmt.Errorf("loading cilium: %w", err) @@ -128,7 +129,7 @@ func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSe if err != nil { return nil, fmt.Errorf("loading constellation-services: %w", err) } - if err := extendConstellationServicesValues(conServicesRelease.Values, config, masterSecret, salt); err != nil { + if err := extendConstellationServicesValues(conServicesRelease.Values, config, masterSecret, salt, maaURL); err != nil { return nil, fmt.Errorf("extending constellation-services values: %w", err) } @@ -467,7 +468,9 @@ func (i *ChartLoader) loadConstellationServicesValues() (map[string]any, error) // reuse user input from the init step. However, we can't rely on reuse-values, because // during upgrades all values need to be set locally as they might have changed. // Also, the charts are not rendered correctly without all of these values. -func extendConstellationServicesValues(in map[string]any, config *config.Config, masterSecret, salt []byte) error { +func extendConstellationServicesValues( + in map[string]any, config *config.Config, masterSecret, salt []byte, maaURL string, +) error { keyServiceValues, ok := in["key-service"].(map[string]any) if !ok { return errors.New("missing 'key-service' key") @@ -494,13 +497,17 @@ func extendConstellationServicesValues(in map[string]any, config *config.Config, if !ok { return errors.New("invalid join-service values") } - joinServiceVals["enforceIdKeyDigest"] = config.EnforcesIDKeyDigest() - marshalledDigests, err := json.Marshal(config.IDKeyDigests()) - if err != nil { - return fmt.Errorf("marshalling id key digests: %w", err) + idKeyCfg := idkeydigest.Config{ + IDKeyDigests: config.IDKeyDigests(), + EnforcementPolicy: config.IDKeyDigestPolicy(), + MAAURL: maaURL, } - joinServiceVals["idkeydigests"] = string(marshalledDigests) + marshalledCfg, err := json.Marshal(idKeyCfg) + if err != nil { + return fmt.Errorf("marshalling id key digest config: %w", err) + } + joinServiceVals["idKeyConfig"] = string(marshalledCfg) in["azure"] = map[string]any{ "deployCSIDriver": config.DeployCSIDriver(), diff --git a/cli/internal/helm/loader_test.go b/cli/internal/helm/loader_test.go index ac3634c9b..2d5060cda 100644 --- a/cli/internal/helm/loader_test.go +++ b/cli/internal/helm/loader_test.go @@ -18,6 +18,7 @@ import ( "strings" "testing" + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/config" @@ -38,7 +39,7 @@ func TestLoad(t *testing.T) { config := &config.Config{Provider: config.ProviderConfig{GCP: &config.GCPConfig{}}} chartLoader := ChartLoader{csp: config.GetProvider()} - release, err := chartLoader.Load(config, true, []byte("secret"), []byte("salt")) + release, err := chartLoader.Load(config, true, []byte("secret"), []byte("salt"), "https://192.0.2.1:8080/maa") require.NoError(err) var helmReleases helm.Releases @@ -72,7 +73,11 @@ func TestConstellationServices(t *testing.T) { AttestationVariant: oid.AzureSEVSNP{}.String(), Provider: config.ProviderConfig{Azure: &config.AzureConfig{ DeployCSIDriver: toPtr(true), - EnforceIDKeyDigest: toPtr(true), + EnforceIDKeyDigest: idkeydigest.StrictChecking, + IDKeyDigest: [][]byte{ + {0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad}, + {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}, + }, }}, }, enforceIDKeyDigest: true, @@ -127,7 +132,7 @@ func TestConstellationServices(t *testing.T) { require.NoError(err) values, err := chartLoader.loadConstellationServicesValues() require.NoError(err) - err = extendConstellationServicesValues(values, tc.config, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + err = extendConstellationServicesValues(values, tc.config, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "https://192.0.2.1:8080/maa") require.NoError(err) options := chartutil.ReleaseOptions{ diff --git a/cli/internal/helm/testdata/Azure/constellation-services/charts/join-service/templates/configmap.yaml b/cli/internal/helm/testdata/Azure/constellation-services/charts/join-service/templates/configmap.yaml index f58d45c0c..50a5c4f3b 100644 --- a/cli/internal/helm/testdata/Azure/constellation-services/charts/join-service/templates/configmap.yaml +++ b/cli/internal/helm/testdata/Azure/constellation-services/charts/join-service/templates/configmap.yaml @@ -5,7 +5,6 @@ metadata: namespace: testNamespace data: measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}" - enforceIdKeyDigest: "true" - idkeydigests: "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]" + idKeyConfig: "{\"idKeyDigests\":[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\",\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"],\"enforcementPolicy\":\"StrictChecking\",\"maaURL\":\"https://192.0.2.1:8080/maa\"}" binaryData: measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA diff --git a/go.mod b/go.mod index 1426b601e..b5f7f4859 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ replace ( replace ( github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api => ./operators/constellation-node-operator/api + github.com/google/go-tpm => github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299 github.com/google/go-tpm-tools => github.com/daniel-weisse/go-tpm-tools v0.0.0-20230105122812-f7474d459dfc ) @@ -68,6 +69,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 github.com/docker/docker v20.10.22+incompatible github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 + github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f github.com/fsnotify/fsnotify v1.6.0 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 @@ -187,6 +189,7 @@ require ( github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.0.2 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/errors v0.20.3 // indirect diff --git a/go.sum b/go.sum index 9b0390df5..dd057cdbc 100644 --- a/go.sum +++ b/go.sum @@ -405,6 +405,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU= +github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= @@ -473,6 +475,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -677,8 +681,6 @@ github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVgg github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no= github.com/google/go-sev-guest v0.4.1 h1:IjxtGAvzR+zSyAqMc1FWfYKCg1cwPkBly9+Xog3YMZc= github.com/google/go-sev-guest v0.4.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q= -github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= -github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= @@ -1319,6 +1321,8 @@ github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q91 github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug= github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA= github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA= +github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299 h1:4/WcA7Q05f5to+jVERKcZjU5NJ89DiRHP3UUZHLfAtw= +github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= @@ -1483,6 +1487,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/hack/go.mod b/hack/go.mod index 43d5b2399..fe1dacf9d 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -50,10 +50,14 @@ require ( require ( cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect + code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.28 // indirect @@ -114,6 +118,7 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 // indirect + github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect @@ -124,6 +129,7 @@ require ( github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-gorp/gorp/v3 v3.0.2 // indirect + github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/errors v0.20.3 // indirect @@ -139,6 +145,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -197,6 +204,7 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/microsoft/ApplicationInsights-Go v0.4.4 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect diff --git a/hack/go.sum b/hack/go.sum index 40f2ade59..143b8f644 100644 --- a/hack/go.sum +++ b/hack/go.sum @@ -63,6 +63,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= +code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c h1:5eeuG0BHx1+DHeT3AP+ISKZ2ht1UjGhm581ljqYpVeQ= +code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8= code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= @@ -85,6 +87,16 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 h1:T8quHYlUGyb/oqtSTwqlC github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM= github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0 h1:BpGGvzarSyE7kQF1x1hptUcGmNzZEE3yYI+uqBSNRxk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0/go.mod h1:1ijUM40peD7YK5MFEJja2wjjp4eimFNWv0NXoY3nsZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 h1:H3g2mkmu105ON0c/Gqx3Bm+bzoIijLom8LmV9Gjn7X0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0 h1:Vjq3Uy3JAU1DTxbA+uX6BegIhgO2pyFltbfbmDa9KdI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0/go.mod h1:Q3u+T/qw3Kb1Wf3DFKiFwEZlyaAyPb4yBgWm9wq7yh8= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0 h1:mk57wRUA8fyjFxVcPPGv4shLcWDXPFYokTJL9zJxQtE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0/go.mod h1:mU96hbp8qJDA9OzTV1Ji7wCyPyaqC5kI6ZPsZfJ8sE4= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= @@ -102,6 +114,7 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= @@ -361,6 +374,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU= +github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= @@ -436,6 +451,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4= github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -541,6 +558,9 @@ github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -961,6 +981,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microsoft/ApplicationInsights-Go v0.4.4 h1:G4+H9WNs6ygSCe6sUyxRc2U81TI5Es90b2t/MwX5KqY= +github.com/microsoft/ApplicationInsights-Go v0.4.4/go.mod h1:fKRUseBqkw6bDiXTs3ESTiU/4YTIHsQS4W3fP2ieF4U= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -1037,6 +1059,7 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= @@ -1270,6 +1293,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0= github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw= github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug= github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA= @@ -1440,6 +1464,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/internal/attestation/aws/BUILD.bazel b/internal/attestation/aws/BUILD.bazel index a77f64564..6018b7cee 100644 --- a/internal/attestation/aws/BUILD.bazel +++ b/internal/attestation/aws/BUILD.bazel @@ -43,6 +43,7 @@ go_test( "@com_github_aws_aws_sdk_go_v2_service_ec2//types", "@com_github_aws_smithy_go//middleware", "@com_github_google_go_tpm_tools//client", + "@com_github_google_go_tpm_tools//proto/attest", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], diff --git a/internal/attestation/aws/issuer.go b/internal/attestation/aws/issuer.go index 539587ea4..eecb0f2da 100644 --- a/internal/attestation/aws/issuer.go +++ b/internal/attestation/aws/issuer.go @@ -52,8 +52,8 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) { // getInstanceInfo returns information about the current instance using the aws Metadata SDK. // The returned bytes will be written into the attestation document. -func getInstanceInfo(client awsMetaData) func(tpm io.ReadWriteCloser) ([]byte, error) { - return func(io.ReadWriteCloser) ([]byte, error) { +func getInstanceInfo(client awsMetaData) func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { + return func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { ec2InstanceIdentityOutput, err := client.GetInstanceIdentityDocument(context.Background(), &imds.GetInstanceIdentityDocumentInput{}) if err != nil { return nil, errors.New("unable to fetch instance identity document") diff --git a/internal/attestation/aws/issuer_test.go b/internal/attestation/aws/issuer_test.go index 4f707b481..07884e81c 100644 --- a/internal/attestation/aws/issuer_test.go +++ b/internal/attestation/aws/issuer_test.go @@ -91,7 +91,7 @@ func TestGetInstanceInfo(t *testing.T) { instanceInfoFunc := getInstanceInfo(&tc.client) assert.NotNil(instanceInfoFunc) - info, err := instanceInfoFunc(tpm) + info, err := instanceInfoFunc(context.Background(), tpm, nil) if tc.wantErr { assert.Error(err) assert.Nil(info) diff --git a/internal/attestation/aws/validator.go b/internal/attestation/aws/validator.go index 57d7d5c94..e4b56d2b9 100644 --- a/internal/attestation/aws/validator.go +++ b/internal/attestation/aws/validator.go @@ -44,9 +44,9 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator { // getTrustedKeys return the public area of the provides attestation key. // Normally, here the trust of this key should be verified, but currently AWS does not provide this feature. -func getTrustedKey(akPub []byte, _ []byte) (crypto.PublicKey, error) { +func getTrustedKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { // Copied from https://github.com/edgelesssys/constellation/blob/main/internal/attestation/qemu/validator.go - pubArea, err := tpm2.DecodePublic(akPub) + pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) if err != nil { return nil, err } diff --git a/internal/attestation/aws/validator_test.go b/internal/attestation/aws/validator_test.go index 61e97e7f5..0ee5d2032 100644 --- a/internal/attestation/aws/validator_test.go +++ b/internal/attestation/aws/validator_test.go @@ -16,23 +16,24 @@ import ( "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" + "github.com/google/go-tpm-tools/proto/attest" "github.com/stretchr/testify/assert" ) func TestGeTrustedKey(t *testing.T) { testCases := map[string]struct { - attDoc []byte - nonce []byte + akPub []byte + info []byte wantErr bool }{ "nul byte docs": { - attDoc: []byte{0x00, 0x00, 0x00, 0x00}, - nonce: []byte{0x00, 0x00, 0x00, 0x00}, + akPub: []byte{0x00, 0x00, 0x00, 0x00}, + info: []byte{0x00, 0x00, 0x00, 0x00}, wantErr: true, }, "nil": { - attDoc: nil, - nonce: nil, + akPub: nil, + info: nil, wantErr: true, }, } @@ -40,7 +41,16 @@ func TestGeTrustedKey(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) - out, err := getTrustedKey(tc.attDoc, tc.nonce) + out, err := getTrustedKey( + context.Background(), + vtpm.AttestationDocument{ + Attestation: &attest.Attestation{ + AkPub: tc.akPub, + }, + InstanceInfo: tc.info, + }, + nil, + ) if tc.wantErr { assert.Error(err) diff --git a/internal/attestation/azure/snp/BUILD.bazel b/internal/attestation/azure/snp/BUILD.bazel index afaf0ba92..203f07f6d 100644 --- a/internal/attestation/azure/snp/BUILD.bazel +++ b/internal/attestation/azure/snp/BUILD.bazel @@ -7,6 +7,7 @@ go_library( "errors.go", "imds.go", "issuer.go", + "maa.go", "snp.go", "validator.go", ], @@ -16,8 +17,10 @@ go_library( "//internal/attestation/idkeydigest", "//internal/attestation/measurements", "//internal/attestation/vtpm", + "//internal/cloud/azure", "//internal/crypto", "//internal/oid", + "@com_github_edgelesssys_go_azguestattestation//maa", "@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm_tools//client", "@com_github_google_go_tpm_tools//proto/attest", @@ -41,8 +44,11 @@ go_test( "//internal/attestation/idkeydigest", "//internal/attestation/simulator", "//internal/attestation/vtpm", + "//internal/logger", + "@com_github_edgelesssys_go_azguestattestation//maa", "@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm_tools//client", + "@com_github_google_go_tpm_tools//proto/attest", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], diff --git a/internal/attestation/azure/snp/errors.go b/internal/attestation/azure/snp/errors.go index 7a803da23..100a342a0 100644 --- a/internal/attestation/azure/snp/errors.go +++ b/internal/attestation/azure/snp/errors.go @@ -59,7 +59,7 @@ func (e *idKeyError) Unwrap() error { } func (e *idKeyError) Error() string { - return fmt.Sprintf("configured idkeydigests %x doesn't contain reported idkeydigest %x", e.expectedValues, e.encounteredValue) + return fmt.Sprintf("configured idkeydigests %x don't contain reported idkeydigest %x", e.expectedValues, e.encounteredValue) } type versionError struct { diff --git a/internal/attestation/azure/snp/imds.go b/internal/attestation/azure/snp/imds.go index 274ca7244..9b1c24cef 100644 --- a/internal/attestation/azure/snp/imds.go +++ b/internal/attestation/azure/snp/imds.go @@ -8,42 +8,28 @@ package snp import ( "context" - "encoding/json" - "net/http" + "fmt" + + "github.com/edgelesssys/constellation/v2/internal/cloud/azure" ) -// Modified version of bootstrapper/cloudprovider/azure/imds.go - -const ( - imdsVcekURL = "http://169.254.169.254/metadata/THIM/amd/certification" -) +const tagMAAURL = "constellation-maa-url" type imdsClient struct { - client *http.Client + imdsClient *azure.IMDSClient } -// Retrieve retrieves instance metadata from the azure imds API. -func (c imdsClient) getVcek(ctx context.Context) (vcekResponse, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, imdsVcekURL, http.NoBody) +func newIMDSClient() *imdsClient { + return &imdsClient{ + imdsClient: azure.NewIMDSClient(), + } +} + +func (c *imdsClient) getMAAURL(ctx context.Context) (string, error) { + tags, err := c.imdsClient.Tags(ctx) if err != nil { - return vcekResponse{}, err - } - req.Header.Add("Metadata", "True") - resp, err := c.client.Do(req) - if err != nil { - return vcekResponse{}, err - } - defer resp.Body.Close() - - var res vcekResponse - if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { - return vcekResponse{}, err + return "", fmt.Errorf("getting tags: %w", err) } - return res, nil -} - -type vcekResponse struct { - VcekCert string - CertificateChain string + return tags[tagMAAURL], nil } diff --git a/internal/attestation/azure/snp/issuer.go b/internal/attestation/azure/snp/issuer.go index 7e010dbbf..3cfecaa08 100644 --- a/internal/attestation/azure/snp/issuer.go +++ b/internal/attestation/azure/snp/issuer.go @@ -7,106 +7,76 @@ SPDX-License-Identifier: AGPL-3.0-only package snp import ( - "bytes" "context" "encoding/json" "fmt" "io" - "net/http" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/oid" + "github.com/edgelesssys/go-azguestattestation/maa" tpmclient "github.com/google/go-tpm-tools/client" - "github.com/google/go-tpm/tpm2" ) -const ( - lenHclHeader = 0x20 - lenSnpReport = 0x4a0 - lenSnpReportRuntimeDataPadding = 0x14 - tpmReportIdx = 0x01400001 - tpmAkIdx = 0x81000003 -) - -// GetIDKeyDigest reads the idkeydigest from the snp report saved in the TPM's non-volatile memory. -func GetIDKeyDigest(open vtpm.TPMOpenFunc) ([]byte, error) { - tpm, err := open() - if err != nil { - return nil, err - } - defer tpm.Close() - - reportRaw, err := tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0) - if err != nil { - return nil, fmt.Errorf("reading idx %x from TPM: %w", tpmReportIdx, err) - } - - report, err := newSNPReportFromBytes(reportRaw[lenHclHeader:]) - if err != nil { - return nil, fmt.Errorf("creating snp report: %w", err) - } - - return report.IDKeyDigest[:], nil -} +const tpmAkIdx = 0x81000003 // Issuer for Azure TPM attestation. type Issuer struct { oid.AzureSEVSNP *vtpm.Issuer + + imds imdsAPI + maa maaTokenCreator } // NewIssuer initializes a new Azure Issuer. func NewIssuer(log vtpm.AttestationLogger) *Issuer { - imdsAPI := imdsClient{ - client: &http.Client{Transport: &http.Transport{Proxy: nil}}, + i := &Issuer{ + imds: newIMDSClient(), + maa: newMAAClient(), } - return &Issuer{ - Issuer: vtpm.NewIssuer( - vtpm.OpenVTPM, - getAttestationKey, - getInstanceInfo(&tpmReport{}, imdsAPI), - log, - ), - } + i.Issuer = vtpm.NewIssuer( + vtpm.OpenVTPM, + getAttestationKey, + i.getInstanceInfo, + log, + ) + return i } -// getInstanceInfo loads and returns the SEV-SNP attestation report [1] and the -// AMD VCEK certificate chain. -// The attestation report is loaded from the TPM, the certificate chain is queried -// from the cloud metadata API. -// [1] https://github.com/AMDESE/sev-guest/blob/main/include/attestation.h -func getInstanceInfo(reportGetter tpmReportGetter, imdsapi imdsAPI) func(tpm io.ReadWriteCloser) ([]byte, error) { - return func(tpm io.ReadWriteCloser) ([]byte, error) { - hclReport, err := reportGetter.get(tpm) - if err != nil { - return nil, fmt.Errorf("reading report from TPM: %w", err) - } - if len(hclReport) < lenHclHeader+lenSnpReport+lenSnpReportRuntimeDataPadding { - return nil, fmt.Errorf("report read from TPM is shorter then expected: %x", hclReport) - } - hclReport = hclReport[lenHclHeader:] - - runtimeData, _, _ := bytes.Cut(hclReport[lenSnpReport+lenSnpReportRuntimeDataPadding:], []byte{0}) - - vcekResponse, err := imdsapi.getVcek(context.TODO()) - if err != nil { - return nil, fmt.Errorf("getVcekFromIMDS: %w", err) - } - - instanceInfo := azureInstanceInfo{ - Vcek: []byte(vcekResponse.VcekCert), - CertChain: []byte(vcekResponse.CertificateChain), - AttestationReport: hclReport[:0x4a0], - RuntimeData: runtimeData, - } - statement, err := json.Marshal(instanceInfo) - if err != nil { - return nil, fmt.Errorf("marshalling AzureInstanceInfo: %w", err) - } - - return statement, nil +func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, userData []byte) ([]byte, error) { + params, err := i.maa.newParameters(ctx, userData, tpm) + if err != nil { + return nil, fmt.Errorf("getting system parameters: %w", err) } + + var maaToken string + + maaURL, err := i.imds.getMAAURL(ctx) + if err != nil { + return nil, fmt.Errorf("retrieving MAA URL from IMDS API: %w", err) + } + if maaURL != "" { + maaToken, err = i.maa.createToken(ctx, tpm, maaURL, userData, params) + if err != nil { + return nil, fmt.Errorf("creating MAA token: %w", err) + } + } + + instanceInfo := azureInstanceInfo{ + Vcek: params.VcekCert, + CertChain: params.VcekChain, + AttestationReport: params.SNPReport, + RuntimeData: params.RuntimeData, + MAAToken: maaToken, + } + statement, err := json.Marshal(instanceInfo) + if err != nil { + return nil, fmt.Errorf("marshalling AzureInstanceInfo: %w", err) + } + + return statement, nil } // getAttestationKey reads the attestation key put into the TPM during early boot. @@ -119,16 +89,11 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) { return ak, nil } -type tpmReport struct{} - -func (s *tpmReport) get(tpm io.ReadWriteCloser) ([]byte, error) { - return tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0) -} - -type tpmReportGetter interface { - get(tpm io.ReadWriteCloser) ([]byte, error) -} - type imdsAPI interface { - getVcek(ctx context.Context) (vcekResponse, error) + getMAAURL(ctx context.Context) (string, error) +} + +type maaTokenCreator interface { + newParameters(context.Context, []byte, io.ReadWriter) (maa.Parameters, error) + createToken(context.Context, io.ReadWriter, string, []byte, maa.Parameters) (string, error) } diff --git a/internal/attestation/azure/snp/issuer_test.go b/internal/attestation/azure/snp/issuer_test.go index 9f5ee8feb..799e4140b 100644 --- a/internal/attestation/azure/snp/issuer_test.go +++ b/internal/attestation/azure/snp/issuer_test.go @@ -8,13 +8,13 @@ package snp import ( "context" - "encoding/hex" "encoding/json" "errors" "io" "testing" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator" + "github.com/edgelesssys/go-azguestattestation/maa" tpmclient "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm/tpm2" "github.com/stretchr/testify/assert" @@ -23,27 +23,34 @@ import ( func TestGetSNPAttestation(t *testing.T) { testCases := map[string]struct { - report string - vcek string - certChain string + maaURL string + maaToken string apiError error + tokenErr error + paramsErr error wantErr bool }{ - "success": { - report: "48434c41010000001c070000020000000000000000000000000000000000000002000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c020000010000000200000001000000480200007b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - vcek: "someVCEKCert", - certChain: "ASKARK", - wantErr: false, + "success without maa": { + wantErr: false, }, - "report too short": { - report: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - wantErr: true, + "success with maa": { + maaURL: "maaurl", + maaToken: "maatoken", + wantErr: false, }, - "vcek api fails": { - report: "48434c41010000001c070000020000000000000000000000000000000000000002000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c020000010000000200000001000000480200007b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "api fails": { apiError: errors.New(""), wantErr: true, }, + "createToken fails": { + maaURL: "maaurl", + tokenErr: errors.New(""), + wantErr: true, + }, + "newParameters fails": { + paramsErr: errors.New(""), + wantErr: true, + }, } for name, tc := range testCases { @@ -51,40 +58,55 @@ func TestGetSNPAttestation(t *testing.T) { assert := assert.New(t) require := require.New(t) - tpm, err := simulator.OpenSimulatedTPM() - require.NoError(err) - defer tpm.Close() - imdsClient := stubImdsClient{ - vcek: tc.vcek, - certChain: tc.certChain, - apiError: tc.apiError, - } - reportDecoded, err := hex.DecodeString(tc.report) - assert.NoError(err) - snpAttestationReport := stubTpmReport{ - tpmContent: reportDecoded, - err: nil, + maaURL: tc.maaURL, + apiError: tc.apiError, } - attestationJSON, err := getInstanceInfo(&snpAttestationReport, imdsClient)(tpm) + params := maa.Parameters{ + SNPReport: []byte("snpreport"), + RuntimeData: []byte("runtimedata"), + VcekCert: []byte("vcekcert"), + VcekChain: []byte("vcekchain"), + } + + maa := &stubMaaTokenCreator{ + token: tc.maaToken, + tokenErr: tc.tokenErr, + params: params, + paramsErr: tc.paramsErr, + } + + issuer := Issuer{ + imds: imdsClient, + maa: maa, + } + + data := []byte("data") + + attestationJSON, err := issuer.getInstanceInfo(context.Background(), nil, data) if tc.wantErr { assert.Error(err) return } - assert.NoError(err) + require.NoError(err) + + assert.Equal(data, maa.gotParamsData) + if tc.maaURL == "" { + assert.Empty(maa.gotTokenData) + } else { + assert.Equal(data, maa.gotTokenData) + } var instanceInfo azureInstanceInfo err = json.Unmarshal(attestationJSON, &instanceInfo) - assert.NoError(err) + require.NoError(err) - if tc.wantErr { - assert.NotEqualValues(tc.report[0x20:], instanceInfo.AttestationReport) - } else { - assert.EqualValues(reportDecoded[0x20:0x4a0+0x20], instanceInfo.AttestationReport) - assert.Equal(tc.vcek, string(instanceInfo.Vcek)) - assert.Equal(tc.certChain, string(instanceInfo.CertChain)) - } + assert.Equal(params.VcekCert, instanceInfo.Vcek) + assert.Equal(params.VcekChain, instanceInfo.CertChain) + assert.Equal(params.SNPReport, instanceInfo.AttestationReport) + assert.Equal(params.RuntimeData, instanceInfo.RuntimeData) + assert.Equal(tc.maaToken, instanceInfo.MAAToken) }) } } @@ -126,30 +148,30 @@ func TestGetHCLAttestationKey(t *testing.T) { } type stubImdsClient struct { - vcek string - certChain string - apiError error + maaURL string + apiError error } -func (c stubImdsClient) getVcek(_ context.Context) (vcekResponse, error) { - if c.apiError != nil { - return vcekResponse{}, c.apiError - } - - return vcekResponse{ - VcekCert: c.vcek, - CertificateChain: c.certChain, - }, nil +func (c stubImdsClient) getMAAURL(_ context.Context) (string, error) { + return c.maaURL, c.apiError } -type stubTpmReport struct { - tpmContent []byte - err error +type stubMaaTokenCreator struct { + token string + tokenErr error + gotTokenData []byte + + params maa.Parameters + paramsErr error + gotParamsData []byte } -func (s *stubTpmReport) get(_ io.ReadWriteCloser) ([]byte, error) { - if s.err != nil { - return nil, s.err - } - return s.tpmContent, nil +func (s *stubMaaTokenCreator) newParameters(_ context.Context, data []byte, _ io.ReadWriter) (maa.Parameters, error) { + s.gotParamsData = data + return s.params, s.paramsErr +} + +func (s *stubMaaTokenCreator) createToken(_ context.Context, _ io.ReadWriter, _ string, data []byte, _ maa.Parameters) (string, error) { + s.gotTokenData = data + return s.token, s.tokenErr } diff --git a/internal/attestation/azure/snp/maa.go b/internal/attestation/azure/snp/maa.go new file mode 100644 index 000000000..2cf82766a --- /dev/null +++ b/internal/attestation/azure/snp/maa.go @@ -0,0 +1,80 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package snp + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "io" + "net/http" + + "github.com/edgelesssys/go-azguestattestation/maa" +) + +type maaClient struct { + client *http.Client +} + +func newMAAClient() *maaClient { + return &maaClient{ + client: &http.Client{Transport: &http.Transport{Proxy: nil}}, + } +} + +func (m *maaClient) newParameters(ctx context.Context, nonce []byte, tpmHandle io.ReadWriter) (maa.Parameters, error) { + return maa.NewParameters(ctx, nonce, m.client, tpmHandle) +} + +func (m *maaClient) createToken( + ctx context.Context, tpm io.ReadWriter, maaURL string, data []byte, params maa.Parameters, +) (string, error) { + tokenEnc, err := maa.GetEncryptedToken(ctx, params, data, maaURL, m.client) + if err != nil { + return "", fmt.Errorf("getting encrypted token: %w", err) + } + + token, err := maa.DecryptToken(tokenEnc, tpm) + if err != nil { + return "", fmt.Errorf("decrypting token: %w", err) + } + + return token, nil +} + +func (m *maaClient) validateToken(ctx context.Context, maaURL, token string, extraData []byte) error { + keySet, err := maa.GetKeySet(ctx, maaURL, m.client) + if err != nil { + return fmt.Errorf("getting key set from MAA: %w", err) + } + claims, err := maa.ValidateToken(token, keySet) + if err != nil { + return fmt.Errorf("validating token: %w", err) + } + return m.validateClaims(claims, extraData) +} + +func (m *maaClient) validateClaims(claims map[string]interface{}, extraData []byte) error { + runtime, ok := claims["x-ms-runtime"].(map[string]interface{}) + if !ok { + return errors.New("invalid claims: missing x-ms-runtime") + } + payload, ok := runtime["client-payload"].(map[string]interface{}) + if !ok { + return errors.New("invalid claims: missing client-payload") + } + nonce, ok := payload["nonce"].(string) + if !ok { + return errors.New("invalid claims: missing nonce") + } + + if nonce != base64.StdEncoding.EncodeToString(extraData) { + return errors.New("invalid claims: nonce does not match extra data") + } + return nil +} diff --git a/internal/attestation/azure/snp/validator.go b/internal/attestation/azure/snp/validator.go index e1c0069a0..060823fc2 100644 --- a/internal/attestation/azure/snp/validator.go +++ b/internal/attestation/azure/snp/validator.go @@ -8,6 +8,7 @@ package snp import ( "bytes" + "context" "crypto" "crypto/sha256" "crypto/x509" @@ -41,18 +42,36 @@ const ( type Validator struct { oid.AzureSEVSNP *vtpm.Validator + hclValidator hclAkValidator + maa maaValidator + + idKeyDigests idkeydigest.IDKeyDigests + enforceIDKeyDigest idkeydigest.EnforceIDKeyDigest + maaURL string + + log vtpm.AttestationLogger } // NewValidator initializes a new Azure validator with the provided PCR values. -func NewValidator(pcrs measurements.M, idKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator { - return &Validator{ - Validator: vtpm.NewValidator( - pcrs, - getTrustedKey(&azureInstanceInfo{}, idKeyDigests, enforceIDKeyDigest, log), - validateCVM, - log, - ), +func NewValidator(pcrs measurements.M, idKeyConf idkeydigest.Config, log vtpm.AttestationLogger) *Validator { + if log == nil { + log = nopAttestationLogger{} } + v := &Validator{ + hclValidator: &azureInstanceInfo{}, + maa: newMAAClient(), + idKeyDigests: idKeyConf.IDKeyDigests, + enforceIDKeyDigest: idKeyConf.EnforcementPolicy, + maaURL: idKeyConf.MAAURL, + log: log, + } + v.Validator = vtpm.NewValidator( + pcrs, + v.getTrustedKey, + validateCVM, + log, + ) + return v } // validateCVM is a stub, since SEV-SNP attestation is already verified in trustedKeyFromSNP(). @@ -77,40 +96,36 @@ func reverseEndian(b []byte) { // getTrustedKey establishes trust in the given public key. // It does so by verifying the SNP attestation statement in instanceInfo. -func getTrustedKey( - hclAk HCLAkValidator, idKeyDigest idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger, -) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { - return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { - var instanceInfo azureInstanceInfo - if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil { - return nil, fmt.Errorf("unmarshalling instanceInfoRaw: %w", err) - } - - report, err := newSNPReportFromBytes(instanceInfo.AttestationReport) - if err != nil { - return nil, fmt.Errorf("parsing attestation report: %w", err) - } - - vcek, err := validateVCEK(instanceInfo.Vcek, instanceInfo.CertChain) - if err != nil { - return nil, fmt.Errorf("validating VCEK: %w", err) - } - - if err := validateSNPReport(vcek, idKeyDigest, enforceIDKeyDigest, report, log); err != nil { - return nil, fmt.Errorf("validating SNP report: %w", err) - } - - pubArea, err := tpm2.DecodePublic(akPub) - if err != nil { - return nil, err - } - - if err = hclAk.validateAk(instanceInfo.RuntimeData, report.ReportData[:], pubArea.RSAParameters); err != nil { - return nil, fmt.Errorf("validating HCLAkPub: %w", err) - } - - return pubArea.Key() +func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDocument, extraData []byte) (crypto.PublicKey, error) { + var instanceInfo azureInstanceInfo + if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil { + return nil, fmt.Errorf("unmarshalling instanceInfoRaw: %w", err) } + + report, err := newSNPReportFromBytes(instanceInfo.AttestationReport) + if err != nil { + return nil, fmt.Errorf("parsing attestation report: %w", err) + } + + vcek, err := validateVCEK(instanceInfo.Vcek, instanceInfo.CertChain) + if err != nil { + return nil, fmt.Errorf("validating VCEK: %w", err) + } + + if err := v.validateSNPReport(ctx, vcek, report, instanceInfo.MAAToken, extraData); err != nil { + return nil, fmt.Errorf("validating SNP report: %w", err) + } + + pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) + if err != nil { + return nil, err + } + + if err = v.hclValidator.validateAk(instanceInfo.RuntimeData, report.ReportData[:], pubArea.RSAParameters); err != nil { + return nil, fmt.Errorf("validating HCLAkPub: %w", err) + } + + return pubArea.Key() } // validateVCEK takes the PEM-encoded X509 certificate VCEK, ASK and ARK and verifies the integrity of the chain. @@ -143,9 +158,8 @@ func validateVCEK(vcekRaw []byte, certChain []byte) (*x509.Certificate, error) { return vcek, nil } -func validateSNPReport( - cert *x509.Certificate, expectedIDKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, - report snpAttestationReport, log vtpm.AttestationLogger, +func (v *Validator) validateSNPReport( + ctx context.Context, cert *x509.Certificate, report snpAttestationReport, maaToken string, extraData []byte, ) error { if report.Policy.Debug() { return errDebugEnabled @@ -191,7 +205,7 @@ func validateSNPReport( } hasExpectedIDKeyDigest := false - for _, digest := range expectedIDKeyDigests { + for _, digest := range v.idKeyDigests { if bytes.Equal(digest, report.IDKeyDigest[:]) { hasExpectedIDKeyDigest = true break @@ -199,11 +213,14 @@ func validateSNPReport( } if !hasExpectedIDKeyDigest { - if enforceIDKeyDigest { - return &idKeyError{report.IDKeyDigest[:], expectedIDKeyDigests} - } - if log != nil { - log.Warnf("configured idkeydigests %x doesn't contain reported idkeydigest %x", expectedIDKeyDigests, report.IDKeyDigest[:]) + switch v.enforceIDKeyDigest { + case idkeydigest.MAAFallback: + v.log.Infof("configured idkeydigests %x don't contain reported idkeydigest %x, falling back to MAA validation", v.idKeyDigests, report.IDKeyDigest[:]) + return v.maa.validateToken(ctx, v.maaURL, maaToken, extraData) + case idkeydigest.WarnOnly: + v.log.Warnf("configured idkeydigests %x don't contain reported idkeydigest %x", v.idKeyDigests, report.IDKeyDigest[:]) + default: + return &idKeyError{report.IDKeyDigest[:], v.idKeyDigests} } } @@ -270,6 +287,7 @@ type azureInstanceInfo struct { CertChain []byte AttestationReport []byte RuntimeData []byte + MAAToken string } // validateAk validates that the attestation key from the TPM is trustworthy. The steps are: @@ -320,10 +338,10 @@ func (a *azureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte, return nil } -// HCLAkValidator validates an attestation key issued by the Host Compatibility Layer (HCL). +// hclAkValidator validates an attestation key issued by the Host Compatibility Layer (HCL). // The HCL is written by Azure, and sits between the Hypervisor and CVM OS. // The HCL runs in the protected context of the CVM. -type HCLAkValidator interface { +type hclAkValidator interface { validateAk(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error } @@ -413,3 +431,16 @@ type akPub struct { type runtimeData struct { Keys []akPub } + +// nopAttestationLogger is a no-op implementation of AttestationLogger. +type nopAttestationLogger struct{} + +// Infof is a no-op. +func (nopAttestationLogger) Infof(string, ...interface{}) {} + +// Warnf is a no-op. +func (nopAttestationLogger) Warnf(string, ...interface{}) {} + +type maaValidator interface { + validateToken(ctx context.Context, maaURL string, token string, extraData []byte) error +} diff --git a/internal/attestation/azure/snp/validator_test.go b/internal/attestation/azure/snp/validator_test.go index dbf74164e..e0697967c 100644 --- a/internal/attestation/azure/snp/validator_test.go +++ b/internal/attestation/azure/snp/validator_test.go @@ -8,6 +8,7 @@ package snp import ( "bytes" + "context" "crypto/sha256" "encoding/base64" "encoding/binary" @@ -20,7 +21,9 @@ import ( "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" + "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/google/go-tpm-tools/client" + "github.com/google/go-tpm-tools/proto/attest" "github.com/google/go-tpm/tpm2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -51,8 +54,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData string vcek string certChain string - idkeydigest idkeydigest.IDKeyDigests - enforceIDKeyDigest bool + idkeydigests idkeydigest.IDKeyDigests + enforceIDKeyDigest idkeydigest.EnforceIDKeyDigest wantErr bool assertCorrectError func(error) }{ @@ -61,16 +64,16 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, }, "invalid report signature": { report: "02000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ccb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c02000001000000020000000100000048020000", runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &signatureError{} @@ -82,8 +85,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: "-----BEGIN CERTIFICATE-----\nMIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA\noRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD\nVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs\nYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl\nczESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIyMDYxMTE2MjE0OFoXDTI5MDYxMTE2\nMjE0OFowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD\nVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk\nIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF\nK4EEACIDYgAEdBdzuRwdnQvH5MMutYl0GqQc6kwh0NRueQhDm00i5XLCgV0NLvlX\nrhKuomRLKFdyT9ddzcgZGYlB5lpc1MPZvOOKWAxsniZ1fB3EcMcbS5blPM3yl1Ca\nGzwBMEq/P7dfo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC\nBAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAjARBgorBgEEAZx4AQMCBAMC\nAQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE\nAZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB\nBjARBgorBgEEAZx4AQMIBAMCAV0wTQYJKwYBBAGceAEEBEDTy4KCFroFQ6PWI7+Q\nTHqQj0c5tch4VsqwbhdisV/kXizUuNiEBniVILiO2mLY3zZYMYHKEDm3NbhCaVO+\nQOgSMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B\nAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQBkge6Vsbnni2DTPrXw4To9\nSR6vwsgRKiUxyu/tB18DiDcjO/HpFa1l8COt6Gg690Hnn4ezYAIYw4co/SoJQ0+v\nerxiQqJJAHgpKQ3QqNdnESlyM9eZaZN5ND4S+gXtnFb4K42FzAAy6cyuXKVsrh8B\nuMJnK0sHm3+8qDUXz/NjL95kG/wGSB6LOSk0sll2VFKaolMmH+VhR4m6zO7z8/SA\nt/cPXsZ+/Me/ZP46WibHGHhvLD8kSuyVmt+SIlx9wjdRHqNBNtvx4VEMMZwJX2o+\n0T6nuZ4cnrHK18zdb+K8/3qCFprdHRDD5bo491fnIsAYGGIfcNmAz2uCI9j9TE6R\nGGS2k1jQgWls19nw/Ra++8Kf/roR6WVax8k2R8+XV9eRZ33TqDXAHSbGcZbynaEb\neo6V8MKwLbVNi/7MP6b90nEtvN0SLRbKCJvEn/iHUHQa9BnT14zeTJw2gR/uG+M5\nxC+q9+nKMhIAGOIyxpFp67XBJSSOJut9bmvaPpLIC+/Mr+GKiM+YMQHzYwH6fuDT\no8LVlmuDiOz78BzmD/zy3DaYWTkHourKa7x/DSwuF8MQkvquwEHwkuikpLyo+b54\nSzqt81FR21rUxbkaUv9urRyYfzZ3m3ogApAveGdYPo1y4sl1FPd8X5+aVeghGyXU\nL9lHQ8+Y53Pf/ZQ/gYI0dQ==\n-----END CERTIFICATE-----\n", certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &vcekError{} @@ -95,8 +98,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: "-----BEGIN CERTIFICATE-----\nMIIGiTCCBDigAwIBAgIDAQABMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTgyNDIwWhcNNDUxMDIy\nMTgyNDIwWjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YV5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJU0VWLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAnU2drrNTfbhNQIllf+W2y+ROCbSzId1aKZft\n2T9zjZQOzjGccl17i1mIKWl7NTcB0VYXt3JxZSzOZjsjLNVAEN2MGj9TiedL+Qew\nKZX0JmQEuYjm+WKksLtxgdLp9E7EZNwNDqV1r0qRP5tB8OWkyQbIdLeu4aCz7j/S\nl1FkBytev9sbFGzt7cwnjzi9m7noqsk+uRVBp3+In35QPdcj8YflEmnHBNvuUDJh\nLCJMW8KOjP6++Phbs3iCitJcANEtW4qTNFoKW3CHlbcSCjTM8KsNbUx3A8ek5EVL\njZWH1pt9E3TfpR6XyfQKnY6kl5aEIPwdW3eFYaqCFPrIo9pQT6WuDSP4JCYJbZne\nKKIbZjzXkJt3NQG32EukYImBb9SCkm9+fS5LZFg9ojzubMX3+NkBoSXI7OPvnHMx\njup9mw5se6QUV7GqpCA2TNypolmuQ+cAaxV7JqHE8dl9pWf+Y3arb+9iiFCwFt4l\nAlJw5D0CTRTC1Y5YWFDBCrA/vGnmTnqG8C+jjUAS7cjjR8q4OPhyDmJRPnaC/ZG5\nuP0K0z6GoO/3uen9wqshCuHegLTpOeHEJRKrQFr4PVIwVOB0+ebO5FgoyOw43nyF\nD5UKBDxEB4BKo/0uAiKHLRvvgLbORbU8KARIs1EoqEjmF8UtrmQWV2hUjwzqwvHF\nei8rPxMCAwEAAaOBozCBoDAdBgNVHQ4EFgQUO8ZuGCrD/T1iZEib47dHLLT8v/gw\nHwYDVR0jBBgwFoAUhawa0UP3yKxV1MUdQUir1XhK1FMwEgYDVR0TAQH/BAgwBgEB\n/wIBADAOBgNVHQ8BAf8EBAMCAQQwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cHM6Ly9r\nZHNpbnRmLmFtZC5jb20vdmNlay92MS9NaWxhbi9jcmwwRgYJKoZIhvcNAQEKMDmg\nDzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKID\nAgEwowMCAQEDggIBAIgeUQScAf3lDYqgWU1VtlDbmIN8S2dC5kmQzsZ/HtAjQnLE\nPI1jh3gJbLxL6gf3K8jxctzOWnkYcbdfMOOr28KT35IaAR20rekKRFptTHhe+DFr\n3AFzZLDD7cWK29/GpPitPJDKCvI7A4Ug06rk7J0zBe1fz/qe4i2/F12rvfwCGYhc\nRxPy7QF3q8fR6GCJdB1UQ5SlwCjFxD4uezURztIlIAjMkt7DFvKRh+2zK+5plVGG\nFsjDJtMz2ud9y0pvOE4j3dH5IW9jGxaSGStqNrabnnpF236ETr1/a43b8FFKL5QN\nmt8Vr9xnXRpznqCRvqjr+kVrb6dlfuTlliXeQTMlBoRWFJORL8AcBJxGZ4K2mXft\nl1jU5TLeh5KXL9NW7a/qAOIUs2FiOhqrtzAhJRg9Ij8QkQ9Pk+cKGzw6El3T3kFr\nEg6zkxmvMuabZOsdKfRkWfhH2ZKcTlDfmH1H0zq0Q2bG3uvaVdiCtFY1LlWyB38J\nS2fNsR/Py6t5brEJCFNvzaDky6KeC4ion/cVgUai7zzS3bGQWzKDKU35SqNU2WkP\nI8xCZ00WtIiKKFnXWUQxvlKmmgZBIYPe01zD0N8atFxmWiSnfJl690B9rJpNR/fI\najxCW3Seiws6r1Zm+tCuVbMiNtpS9ThjNX4uve5thyfE2DgoxRFvY1CsoF5M\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIGiTCCBDigAwIBAgIDAQABMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTgyNDIwWhcNNDUxMDIy\nMTgyNDIwWjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJU0VWLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAnU2drrNTfbhNQIllf+W2y+ROCbSzId1aKZft\n2T9zjZQOzjGccl17i1mIKWl7NTcB0VYXt3JxZSzOZjsjLNVAEN2MGj9TiedL+Qew\nKZX0JmQEuYjm+WKksLtxgdLp9E7EZNwNDqV1r0qRP5tB8OWkyQbIdLeu4aCz7j/S\nl1FkBytev9sbFGzt7cwnjzi9m7noqsk+uRVBp3+In35QPdcj8YflEmnHBNvuUDJh\nLCJMW8KOjP6++Phbs3iCitJcANEtW4qTNFoKW3CHlbcSCjTM8KsNbUx3A8ek5EVL\njZWH1pt9E3TfpR6XyfQKnY6kl5aEIPwdW3eFYaqCFPrIo9pQT6WuDSP4JCYJbZne\nKKIbZjzXkJt3NQG32EukYImBb9SCkm9+fS5LZFg9ojzubMX3+NkBoSXI7OPvnHMx\njup9mw5se6QUV7GqpCA2TNypolmuQ+cAaxV7JqHE8dl9pWf+Y3arb+9iiFCwFt4l\nAlJw5D0CTRTC1Y5YWFDBCrA/vGnmTnqG8C+jjUAS7cjjR8q4OPhyDmJRPnaC/ZG5\nuP0K0z6GoO/3uen9wqshCuHegLTpOeHEJRKrQFr4PVIwVOB0+ebO5FgoyOw43nyF\nD5UKBDxEB4BKo/0uAiKHLRvvgLbORbU8KARIs1EoqEjmF8UtrmQWV2hUjwzqwvHF\nei8rPxMCAwEAAaOBozCBoDAdBgNVHQ4EFgQUO8ZuGCrD/T1iZEib47dHLLT8v/gw\nHwYDVR0jBBgwFoAUhawa0UP3yKxV1MUdQUir1XhK1FMwEgYDVR0TAQH/BAgwBgEB\n/wIBADAOBgNVHQ8BAf8EBAMCAQQwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cHM6Ly9r\nZHNpbnRmLmFtZC5jb20vdmNlay92MS9NaWxhbi9jcmwwRgYJKoZIhvcNAQEKMDmg\nDzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKID\nAgEwowMCAQEDggIBAIgeUQScAf3lDYqgWU1VtlDbmIN8S2dC5kmQzsZ/HtAjQnLE\nPI1jh3gJbLxL6gf3K8jxctzOWnkYcbdfMOOr28KT35IaAR20rekKRFptTHhe+DFr\n3AFzZLDD7cWK29/GpPitPJDKCvI7A4Ug06rk7J0zBe1fz/qe4i2/F12rvfwCGYhc\nRxPy7QF3q8fR6GCJdB1UQ5SlwCjFxD4uezURztIlIAjMkt7DFvKRh+2zK+5plVGG\nFsjDJtMz2ud9y0pvOE4j3dH5IW9jGxaSGStqNrabnnpF236ETr1/a43b8FFKL5QN\nmt8Vr9xnXRpznqCRvqjr+kVrb6dlfuTlliXeQTMlBoRWFJORL8AcBJxGZ4K2mXft\nl1jU5TLeh5KXL9NW7a/qAOIUs2FiOhqrtzAhJRg9Ij8QkQ9Pk+cKGzw6El3T3kFr\nEg6zkxmvMuabZOsdKfRkWfhH2ZKcTlDfmH1H0zq0Q2bG3uvaVdiCtFY1LlWyB38J\nS2fNsR/Py6t5brEJCFNvzaDky6KeC4ion/cVgUai7zzS3bGQWzKDKU35SqNU2WkP\nI8xCZ00WtIiKKFnXWUQxvlKmmgZBIYPe01zD0N8atFxmWiSnfJl690B9rJpNR/fI\najxCW3Seiws6r1Zm+tCuVbMiNtpS9ThjNX4uve5thyfE2DgoxRFvY1CsoF5M\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n", - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &askError{} @@ -108,8 +111,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: "7b226b657973223a5b7b226b6964223a2248434c426b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d", vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := errors.New("unexpected runtimeData digest in TPM") @@ -121,8 +124,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: idkeydigest.IDKeyDigests{[]byte{0x00}}, - enforceIDKeyDigest: true, + idkeydigests: idkeydigest.IDKeyDigests{[]byte{0x00}}, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &idKeyError{} @@ -130,19 +133,20 @@ func TestTrustedKeyFromSNP(t *testing.T) { }, }, "don't enforce idkeydigest": { - report: defaultReport, - runtimeData: defaultRuntimeData, - vcek: defaultVCEK, - certChain: defaultCertChain, - idkeydigest: idkeydigest.IDKeyDigests{[]byte{0x00}}, + report: defaultReport, + runtimeData: defaultRuntimeData, + vcek: defaultVCEK, + certChain: defaultCertChain, + idkeydigests: idkeydigest.IDKeyDigests{[]byte{0x00}}, + enforceIDKeyDigest: idkeydigest.WarnOnly, }, "unsupported microcode version": { report: "02000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065c0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c02000001000000020000000100000048020000", runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &versionError{} @@ -154,8 +158,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &versionError{} @@ -167,8 +171,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { target := &versionError{} @@ -180,8 +184,8 @@ func TestTrustedKeyFromSNP(t *testing.T) { runtimeData: defaultRuntimeData, vcek: defaultVCEK, certChain: defaultCertChain, - idkeydigest: defaultIDKeyDigest, - enforceIDKeyDigest: true, + idkeydigests: defaultIDKeyDigest, + enforceIDKeyDigest: idkeydigest.StrictChecking, wantErr: true, assertCorrectError: func(err error) { assert.ErrorIs(t, err, errDebugEnabled) @@ -201,7 +205,21 @@ func TestTrustedKeyFromSNP(t *testing.T) { assert.Error(err) } - key, err := getTrustedKey(&instanceInfo, tc.idkeydigest, tc.enforceIDKeyDigest, nil)(akPub, statement) + attDoc := vtpm.AttestationDocument{ + InstanceInfo: statement, + Attestation: &attest.Attestation{ + AkPub: akPub, + }, + } + + validator := &Validator{ + hclValidator: &instanceInfo, + idKeyDigests: tc.idkeydigests, + enforceIDKeyDigest: tc.enforceIDKeyDigest, + log: logger.NewTest(t), + } + + key, err := validator.getTrustedKey(context.Background(), attDoc, nil) if tc.wantErr { tc.assertCorrectError(err) } else { diff --git a/internal/attestation/azure/trustedlaunch/BUILD.bazel b/internal/attestation/azure/trustedlaunch/BUILD.bazel index e83cdca13..84dc04a5c 100644 --- a/internal/attestation/azure/trustedlaunch/BUILD.bazel +++ b/internal/attestation/azure/trustedlaunch/BUILD.bazel @@ -33,10 +33,12 @@ go_test( deps = [ "//internal/attestation/measurements", "//internal/attestation/simulator", + "//internal/attestation/vtpm", "//internal/crypto", "//internal/logger", "@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm_tools//client", + "@com_github_google_go_tpm_tools//proto/attest", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", ], diff --git a/internal/attestation/azure/trustedlaunch/issuer.go b/internal/attestation/azure/trustedlaunch/issuer.go index c8dd4d38e..c976dd862 100644 --- a/internal/attestation/azure/trustedlaunch/issuer.go +++ b/internal/attestation/azure/trustedlaunch/issuer.go @@ -14,7 +14,6 @@ import ( "fmt" "io" "net/http" - "time" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/oid" @@ -55,7 +54,7 @@ type akSigner struct { } // getAttestationCert returns the DER encoded certificate of the TPM's attestation key and it's CA. -func (i *Issuer) getAttestationCert(tpm io.ReadWriteCloser) ([]byte, error) { +func (i *Issuer) getAttestationCert(ctx context.Context, tpm io.ReadWriteCloser, _ []byte) ([]byte, error) { certDER, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0) if err != nil { return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err) @@ -71,8 +70,6 @@ func (i *Issuer) getAttestationCert(tpm io.ReadWriteCloser) ([]byte, error) { // if no CA certificate can be loaded, an error is returned var caCert *x509.Certificate for _, caCertURL := range cert.IssuingCertificateURL { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, caCertURL, nil) if err != nil { continue diff --git a/internal/attestation/azure/trustedlaunch/trustedlaunch_test.go b/internal/attestation/azure/trustedlaunch/trustedlaunch_test.go index d71cc33cc..69645b0f2 100644 --- a/internal/attestation/azure/trustedlaunch/trustedlaunch_test.go +++ b/internal/attestation/azure/trustedlaunch/trustedlaunch_test.go @@ -8,6 +8,7 @@ package trustedlaunch import ( "bytes" + "context" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -19,9 +20,11 @@ import ( "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator" + "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/logger" tpmclient "github.com/google/go-tpm-tools/client" + "github.com/google/go-tpm-tools/proto/attest" "github.com/google/go-tpm/tpm2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -183,13 +186,20 @@ func TestGetAttestationCert(t *testing.T) { issuer := NewIssuer(logger.NewTest(t)) issuer.hClient = newTestClient(tc.crlServer) - certs, err := issuer.getAttestationCert(tpm) + certs, err := issuer.getAttestationCert(context.Background(), tpm, nil) if tc.wantIssueErr { assert.Error(err) return } require.NoError(err) + attDoc := vtpm.AttestationDocument{ + InstanceInfo: certs, + Attestation: &attest.Attestation{ + AkPub: akPub, + }, + } + validator := NewValidator(measurements.M{}, nil) cert, err := x509.ParseCertificate(rootCert.Raw) require.NoError(err) @@ -197,7 +207,7 @@ func TestGetAttestationCert(t *testing.T) { roots.AddCert(cert) validator.roots = roots - key, err := validator.verifyAttestationKey(akPub, certs) + key, err := validator.verifyAttestationKey(context.Background(), attDoc, nil) if tc.wantValidateErr { assert.Error(err) return diff --git a/internal/attestation/azure/trustedlaunch/validator.go b/internal/attestation/azure/trustedlaunch/validator.go index f0d95efbf..19153377f 100644 --- a/internal/attestation/azure/trustedlaunch/validator.go +++ b/internal/attestation/azure/trustedlaunch/validator.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package trustedlaunch import ( + "context" "crypto" "crypto/rsa" "crypto/x509" @@ -49,14 +50,14 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator { // verifyAttestationKey establishes trust in an attestation key. // It does so by verifying the certificate chain of the attestation key certificate. -func (v *Validator) verifyAttestationKey(akPub, instanceInfo []byte) (crypto.PublicKey, error) { - pubArea, err := tpm2.DecodePublic(akPub) +func (v *Validator) verifyAttestationKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { + pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) if err != nil { return nil, fmt.Errorf("decoding attestation key public area: %w", err) } var akSigner akSigner - if err := json.Unmarshal(instanceInfo, &akSigner); err != nil { + if err := json.Unmarshal(attDoc.InstanceInfo, &akSigner); err != nil { return nil, fmt.Errorf("unmarshaling attestation key signer info: %w", err) } diff --git a/internal/attestation/choose/BUILD.bazel b/internal/attestation/choose/BUILD.bazel index 012add67f..c99301d0b 100644 --- a/internal/attestation/choose/BUILD.bazel +++ b/internal/attestation/choose/BUILD.bazel @@ -25,6 +25,7 @@ go_test( srcs = ["choose_test.go"], embed = [":choose"], deps = [ + "//internal/attestation/idkeydigest", "//internal/oid", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", diff --git a/internal/attestation/choose/choose.go b/internal/attestation/choose/choose.go index 3541b0814..db7ec099e 100644 --- a/internal/attestation/choose/choose.go +++ b/internal/attestation/choose/choose.go @@ -43,9 +43,7 @@ func Issuer(variant oid.Getter, log vtpm.AttestationLogger) (atls.Issuer, error) // Validator returns the validator for the given variant. func Validator( - variant oid.Getter, measurements measurements.M, - idKeyDigest idkeydigest.IDKeyDigests, enfoceIDKeyDigest bool, - log vtpm.AttestationLogger, + variant oid.Getter, measurements measurements.M, idKeyCfg idkeydigest.Config, log vtpm.AttestationLogger, ) (atls.Validator, error) { switch variant { case oid.AWSNitroTPM{}: @@ -53,7 +51,7 @@ func Validator( case oid.AzureTrustedLaunch{}: return trustedlaunch.NewValidator(measurements, log), nil case oid.AzureSEVSNP{}: - return snp.NewValidator(measurements, idKeyDigest, enfoceIDKeyDigest, log), nil + return snp.NewValidator(measurements, idKeyCfg, log), nil case oid.GCPSEVES{}: return gcp.NewValidator(measurements, log), nil case oid.QEMUVTPM{}: diff --git a/internal/attestation/choose/choose_test.go b/internal/attestation/choose/choose_test.go index 9d23e6740..346a8394a 100644 --- a/internal/attestation/choose/choose_test.go +++ b/internal/attestation/choose/choose_test.go @@ -10,6 +10,7 @@ import ( "encoding/asn1" "testing" + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/oid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -95,7 +96,7 @@ func TestValidator(t *testing.T) { assert := assert.New(t) require := require.New(t) - validator, err := Validator(tc.variant, nil, nil, false, nil) + validator, err := Validator(tc.variant, nil, idkeydigest.Config{}, nil) if tc.wantErr { assert.Error(err) diff --git a/internal/attestation/gcp/issuer.go b/internal/attestation/gcp/issuer.go index ed1f650cb..bf88d39be 100644 --- a/internal/attestation/gcp/issuer.go +++ b/internal/attestation/gcp/issuer.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package gcp import ( + "context" "encoding/json" "errors" "io" @@ -37,12 +38,12 @@ func NewIssuer(log vtpm.AttestationLogger) *Issuer { } // getGCEInstanceInfo fetches VM metadata used for attestation. -func getGCEInstanceInfo(client gcpMetadataClient) func(io.ReadWriteCloser) ([]byte, error) { +func getGCEInstanceInfo(client gcpMetadataClient) func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { // Ideally we would want to use the endorsement public key certificate // However, this is not available on GCE instances // Workaround: Provide ShieldedVM instance info // The attestating party can request the VMs signing key using Google's API - return func(io.ReadWriteCloser) ([]byte, error) { + return func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { projectID, err := client.projectID() if err != nil { return nil, errors.New("unable to fetch projectID") diff --git a/internal/attestation/gcp/issuer_test.go b/internal/attestation/gcp/issuer_test.go index e53153ab6..4ad64c7a2 100644 --- a/internal/attestation/gcp/issuer_test.go +++ b/internal/attestation/gcp/issuer_test.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package gcp import ( + "context" "encoding/json" "errors" "io" @@ -65,7 +66,7 @@ func TestGetGCEInstanceInfo(t *testing.T) { require := require.New(t) var tpm io.ReadWriteCloser - out, err := getGCEInstanceInfo(tc.client)(tpm) + out, err := getGCEInstanceInfo(tc.client)(context.Background(), tpm, nil) if tc.wantErr { assert.Error(err) } else { diff --git a/internal/attestation/gcp/validator.go b/internal/attestation/gcp/validator.go index 9e894d3d3..473d52522 100644 --- a/internal/attestation/gcp/validator.go +++ b/internal/attestation/gcp/validator.go @@ -13,7 +13,6 @@ import ( "encoding/json" "encoding/pem" "fmt" - "time" compute "cloud.google.com/go/compute/apiv1" "cloud.google.com/go/compute/apiv1/computepb" @@ -31,18 +30,23 @@ const minimumGceVersion = 1 type Validator struct { oid.GCPSEVES *vtpm.Validator + + restClient func(context.Context, ...option.ClientOption) (gcpRestClient, error) } // NewValidator initializes a new GCP validator with the provided PCR values. func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator { - return &Validator{ - Validator: vtpm.NewValidator( - pcrs, - trustedKeyFromGCEAPI(newInstanceClient), - validateCVM, - log, - ), + v := &Validator{ + restClient: newInstanceClient, } + v.Validator = vtpm.NewValidator( + pcrs, + v.trustedKeyFromGCEAPI, + validateCVM, + log, + ) + + return v } type gcpRestClient interface { @@ -64,41 +68,38 @@ func newInstanceClient(ctx context.Context, opts ...option.ClientOption) (gcpRes // trustedKeyFromGCEAPI queries the GCE API for a shieldedVM's public signing key. // This key can be used to verify attestation statements issued by the VM. -func trustedKeyFromGCEAPI(getClient func(ctx context.Context, opts ...option.ClientOption) (gcpRestClient, error)) func(akPub []byte, instanceInfoRaw []byte) (crypto.PublicKey, error) { - return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { - var instanceInfo attest.GCEInstanceInfo - if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil { - return nil, err - } - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() - client, err := getClient(ctx) - if err != nil { - return nil, fmt.Errorf("creating GCE client: %w", err) - } - defer client.Close() - - instance, err := client.GetShieldedInstanceIdentity(ctx, &computepb.GetShieldedInstanceIdentityInstanceRequest{ - Instance: instanceInfo.GetInstanceName(), - Project: instanceInfo.GetProjectId(), - Zone: instanceInfo.GetZone(), - }) - if err != nil { - return nil, fmt.Errorf("retrieving VM identity: %w", err) - } - - if instance.SigningKey == nil || instance.SigningKey.EkPub == nil { - return nil, fmt.Errorf("received no signing key from GCP API") - } - - // Parse the signing key return by GetShieldedInstanceIdentity - block, _ := pem.Decode([]byte(*instance.SigningKey.EkPub)) - if block == nil || block.Type != "PUBLIC KEY" { - return nil, fmt.Errorf("failed to decode PEM block containing public key") - } - - return x509.ParsePKIXPublicKey(block.Bytes) +func (v *Validator) trustedKeyFromGCEAPI(ctx context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { + client, err := v.restClient(ctx) + if err != nil { + return nil, fmt.Errorf("creating GCE client: %w", err) } + defer client.Close() + + var instanceInfo attest.GCEInstanceInfo + if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil { + return nil, err + } + + instance, err := client.GetShieldedInstanceIdentity(ctx, &computepb.GetShieldedInstanceIdentityInstanceRequest{ + Instance: instanceInfo.GetInstanceName(), + Project: instanceInfo.GetProjectId(), + Zone: instanceInfo.GetZone(), + }) + if err != nil { + return nil, fmt.Errorf("retrieving VM identity: %w", err) + } + + if instance.SigningKey == nil || instance.SigningKey.EkPub == nil { + return nil, fmt.Errorf("received no signing key from GCP API") + } + + // Parse the signing key return by GetShieldedInstanceIdentity + block, _ := pem.Decode([]byte(*instance.SigningKey.EkPub)) + if block == nil || block.Type != "PUBLIC KEY" { + return nil, fmt.Errorf("failed to decode PEM block containing public key") + } + + return x509.ParsePKIXPublicKey(block.Bytes) } // validateCVM checks that the machine state represents a GCE AMD-SEV VM. diff --git a/internal/attestation/gcp/validator_test.go b/internal/attestation/gcp/validator_test.go index 6e20beda8..e78aa23dc 100644 --- a/internal/attestation/gcp/validator_test.go +++ b/internal/attestation/gcp/validator_test.go @@ -146,7 +146,12 @@ Y+t5OxL3kL15VzY1Ob0d5cMCAwEAAQ== t.Run(name, func(t *testing.T) { assert := assert.New(t) - out, err := trustedKeyFromGCEAPI(tc.getClient)(nil, tc.instanceInfo) + v := &Validator{ + restClient: tc.getClient, + } + attDoc := vtpm.AttestationDocument{InstanceInfo: tc.instanceInfo} + + out, err := v.trustedKeyFromGCEAPI(context.Background(), attDoc, nil) if tc.wantErr { assert.Error(err) diff --git a/internal/attestation/idkeydigest/BUILD.bazel b/internal/attestation/idkeydigest/BUILD.bazel index fb2102eff..7a872e157 100644 --- a/internal/attestation/idkeydigest/BUILD.bazel +++ b/internal/attestation/idkeydigest/BUILD.bazel @@ -3,7 +3,10 @@ load("//bazel/go:go_test.bzl", "go_test") go_library( name = "idkeydigest", - srcs = ["idkeydigest.go"], + srcs = [ + "enforceidkeydigest_string.go", + "idkeydigest.go", + ], importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest", visibility = ["//:__subpackages__"], deps = ["//internal/cloud/cloudprovider"], diff --git a/internal/attestation/idkeydigest/enforceidkeydigest_string.go b/internal/attestation/idkeydigest/enforceidkeydigest_string.go new file mode 100644 index 000000000..c8869f9de --- /dev/null +++ b/internal/attestation/idkeydigest/enforceidkeydigest_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=EnforceIDKeyDigest"; DO NOT EDIT. + +package idkeydigest + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Unknown-0] + _ = x[StrictChecking-1] + _ = x[MAAFallback-2] + _ = x[WarnOnly-3] +} + +const _EnforceIDKeyDigest_name = "UnknownStrictCheckingMAAFallbackWarnOnly" + +var _EnforceIDKeyDigest_index = [...]uint8{0, 7, 21, 32, 40} + +func (i EnforceIDKeyDigest) String() string { + if i >= EnforceIDKeyDigest(len(_EnforceIDKeyDigest_index)-1) { + return "EnforceIDKeyDigest(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _EnforceIDKeyDigest_name[_EnforceIDKeyDigest_index[i]:_EnforceIDKeyDigest_index[i+1]] +} diff --git a/internal/attestation/idkeydigest/idkeydigest.go b/internal/attestation/idkeydigest/idkeydigest.go index f7da37eb1..f669fc497 100644 --- a/internal/attestation/idkeydigest/idkeydigest.go +++ b/internal/attestation/idkeydigest/idkeydigest.go @@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ +// Package idkeydigest contains policies and type definitions +// for checking the ID Key Digest value in SEV-SNP attestation. package idkeydigest import ( @@ -11,10 +13,105 @@ import ( "encoding/json" "errors" "fmt" + "strings" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" ) +// Config contains the configuration for ID Key Digest validation. +type Config struct { + IDKeyDigests IDKeyDigests `json:"idKeyDigests"` + EnforcementPolicy EnforceIDKeyDigest `json:"enforcementPolicy"` + MAAURL string `json:"maaURL,omitempty"` +} + +//go:generate stringer -type=EnforceIDKeyDigest + +// EnforceIDKeyDigest defines the behavior of the validator when the ID key digest is not found in the expected list. +type EnforceIDKeyDigest uint32 + +// TODO: Decide on final value naming. +const ( + // Unknown is reserved for invalid configurations. + Unknown EnforceIDKeyDigest = iota + // StrictChecking will return an error if the ID key digest is not found in the expected list. + StrictChecking + // MAAFallback attempts to verify the attestation using Microsoft Azure Attestation (MAA), + // if the ID key digest is not found in the expected list. + MAAFallback + // WarnOnly logs a warning if the ID key digest is not found in the expected list. + // No error is returned. + WarnOnly +) + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (e *EnforceIDKeyDigest) UnmarshalJSON(b []byte) error { + return e.unmarshal(func(val any) error { + return json.Unmarshal(b, val) + }) +} + +// MarshalJSON implements the json.Marshaler interface. +func (e EnforceIDKeyDigest) MarshalJSON() ([]byte, error) { + return json.Marshal(e.String()) +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (e *EnforceIDKeyDigest) UnmarshalYAML(unmarshal func(any) error) error { + return e.unmarshal(unmarshal) +} + +// MarshalYAML implements the yaml.Marshaler interface. +func (e EnforceIDKeyDigest) MarshalYAML() (any, error) { + return e.String(), nil +} + +func (e *EnforceIDKeyDigest) unmarshal(unmarshalFunc func(any) error) error { + // Check for legacy format: EnforceIDKeyDigest might be a boolean. + // If set to true, the value will be set to StrictChecking. + // If set to false, the value will be set to WarnOnly. + var legacyEnforce bool + legacyErr := unmarshalFunc(&legacyEnforce) + if legacyErr == nil { + if legacyEnforce { + *e = StrictChecking + } else { + *e = WarnOnly + } + return nil + } + + var enforce string + if err := unmarshalFunc(&enforce); err != nil { + return errors.Join( + err, + fmt.Errorf("trying legacy format: %w", legacyErr), + ) + } + + *e = EnforcePolicyFromString(enforce) + if *e == Unknown { + return fmt.Errorf("unknown EnforceIDKeyDigest value: %q", enforce) + } + + return nil +} + +// EnforcePolicyFromString returns EnforceIDKeyDigest from string. +func EnforcePolicyFromString(s string) EnforceIDKeyDigest { + s = strings.ToLower(s) + switch s { + case "strictchecking": + return StrictChecking + case "maafallback": + return MAAFallback + case "warnonly": + return WarnOnly + default: + return Unknown + } +} + // IDKeyDigests is a list of trusted digest values for the ID key. type IDKeyDigests [][]byte diff --git a/internal/attestation/idkeydigest/idkeydigest_test.go b/internal/attestation/idkeydigest/idkeydigest_test.go index 76229160a..71911c44e 100644 --- a/internal/attestation/idkeydigest/idkeydigest_test.go +++ b/internal/attestation/idkeydigest/idkeydigest_test.go @@ -117,3 +117,126 @@ func TestUnmarshal(t *testing.T) { }) } } + +func TestEnforceIDKeyDigestMarshal(t *testing.T) { + testCases := map[string]struct { + input EnforceIDKeyDigest + wantJSON string + wantYAML string + }{ + "strict": { + input: StrictChecking, + wantJSON: `"StrictChecking"`, + wantYAML: "StrictChecking", + }, + "maaFallback": { + input: MAAFallback, + wantJSON: `"MAAFallback"`, + wantYAML: "MAAFallback", + }, + "warnOnly": { + input: WarnOnly, + wantJSON: `"WarnOnly"`, + wantYAML: "WarnOnly", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + { + // YAML + yaml, err := yaml.Marshal(tc.input) + require.NoError(err) + assert.YAMLEq(tc.wantYAML, string(yaml)) + } + + { + // JSON + json, err := json.Marshal(tc.input) + require.NoError(err) + assert.JSONEq(tc.wantJSON, string(json)) + } + }) + } +} + +func TestEnforceIDKeyDigestUnmarshal(t *testing.T) { + testCases := map[string]struct { + inputJSON string + inputYAML string + want EnforceIDKeyDigest + wantErr bool + }{ + "strict": { + inputJSON: `"StrictChecking"`, + inputYAML: "StrictChecking", + want: StrictChecking, + }, + "maaFallback": { + inputJSON: `"MAAFallback"`, + inputYAML: "MAAFallback", + want: MAAFallback, + }, + "warnOnly": { + inputJSON: `"WarnOnly"`, + inputYAML: "WarnOnly", + want: WarnOnly, + }, + "legacyTrue": { + inputJSON: `true`, + inputYAML: "true", + want: StrictChecking, + }, + "legacyFalse": { + inputJSON: `false`, + inputYAML: "false", + want: WarnOnly, + }, + "invalid": { + inputJSON: `"invalid"`, + inputYAML: "invalid", + wantErr: true, + }, + "invalidType": { + inputJSON: `{"object": "invalid"}`, + inputYAML: "object: invalid", + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + { + // YAML + var got EnforceIDKeyDigest + err := yaml.Unmarshal([]byte(tc.inputYAML), &got) + if tc.wantErr { + assert.Error(err) + return + } + + require.NoError(err) + assert.Equal(tc.want, got) + } + + { + // JSON + var got EnforceIDKeyDigest + err := json.Unmarshal([]byte(tc.inputJSON), &got) + if tc.wantErr { + assert.Error(err) + return + } + + require.NoError(err) + assert.Equal(tc.want, got) + } + }) + } +} diff --git a/internal/attestation/qemu/issuer.go b/internal/attestation/qemu/issuer.go index 46794970e..3177d67d6 100644 --- a/internal/attestation/qemu/issuer.go +++ b/internal/attestation/qemu/issuer.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package qemu import ( + "context" "io" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" @@ -26,7 +27,7 @@ func NewIssuer(log vtpm.AttestationLogger) *Issuer { Issuer: vtpm.NewIssuer( vtpm.OpenVTPM, tpmclient.AttestationKeyRSA, - func(tpm io.ReadWriteCloser) ([]byte, error) { return nil, nil }, + func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { return nil, nil }, log, ), } diff --git a/internal/attestation/qemu/validator.go b/internal/attestation/qemu/validator.go index f1447461c..04c288036 100644 --- a/internal/attestation/qemu/validator.go +++ b/internal/attestation/qemu/validator.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package qemu import ( + "context" "crypto" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" @@ -35,8 +36,8 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator { } // unconditionalTrust returns the given public key as the trusted attestation key. -func unconditionalTrust(akPub, _ []byte) (crypto.PublicKey, error) { - pubArea, err := tpm2.DecodePublic(akPub) +func unconditionalTrust(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { + pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) if err != nil { return nil, err } diff --git a/internal/attestation/vtpm/attestation.go b/internal/attestation/vtpm/attestation.go index f991061d3..c3ab06015 100644 --- a/internal/attestation/vtpm/attestation.go +++ b/internal/attestation/vtpm/attestation.go @@ -8,6 +8,7 @@ package vtpm import ( "bytes" + "context" "crypto" "crypto/sha256" "encoding/json" @@ -58,9 +59,9 @@ type ( // GetTPMAttestationKey loads a TPM key to perform attestation. GetTPMAttestationKey func(tpm io.ReadWriter) (*tpmClient.Key, error) // GetTPMTrustedAttestationPublicKey verifies and returns the attestation public key. - GetTPMTrustedAttestationPublicKey func(akPub []byte, instanceInfo []byte) (crypto.PublicKey, error) + GetTPMTrustedAttestationPublicKey func(context.Context, AttestationDocument, []byte) (crypto.PublicKey, error) // GetInstanceInfo returns VM metdata. - GetInstanceInfo func(tpm io.ReadWriteCloser) ([]byte, error) + GetInstanceInfo func(ctx context.Context, tpm io.ReadWriteCloser, extraData []byte) ([]byte, error) // ValidateCVM validates confidential computing capabilities of the instance issuing the attestation. ValidateCVM func(attestation AttestationDocument, state *attest.MachineState) error ) @@ -135,7 +136,7 @@ func (i *Issuer) Issue(userData []byte, nonce []byte) (res []byte, err error) { } // Fetch instance info of the VM - instanceInfo, err := i.getInstanceInfo(tpm) + instanceInfo, err := i.getInstanceInfo(context.TODO(), tpm, extraData) // TODO(daniel-weisse): update Issue/Validate to use context if err != nil { return nil, fmt.Errorf("fetching instance info: %w", err) } @@ -193,8 +194,10 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e return nil, fmt.Errorf("unmarshaling TPM attestation document: %w", err) } + extraData := makeExtraData(attDoc.UserData, nonce) + // Verify and retrieve the trusted attestation public key using the provided instance info - aKP, err := v.getTrustedKey(attDoc.Attestation.AkPub, attDoc.InstanceInfo) + aKP, err := v.getTrustedKey(context.TODO(), attDoc, extraData) if err != nil { return nil, fmt.Errorf("validating attestation public key: %w", err) } @@ -203,7 +206,7 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e state, err := tpmServer.VerifyAttestation( attDoc.Attestation, tpmServer.VerifyOpts{ - Nonce: makeExtraData(attDoc.UserData, nonce), + Nonce: extraData, TrustedAKs: []crypto.PublicKey{aKP}, AllowSHA1: false, }, diff --git a/internal/attestation/vtpm/attestation_test.go b/internal/attestation/vtpm/attestation_test.go index 6c07a3300..14f844222 100644 --- a/internal/attestation/vtpm/attestation_test.go +++ b/internal/attestation/vtpm/attestation_test.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package vtpm import ( + "context" "crypto" "encoding/json" "errors" @@ -51,7 +52,7 @@ func (s simTPMWithEventLog) EventLog() ([]byte, error) { return header, nil } -func fakeGetInstanceInfo(_ io.ReadWriteCloser) ([]byte, error) { +func fakeGetInstanceInfo(_ context.Context, _ io.ReadWriteCloser, _ []byte) ([]byte, error) { return []byte("unit-test"), nil } @@ -59,8 +60,8 @@ func TestValidate(t *testing.T) { require := require.New(t) fakeValidateCVM := func(AttestationDocument, *attest.MachineState) error { return nil } - fakeGetTrustedKey := func(aKPub, instanceInfo []byte) (crypto.PublicKey, error) { - pubArea, err := tpm2.DecodePublic(aKPub) + fakeGetTrustedKey := func(_ context.Context, attDoc AttestationDocument, _ []byte) (crypto.PublicKey, error) { + pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub) if err != nil { return nil, err } @@ -175,7 +176,7 @@ func TestValidate(t *testing.T) { "untrusted attestation public key": { validator: NewValidator( testExpectedPCRs, - func(akPub, instanceInfo []byte) (crypto.PublicKey, error) { + func(context.Context, AttestationDocument, []byte) (crypto.PublicKey, error) { return nil, errors.New("untrusted") }, fakeValidateCVM, warnLog), @@ -301,7 +302,7 @@ func TestFailIssuer(t *testing.T) { issuer: NewIssuer( newSimTPMWithEventLog, tpmclient.AttestationKeyRSA, - func(io.ReadWriteCloser) ([]byte, error) { return nil, errors.New("failure") }, + func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { return nil, errors.New("failure") }, nil, ), userData: []byte("Constellation"), diff --git a/internal/cloud/azure/azure.go b/internal/cloud/azure/azure.go index 943d44888..5e94700d0 100644 --- a/internal/cloud/azure/azure.go +++ b/internal/cloud/azure/azure.go @@ -20,7 +20,6 @@ import ( "encoding/json" "errors" "fmt" - "net/http" "path" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" @@ -51,12 +50,8 @@ func New(ctx context.Context) (*Cloud, error) { if err != nil { return nil, fmt.Errorf("loading credentials: %w", err) } - // The default http client may use a system-wide proxy and it is recommended to disable the proxy explicitly: - // https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#proxies - // See also: https://github.com/microsoft/azureimds/blob/master/imdssample.go#L10 - imdsAPI := imdsClient{ - client: &http.Client{Transport: &http.Transport{Proxy: nil}}, - } + + imdsAPI := NewIMDSClient() subscriptionID, err := imdsAPI.subscriptionID(ctx) if err != nil { return nil, fmt.Errorf("retrieving subscription ID: %w", err) @@ -91,7 +86,7 @@ func New(ctx context.Context) (*Cloud, error) { } return &Cloud{ - imds: &imdsAPI, + imds: imdsAPI, netIfacAPI: networkInterfacesAPI, virtNetAPI: virtualNetworksAPI, secGroupAPI: securityGroupsAPI, diff --git a/internal/cloud/azure/imds.go b/internal/cloud/azure/imds.go index 93f7368ac..21309b38a 100644 --- a/internal/cloud/azure/imds.go +++ b/internal/cloud/azure/imds.go @@ -28,15 +28,42 @@ const ( maxCacheAge = 12 * time.Hour ) -type imdsClient struct { +// IMDSClient is a client for the Azure Instance Metadata Service. +type IMDSClient struct { client *http.Client cache metadataResponse cacheTime time.Time } +// NewIMDSClient creates a new IMDSClient. +func NewIMDSClient() *IMDSClient { + // The default http client may use a system-wide proxy and it is recommended to disable the proxy explicitly: + // https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#proxies + // See also: https://github.com/microsoft/azureimds/blob/master/imdssample.go#L10 + return &IMDSClient{ + client: &http.Client{Transport: &http.Transport{Proxy: nil}}, + } +} + +// Tags returns the tags of the instance the function is called from. +func (c *IMDSClient) Tags(ctx context.Context) (map[string]string, error) { + if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 { + if err := c.update(ctx); err != nil { + return nil, err + } + } + + tags := make(map[string]string, len(c.cache.Compute.Tags)) + for _, tag := range c.cache.Compute.Tags { + tags[tag.Name] = tag.Value + } + + return tags, nil +} + // providerID returns the provider ID of the instance the function is called from. -func (c *imdsClient) providerID(ctx context.Context) (string, error) { +func (c *IMDSClient) providerID(ctx context.Context) (string, error) { if c.timeForUpdate() || c.cache.Compute.ResourceID == "" { if err := c.update(ctx); err != nil { return "", err @@ -50,7 +77,7 @@ func (c *imdsClient) providerID(ctx context.Context) (string, error) { return c.cache.Compute.ResourceID, nil } -func (c *imdsClient) name(ctx context.Context) (string, error) { +func (c *IMDSClient) name(ctx context.Context) (string, error) { if c.timeForUpdate() || c.cache.Compute.OSProfile.ComputerName == "" { if err := c.update(ctx); err != nil { return "", err @@ -66,7 +93,7 @@ func (c *imdsClient) name(ctx context.Context) (string, error) { // subscriptionID returns the subscription ID of the instance the function // is called from. -func (c *imdsClient) subscriptionID(ctx context.Context) (string, error) { +func (c *IMDSClient) subscriptionID(ctx context.Context) (string, error) { if c.timeForUpdate() || c.cache.Compute.SubscriptionID == "" { if err := c.update(ctx); err != nil { return "", err @@ -82,7 +109,7 @@ func (c *imdsClient) subscriptionID(ctx context.Context) (string, error) { // resourceGroup returns the resource group of the instance the function // is called from. -func (c *imdsClient) resourceGroup(ctx context.Context) (string, error) { +func (c *IMDSClient) resourceGroup(ctx context.Context) (string, error) { if c.timeForUpdate() || c.cache.Compute.ResourceGroup == "" { if err := c.update(ctx); err != nil { return "", err @@ -98,7 +125,7 @@ func (c *imdsClient) resourceGroup(ctx context.Context) (string, error) { // uid returns the UID of the cluster, based on the tags on the instance // the function is called from, which are inherited from the scale set. -func (c *imdsClient) uid(ctx context.Context) (string, error) { +func (c *IMDSClient) uid(ctx context.Context) (string, error) { if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 { if err := c.update(ctx); err != nil { return "", err @@ -116,7 +143,7 @@ func (c *imdsClient) uid(ctx context.Context) (string, error) { // initSecretHash returns the hash of the init secret of the cluster, based on the tags on the instance // the function is called from, which are inherited from the scale set. -func (c *imdsClient) initSecretHash(ctx context.Context) (string, error) { +func (c *IMDSClient) initSecretHash(ctx context.Context) (string, error) { if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 { if err := c.update(ctx); err != nil { return "", err @@ -133,7 +160,7 @@ func (c *imdsClient) initSecretHash(ctx context.Context) (string, error) { } // role returns the role of the instance the function is called from. -func (c *imdsClient) role(ctx context.Context) (role.Role, error) { +func (c *IMDSClient) role(ctx context.Context) (role.Role, error) { if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 { if err := c.update(ctx); err != nil { return role.Unknown, err @@ -150,12 +177,12 @@ func (c *imdsClient) role(ctx context.Context) (role.Role, error) { } // timeForUpdate checks whether an update is needed due to cache age. -func (c *imdsClient) timeForUpdate() bool { +func (c *IMDSClient) timeForUpdate() bool { return time.Since(c.cacheTime) > maxCacheAge } // update updates instance metadata from the azure imds API. -func (c *imdsClient) update(ctx context.Context) error { +func (c *IMDSClient) update(ctx context.Context) error { req, err := http.NewRequestWithContext(ctx, http.MethodGet, imdsURL, http.NoBody) if err != nil { return err diff --git a/internal/cloud/azure/imds_test.go b/internal/cloud/azure/imds_test.go index 75ed83e4d..b0afa517e 100644 --- a/internal/cloud/azure/imds_test.go +++ b/internal/cloud/azure/imds_test.go @@ -92,6 +92,11 @@ func TestIMDSClient(t *testing.T) { }, } + defaultWantTags := map[string]string{ + cloud.TagUID: "uid", + cloud.TagRole: "worker", + } + testCases := map[string]struct { server httpBufconnServer wantProviderIDErr bool @@ -106,6 +111,8 @@ func TestIMDSClient(t *testing.T) { wantName string wantSubscriptionErr bool wantSubscriptionID string + wantTagsErr bool + wantTags map[string]string }{ "metadata response parsed": { server: newHTTPBufconnServerWithMetadataResponse(response), @@ -115,6 +122,7 @@ func TestIMDSClient(t *testing.T) { wantRole: role.Worker, wantName: "computer-name", wantSubscriptionID: "subscription-id", + wantTags: defaultWantTags, }, "metadata response without resource ID": { server: newHTTPBufconnServerWithMetadataResponse(responseWithoutID), @@ -124,6 +132,7 @@ func TestIMDSClient(t *testing.T) { wantRole: role.Worker, wantName: "computer-name", wantSubscriptionID: "subscription-id", + wantTags: defaultWantTags, }, "metadata response without UID tag": { server: newHTTPBufconnServerWithMetadataResponse(responseWithoutUID), @@ -133,6 +142,7 @@ func TestIMDSClient(t *testing.T) { wantRole: role.Worker, wantName: "computer-name", wantSubscriptionID: "subscription-id", + wantTags: map[string]string{cloud.TagRole: "worker"}, }, "metadata response without role tag": { server: newHTTPBufconnServerWithMetadataResponse(responseWithoutRole), @@ -142,6 +152,7 @@ func TestIMDSClient(t *testing.T) { wantRoleErr: true, wantName: "computer-name", wantSubscriptionID: "subscription-id", + wantTags: map[string]string{cloud.TagUID: "uid"}, }, "metadata response without resource group": { server: newHTTPBufconnServerWithMetadataResponse(responseWithoutGroup), @@ -151,6 +162,7 @@ func TestIMDSClient(t *testing.T) { wantRole: role.Worker, wantName: "computer-name", wantSubscriptionID: "subscription-id", + wantTags: defaultWantTags, }, "metadata response without name": { server: newHTTPBufconnServerWithMetadataResponse(responseWithoutName), @@ -160,6 +172,7 @@ func TestIMDSClient(t *testing.T) { wantRole: role.Worker, wantNameErr: true, wantSubscriptionID: "subscription-id", + wantTags: defaultWantTags, }, "metadata response without subscription ID": { server: newHTTPBufconnServerWithMetadataResponse(responseWithoutSubscriptionID), @@ -169,6 +182,7 @@ func TestIMDSClient(t *testing.T) { wantRole: role.Worker, wantName: "computer-name", wantSubscriptionErr: true, + wantTags: defaultWantTags, }, "invalid imds response detected": { server: newHTTPBufconnServer(func(writer http.ResponseWriter, request *http.Request) { @@ -180,6 +194,7 @@ func TestIMDSClient(t *testing.T) { wantRoleErr: true, wantNameErr: true, wantSubscriptionErr: true, + wantTagsErr: true, }, } @@ -197,7 +212,7 @@ func TestIMDSClient(t *testing.T) { DialTLS: tc.server.Dial, }, } - iClient := imdsClient{client: &hClient} + iClient := IMDSClient{client: &hClient} ctx := context.Background() @@ -248,6 +263,14 @@ func TestIMDSClient(t *testing.T) { assert.NoError(err) assert.Equal(tc.wantSubscriptionID, subscriptionID) } + + tags, err := iClient.Tags(ctx) + if tc.wantTagsErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tc.wantTags, tags) + } }) } } diff --git a/internal/cloud/azure/logger.go b/internal/cloud/azure/logger.go index 34b971f3a..160a77b1d 100644 --- a/internal/cloud/azure/logger.go +++ b/internal/cloud/azure/logger.go @@ -31,7 +31,7 @@ func NewLogger(ctx context.Context) (*Logger, error) { if err != nil { return nil, fmt.Errorf("loading credentials: %w", err) } - imdsAPI := &imdsClient{ + imdsAPI := &IMDSClient{ client: &http.Client{Transport: &http.Transport{Proxy: nil}}, } subscriptionID, err := imdsAPI.subscriptionID(ctx) diff --git a/internal/config/config.go b/internal/config/config.go index b6b40cd7d..8d32a32bc 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -192,7 +192,7 @@ type AzureConfig struct { IDKeyDigest Digests `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty"` // description: | // Enforce the specified idKeyDigest value during remote attestation. - EnforceIDKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"` + EnforceIDKeyDigest idkeydigest.EnforceIDKeyDigest `yaml:"enforceIdKeyDigest" validate:"required"` // description: | // Expected confidential VM measurements. Measurements Measurements `yaml:"measurements" validate:"required,no_placeholders"` @@ -329,7 +329,7 @@ func Default() *Config { StateDiskType: "Premium_LRS", DeployCSIDriver: toPtr(true), IDKeyDigest: idkeydigest.DefaultsFor(cloudprovider.Azure), - EnforceIDKeyDigest: toPtr(true), + EnforceIDKeyDigest: idkeydigest.MAAFallback, ConfidentialVM: toPtr(true), SecureBoot: toPtr(false), Measurements: measurements.DefaultsFor(cloudprovider.Azure), @@ -514,9 +514,12 @@ func (c *Config) GetMeasurements() measurements.M { return nil } -// EnforcesIDKeyDigest checks whether ID Key Digest should be enforced for respective cloud provider. -func (c *Config) EnforcesIDKeyDigest() bool { - return c.Provider.Azure != nil && c.Provider.Azure.EnforceIDKeyDigest != nil && *c.Provider.Azure.EnforceIDKeyDigest +// IDKeyDigestPolicy returns the IDKeyDigest checking policy for a cloud provider. +func (c *Config) IDKeyDigestPolicy() idkeydigest.EnforceIDKeyDigest { + if c.Provider.Azure != nil { + return c.Provider.Azure.EnforceIDKeyDigest + } + return idkeydigest.Unknown } // EnforcedPCRs returns the list of enforced PCRs for the configured cloud provider. diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go index 70351f401..f4948f04b 100644 --- a/internal/config/config_doc.go +++ b/internal/config/config_doc.go @@ -265,7 +265,7 @@ func init() { AzureConfigDoc.Fields[12].Description = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf" AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf" AzureConfigDoc.Fields[13].Name = "enforceIdKeyDigest" - AzureConfigDoc.Fields[13].Type = "bool" + AzureConfigDoc.Fields[13].Type = "EnforceIDKeyDigest" AzureConfigDoc.Fields[13].Note = "" AzureConfigDoc.Fields[13].Description = "Enforce the specified idKeyDigest value during remote attestation." AzureConfigDoc.Fields[13].Comments[encoder.LineComment] = "Enforce the specified idKeyDigest value during remote attestation." diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 8be6e4f73..5d8ef3d2f 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -740,7 +740,7 @@ func TestConfigVersionCompatibility(t *testing.T) { ConfidentialVM: toPtr(true), InstanceType: "Standard_DC4as_v5", IDKeyDigest: idkeydigest.IDKeyDigests{{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3}}, - EnforceIDKeyDigest: toPtr(false), + EnforceIDKeyDigest: idkeydigest.WarnOnly, SecureBoot: toPtr(false), DeployCSIDriver: toPtr(true), Measurements: measurements.DefaultsFor(cloudprovider.Azure), @@ -772,7 +772,7 @@ func TestConfigVersionCompatibility(t *testing.T) { {0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96}, {0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3}, }, - EnforceIDKeyDigest: toPtr(false), + EnforceIDKeyDigest: idkeydigest.WarnOnly, SecureBoot: toPtr(false), DeployCSIDriver: toPtr(true), Measurements: measurements.DefaultsFor(cloudprovider.Azure), diff --git a/internal/constants/constants.go b/internal/constants/constants.go index e439cd8fe..3b0353541 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -116,6 +116,9 @@ const ( IDKeyDigestFilename = "idkeydigests" // EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not. EnforceIDKeyDigestFilename = "enforceIdKeyDigest" + // IDKeyConfigFilename is the name of the file holding the configuration for validating the SEV-SNP ID key digest. + IDKeyConfigFilename = "idKeyConfig" + // K8sVersionFieldName is the name of the of the key holding the wanted Kubernetes version. K8sVersionFieldName = "cluster-version" // ComponentsListKey is the name of the key holding the list of components in the components configMap. diff --git a/internal/watcher/BUILD.bazel b/internal/watcher/BUILD.bazel index 9a7811f72..0597f37b4 100644 --- a/internal/watcher/BUILD.bazel +++ b/internal/watcher/BUILD.bazel @@ -19,6 +19,7 @@ go_library( "//internal/logger", "//internal/oid", "@com_github_fsnotify_fsnotify//:fsnotify", + "@com_github_spf13_afero//:afero", "@org_uber_go_zap//:zap", ], ) diff --git a/internal/watcher/validator.go b/internal/watcher/validator.go index a366efa47..1ebced7a5 100644 --- a/internal/watcher/validator.go +++ b/internal/watcher/validator.go @@ -9,9 +9,9 @@ package watcher import ( "encoding/asn1" "encoding/json" + "errors" "fmt" "path/filepath" - "strconv" "sync" "github.com/edgelesssys/constellation/v2/internal/atls" @@ -22,6 +22,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/oid" + "github.com/spf13/afero" ) // Updatable implements an updatable atls.Validator. @@ -74,32 +75,48 @@ func (u *Updatable) Update() error { } u.log.Debugf("New measurements: %+v", measurements) - var digest idkeydigest.IDKeyDigests - var enforceIDKeyDigest bool + // Read ID Key config + var idKeyCfg idkeydigest.Config if u.variant.OID().Equal(oid.AzureSEVSNP{}.OID()) { - u.log.Infof("Updating encforceIdKeyDigest value") - enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename)) - if err != nil { - return err - } - enforceIDKeyDigest, err = strconv.ParseBool(string(enforceRaw)) - if err != nil { - return fmt.Errorf("parsing content of EnforceIdKeyDigestFilename: %s: %w", enforceRaw, err) - } - u.log.Debugf("New encforceIdKeyDigest value: %v", enforceIDKeyDigest) + u.log.Infof("Updating SEV-SNP ID Key config") - u.log.Infof("Updating expected idkeydigest") - idkeydigestRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename)) + err := u.fileHandler.ReadJSON(filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), &idKeyCfg) if err != nil { - return err + if !errors.Is(err, afero.ErrFileNotFound) { + return fmt.Errorf("reading ID Key config: %w", err) + } + + u.log.Warnf("ID Key config file not found, falling back to old format (v2.6 or earlier)") + + // v2.6 fallback + // TODO: Remove after v2.7 release + var digest idkeydigest.IDKeyDigests + var enforceIDKeyDigest idkeydigest.EnforceIDKeyDigest + enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename)) + if err != nil { + return err + } + enforceIDKeyDigest = idkeydigest.EnforcePolicyFromString(string(enforceRaw)) + if err != nil { + return fmt.Errorf("parsing content of EnforceIdKeyDigestFilename: %s: %w", enforceRaw, err) + } + + idkeydigestRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename)) + if err != nil { + return err + } + if err = json.Unmarshal(idkeydigestRaw, &digest); err != nil { + return fmt.Errorf("unmarshaling content of IDKeyDigestFilename: %s: %w", idkeydigestRaw, err) + } + + idKeyCfg.IDKeyDigests = digest + idKeyCfg.EnforcementPolicy = enforceIDKeyDigest } - if err = json.Unmarshal(idkeydigestRaw, &digest); err != nil { - return fmt.Errorf("unmarshaling content of IDKeyDigestFilename: %s: %w", idkeydigestRaw, err) - } - u.log.Debugf("New idkeydigest: %v", digest) + + u.log.Debugf("New ID Key config: %+v", idKeyCfg) } - validator, err := choose.Validator(u.variant, measurements, digest, enforceIDKeyDigest, u.log) + validator, err := choose.Validator(u.variant, measurements, idKeyCfg, u.log) if err != nil { return fmt.Errorf("updating validator: %w", err) } diff --git a/internal/watcher/validator_test.go b/internal/watcher/validator_test.go index 4b6d8d532..bc3bd4d4a 100644 --- a/internal/watcher/validator_test.go +++ b/internal/watcher/validator_test.go @@ -79,15 +79,13 @@ func TestNewUpdateableValidator(t *testing.T) { filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), measurements.M{11: measurements.WithAllBytes(0x00, false)}, )) - keyDigest, err := json.Marshal(idkeydigest.DefaultsFor(cloudprovider.Azure)) - require.NoError(err) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), - keyDigest, - )) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename), - []byte("false"), + + require.NoError(handler.WriteJSON( + filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), + idkeydigest.Config{ + IDKeyDigests: idkeydigest.DefaultsFor(cloudprovider.Azure), + EnforcementPolicy: idkeydigest.WarnOnly, + }, )) } @@ -126,13 +124,12 @@ func TestUpdate(t *testing.T) { filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), measurements.M{11: measurements.WithAllBytes(0x00, false)}, )) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), - []byte{}, - )) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename), - []byte("false"), + require.NoError(handler.WriteJSON( + filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), + idkeydigest.Config{ + IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}}, + EnforcementPolicy: idkeydigest.WarnOnly, + }, )) // call update once to initialize the server's validator @@ -167,6 +164,18 @@ func TestUpdate(t *testing.T) { defer resp.Body.Close() } assert.Error(err) + + // test old ID Key Digest format + require.NoError(handler.Write( + filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), + []byte{}, + )) + require.NoError(handler.Write( + filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename), + []byte("false"), + )) + + assert.NoError(validator.Update()) } func TestOIDConcurrency(t *testing.T) { @@ -178,9 +187,12 @@ func TestOIDConcurrency(t *testing.T) { filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), measurements.M{11: measurements.WithAllBytes(0x00, false)}, )) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), - []byte{}, + require.NoError(handler.WriteJSON( + filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), + idkeydigest.Config{ + IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}}, + EnforcementPolicy: idkeydigest.WarnOnly, + }, )) // create server @@ -223,13 +235,12 @@ func TestUpdateConcurrency(t *testing.T) { measurements.M{11: measurements.WithAllBytes(0x00, false)}, file.OptNone, )) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), - []byte{}, - )) - require.NoError(handler.Write( - filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename), - []byte("false"), + require.NoError(handler.WriteJSON( + filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), + idkeydigest.Config{ + IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}}, + EnforcementPolicy: idkeydigest.WarnOnly, + }, )) var wg sync.WaitGroup