github.com/Venafi/vcert/v5@v5.10.2/pkg/venafi/tpp/tpp.go (about)

     1  /*
     2   * Copyright 2018-2025 Venafi, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *  http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package tpp
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"encoding/base64"
    24  	"encoding/json"
    25  	"fmt"
    26  	"io"
    27  	"log"
    28  	"net"
    29  	"net/http"
    30  	"regexp"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/go-http-utils/headers"
    35  
    36  	"github.com/Venafi/vcert/v5/pkg/certificate"
    37  	"github.com/Venafi/vcert/v5/pkg/endpoint"
    38  )
    39  
    40  const defaultKeySize = 2048
    41  const defaultSignatureAlgorithm = x509.SHA256WithRSA
    42  const defaultClientID = "vcert-sdk"
    43  const defaultScope = "certificate:manage,revoke"
    44  const defaultWorkloadName = "Default"
    45  
    46  type customField struct {
    47  	Name   string
    48  	Values []string
    49  }
    50  
    51  type application struct {
    52  	ObjectName     string
    53  	Class          string
    54  	DriverName     string
    55  	ValidationHost string `json:",omitempty"`
    56  	ValidationPort string `json:",omitempty"`
    57  }
    58  
    59  type device struct {
    60  	PolicyDN     string
    61  	ObjectName   string
    62  	Host         string
    63  	Applications []application
    64  }
    65  
    66  type certificateRequest struct {
    67  	PolicyDN                string          `json:",omitempty"`
    68  	CADN                    string          `json:",omitempty"`
    69  	ObjectName              string          `json:",omitempty"`
    70  	Subject                 string          `json:",omitempty"`
    71  	OrganizationalUnit      string          `json:",omitempty"`
    72  	Organization            string          `json:",omitempty"`
    73  	City                    string          `json:",omitempty"`
    74  	State                   string          `json:",omitempty"`
    75  	Country                 string          `json:",omitempty"`
    76  	SubjectAltNames         []sanItem       `json:",omitempty"`
    77  	Contacts                []IdentityEntry `json:",omitempty"`
    78  	CASpecificAttributes    []nameValuePair `json:",omitempty"`
    79  	Origin                  string          `json:",omitempty"`
    80  	PKCS10                  string          `json:",omitempty"`
    81  	KeyAlgorithm            string          `json:",omitempty"`
    82  	KeyBitSize              int             `json:",omitempty"`
    83  	EllipticCurve           string          `json:",omitempty"`
    84  	DisableAutomaticRenewal bool            `json:",omitempty"`
    85  	CustomFields            []customField   `json:",omitempty"`
    86  	Devices                 []device        `json:",omitempty"`
    87  	CertificateType         string          `json:",omitempty"`
    88  	Reenable                bool            `json:",omitempty"`
    89  	WorkToDoTimeout         string          `json:",omitempty"`
    90  }
    91  
    92  type certificateRetrieveRequest struct {
    93  	CertificateDN     string `json:",omitempty"`
    94  	Format            string `json:",omitempty"`
    95  	Password          string `json:",omitempty"`
    96  	IncludePrivateKey bool   `json:",omitempty"`
    97  	IncludeChain      bool   `json:",omitempty"`
    98  	FriendlyName      string `json:",omitempty"`
    99  	RootFirstOrder    bool   `json:",omitempty"`
   100  }
   101  
   102  type certificateRetrieveResponse struct {
   103  	CertificateData string `json:",omitempty"`
   104  	Format          string `json:",omitempty"`
   105  	Filename        string `json:",omitempty"`
   106  	Status          string `json:",omitempty"`
   107  	Stage           int    `json:",omitempty"`
   108  }
   109  
   110  type RevocationReason int
   111  
   112  // RevocationReasonsMap maps *certificate.RevocationRequest.Reason to TPP-specific webSDK codes
   113  var RevocationReasonsMap = map[string]RevocationReason{
   114  	"":                       0, // NoReason
   115  	"none":                   0, //
   116  	"key-compromise":         1, // UserKeyCompromised
   117  	"ca-compromise":          2, // CAKeyCompromised
   118  	"affiliation-changed":    3, // UserChangedAffiliation
   119  	"superseded":             4, // CertificateSuperseded
   120  	"cessation-of-operation": 5, // OriginalUseNoLongerValid
   121  }
   122  
   123  type certificateRevokeRequest struct {
   124  	CertificateDN string           `json:",omitempty"`
   125  	Thumbprint    string           `json:",omitempty"`
   126  	Reason        RevocationReason `json:",omitempty"`
   127  	Comments      string           `json:",omitempty"`
   128  	Disable       bool             `json:",omitempty"`
   129  }
   130  
   131  // {Requested:true  Success:true Error:} -- means requested
   132  // {Requested:false Success:true Error:} -- means already revoked
   133  type certificateRevokeResponse struct {
   134  	Requested bool   `json:",omitempty"`
   135  	Success   bool   `json:",omitempty"`
   136  	Error     string `json:",omitempty"`
   137  }
   138  
   139  type certificateRenewRequest struct {
   140  	CertificateDN string `json:",omitempty"`
   141  	PKCS10        string `json:",omitempty"`
   142  }
   143  
   144  type certificateRenewResponse struct {
   145  	Success bool   `json:",omitempty"`
   146  	Error   string `json:",omitempty"`
   147  }
   148  
   149  type certificateResetRequest struct {
   150  	CertificateDN string `json:",omitempty"`
   151  	Restart       bool   `json:",omitempty"`
   152  }
   153  
   154  type certificateResetResponse struct {
   155  	Error string `json:"Error"`
   156  }
   157  
   158  type BrowseIdentitiesRequest struct {
   159  	Filter       string
   160  	Limit        int
   161  	IdentityType int
   162  }
   163  
   164  type BrowseIdentitiesResponse struct {
   165  	Identities []IdentityEntry
   166  }
   167  
   168  type IdentitySelfResponse struct {
   169  	Identities []IdentityEntry
   170  }
   171  
   172  type ValidateIdentityRequest struct {
   173  	ID IdentityInformation
   174  }
   175  
   176  type ValidateIdentityResponse struct {
   177  	ID IdentityEntry
   178  }
   179  
   180  type IdentityInformation struct {
   181  	PrefixedUniversal string
   182  }
   183  
   184  type IdentityEntry struct {
   185  	FullName          string `json:",omitempty"`
   186  	Name              string `json:",omitempty"`
   187  	Prefix            string `json:",omitempty"`
   188  	PrefixedName      string `json:",omitempty"`
   189  	PrefixedUniversal string `json:",omitempty"`
   190  	Type              int    `json:",omitempty"`
   191  	Universal         string `json:",omitempty"`
   192  }
   193  
   194  type sanItem struct {
   195  	Type int    `json:""`
   196  	Name string `json:""`
   197  }
   198  
   199  type nameValuePair struct {
   200  	Name  string `json:",omitempty"`
   201  	Value string `json:",omitempty"`
   202  }
   203  
   204  type nameSliceValuePair struct {
   205  	Name  string
   206  	Value []string
   207  }
   208  
   209  type certificateRequestResponse struct {
   210  	CertificateDN string `json:",omitempty"`
   211  	Error         string `json:",omitempty"`
   212  }
   213  
   214  type importRequest struct {
   215  	PolicyDN        string `json:",omitempty"`
   216  	ObjectName      string `json:",omitempty"`
   217  	CertificateData string `json:",omitempty"`
   218  	PrivateKeyData  string `json:",omitempty"`
   219  	Password        string `json:",omitempty"`
   220  	Reconcile       bool   `json:",omitempty"`
   221  }
   222  
   223  type authorizeResponse struct {
   224  	APIKey     string `json:",omitempty"`
   225  	ValidUntil string `json:",omitempty"` //todo: add usage
   226  }
   227  
   228  type authorizeResquest struct {
   229  	Username string `json:",omitempty"`
   230  	Password string `json:",omitempty"`
   231  }
   232  
   233  type refreshAccessTokenResquest struct {
   234  	Client_id     string `json:"client_id"`
   235  	Refresh_token string `json:"refresh_token"`
   236  }
   237  
   238  type oauthGetRefreshTokenRequest struct {
   239  	Client_id string `json:"client_id"`
   240  	Username  string `json:"username"`
   241  	Password  string `json:"password"`
   242  	Scope     string `json:"scope"`
   243  }
   244  type OauthGetRefreshTokenResponse struct {
   245  	Access_token  string `json:"access_token,omitempty"`
   246  	Expires       int    `json:"expires,omitempty"`
   247  	ExpiresIn     int    `json:"expires_in,omitempty"` //Attribute added as it's used on vSSH
   248  	Identity      string `json:"identity,omitempty"`
   249  	Refresh_token string `json:"refresh_token,omitempty"`
   250  	Refresh_until int    `json:"refresh_until,omitempty"`
   251  	Scope         string `json:"scope,omitempty"`
   252  	Token_type    string `json:"token_type,omitempty"`
   253  }
   254  
   255  type oauthRefreshAccessTokenRequest struct {
   256  	Refresh_token string `json:"refresh_token,omitempty"`
   257  	Client_id     string `json:"client_id"`
   258  }
   259  
   260  type oauthCertificateTokenRequest struct {
   261  	Client_id string `json:"client_id"`
   262  	Scope     string `json:"scope,omitempty"`
   263  }
   264  
   265  type OauthRefreshAccessTokenResponse struct {
   266  	Access_token  string `json:"access_token,omitempty"`
   267  	Expires       int    `json:"expires,omitempty"`
   268  	Identity      string `json:"identity,omitempty"`
   269  	Refresh_token string `json:"refresh_token,omitempty"`
   270  	Refresh_until int    `json:"refresh_until,omitempty"`
   271  	Token_type    string `json:"token_type,omitempty"`
   272  }
   273  
   274  type OauthVerifyTokenResponse struct {
   275  	AccessIssuedOn string `json:"access_issued_on_ISO8601,omitempty"`
   276  	ClientID       string `json:"application,omitempty"`
   277  	Expires        string `json:"expires_ISO8601,omitempty"`
   278  	GrantIssuedOn  string `json:"grant_issued_on_ISO8601,omitempty"`
   279  	Identity       string `json:"identity,omitempty"`
   280  	Scope          string `json:"scope,omitempty"`
   281  	ValidFor       int    `json:"valid_for,omitempty"`
   282  }
   283  
   284  type policyRequest struct {
   285  	ObjectDN      string `json:",omitempty"`
   286  	Class         string `json:",omitempty"`
   287  	AttributeName string `json:",omitempty"`
   288  }
   289  
   290  type metadataItem struct {
   291  	AllowedValues     []string `json:",omitempty"`
   292  	Classes           []string `json:",omitempty"`
   293  	ConfigAttribute   string   `json:",omitempty"`
   294  	DefaultValues     []string `json:",omitempty"`
   295  	DN                string   `json:",omitempty"`
   296  	ErrorMessage      string   `json:",omitempty"`
   297  	Guid              string   `json:",omitempty"`
   298  	Help              string   `json:",omitempty"`
   299  	Label             string   `json:",omitempty"`
   300  	Name              string   `json:",omitempty"`
   301  	Policyable        bool     `json:",omitempty"`
   302  	RegularExpression string   `json:",omitempty"`
   303  	RenderHidden      bool     `json:",omitempty"`
   304  	RenderReadOnly    bool     `json:",omitempty"`
   305  	Type              int      `json:",omitempty"`
   306  }
   307  type metadataKeyValueSet struct {
   308  	Key   metadataItem `json:",omitempty"`
   309  	Value []string     `json:",omitempty"`
   310  }
   311  
   312  type metadataGetItemsRequest struct {
   313  	ObjectDN string `json:"DN"`
   314  }
   315  type metadataGetItemsResponse struct {
   316  	Items  []metadataItem `json:",omitempty"`
   317  	Locked bool           `json:",omitempty"`
   318  }
   319  type metadataGetResponse struct {
   320  	Data   []metadataKeyValueSet
   321  	Locked bool `json:",omitempty"`
   322  }
   323  type guidData struct {
   324  	ItemGuid string   `json:",omitempty"`
   325  	List     []string `json:",omitempty"`
   326  }
   327  type metadataSetRequest struct {
   328  	DN           string     `json:"DN"`
   329  	GuidData     []guidData `json:"GuidData"`
   330  	KeepExisting bool       `json:"KeepExisting"`
   331  }
   332  type metadataSetResponse struct {
   333  	Locked bool `json:",omitempty"`
   334  	Result int  `json:",omitempty"`
   335  }
   336  
   337  type DNToGUIDResponse struct {
   338  	ClassName        string `json:"ClassName"`
   339  	GUID             string `json:"GUID"`
   340  	HierarchicalGUID string `json:"HierarchicalGUID"`
   341  	Result           int    `json:"Result"`
   342  	Revision         int    `json:"Revision"`
   343  }
   344  
   345  type DNToGUIDRequest struct {
   346  	ObjectDN string `json:"ObjectDN"`
   347  }
   348  
   349  type LogPostResponse struct {
   350  	LogResult int `json:"LogResult"`
   351  }
   352  
   353  type policyObject struct {
   354  	AbsoluteGUID string `json:"AbsoluteGUID"`
   355  	DN           string `json:"DN"`
   356  	GUID         string `json:"GUID"`
   357  	ID           int32  `json:"Id"`
   358  	Name         string `json:"Name"`
   359  	Parent       string `json:"Parent"`
   360  	Revision     int64  `json:"Revision"`
   361  	TypeName     string `json:"TypeName"`
   362  }
   363  
   364  type findObjectsOfClassRequest struct {
   365  	Class    string `json:"Class"`
   366  	ObjectDN string `json:"ObjectDN"`
   367  }
   368  
   369  type findObjectsOfClassResponse struct {
   370  	PolicyObjects []policyObject `json:"Objects,omitempty"`
   371  }
   372  
   373  type identitiesResponse struct {
   374  	Identities []identity `json:"Identities"`
   375  }
   376  
   377  type identity struct {
   378  	FullName          string `json:"FullName"`
   379  	Name              string `json:"Name"`
   380  	Prefix            string `json:"Prefix"`
   381  	PrefixedName      string `json:"PrefixedName"`
   382  	PrefixedUniversal string `json:"PrefixedUniversal"`
   383  	Type              int    `json:"Type"`
   384  	Universal         string `json:"Universal"`
   385  }
   386  
   387  type systemStatusVersionResponse string
   388  
   389  type urlResource string
   390  
   391  const (
   392  	urlResourceAuthorize              urlResource = "vedsdk/authorize/"
   393  	urlResourceAuthorizeIsAuthServer  urlResource = "vedauth/authorize/isAuthServer"
   394  	urlResourceAuthorizeCertificate   urlResource = "vedauth/authorize/certificate"
   395  	urlResourceAuthorizeOAuth         urlResource = "vedauth/authorize/oauth"
   396  	urlResourceAuthorizeVerify        urlResource = "vedauth/authorize/verify"
   397  	urlResourceRefreshAccessToken     urlResource = "vedauth/authorize/token" // #nosec
   398  	urlResourceRevokeAccessToken      urlResource = "vedauth/revoke/token"    // #nosec
   399  	urlResourceCertificateImport      urlResource = "vedsdk/certificates/import"
   400  	urlResourceCertificatePolicy      urlResource = "vedsdk/certificates/checkpolicy"
   401  	urlResourceCertificateRenew       urlResource = "vedsdk/certificates/renew"
   402  	urlResourceCertificateRequest     urlResource = "vedsdk/certificates/request"
   403  	urlResourceCertificateRetrieve    urlResource = "vedsdk/certificates/retrieve"
   404  	urlResourceCertificateRevoke      urlResource = "vedsdk/certificates/revoke"
   405  	urlResourceCertificatesAssociate  urlResource = "vedsdk/certificates/associate"
   406  	urlResourceCertificatesDissociate urlResource = "vedsdk/certificates/dissociate"
   407  	urlResourceCertificateReset       urlResource = "vedsdk/certificates/reset"
   408  	urlResourceCertificate            urlResource = "vedsdk/certificates/"
   409  	urlResourceCertificateSearch                  = urlResourceCertificate
   410  	urlResourceCertificatesList                   = urlResourceCertificate
   411  	urlResourceConfigDnToGuid         urlResource = "vedsdk/config/dntoguid"
   412  	urlResourceConfigReadDn           urlResource = "vedsdk/config/readdn"
   413  	urlResourceFindPolicy             urlResource = "vedsdk/config/findpolicy"
   414  	urlResourceMetadataSet            urlResource = "vedsdk/metadata/set"
   415  	urlResourceAllMetadataGet         urlResource = "vedsdk/metadata/getitems"
   416  	urlResourceMetadataGet            urlResource = "vedsdk/metadata/get"
   417  	urlResourceSystemStatusVersion    urlResource = "vedsdk/systemstatus/version"
   418  	urlRetrieveSelfIdentity           urlResource = "vedsdk/Identity/Self"
   419  	urlResourceCreatePolicy           urlResource = "vedsdk/Config/Create"
   420  	urlResourceWritePolicy            urlResource = "vedsdk/Config/WritePolicy"
   421  	urlResourceReadPolicy             urlResource = "vedsdk/Config/ReadPolicy"
   422  	urlResourceIsValidPolicy          urlResource = "vedsdk/Config/isvalid"
   423  	urlResourceCheckPolicy            urlResource = "vedsdk/certificates/checkpolicy"
   424  	urlResourceCleanPolicy            urlResource = "vedsdk/config/clearpolicyattribute"
   425  	urlResourceBrowseIdentities       urlResource = "vedsdk/Identity/Browse"
   426  	urlResourceValidateIdentity       urlResource = "vedsdk/Identity/Validate"
   427  	urlResourceSshCertReq             urlResource = "vedsdk/SSHCertificates/request"
   428  	urlResourceSshCertRet             urlResource = "vedsdk/SSHCertificates/retrieve"
   429  	urlResourceSshCAPubKey            urlResource = "vedsdk/SSHCertificates/Template/Retrieve/PublicKeyData"
   430  	urlResourceSshCADetails           urlResource = "vedsdk/SSHCertificates/Template/Retrieve"
   431  	urlResourceSshTemplateAvaliable   urlResource = "vedsdk/SSHCertificates/Template/Available"
   432  	urlResourceDNToGUID               urlResource = "vedsdk/Config/DnToGuid"
   433  	urlResourceFindObjectsOfClass     urlResource = "vedsdk/config/findobjectsofclass"
   434  	urlResourceLog                    urlResource = "vedsdk/Log"
   435  )
   436  
   437  const (
   438  	tppAttributeOrg            = "Organization"
   439  	tppAttributeOrgUnit        = "Organizational Unit"
   440  	tppAttributeCountry        = "Country"
   441  	tppAttributeState          = "State"
   442  	tppAttributeLocality       = "City"
   443  	tppAttributeKeyAlgorithm   = "Key Algorithm"
   444  	tppAttributeKeySize        = "Key Bit Strength"
   445  	tppAttributeEllipticCurve  = "Elliptic Curve"
   446  	tppAttributeRequestHash    = "PKCS10 Hash Algorithm"
   447  	tppAttributeManagementType = "Management Type"
   448  	tppAttributeManualCSR      = "Manual Csr"
   449  )
   450  
   451  type tppPolicyData struct {
   452  	Error  string   `json:",omitempty"`
   453  	Result int      `json:",omitempty"`
   454  	Values []string `json:",omitempty"`
   455  	Locked bool     `json:",omitempty"`
   456  }
   457  
   458  type retrieveChainOption int
   459  
   460  const (
   461  	retrieveChainOptionRootLast retrieveChainOption = iota
   462  	retrieveChainOptionRootFirst
   463  	retrieveChainOptionIgnore
   464  )
   465  
   466  const (
   467  	pkcs10HashAlgorithmSha1   = 0
   468  	pkcs10HashAlgorithmSha256 = 1
   469  	pkcs10HashAlgorithmSha384 = 2
   470  	pkcs10HashAlgorithmSha512 = 3
   471  )
   472  
   473  func retrieveChainOptionFromString(order string) retrieveChainOption {
   474  	switch strings.ToLower(order) {
   475  	case "root-first":
   476  		return retrieveChainOptionRootFirst
   477  	case "ignore":
   478  		return retrieveChainOptionIgnore
   479  	default:
   480  		return retrieveChainOptionRootLast
   481  	}
   482  }
   483  
   484  func (c *Connector) request(method string, resource urlResource, data interface{}) (statusCode int, statusText string, body []byte, err error) {
   485  	url := c.baseURL + string(resource)
   486  	var payload io.Reader
   487  	var b []byte
   488  	if method == "POST" || method == "PUT" {
   489  		b, _ = json.Marshal(data)
   490  		payload = bytes.NewReader(b)
   491  	}
   492  
   493  	r, _ := http.NewRequest(method, url, payload)
   494  	r.Close = true
   495  	r.Header.Set(headers.UserAgent, c.userAgent)
   496  	if c.accessToken != "" {
   497  		r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.accessToken))
   498  	} else if c.apiKey != "" {
   499  		r.Header.Add("x-venafi-api-key", c.apiKey)
   500  	}
   501  	r.Header.Add("content-type", "application/json")
   502  	r.Header.Add("cache-control", "no-cache")
   503  
   504  	res, err := c.getHTTPClient().Do(r)
   505  	if res != nil {
   506  		statusCode = res.StatusCode
   507  		statusText = res.Status
   508  	}
   509  	if err != nil {
   510  		return
   511  	}
   512  
   513  	defer res.Body.Close()
   514  	body, err = io.ReadAll(res.Body)
   515  	// Do not enable trace in production
   516  	trace := false // IMPORTANT: sensitive information can be diclosured
   517  	// I hope you know what are you doing
   518  	if trace {
   519  		log.Println("#################")
   520  		log.Printf("Headers are:\n%s", r.Header)
   521  		if method == "POST" || method == "PUT" {
   522  			log.Printf("JSON sent for %s\nRequest:\n%s\n", url, string(b))
   523  		} else {
   524  			log.Printf("%s request sent to %s\n", method, url)
   525  		}
   526  		log.Printf("\nResponse:\n%s\n", string(body))
   527  	} else if c.verbose {
   528  		log.Printf("Got %s status for %s %s\n", statusText, method, url)
   529  	}
   530  	return
   531  }
   532  
   533  func (c *Connector) getHTTPClient() *http.Client {
   534  	if c.client != nil {
   535  		return c.client
   536  	}
   537  	var netTransport = &http.Transport{
   538  		Proxy: http.ProxyFromEnvironment,
   539  		DialContext: (&net.Dialer{
   540  			Timeout:   30 * time.Second,
   541  			KeepAlive: 30 * time.Second,
   542  			DualStack: true,
   543  		}).DialContext,
   544  		MaxIdleConns:          100,
   545  		IdleConnTimeout:       90 * time.Second,
   546  		TLSHandshakeTimeout:   10 * time.Second,
   547  		ExpectContinueTimeout: 1 * time.Second,
   548  	}
   549  	tlsConfig := http.DefaultTransport.(*http.Transport).TLSClientConfig
   550  	/* #nosec */
   551  	if c.trust != nil {
   552  		if tlsConfig == nil {
   553  			tlsConfig = &tls.Config{
   554  				MinVersion: tls.VersionTLS12,
   555  			}
   556  		} else {
   557  			tlsConfig = tlsConfig.Clone()
   558  		}
   559  		tlsConfig.RootCAs = c.trust
   560  	}
   561  	netTransport.TLSClientConfig = tlsConfig
   562  	c.client = &http.Client{
   563  		Timeout:   time.Second * 30,
   564  		Transport: netTransport,
   565  	}
   566  	return c.client
   567  }
   568  
   569  // GenerateRequest creates a new certificate request, based on the zone/policy configuration and the user data
   570  func (c *Connector) GenerateRequest(config *endpoint.ZoneConfiguration, req *certificate.Request) (err error) {
   571  	if req.KeyType == certificate.KeyTypeED25519 {
   572  		return fmt.Errorf("Unable to request certificate from TPP, ed25519 key type is not for TPP")
   573  	}
   574  
   575  	if config == nil {
   576  		config, err = c.ReadZoneConfiguration()
   577  		if err != nil {
   578  			return fmt.Errorf("could not read zone configuration: %s", err)
   579  		}
   580  	}
   581  
   582  	tppMgmtType := config.CustomAttributeValues[tppAttributeManagementType]
   583  	if tppMgmtType == "Monitoring" || tppMgmtType == "Unassigned" {
   584  		return fmt.Errorf("Unable to request certificate from TPP, current TPP configuration would not allow the request to be processed")
   585  	}
   586  
   587  	config.UpdateCertificateRequest(req)
   588  	switch req.CsrOrigin {
   589  	case certificate.LocalGeneratedCSR:
   590  		if config.CustomAttributeValues[tppAttributeManualCSR] == "0" {
   591  			return fmt.Errorf("Unable to request certificate by local generated CSR when zone configuration is 'Manual Csr' = 0")
   592  		}
   593  		err = req.GeneratePrivateKey()
   594  		if err != nil {
   595  			return err
   596  		}
   597  		err = req.GenerateCSR()
   598  		if err != nil {
   599  			return err
   600  		}
   601  	case certificate.UserProvidedCSR:
   602  		if config.CustomAttributeValues[tppAttributeManualCSR] == "0" {
   603  			return fmt.Errorf("Unable to request certificate with user provided CSR when zone configuration is 'Manual Csr' = 0")
   604  		}
   605  		if len(req.GetCSR()) == 0 {
   606  			return fmt.Errorf("CSR was supposed to be provided by user, but it's empty")
   607  		}
   608  
   609  	case certificate.ServiceGeneratedCSR:
   610  	}
   611  	return nil
   612  }
   613  
   614  func getPolicyDN(zone string) string {
   615  	modified := zone
   616  	reg := regexp.MustCompile(`^\\VED\\Policy`)
   617  	if reg.FindStringIndex(modified) == nil {
   618  		reg = regexp.MustCompile(`^\\`)
   619  		if reg.FindStringIndex(modified) == nil {
   620  			modified = "\\" + modified
   621  		}
   622  		modified = "\\VED\\Policy" + modified
   623  	}
   624  	return modified
   625  }
   626  
   627  func getDeviceDN(zone string, location certificate.Location) string {
   628  	if location.Zone != "" {
   629  		// A specific device location was specified
   630  		zone = location.Zone
   631  	}
   632  
   633  	workload := location.Workload
   634  	if workload == "" {
   635  		workload = "Default"
   636  	}
   637  	return getPolicyDN(zone + "\\" + location.Instance + "\\" + workload)
   638  }
   639  
   640  func getCertificateDN(zone, friendlyName string, cn string) string {
   641  	if friendlyName != "" {
   642  		return getPolicyDN(zone + "\\" + friendlyName)
   643  	}
   644  
   645  	return getPolicyDN(zone + "\\" + cn)
   646  }
   647  
   648  func stripBackSlashes(s string) string {
   649  
   650  	var r = regexp.MustCompile(`\\+`)
   651  
   652  	result := r.ReplaceAll([]byte(s), []byte("\\"))
   653  	return string(result)
   654  }
   655  
   656  func parseConfigResult(httpStatusCode int, httpStatus string, body []byte) (tppData tppPolicyData, err error) {
   657  	tppData = tppPolicyData{}
   658  	switch httpStatusCode {
   659  	case http.StatusOK:
   660  		tppData, err := parseConfigData(body)
   661  		if err != nil {
   662  			return tppData, err
   663  		}
   664  		return tppData, nil
   665  	default:
   666  		return tppData, fmt.Errorf("Unexpected status code on TPP Config Operation. Status: %s", httpStatus)
   667  	}
   668  }
   669  
   670  func parseConfigData(b []byte) (data tppPolicyData, err error) {
   671  	err = json.Unmarshal(b, &data)
   672  	return
   673  }
   674  
   675  func parseRequestResult(httpStatusCode int, httpStatus string, body []byte) (string, error) {
   676  	switch httpStatusCode {
   677  	case http.StatusOK, http.StatusCreated:
   678  		reqData, err := parseRequestData(body)
   679  		if err != nil {
   680  			return "", err
   681  		}
   682  		return reqData.CertificateDN, nil
   683  	default:
   684  		return "", fmt.Errorf("Unexpected status code on TPP Certificate Request.\n Status:\n %s. \n Body:\n %s\n", httpStatus, body)
   685  	}
   686  }
   687  
   688  func parseRequestData(b []byte) (data certificateRequestResponse, err error) {
   689  	err = json.Unmarshal(b, &data)
   690  	return
   691  }
   692  
   693  func parseRetrieveResult(httpStatusCode int, httpStatus string, body []byte) (certificateRetrieveResponse, error) {
   694  	var retrieveResponse certificateRetrieveResponse
   695  	switch httpStatusCode {
   696  	case http.StatusOK, http.StatusAccepted:
   697  		retrieveResponse, err := parseRetrieveData(body)
   698  		if err != nil {
   699  			return retrieveResponse, err
   700  		}
   701  		return retrieveResponse, nil
   702  	default:
   703  		return retrieveResponse, fmt.Errorf("Unexpected status code on TPP Certificate Retrieval. Status: %s", httpStatus)
   704  	}
   705  }
   706  
   707  func parseRetrieveData(b []byte) (data certificateRetrieveResponse, err error) {
   708  	err = json.Unmarshal(b, &data)
   709  	return
   710  }
   711  
   712  func parseRevokeResult(httpStatusCode int, httpStatus string, body []byte) (certificateRevokeResponse, error) {
   713  	var revokeResponse certificateRevokeResponse
   714  	switch httpStatusCode {
   715  	case http.StatusOK, http.StatusAccepted:
   716  		revokeResponse, err := parseRevokeData(body)
   717  		if err != nil {
   718  			return revokeResponse, err
   719  		}
   720  		return revokeResponse, nil
   721  	default:
   722  		return revokeResponse, fmt.Errorf("Unexpected status code on TPP Certificate Revocation. Status: %s", httpStatus)
   723  	}
   724  }
   725  
   726  func parseRevokeData(b []byte) (data certificateRevokeResponse, err error) {
   727  	err = json.Unmarshal(b, &data)
   728  	return
   729  }
   730  
   731  func parseRenewResult(httpStatusCode int, httpStatus string, body []byte) (resp certificateRenewResponse, err error) {
   732  	resp, err = parseRenewData(body)
   733  	if err != nil {
   734  		return resp, fmt.Errorf("failed to parse certificate renewal response. status: %s", httpStatus)
   735  	}
   736  	return resp, nil
   737  }
   738  
   739  func parseRenewData(b []byte) (data certificateRenewResponse, err error) {
   740  	err = json.Unmarshal(b, &data)
   741  	return
   742  }
   743  
   744  func parseLogResponse(b []byte) (data LogPostResponse, err error) {
   745  	err = json.Unmarshal(b, &data)
   746  	return
   747  }
   748  
   749  func newPEMCollectionFromResponse(base64Response string, chainOrder certificate.ChainOption) (*certificate.PEMCollection, error) {
   750  	if base64Response != "" {
   751  		certBytes, err := base64.StdEncoding.DecodeString(base64Response)
   752  		if err != nil {
   753  			return nil, err
   754  		}
   755  
   756  		return certificate.PEMCollectionFromBytes(certBytes, chainOrder)
   757  	}
   758  	return nil, nil
   759  }
   760  
   761  func parseBrowseIdentitiesResult(httpStatusCode int, httpStatus string, body []byte) (BrowseIdentitiesResponse, error) {
   762  	var browseIdentitiesResponse BrowseIdentitiesResponse
   763  	switch httpStatusCode {
   764  	case http.StatusOK, http.StatusAccepted:
   765  		browseIdentitiesResponse, err := parseBrowseIdentitiesData(body)
   766  		if err != nil {
   767  			return browseIdentitiesResponse, err
   768  		}
   769  		return browseIdentitiesResponse, nil
   770  	default:
   771  		return browseIdentitiesResponse, fmt.Errorf("Unexpected status code on TPP Browse Identities. Status: %s", httpStatus)
   772  	}
   773  }
   774  
   775  func parseBrowseIdentitiesData(b []byte) (data BrowseIdentitiesResponse, err error) {
   776  	err = json.Unmarshal(b, &data)
   777  	return
   778  }
   779  
   780  func parseValidateIdentityResponse(httpStatusCode int, httpStatus string, body []byte) (ValidateIdentityResponse, error) {
   781  	var validateIdentityResponse ValidateIdentityResponse
   782  	switch httpStatusCode {
   783  	case http.StatusOK, http.StatusAccepted:
   784  		validateIdentityResponse, err := parseValidateIdentityData(body)
   785  		if err != nil {
   786  			return validateIdentityResponse, err
   787  		}
   788  		return validateIdentityResponse, nil
   789  	default:
   790  		return validateIdentityResponse, fmt.Errorf("Unexpected status code on TPP Validate Identity. Status: %s", httpStatus)
   791  	}
   792  }
   793  
   794  func parseValidateIdentityData(b []byte) (data ValidateIdentityResponse, err error) {
   795  	err = json.Unmarshal(b, &data)
   796  	return
   797  }
   798  
   799  func parseFindObjectsOfClassResponse(httpStatusCode int, httpStatus string, body []byte) (findObjectsOfClassResponse, error) {
   800  	var response findObjectsOfClassResponse
   801  	switch httpStatusCode {
   802  	case http.StatusOK, http.StatusAccepted:
   803  		err := json.Unmarshal(body, &response)
   804  		if err != nil {
   805  			return response, err
   806  		}
   807  		return response, nil
   808  	default:
   809  		return response, fmt.Errorf("Unexpected status from FindObjectsOfClass. Status: %s", httpStatus)
   810  	}
   811  }
   812  
   813  type _strValue struct {
   814  	Locked bool
   815  	Value  string
   816  }
   817  
   818  type serverPolicy struct {
   819  	CertificateAuthority _strValue
   820  	CsrGeneration        _strValue
   821  	KeyGeneration        _strValue
   822  	KeyPair              struct {
   823  		KeyAlgorithm _strValue
   824  		KeySize      struct {
   825  			Locked bool
   826  			Value  int
   827  		}
   828  		EllipticCurve struct {
   829  			Locked bool
   830  			Value  string
   831  		}
   832  	}
   833  	ManagementType _strValue
   834  
   835  	PrivateKeyReuseAllowed  bool
   836  	SubjAltNameDnsAllowed   bool
   837  	SubjAltNameEmailAllowed bool
   838  	SubjAltNameIpAllowed    bool
   839  	SubjAltNameUpnAllowed   bool
   840  	SubjAltNameUriAllowed   bool
   841  	Subject                 struct {
   842  		City               _strValue
   843  		Country            _strValue
   844  		Organization       _strValue
   845  		OrganizationalUnit struct {
   846  			Locked bool
   847  			Values []string
   848  		}
   849  
   850  		State _strValue
   851  	}
   852  	UniqueSubjectEnforced bool
   853  	WhitelistedDomains    []string
   854  	WildcardsAllowed      bool
   855  }
   856  
   857  func (sp serverPolicy) toZoneConfig(zc *endpoint.ZoneConfiguration) {
   858  	zc.Country = sp.Subject.Country.Value
   859  	zc.Organization = sp.Subject.Organization.Value
   860  	zc.OrganizationalUnit = sp.Subject.OrganizationalUnit.Values
   861  	zc.Province = sp.Subject.State.Value
   862  	zc.Locality = sp.Subject.City.Value
   863  	key := endpoint.AllowedKeyConfiguration{}
   864  	err := key.KeyType.Set(sp.KeyPair.KeyAlgorithm.Value, sp.KeyPair.EllipticCurve.Value)
   865  	if err != nil {
   866  		return
   867  	}
   868  	if sp.KeyPair.KeySize.Value != 0 {
   869  		key.KeySizes = []int{sp.KeyPair.KeySize.Value}
   870  	}
   871  	if sp.KeyPair.EllipticCurve.Value != "" {
   872  		curve := certificate.EllipticCurveNotSet
   873  		err = curve.Set(sp.KeyPair.EllipticCurve.Value)
   874  		if err == nil {
   875  			key.KeyCurves = append(key.KeyCurves, curve)
   876  		}
   877  	}
   878  	zc.KeyConfiguration = &key
   879  }
   880  
   881  func (sp serverPolicy) toPolicy() (p endpoint.Policy) {
   882  	const allAllowedRegex = ".*"
   883  
   884  	addStartEnd := func(s string) string {
   885  		if !strings.HasPrefix(s, "^") {
   886  			s = "^" + s
   887  		}
   888  		if !strings.HasSuffix(s, "$") {
   889  			s = s + "$"
   890  		}
   891  		return s
   892  	}
   893  	escapeOne := func(s string) string {
   894  		return addStartEnd(regexp.QuoteMeta(s))
   895  	}
   896  	escapeArray := func(l []string) []string {
   897  		escaped := make([]string, len(l))
   898  		for i, r := range l {
   899  			escaped[i] = escapeOne(r)
   900  		}
   901  		return escaped
   902  	}
   903  	domainRegex := func(domain string, wildcardsAllowed bool) string {
   904  		requiresPrefix := false
   905  		if len(domain) > 0 && domain[0] == '.' {
   906  			domain = domain[1:]
   907  			requiresPrefix = true
   908  		}
   909  
   910  		switch {
   911  		case wildcardsAllowed && requiresPrefix:
   912  			return addStartEnd(`([\p{L}\p{N}-*]+\.)+` + regexp.QuoteMeta(domain))
   913  		case wildcardsAllowed && !requiresPrefix:
   914  			return addStartEnd(`([\p{L}\p{N}-*]+\.)*` + regexp.QuoteMeta(domain))
   915  		case !wildcardsAllowed && requiresPrefix:
   916  			return addStartEnd(`([\p{L}\p{N}-]+\.)+` + regexp.QuoteMeta(domain))
   917  		case !wildcardsAllowed && !requiresPrefix:
   918  			return addStartEnd(`([\p{L}\p{N}-]+\.)*` + regexp.QuoteMeta(domain))
   919  		}
   920  
   921  		panic("unreachable")
   922  	}
   923  	domainRegexes := func(domains []string, wildcardsAllowed bool, defaultAllowAll bool) []string {
   924  		if len(domains) == 0 {
   925  			if defaultAllowAll {
   926  				return []string{allAllowedRegex}
   927  			}
   928  			return []string{}
   929  		}
   930  
   931  		regexes := make([]string, len(domains))
   932  		for i, d := range domains {
   933  			regexes[i] = domainRegex(d, wildcardsAllowed)
   934  		}
   935  		return regexes
   936  	}
   937  
   938  	p.SubjectCNRegexes = domainRegexes(sp.WhitelistedDomains, sp.WildcardsAllowed, true)
   939  	if sp.Subject.OrganizationalUnit.Locked {
   940  		p.SubjectOURegexes = escapeArray(sp.Subject.OrganizationalUnit.Values)
   941  	} else {
   942  		p.SubjectOURegexes = []string{allAllowedRegex}
   943  	}
   944  	if sp.Subject.Organization.Locked {
   945  		p.SubjectORegexes = []string{escapeOne(sp.Subject.Organization.Value)}
   946  	} else {
   947  		p.SubjectORegexes = []string{allAllowedRegex}
   948  	}
   949  	if sp.Subject.City.Locked {
   950  		p.SubjectLRegexes = []string{escapeOne(sp.Subject.City.Value)}
   951  	} else {
   952  		p.SubjectLRegexes = []string{allAllowedRegex}
   953  	}
   954  	if sp.Subject.State.Locked {
   955  		p.SubjectSTRegexes = []string{escapeOne(sp.Subject.State.Value)}
   956  	} else {
   957  		p.SubjectSTRegexes = []string{allAllowedRegex}
   958  	}
   959  	if sp.Subject.Country.Locked {
   960  		p.SubjectCRegexes = []string{escapeOne(sp.Subject.Country.Value)}
   961  	} else {
   962  		p.SubjectCRegexes = []string{allAllowedRegex}
   963  	}
   964  	p.DnsSanRegExs = domainRegexes(sp.WhitelistedDomains, sp.WildcardsAllowed, sp.SubjAltNameDnsAllowed)
   965  	if sp.SubjAltNameIpAllowed {
   966  		p.IpSanRegExs = []string{allAllowedRegex}
   967  	} else {
   968  		p.IpSanRegExs = []string{}
   969  	}
   970  	if sp.SubjAltNameEmailAllowed {
   971  		p.EmailSanRegExs = []string{allAllowedRegex}
   972  	} else {
   973  		p.EmailSanRegExs = []string{}
   974  	}
   975  	if sp.SubjAltNameUriAllowed {
   976  		p.UriSanRegExs = []string{allAllowedRegex}
   977  	} else {
   978  		p.UriSanRegExs = []string{}
   979  	}
   980  	if sp.SubjAltNameUpnAllowed {
   981  		p.UpnSanRegExs = []string{allAllowedRegex}
   982  	} else {
   983  		p.UpnSanRegExs = []string{}
   984  	}
   985  	if sp.KeyPair.KeyAlgorithm.Locked {
   986  		var keyType certificate.KeyType
   987  		if err := keyType.Set(sp.KeyPair.KeyAlgorithm.Value, sp.KeyPair.EllipticCurve.Value); err != nil {
   988  			panic(err)
   989  		}
   990  		key := endpoint.AllowedKeyConfiguration{KeyType: keyType}
   991  		if keyType == certificate.KeyTypeRSA {
   992  			if sp.KeyPair.KeySize.Locked {
   993  				for _, i := range certificate.AllSupportedKeySizes() {
   994  					if i >= sp.KeyPair.KeySize.Value {
   995  						key.KeySizes = append(key.KeySizes, i)
   996  					}
   997  				}
   998  			} else {
   999  				key.KeySizes = certificate.AllSupportedKeySizes()
  1000  			}
  1001  		} else {
  1002  			var curve certificate.EllipticCurve
  1003  			if sp.KeyPair.EllipticCurve.Locked {
  1004  				if err := curve.Set(sp.KeyPair.EllipticCurve.Value); err != nil {
  1005  					panic(err)
  1006  				}
  1007  				key.KeyCurves = append(key.KeyCurves, curve)
  1008  			} else {
  1009  				key.KeyCurves = certificate.AllSupportedCurves()
  1010  			}
  1011  		}
  1012  
  1013  		p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, key)
  1014  	} else {
  1015  		var ks []int
  1016  		for _, s := range certificate.AllSupportedKeySizes() {
  1017  			if !sp.KeyPair.KeySize.Locked || s >= sp.KeyPair.KeySize.Value {
  1018  				ks = append(ks, s)
  1019  			}
  1020  		}
  1021  		p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, endpoint.AllowedKeyConfiguration{
  1022  			KeyType: certificate.KeyTypeRSA, KeySizes: ks,
  1023  		})
  1024  		if sp.KeyPair.EllipticCurve.Locked {
  1025  			var curve certificate.EllipticCurve
  1026  			if err := curve.Set(sp.KeyPair.EllipticCurve.Value); err != nil {
  1027  				panic(err)
  1028  			}
  1029  			p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, endpoint.AllowedKeyConfiguration{
  1030  				KeyType: certificate.KeyTypeECDSA, KeyCurves: []certificate.EllipticCurve{curve},
  1031  			})
  1032  		} else {
  1033  			p.AllowedKeyConfigurations = append(p.AllowedKeyConfigurations, endpoint.AllowedKeyConfiguration{
  1034  				KeyType: certificate.KeyTypeECDSA, KeyCurves: certificate.AllSupportedCurves(),
  1035  			})
  1036  		}
  1037  	}
  1038  	p.AllowWildcards = sp.WildcardsAllowed
  1039  	p.AllowKeyReuse = sp.PrivateKeyReuseAllowed
  1040  	return
  1041  }