Skip to content

Commit

Permalink
feat: add ability to dump 'archive decryption key' from AEA (@Siguza)
Browse files Browse the repository at this point in the history
  • Loading branch information
blacktop committed Jun 12, 2024
1 parent f523472 commit b6304a5
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 57 deletions.
30 changes: 24 additions & 6 deletions cmd/ipsw/cmd/fw/aea.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func init() {
FwCmd.AddCommand(aeaCmd)

aeaCmd.Flags().BoolP("info", "i", false, "Print info")
aeaCmd.Flags().BoolP("key", "k", false, "Get private key JSON")
aeaCmd.Flags().BoolP("fcs-key", "f", false, "Get fcs-key JSON")
aeaCmd.Flags().BoolP("key", "k", false, "Get archive decryption key")
aeaCmd.Flags().StringP("pem", "p", "", "AEA private_key.pem file")
aeaCmd.Flags().StringP("output", "o", "", "Folder to extract files to")
aeaCmd.MarkFlagDirname("output")
Expand All @@ -61,15 +62,16 @@ var aeaCmd = &cobra.Command{
Short: "Parse ANE1 DMGs",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
var key []byte
var pemData []byte

if viper.GetBool("verbose") {
log.SetLevel(log.DebugLevel)
}
color.NoColor = viper.GetBool("no-color")

// flags
keyJSON := viper.GetBool("fw.aea.key")
fcsKey := viper.GetBool("fw.aea.fcs-key")
adKey := viper.GetBool("fw.aea.key")
showInfo := viper.GetBool("fw.aea.info")
pemFile := viper.GetString("fw.aea.pem")
output := viper.GetString("fw.aea.output")
Expand All @@ -96,7 +98,7 @@ var aeaCmd = &cobra.Command{
}
}
}
} else if keyJSON {
} else if fcsKey {
metadata, err := aea.Info(args[0])
if err != nil {
return fmt.Errorf("failed to parse AEA: %v", err)
Expand All @@ -122,17 +124,33 @@ var aeaCmd = &cobra.Command{
return fmt.Errorf("failed to highlight json: %v", err)
}
}
} else if adKey {
if pemFile != "" {
pemData, err = os.ReadFile(pemFile)
if err != nil {
return fmt.Errorf("failed to read pem file: %v", err)
}
}
metadata, err := aea.Info(args[0])
if err != nil {
return fmt.Errorf("failed to parse AEA: %v", err)
}
wkey, err := metadata.DecryptFCS(pemData)
if err != nil {
return fmt.Errorf("failed to HPKE decrypt fcs-key: %v", err)
}
fmt.Printf("base64:%s\n", wkey)
} else {
if pemFile != "" {
key, err = os.ReadFile(pemFile)
pemData, err = os.ReadFile(pemFile)
if err != nil {
return fmt.Errorf("failed to read pem file: %v", err)
}
}
if err := os.MkdirAll(output, 0o750); err != nil {
return fmt.Errorf("failed to create output directory: %v", err)
}
out, err := aea.Decrypt(args[0], output, key)
out, err := aea.Decrypt(args[0], output, pemData)
if err != nil {
return fmt.Errorf("failed to parse AEA: %v", err)
}
Expand Down
111 changes: 60 additions & 51 deletions pkg/aea/aea.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,62 @@ func (md Metadata) GetPrivateKey(data []byte) (map[string]PrivateKey, error) {
return out, nil
}

func (md Metadata) DecryptFCS(pemData []byte) (string, error) {
ddata, ok := md["com.apple.wkms.fcs-response"]
if !ok {
return "", fmt.Errorf("no 'com.apple.wkms.fcs-response' found in AEA metadata")
}
var fcsResp fcsResponse
if err := json.Unmarshal(ddata, &fcsResp); err != nil {
return "", err
}
encRequestData, err := base64.StdEncoding.WithPadding(base64.StdPadding).DecodeString(fcsResp.EncRequest)
if err != nil {
return "", err
}
wrappedKeyData, err := base64.StdEncoding.WithPadding(base64.StdPadding).DecodeString(fcsResp.WrappedKey)
if err != nil {
return "", err
}

pkmap, err := md.GetPrivateKey(pemData)
if err != nil {
return "", err
}
var privKey []byte
for _, pk := range pkmap {
privKey, err = pk.UnmarshalBinaryPrivateKey()
if err != nil {
return "", err
}
}

kemID := hpke.KEM_P256_HKDF_SHA256
kdfID := hpke.KDF_HKDF_SHA256
aeadID := hpke.AEAD_AES256GCM

suite := hpke.NewSuite(kemID, kdfID, aeadID)

privateKey, err := kemID.Scheme().UnmarshalBinaryPrivateKey(privKey)
if err != nil {
return "", err
}
recv, err := suite.NewReceiver(privateKey, nil)
if err != nil {
return "", err
}
opener, err := recv.Setup(encRequestData)
if err != nil {
return "", err
}
wkey, err := opener.Open(wrappedKeyData, nil)
if err != nil {
return "", err
}

return base64.StdEncoding.EncodeToString(wkey), nil
}

func Info(in string) (Metadata, error) {
var metadata Metadata
f, err := os.Open(in)
Expand Down Expand Up @@ -172,59 +228,12 @@ func Decrypt(in, out string, privKeyData []byte) (string, error) {
return "", fmt.Errorf("failed to parse AEA: %v", err)
}

ddata, ok := metadata["com.apple.wkms.fcs-response"]
if !ok {
return "", fmt.Errorf("no fcs response found")
}
var fcsResp fcsResponse
if err := json.Unmarshal(ddata, &fcsResp); err != nil {
return "", err
}
encRequestData, err := base64.StdEncoding.WithPadding(base64.StdPadding).DecodeString(fcsResp.EncRequest)
if err != nil {
return "", err
}
wrappedKeyData, err := base64.StdEncoding.WithPadding(base64.StdPadding).DecodeString(fcsResp.WrappedKey)
if err != nil {
return "", err
}

pkmap, err := metadata.GetPrivateKey(privKeyData)
if err != nil {
return "", err
}
var privKey []byte
for _, pk := range pkmap {
privKey, err = pk.UnmarshalBinaryPrivateKey()
if err != nil {
return "", err
}
}

kemID := hpke.KEM_P256_HKDF_SHA256
kdfID := hpke.KDF_HKDF_SHA256
aeadID := hpke.AEAD_AES256GCM

suite := hpke.NewSuite(kemID, kdfID, aeadID)

privateKey, err := kemID.Scheme().UnmarshalBinaryPrivateKey(privKey)
if err != nil {
return "", err
}
recv, err := suite.NewReceiver(privateKey, nil)
if err != nil {
return "", err
}
opener, err := recv.Setup(encRequestData)
if err != nil {
return "", err
}
wkey, err := opener.Open(wrappedKeyData, nil)
wkey, err := metadata.DecryptFCS(privKeyData)
if err != nil {
return "", err
return "", fmt.Errorf("failed to HPKE decrypt fcs-key: %v", err)
}

return aea(in, filepath.Join(out, filepath.Base(strings.TrimSuffix(in, filepath.Ext(in)))), base64.StdEncoding.EncodeToString(wkey))
return aea(in, filepath.Join(out, filepath.Base(strings.TrimSuffix(in, filepath.Ext(in)))), wkey)
}

func aea(in, out, key string) (string, error) {
Expand All @@ -236,5 +245,5 @@ func aea(in, out, key string) (string, error) {
}
return out, nil
}
return "", fmt.Errorf("only supported on macOS")
return "", fmt.Errorf("only supported on macOS (due to `aea` binary requirement)")
}

0 comments on commit b6304a5

Please sign in to comment.