diff --git a/cmd/ipsw/cmd/fw/aea.go b/cmd/ipsw/cmd/fw/aea.go index 72ae9bcc7..ae3967de3 100644 --- a/cmd/ipsw/cmd/fw/aea.go +++ b/cmd/ipsw/cmd/fw/aea.go @@ -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") @@ -61,7 +62,7 @@ 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) @@ -69,7 +70,8 @@ var aeaCmd = &cobra.Command{ 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") @@ -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) @@ -122,9 +124,25 @@ 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) } @@ -132,7 +150,7 @@ var aeaCmd = &cobra.Command{ 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) } diff --git a/pkg/aea/aea.go b/pkg/aea/aea.go index 93cdafced..b02c82ec3 100644 --- a/pkg/aea/aea.go +++ b/pkg/aea/aea.go @@ -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) @@ -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) { @@ -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)") }