package auth import ( "crypto/aes" "encoding/base64" "errors" "fmt" ) func DecryptCredential(ciphertextBase64, key string) (string, error) { ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64) if err != nil { return "", err } if len(ciphertext) == 0 || len(ciphertext)%aes.BlockSize != 0 { return "", errors.New("invalid AES ciphertext length") } block, err := aes.NewCipher(repeatKey(key, aes.BlockSize)) if err != nil { return "", err } plain := make([]byte, len(ciphertext)) for start := 0; start < len(ciphertext); start += aes.BlockSize { block.Decrypt(plain[start:start+aes.BlockSize], ciphertext[start:start+aes.BlockSize]) } plain, err = unpadPKCS7(plain, aes.BlockSize) if err != nil { return "", err } return string(plain), nil } // DecryptCredentialOrRaw mirrors the plugin's z4A: try AES-ECB decrypt with the // given key, falling back to the raw value when the field is plaintext. func DecryptCredentialOrRaw(value, key string) string { if value == "" { return "" } if plain, err := DecryptCredential(value, key); err == nil && plain != "" { return plain } return value } func repeatKey(key string, size int) []byte { src := []byte(key) out := make([]byte, size) if len(src) == 0 { return out } for i := range out { out[i] = src[i%len(src)] } return out } func unpadPKCS7(in []byte, blockSize int) ([]byte, error) { if len(in) == 0 || len(in)%blockSize != 0 { return nil, errors.New("invalid padded data length") } pad := int(in[len(in)-1]) if pad == 0 || pad > blockSize || pad > len(in) { return nil, fmt.Errorf("invalid padding size %d", pad) } for _, b := range in[len(in)-pad:] { if int(b) != pad { return nil, errors.New("invalid padding bytes") } } return in[:len(in)-pad], nil }