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 }