github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/types/asset.go (about) 1 // Copyright (c) 2017 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package types 7 8 import ( 9 "crypto/sha512" 10 "encoding/hex" 11 "fmt" 12 "io/ioutil" 13 "path/filepath" 14 15 "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" 16 ) 17 18 // AssetType describe a type of assets. 19 type AssetType string 20 21 const ( 22 // KernelAsset is a kernel asset. 23 KernelAsset AssetType = "kernel" 24 25 // ImageAsset is an image asset. 26 ImageAsset AssetType = "image" 27 28 // InitrdAsset is an initrd asset. 29 InitrdAsset AssetType = "initrd" 30 31 // HypervisorAsset is an hypervisor asset. 32 HypervisorAsset AssetType = "hypervisor" 33 34 // HypervisorCtlAsset is a hypervisor control asset. 35 HypervisorCtlAsset AssetType = "hypervisorctl" 36 37 // JailerAsset is a jailer asset. 38 JailerAsset AssetType = "jailer" 39 40 // FirmwareAsset is a firmware asset. 41 FirmwareAsset AssetType = "firmware" 42 ) 43 44 // AssetTypes returns a list of all known asset types. 45 // 46 // XXX: New asset types *MUST* be added here. 47 func AssetTypes() []AssetType { 48 return []AssetType{ 49 FirmwareAsset, 50 HypervisorAsset, 51 HypervisorCtlAsset, 52 ImageAsset, 53 InitrdAsset, 54 JailerAsset, 55 KernelAsset, 56 } 57 } 58 59 // AssetAnnotations returns all annotations for all asset types. 60 func AssetAnnotations() ([]string, error) { 61 var result []string 62 63 for _, at := range AssetTypes() { 64 aPath, aHash, err := at.Annotations() 65 if err != nil { 66 return []string{}, err 67 } 68 69 result = append(result, []string{aPath, aHash}...) 70 } 71 72 return result, nil 73 } 74 75 // Annotations returns the path and hash annotations for a given Asset type. 76 func (t AssetType) Annotations() (string, string, error) { 77 switch t { 78 case KernelAsset: 79 return annotations.KernelPath, annotations.KernelHash, nil 80 case ImageAsset: 81 return annotations.ImagePath, annotations.ImageHash, nil 82 case InitrdAsset: 83 return annotations.InitrdPath, annotations.InitrdHash, nil 84 case HypervisorAsset: 85 return annotations.HypervisorPath, annotations.HypervisorHash, nil 86 case HypervisorCtlAsset: 87 return annotations.HypervisorCtlPath, annotations.HypervisorCtlHash, nil 88 case JailerAsset: 89 return annotations.JailerPath, annotations.JailerHash, nil 90 case FirmwareAsset: 91 return annotations.FirmwarePath, annotations.FirmwareHash, nil 92 } 93 94 return "", "", fmt.Errorf("Wrong asset type %s", t) 95 } 96 97 // Asset represents a virtcontainers asset. 98 type Asset struct { 99 path string 100 computedHash string 101 kind AssetType 102 } 103 104 // Path returns an asset path. 105 func (a Asset) Path() string { 106 return a.path 107 } 108 109 // Type returns an asset type. 110 func (a Asset) Type() AssetType { 111 return a.kind 112 } 113 114 // Valid checks if an asset is valid or not. 115 func (a *Asset) Valid() bool { 116 if !filepath.IsAbs(a.path) { 117 return false 118 } 119 120 for _, at := range AssetTypes() { 121 if at == a.kind { 122 return true 123 } 124 } 125 126 return false 127 } 128 129 // Hash returns the hex encoded string for the asset hash 130 func (a *Asset) Hash(hashType string) (string, error) { 131 var hashEncodedLen int 132 var hash string 133 134 // We read the actual asset content 135 bytes, err := ioutil.ReadFile(a.path) 136 if err != nil { 137 return "", err 138 } 139 140 if len(bytes) == 0 { 141 return "", fmt.Errorf("Empty asset file at %s", a.path) 142 } 143 144 // Build the asset hash and convert it to a string. 145 // We only support SHA512 for now. 146 switch hashType { 147 case annotations.SHA512: 148 hashComputed := sha512.Sum512(bytes) 149 hashEncodedLen = hex.EncodedLen(len(hashComputed)) 150 hashEncoded := make([]byte, hashEncodedLen) 151 hex.Encode(hashEncoded, hashComputed[:]) 152 hash = string(hashEncoded[:]) 153 default: 154 return "", fmt.Errorf("Invalid hash type %s", hashType) 155 } 156 157 a.computedHash = hash 158 159 return hash, nil 160 } 161 162 // NewAsset returns a new asset from a slice of annotations. 163 func NewAsset(anno map[string]string, t AssetType) (*Asset, error) { 164 pathAnnotation, hashAnnotation, err := t.Annotations() 165 if err != nil { 166 return nil, err 167 } 168 169 if pathAnnotation == "" || hashAnnotation == "" { 170 return nil, fmt.Errorf("Missing annotation paths for %s", t) 171 } 172 173 path, ok := anno[pathAnnotation] 174 if !ok || path == "" { 175 return nil, nil 176 } 177 178 if !filepath.IsAbs(path) { 179 return nil, fmt.Errorf("%s is not an absolute path", path) 180 } 181 182 a := &Asset{path: path, kind: t} 183 184 hash, ok := anno[hashAnnotation] 185 if !ok || hash == "" { 186 return a, nil 187 } 188 189 // We have a hash annotation, we need to verify the asset against it. 190 hashType, ok := anno[annotations.AssetHashType] 191 if !ok { 192 hashType = annotations.SHA512 193 } 194 195 hashComputed, err := a.Hash(hashType) 196 if err != nil { 197 return a, err 198 } 199 200 // If our computed asset hash does not match the passed annotation, we must exit. 201 if hashComputed != hash { 202 return nil, fmt.Errorf("Invalid hash for %s: computed %s, expecting %s]", a.path, hashComputed, hash) 203 } 204 205 return a, nil 206 }