mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
8e6f2c554b
Refactor string concatenation to use strings.Builder for improved performance in multiple files
264 lines
8.2 KiB
Go
264 lines
8.2 KiB
Go
package appstore
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/blacktop/ipsw/internal/download"
|
|
)
|
|
|
|
type CertificateType string
|
|
|
|
const (
|
|
CT_APPLE_PAY CertificateType = "APPLE_PAY"
|
|
CT_APPLE_PAY_MERCHANT_IDENTITY CertificateType = "APPLE_PAY_MERCHANT_IDENTITY"
|
|
CT_APPLE_PAY_PSP_IDENTITY CertificateType = "APPLE_PAY_PSP_IDENTITY"
|
|
CT_APPLE_PAY_RSA CertificateType = "APPLE_PAY_RSA"
|
|
CT_DEVELOPER_ID_KEXT CertificateType = "DEVELOPER_ID_KEXT"
|
|
CT_DEVELOPER_ID_KEXT_G2 CertificateType = "DEVELOPER_ID_KEXT_G2"
|
|
CT_DEVELOPER_ID_APPLICATION CertificateType = "DEVELOPER_ID_APPLICATION"
|
|
CT_DEVELOPER_ID_APPLICATION_G2 CertificateType = "DEVELOPER_ID_APPLICATION_G2"
|
|
CT_DEVELOPMENT CertificateType = "DEVELOPMENT"
|
|
CT_DISTRIBUTION CertificateType = "DISTRIBUTION"
|
|
CT_IDENTITY_ACCESS CertificateType = "IDENTITY_ACCESS"
|
|
CT_IOS_DEVELOPMENT CertificateType = "IOS_DEVELOPMENT"
|
|
CT_IOS_DISTRIBUTION CertificateType = "IOS_DISTRIBUTION"
|
|
CT_MAC_APP_DISTRIBUTION CertificateType = "MAC_APP_DISTRIBUTION"
|
|
CT_MAC_INSTALLER_DISTRIBUTION CertificateType = "MAC_INSTALLER_DISTRIBUTION"
|
|
CT_MAC_APP_DEVELOPMENT CertificateType = "MAC_APP_DEVELOPMENT"
|
|
CT_PASS_TYPE_ID CertificateType = "PASS_TYPE_ID"
|
|
CT_PASS_TYPE_ID_WITH_NFC CertificateType = "PASS_TYPE_ID_WITH_NFC"
|
|
)
|
|
|
|
var CertTypes = []string{
|
|
string(CT_APPLE_PAY),
|
|
string(CT_APPLE_PAY_MERCHANT_IDENTITY),
|
|
string(CT_APPLE_PAY_PSP_IDENTITY),
|
|
string(CT_APPLE_PAY_RSA),
|
|
string(CT_DEVELOPER_ID_KEXT),
|
|
string(CT_DEVELOPER_ID_KEXT_G2),
|
|
string(CT_DEVELOPER_ID_APPLICATION),
|
|
string(CT_DEVELOPER_ID_APPLICATION_G2),
|
|
string(CT_DEVELOPMENT),
|
|
string(CT_DISTRIBUTION),
|
|
string(CT_IDENTITY_ACCESS),
|
|
string(CT_IOS_DEVELOPMENT),
|
|
string(CT_IOS_DISTRIBUTION),
|
|
string(CT_MAC_APP_DISTRIBUTION),
|
|
string(CT_MAC_INSTALLER_DISTRIBUTION),
|
|
string(CT_MAC_APP_DEVELOPMENT),
|
|
string(CT_PASS_TYPE_ID),
|
|
string(CT_PASS_TYPE_ID_WITH_NFC),
|
|
}
|
|
|
|
type BundleIdPlatform string
|
|
|
|
const (
|
|
BP_IOS BundleIdPlatform = "IOS"
|
|
BP_MAC BundleIdPlatform = "MAC_OS"
|
|
BP_UNIVERSAL BundleIdPlatform = "UNIVERSAL"
|
|
)
|
|
|
|
var BundleIdPlatforms = []string{
|
|
string(BP_IOS),
|
|
string(BP_MAC),
|
|
string(BP_UNIVERSAL),
|
|
}
|
|
|
|
type Certificate struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Attributes struct {
|
|
CertificateContent []byte `json:"certificateContent"`
|
|
DisplayName string `json:"displayName"`
|
|
ExpirationDate Date `json:"expirationDate"`
|
|
Name string `json:"name"`
|
|
Platform BundleIdPlatform `json:"platform"`
|
|
SerialNumber string `json:"serialNumber"`
|
|
CertificateType CertificateType `json:"certificateType"`
|
|
CsrContent any `json:"csrContent"`
|
|
} `json:"attributes"`
|
|
Links Links `json:"links"`
|
|
}
|
|
|
|
func (c Certificate) IsExpired() bool {
|
|
return time.Time(c.Attributes.ExpirationDate).Before(time.Now())
|
|
}
|
|
|
|
type CertificateResponse struct {
|
|
Data Certificate `json:"data"`
|
|
Links Links `json:"links"`
|
|
}
|
|
|
|
type CertificatesResponse struct {
|
|
Data []Certificate `json:"data"`
|
|
Links PagedDocumentLinks `json:"links"`
|
|
Meta Meta `json:"meta"`
|
|
}
|
|
|
|
type CertificateCreateRequest struct {
|
|
Data struct {
|
|
Type string `json:"type"` // certificates
|
|
Attributes struct {
|
|
CertificateType string `json:"certificateType"`
|
|
CSRContent string `json:"csrContent"`
|
|
} `json:"attributes"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
// GetCertificates returns a list certificates and download their data.
|
|
func (as *AppStore) GetCertificates() ([]Certificate, error) {
|
|
|
|
if err := as.createToken(defaultJWTLife); err != nil {
|
|
return nil, fmt.Errorf("failed to create token: %v", err)
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", certificatessURL, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create http GET request: %v", err)
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+as.token)
|
|
|
|
client := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: download.GetProxy(as.Proxy),
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: as.Insecure},
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send http request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
var eresp ErrorResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&eresp); err != nil {
|
|
return nil, fmt.Errorf("failed to JSON decode http response: %v", err)
|
|
}
|
|
var errOut strings.Builder
|
|
for idx, e := range eresp.Errors {
|
|
errOut.WriteString(fmt.Sprintf("%s%s: %s (%s)\n", strings.Repeat("\t", idx), e.Code, e.Title, e.Detail))
|
|
}
|
|
return nil, fmt.Errorf("%s: %s", resp.Status, errOut.String())
|
|
}
|
|
|
|
var certsResp CertificatesResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&certsResp); err != nil {
|
|
return nil, fmt.Errorf("failed to JSON decode http response: %v", err)
|
|
}
|
|
|
|
certificateDataList := make([]Certificate, 0)
|
|
for _, v := range certsResp.Data {
|
|
if v.Type == "certificates" {
|
|
certificateDataList = append(certificateDataList, v)
|
|
}
|
|
}
|
|
|
|
return certificateDataList, nil
|
|
}
|
|
|
|
// CreateCertificate creates a new certificate using a certificate signing request.
|
|
func (as *AppStore) CreateCertificate(ctype string, csrData string) (*Certificate, error) {
|
|
if err := as.createToken(defaultJWTLife); err != nil {
|
|
return nil, fmt.Errorf("failed to create token: %v", err)
|
|
}
|
|
|
|
var profileCreateRequest CertificateCreateRequest
|
|
profileCreateRequest.Data.Type = "certificates"
|
|
profileCreateRequest.Data.Attributes.CertificateType = ctype
|
|
profileCreateRequest.Data.Attributes.CSRContent = csrData
|
|
|
|
jsonStr, err := json.Marshal(&profileCreateRequest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// os.WriteFile("req.json", jsonStr, 0644)
|
|
req, err := http.NewRequest("POST", certificatessURL, bytes.NewBuffer(jsonStr))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+as.token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
client := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: download.GetProxy(as.Proxy),
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: as.Insecure},
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send http request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 201 && resp.StatusCode != 409 {
|
|
var eresp ErrorResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&eresp); err != nil {
|
|
return nil, fmt.Errorf("failed to JSON decode http response: %v", err)
|
|
}
|
|
var errOut strings.Builder
|
|
for idx, e := range eresp.Errors {
|
|
errOut.WriteString(fmt.Sprintf("%s%s: %s (%s)\n", strings.Repeat("\t", idx), e.Code, e.Title, e.Detail))
|
|
}
|
|
return nil, fmt.Errorf("%s: %s", resp.Status, errOut.String())
|
|
}
|
|
|
|
var cert CertificateResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&cert); err != nil {
|
|
return nil, fmt.Errorf("failed to JSON decode http response: %v", err)
|
|
}
|
|
|
|
return &cert.Data, nil
|
|
}
|
|
|
|
// RevokeCertificate revokes a lost, stolen, compromised, or expiring signing certificate.
|
|
func (as *AppStore) RevokeCertificate(id string) error {
|
|
if err := as.createToken(defaultJWTLife); err != nil {
|
|
return fmt.Errorf("failed to create token: %v", err)
|
|
}
|
|
|
|
req, err := http.NewRequest("DELETE", certificatessURL+"/"+id, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+as.token)
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
client := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: download.GetProxy(as.Proxy),
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: as.Insecure},
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send http request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 204 {
|
|
var eresp ErrorResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&eresp); err != nil {
|
|
return fmt.Errorf("failed to JSON decode http response: %v", err)
|
|
}
|
|
var errOut strings.Builder
|
|
for idx, e := range eresp.Errors {
|
|
errOut.WriteString(fmt.Sprintf("%s%s: %s (%s)\n", strings.Repeat("\t", idx), e.Code, e.Title, e.Detail))
|
|
}
|
|
return fmt.Errorf("%s: %s", resp.Status, errOut.String())
|
|
}
|
|
|
|
return nil
|
|
}
|