cmd/core_plugin/agentcrypto/crypto_util.go (78 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package agentcrypto import ( "crypto/ecdsa" "crypto/x509" "encoding/pem" "fmt" "os" "github.com/google/tink/go/aead/subtle" ) // parseCertificate validates certificate is in valid PEM format. func parseCertificate(cert []byte) (*x509.Certificate, error) { block, _ := pem.Decode(cert) if block == nil { return nil, fmt.Errorf("failed to parse PEM certificate") } x509Cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse certificate: %w", err) } return x509Cert, nil } // parsePvtKey validates the key is in valid format and returns the EC Private // Key. func parsePvtKey(pemKey []byte) (*ecdsa.PrivateKey, error) { key, _ := pem.Decode(pemKey) if key == nil { return nil, fmt.Errorf("failed to decode PEM Key") } ecKey, err := x509.ParseECPrivateKey(key.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse EC Private Key: %w", err) } return ecKey, nil } // serialNumber reads the certificate from file and returns the serial number in // hex. func serialNumber(f string) (string, error) { d, err := os.ReadFile(f) if err != nil { return "", fmt.Errorf("unable to read previous client credential file %q: %w", f, err) } crt, err := parseCertificate(d) if err != nil { return "", fmt.Errorf("unable to parse certificate at %q: %w", f, err) } return fmt.Sprintf("%x", crt.SerialNumber), nil } // verifySign verifies the client certificate is valid and signed by root CA. func verifySign(cert []byte, rootCAFile string) error { caCertPEM, err := os.ReadFile(rootCAFile) if err != nil { return fmt.Errorf("failed to read CA PEM file for verifying signature: %w", err) } x509Cert, err := parseCertificate(cert) if err != nil { return fmt.Errorf("failed to parse client certificate for verifying signature: %w", err) } roots := x509.NewCertPool() if !roots.AppendCertsFromPEM(caCertPEM) { return fmt.Errorf("failed to add %q to new certpool for verifying client certificate", rootCAFile) } opts := x509.VerifyOptions{ Roots: roots, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } if _, err := x509Cert.Verify(opts); err != nil { return fmt.Errorf("failed to verify client certificate against root CA %q: %w", rootCAFile, err) } return nil } // encrypt plaintext using AES-GCM. func encrypt(aesKey []byte, plaintext []byte, associatedData []byte) ([]byte, error) { cipher, err := subtle.NewAESGCM(aesKey) if err != nil { return nil, fmt.Errorf("failed to initialize cipher: %w", err) } return cipher.Encrypt(plaintext, associatedData) } // decrypt ciphertext using AES-GCM. func decrypt(aesKey []byte, ciphertext []byte, associatedData []byte) ([]byte, error) { cipher, err := subtle.NewAESGCM(aesKey) if err != nil { return nil, fmt.Errorf("failed to initialize cipher: %w", err) } return cipher.Decrypt(ciphertext, associatedData) }