github.com/in-toto/in-toto-golang@v0.9.1-0.20240517212500-990269f763cf/in_toto/keylib.go (about) 1 package in_toto 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/ed25519" 6 "crypto/rsa" 7 "crypto/sha256" 8 "crypto/x509" 9 "encoding/hex" 10 "encoding/pem" 11 "errors" 12 "fmt" 13 "io" 14 "os" 15 "strings" 16 17 "github.com/secure-systems-lab/go-securesystemslib/cjson" 18 ) 19 20 // ErrFailedPEMParsing gets returned when PKCS1, PKCS8 or PKIX key parsing fails 21 var ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type") 22 23 // ErrNoPEMBlock gets triggered when there is no PEM block in the provided file 24 var ErrNoPEMBlock = errors.New("failed to decode the data as PEM block (are you sure this is a pem file?)") 25 26 // ErrUnsupportedKeyType is returned when we are dealing with a key type different to ed25519 or RSA 27 var ErrUnsupportedKeyType = errors.New("unsupported key type") 28 29 // ErrInvalidSignature is returned when the signature is invalid 30 var ErrInvalidSignature = errors.New("invalid signature") 31 32 // ErrInvalidKey is returned when a given key is none of RSA, ECDSA or ED25519 33 var ErrInvalidKey = errors.New("invalid key") 34 35 const ( 36 rsaKeyType string = "rsa" 37 ecdsaKeyType string = "ecdsa" 38 ed25519KeyType string = "ed25519" 39 rsassapsssha256Scheme string = "rsassa-pss-sha256" 40 ecdsaSha2nistp224 string = "ecdsa-sha2-nistp224" 41 ecdsaSha2nistp256 string = "ecdsa-sha2-nistp256" 42 ecdsaSha2nistp384 string = "ecdsa-sha2-nistp384" 43 ecdsaSha2nistp521 string = "ecdsa-sha2-nistp521" 44 ed25519Scheme string = "ed25519" 45 pemPublicKey string = "PUBLIC KEY" 46 pemPrivateKey string = "PRIVATE KEY" 47 pemRSAPrivateKey string = "RSA PRIVATE KEY" 48 ) 49 50 /* 51 getSupportedKeyIDHashAlgorithms returns a string slice of supported 52 KeyIDHashAlgorithms. We need to use this function instead of a constant, 53 because Go does not support global constant slices. 54 */ 55 func getSupportedKeyIDHashAlgorithms() Set { 56 return NewSet("sha256", "sha512") 57 } 58 59 /* 60 getSupportedRSASchemes returns a string slice of supported RSA Key schemes. 61 We need to use this function instead of a constant because Go does not support 62 global constant slices. 63 */ 64 func getSupportedRSASchemes() []string { 65 return []string{rsassapsssha256Scheme} 66 } 67 68 /* 69 getSupportedEcdsaSchemes returns a string slice of supported ecdsa Key schemes. 70 We need to use this function instead of a constant because Go does not support 71 global constant slices. 72 */ 73 func getSupportedEcdsaSchemes() []string { 74 return []string{ecdsaSha2nistp224, ecdsaSha2nistp256, ecdsaSha2nistp384, ecdsaSha2nistp521} 75 } 76 77 /* 78 getSupportedEd25519Schemes returns a string slice of supported ed25519 Key 79 schemes. We need to use this function instead of a constant because Go does 80 not support global constant slices. 81 */ 82 func getSupportedEd25519Schemes() []string { 83 return []string{ed25519Scheme} 84 } 85 86 /* 87 generateKeyID creates a partial key map and generates the key ID 88 based on the created partial key map via the SHA256 method. 89 The resulting keyID will be directly saved in the corresponding key object. 90 On success generateKeyID will return nil, in case of errors while encoding 91 there will be an error. 92 */ 93 func (k *Key) generateKeyID() error { 94 // Create partial key map used to create the keyid 95 // Unfortunately, we can't use the Key object because this also carries 96 // yet unwanted fields, such as KeyID and KeyVal.Private and therefore 97 // produces a different hash. We generate the keyID exactly as we do in 98 // the securesystemslib to keep interoperability between other in-toto 99 // implementations. 100 var keyToBeHashed = map[string]interface{}{ 101 "keytype": k.KeyType, 102 "scheme": k.Scheme, 103 "keyid_hash_algorithms": k.KeyIDHashAlgorithms, 104 "keyval": map[string]string{ 105 "public": k.KeyVal.Public, 106 }, 107 } 108 keyCanonical, err := cjson.EncodeCanonical(keyToBeHashed) 109 if err != nil { 110 return err 111 } 112 // calculate sha256 and return string representation of keyID 113 keyHashed := sha256.Sum256(keyCanonical) 114 k.KeyID = fmt.Sprintf("%x", keyHashed) 115 err = validateKey(*k) 116 if err != nil { 117 return err 118 } 119 return nil 120 } 121 122 /* 123 generatePEMBlock creates a PEM block from scratch via the keyBytes and the pemType. 124 If successful it returns a PEM block as []byte slice. This function should always 125 succeed, if keyBytes is empty the PEM block will have an empty byte block. 126 Therefore only header and footer will exist. 127 */ 128 func generatePEMBlock(keyBytes []byte, pemType string) []byte { 129 // construct PEM block 130 pemBlock := &pem.Block{ 131 Type: pemType, 132 Headers: nil, 133 Bytes: keyBytes, 134 } 135 return pem.EncodeToMemory(pemBlock) 136 } 137 138 /* 139 setKeyComponents sets all components in our key object. 140 Furthermore it makes sure to remove any trailing and leading whitespaces or newlines. 141 We treat key types differently for interoperability reasons to the in-toto python 142 implementation and the securesystemslib. 143 */ 144 func (k *Key) setKeyComponents(pubKeyBytes []byte, privateKeyBytes []byte, keyType string, scheme string, KeyIDHashAlgorithms []string) error { 145 // assume we have a privateKey if the key size is bigger than 0 146 147 switch keyType { 148 case rsaKeyType: 149 if len(privateKeyBytes) > 0 { 150 k.KeyVal = KeyVal{ 151 Private: strings.TrimSpace(string(generatePEMBlock(privateKeyBytes, pemRSAPrivateKey))), 152 Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))), 153 } 154 } else { 155 k.KeyVal = KeyVal{ 156 Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))), 157 } 158 } 159 case ecdsaKeyType: 160 if len(privateKeyBytes) > 0 { 161 k.KeyVal = KeyVal{ 162 Private: strings.TrimSpace(string(generatePEMBlock(privateKeyBytes, pemPrivateKey))), 163 Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))), 164 } 165 } else { 166 k.KeyVal = KeyVal{ 167 Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, pemPublicKey))), 168 } 169 } 170 case ed25519KeyType: 171 if len(privateKeyBytes) > 0 { 172 k.KeyVal = KeyVal{ 173 Private: strings.TrimSpace(hex.EncodeToString(privateKeyBytes)), 174 Public: strings.TrimSpace(hex.EncodeToString(pubKeyBytes)), 175 } 176 } else { 177 k.KeyVal = KeyVal{ 178 Public: strings.TrimSpace(hex.EncodeToString(pubKeyBytes)), 179 } 180 } 181 default: 182 return fmt.Errorf("%w: %s", ErrUnsupportedKeyType, keyType) 183 } 184 k.KeyType = keyType 185 k.Scheme = scheme 186 k.KeyIDHashAlgorithms = KeyIDHashAlgorithms 187 if err := k.generateKeyID(); err != nil { 188 return err 189 } 190 return nil 191 } 192 193 /* 194 parseKey tries to parse a PEM []byte slice. Using the following standards 195 in the given order: 196 197 - PKCS8 198 - PKCS1 199 - PKIX 200 201 On success it returns the parsed key and nil. 202 On failure it returns nil and the error ErrFailedPEMParsing 203 */ 204 func parseKey(data []byte) (interface{}, error) { 205 key, err := x509.ParsePKCS8PrivateKey(data) 206 if err == nil { 207 return key, nil 208 } 209 key, err = x509.ParsePKCS1PrivateKey(data) 210 if err == nil { 211 return key, nil 212 } 213 key, err = x509.ParsePKIXPublicKey(data) 214 if err == nil { 215 return key, nil 216 } 217 key, err = x509.ParseCertificate(data) 218 if err == nil { 219 return key, nil 220 } 221 key, err = x509.ParseECPrivateKey(data) 222 if err == nil { 223 return key, nil 224 } 225 return nil, ErrFailedPEMParsing 226 } 227 228 /* 229 decodeAndParse receives potential PEM bytes decodes them via pem.Decode 230 and pushes them to parseKey. If any error occurs during this process, 231 the function will return nil and an error (either ErrFailedPEMParsing 232 or ErrNoPEMBlock). On success it will return the decoded pemData, the 233 key object interface and nil as error. We need the decoded pemData, 234 because LoadKey relies on decoded pemData for operating system 235 interoperability. 236 */ 237 func decodeAndParse(pemBytes []byte) (*pem.Block, interface{}, error) { 238 // pem.Decode returns the parsed pem block and a rest. 239 // The rest is everything, that could not be parsed as PEM block. 240 // Therefore we can drop this via using the blank identifier "_" 241 data, _ := pem.Decode(pemBytes) 242 if data == nil { 243 return nil, nil, ErrNoPEMBlock 244 } 245 246 // Try to load private key, if this fails try to load 247 // key as public key 248 key, err := parseKey(data.Bytes) 249 if err != nil { 250 return nil, nil, err 251 } 252 return data, key, nil 253 } 254 255 /* 256 LoadKey loads the key file at specified file path into the key object. 257 It automatically derives the PEM type and the key type. 258 Right now the following PEM types are supported: 259 260 - PKCS1 for private keys 261 - PKCS8 for private keys 262 - PKIX for public keys 263 264 The following key types are supported and will be automatically assigned to 265 the key type field: 266 267 - ed25519 268 - rsa 269 - ecdsa 270 271 The following schemes are supported: 272 273 - ed25519 -> ed25519 274 - rsa -> rsassa-pss-sha256 275 - ecdsa -> ecdsa-sha256-nistp256 276 277 Note that, this behavior is consistent with the securesystemslib, except for 278 ecdsa. We do not use the scheme string as key type in in-toto-golang. 279 Instead we are going with a ecdsa/ecdsa-sha2-nistp256 pair. 280 281 On success it will return nil. The following errors can happen: 282 283 - path not found or not readable 284 - no PEM block in the loaded file 285 - no valid PKCS8/PKCS1 private key or PKIX public key 286 - errors while marshalling 287 - unsupported key types 288 */ 289 func (k *Key) LoadKey(path string, scheme string, KeyIDHashAlgorithms []string) error { 290 pemFile, err := os.Open(path) 291 if err != nil { 292 return err 293 } 294 defer pemFile.Close() 295 296 err = k.LoadKeyReader(pemFile, scheme, KeyIDHashAlgorithms) 297 if err != nil { 298 return err 299 } 300 301 return pemFile.Close() 302 } 303 304 func (k *Key) LoadKeyDefaults(path string) error { 305 pemFile, err := os.Open(path) 306 if err != nil { 307 return err 308 } 309 defer pemFile.Close() 310 311 err = k.LoadKeyReaderDefaults(pemFile) 312 if err != nil { 313 return err 314 } 315 316 return pemFile.Close() 317 } 318 319 // LoadKeyReader loads the key from a supplied reader. The logic matches LoadKey otherwise. 320 func (k *Key) LoadKeyReader(r io.Reader, scheme string, KeyIDHashAlgorithms []string) error { 321 if r == nil { 322 return ErrNoPEMBlock 323 } 324 // Read key bytes 325 pemBytes, err := io.ReadAll(r) 326 if err != nil { 327 return err 328 } 329 // decodeAndParse returns the pemData for later use 330 // and a parsed key object (for operations on that key, like extracting the public Key) 331 pemData, key, err := decodeAndParse(pemBytes) 332 if err != nil { 333 return err 334 } 335 336 return k.loadKey(key, pemData, scheme, KeyIDHashAlgorithms) 337 } 338 339 func (k *Key) LoadKeyReaderDefaults(r io.Reader) error { 340 if r == nil { 341 return ErrNoPEMBlock 342 } 343 // Read key bytes 344 pemBytes, err := io.ReadAll(r) 345 if err != nil { 346 return err 347 } 348 // decodeAndParse returns the pemData for later use 349 // and a parsed key object (for operations on that key, like extracting the public Key) 350 pemData, key, err := decodeAndParse(pemBytes) 351 if err != nil { 352 return err 353 } 354 355 scheme, keyIDHashAlgorithms, err := getDefaultKeyScheme(key) 356 if err != nil { 357 return err 358 } 359 360 return k.loadKey(key, pemData, scheme, keyIDHashAlgorithms) 361 } 362 363 func getDefaultKeyScheme(key interface{}) (scheme string, keyIDHashAlgorithms []string, err error) { 364 keyIDHashAlgorithms = []string{"sha256", "sha512"} 365 366 switch k := key.(type) { 367 case *rsa.PublicKey, *rsa.PrivateKey: 368 scheme = rsassapsssha256Scheme 369 case ed25519.PrivateKey, ed25519.PublicKey: 370 scheme = ed25519Scheme 371 case *ecdsa.PrivateKey, *ecdsa.PublicKey: 372 scheme = ecdsaSha2nistp256 373 case *x509.Certificate: 374 return getDefaultKeyScheme(k.PublicKey) 375 default: 376 err = ErrUnsupportedKeyType 377 } 378 379 return scheme, keyIDHashAlgorithms, err 380 } 381 382 func (k *Key) loadKey(keyObj interface{}, pemData *pem.Block, scheme string, keyIDHashAlgorithms []string) error { 383 switch key := keyObj.(type) { 384 case *rsa.PublicKey: 385 pubKeyBytes, err := x509.MarshalPKIXPublicKey(key) 386 if err != nil { 387 return err 388 } 389 if err := k.setKeyComponents(pubKeyBytes, []byte{}, rsaKeyType, scheme, keyIDHashAlgorithms); err != nil { 390 return err 391 } 392 case *rsa.PrivateKey: 393 // Note: RSA Public Keys will get stored as X.509 SubjectPublicKeyInfo (RFC5280) 394 // This behavior is consistent to the securesystemslib 395 pubKeyBytes, err := x509.MarshalPKIXPublicKey(key.Public()) 396 if err != nil { 397 return err 398 } 399 if err := k.setKeyComponents(pubKeyBytes, pemData.Bytes, rsaKeyType, scheme, keyIDHashAlgorithms); err != nil { 400 return err 401 } 402 case ed25519.PublicKey: 403 if err := k.setKeyComponents(key, []byte{}, ed25519KeyType, scheme, keyIDHashAlgorithms); err != nil { 404 return err 405 } 406 case ed25519.PrivateKey: 407 pubKeyBytes := key.Public() 408 if err := k.setKeyComponents(pubKeyBytes.(ed25519.PublicKey), key, ed25519KeyType, scheme, keyIDHashAlgorithms); err != nil { 409 return err 410 } 411 case *ecdsa.PrivateKey: 412 pubKeyBytes, err := x509.MarshalPKIXPublicKey(key.Public()) 413 if err != nil { 414 return err 415 } 416 if err := k.setKeyComponents(pubKeyBytes, pemData.Bytes, ecdsaKeyType, scheme, keyIDHashAlgorithms); err != nil { 417 return err 418 } 419 case *ecdsa.PublicKey: 420 pubKeyBytes, err := x509.MarshalPKIXPublicKey(key) 421 if err != nil { 422 return err 423 } 424 if err := k.setKeyComponents(pubKeyBytes, []byte{}, ecdsaKeyType, scheme, keyIDHashAlgorithms); err != nil { 425 return err 426 } 427 case *x509.Certificate: 428 err := k.loadKey(key.PublicKey, pemData, scheme, keyIDHashAlgorithms) 429 if err != nil { 430 return err 431 } 432 433 k.KeyVal.Certificate = string(pem.EncodeToMemory(pemData)) 434 435 default: 436 // We should never get here, because we implement all from Go supported Key Types 437 return errors.New("unexpected Error in LoadKey function") 438 } 439 440 return nil 441 } 442 443 /* 444 VerifyCertificateTrust verifies that the certificate has a chain of trust 445 to a root in rootCertPool, possibly using any intermediates in 446 intermediateCertPool 447 */ 448 func VerifyCertificateTrust(cert *x509.Certificate, rootCertPool, intermediateCertPool *x509.CertPool) ([][]*x509.Certificate, error) { 449 verifyOptions := x509.VerifyOptions{ 450 Roots: rootCertPool, 451 Intermediates: intermediateCertPool, 452 } 453 chains, err := cert.Verify(verifyOptions) 454 if len(chains) == 0 || err != nil { 455 return nil, fmt.Errorf("cert cannot be verified by provided roots and intermediates") 456 } 457 return chains, nil 458 }