160 lines
4.6 KiB
Go
160 lines
4.6 KiB
Go
|
// Licensed to Elasticsearch B.V. under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. Elasticsearch B.V. licenses this file to you 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 mage
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/magefile/mage/sh"
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
var _appleKeychain = &appleKeychain{}
|
||
|
|
||
|
type appleKeychain struct{}
|
||
|
|
||
|
// SigningIdentity represents a key pair (public/private) that can be used for
|
||
|
// signing.
|
||
|
type SigningIdentity struct {
|
||
|
ID string
|
||
|
Description string
|
||
|
}
|
||
|
|
||
|
// ListIdentities queries the keychain to get a list of signing identities
|
||
|
// (certificate + private key).
|
||
|
func (k *appleKeychain) ListIdentities() ([]SigningIdentity, error) {
|
||
|
var re = regexp.MustCompile(`(?m)^\s*\d+\)\s+(\w+)\s+"(.+)"$`)
|
||
|
|
||
|
out, err := sh.Output("security", "find-identity", "-v")
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var idents []SigningIdentity
|
||
|
ids := map[string]struct{}{}
|
||
|
for _, match := range re.FindAllStringSubmatch(out, -1) {
|
||
|
ident := SigningIdentity{ID: match[1], Description: match[2]}
|
||
|
|
||
|
// Deduplicate
|
||
|
if _, found := ids[ident.ID]; found {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
idents = append(idents, ident)
|
||
|
ids[ident.ID] = struct{}{}
|
||
|
}
|
||
|
|
||
|
return idents, nil
|
||
|
}
|
||
|
|
||
|
// AppleSigningInfo indicate if signing is enabled and specifies the identities
|
||
|
// to use for signing applications and installers.
|
||
|
type AppleSigningInfo struct {
|
||
|
Sign bool
|
||
|
App SigningIdentity
|
||
|
Installer SigningIdentity
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
appleSigningInfoValue *AppleSigningInfo
|
||
|
appleSigningInfoErr error
|
||
|
appleSigningInfoOnce sync.Once
|
||
|
)
|
||
|
|
||
|
// GetAppleSigningInfo returns the signing identities used for code signing
|
||
|
// apps and installers.
|
||
|
//
|
||
|
// Environment Variables
|
||
|
//
|
||
|
// APPLE_SIGNING_ENABLED - Must be set to true to enable signing. Defaults to
|
||
|
// false.
|
||
|
//
|
||
|
// APPLE_SIGNING_IDENTITY_INSTALLER - filter for selecting the signing identity
|
||
|
// for installers.
|
||
|
//
|
||
|
// APPLE_SIGNING_IDENTITY_APP - filter for selecting the signing identity
|
||
|
// for apps.
|
||
|
func GetAppleSigningInfo() (*AppleSigningInfo, error) {
|
||
|
appleSigningInfoOnce.Do(func() {
|
||
|
appleSigningInfoValue, appleSigningInfoErr = getAppleSigningInfo()
|
||
|
})
|
||
|
|
||
|
return appleSigningInfoValue, appleSigningInfoErr
|
||
|
}
|
||
|
|
||
|
func getAppleSigningInfo() (*AppleSigningInfo, error) {
|
||
|
var (
|
||
|
signingEnabled, _ = strconv.ParseBool(EnvOr("APPLE_SIGNING_ENABLED", "false"))
|
||
|
identityInstaller = strings.ToLower(EnvOr("APPLE_SIGNING_IDENTITY_INSTALLER", "Developer ID Installer"))
|
||
|
identityApp = strings.ToLower(EnvOr("APPLE_SIGNING_IDENTITY_APP", "Developer ID Application"))
|
||
|
)
|
||
|
|
||
|
if !signingEnabled {
|
||
|
return &AppleSigningInfo{Sign: false}, nil
|
||
|
}
|
||
|
|
||
|
idents, err := _appleKeychain.ListIdentities()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var install, app []SigningIdentity
|
||
|
for _, ident := range idents {
|
||
|
id, desc := strings.ToLower(ident.ID), strings.ToLower(ident.Description)
|
||
|
if strings.Contains(id, identityInstaller) || strings.Contains(desc, identityInstaller) {
|
||
|
install = append(install, ident)
|
||
|
}
|
||
|
if strings.Contains(id, identityApp) || strings.Contains(desc, identityApp) {
|
||
|
app = append(app, ident)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(install) == 1 && len(app) == 1 {
|
||
|
log.Printf("Apple Code Signing Identities:\n App: %+v\n Installer: %+v", app[0], install[0])
|
||
|
return &AppleSigningInfo{
|
||
|
Sign: true,
|
||
|
Installer: install[0],
|
||
|
App: app[0],
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
if len(install) > 1 {
|
||
|
return nil, errors.Errorf("found multiple installer signing identities "+
|
||
|
"that match '%v'. Set a more specific APPLE_SIGNING_IDENTITY_INSTALLER "+
|
||
|
"value that will select one of %+v", identityInstaller, install)
|
||
|
}
|
||
|
|
||
|
if len(app) > 1 {
|
||
|
return nil, errors.Errorf("found multiple installer signing identities "+
|
||
|
"that match '%v'. Set a more specific APPLE_SIGNING_IDENTITY_APP "+
|
||
|
"value that will select one of %+v", identityApp, app)
|
||
|
}
|
||
|
|
||
|
if len(install) == 0 || len(app) == 0 {
|
||
|
return nil, errors.Errorf("apple signing was requested with " +
|
||
|
"APPLE_SIGNING_ENABLED=true, but the required signing identities " +
|
||
|
"for app and installer were not found")
|
||
|
}
|
||
|
|
||
|
return &AppleSigningInfo{Sign: false}, nil
|
||
|
}
|