diff --git a/Cargo.lock b/Cargo.lock index d5bf68a..7fb6751 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1355 +1,1247 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] -name = "aes" -version = "0.3.2" +name = "addr2line" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "aes-soft 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli", ] [[package]] -name = "aes-soft" -version = "0.3.2" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "aesni" +name = "aes" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "cipher", ] [[package]] -name = "aho-corasick" -version = "0.7.6" +name = "aes-soft" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", + "opaque-debug", ] [[package]] -name = "ansi_term" -version = "0.11.0" +name = "aesni" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", + "opaque-debug", ] [[package]] -name = "argon2rs" -version = "0.2.5" +name = "ahash" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] -name = "arrayvec" -version = "0.4.8" +name = "ansi_term" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "atty" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi", ] [[package]] name = "autocfg" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] [[package]] -name = "backtrace" -version = "0.3.9" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "backtrace-sys" -version = "0.1.24" +name = "backtrace" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" dependencies = [ - "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] name = "base64" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.4.3", ] [[package]] name = "base64" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bincode" -version = "1.1.4" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "bit_field" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" [[package]] name = "bitflags" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2-rfc" -version = "0.2.18" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "block-modes" -version = "0.2.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "cipher", ] [[package]] name = "block-padding" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "blz-nx" -version = "0.1.0" -source = "git+https://github.com/Thog/blz-nx-rs#7e2dccf2a3e1ccb52408e1db995e8f3971de4f2c" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fdb1b8afc62dd91f33496ef989fa5706c81c1cb9817d346dba2417e616e5f7" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.4.3", ] [[package]] -name = "byte-tools" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bytecount" -version = "0.3.2" +name = "byteorder" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" [[package]] name = "byteorder" -version = "0.5.3" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] -name = "byteorder" -version = "1.3.2" +name = "cargo-platform" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] [[package]] -name = "cargo_metadata" -version = "0.5.8" +name = "cargo-toml2" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22b689b002fa6142753b431e1414d0336065b692e1d0e400504698f75997817d" dependencies = [ - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_derive", + "toml", ] [[package]] name = "cargo_metadata" -version = "0.6.0" -source = "git+https://github.com/roblabla/cargo_metadata#8e2f400d2464f4bd4b5f9005c7201123a22bed2b" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" dependencies = [ - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo-platform", + "semver 0.11.0", + "semver-parser", + "serde", + "serde_json", ] [[package]] name = "cc" -version = "1.0.30" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "0.1.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.11" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "cipher" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] name = "clap" -version = "2.33.0" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] name = "cmac" -version = "0.2.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73d4de4f7724e5fe70addfb2bd37c2abd2f95084a429d7773b0b9645499b4272" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dbl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "dbl", ] [[package]] -name = "constant_time_eq" -version = "0.1.3" +name = "convert_case" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] -name = "crypto-mac" -version = "0.7.0" +name = "cpufeatures" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] -name = "dbl" -version = "0.2.0" +name = "crypto-mac" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", + "generic-array", + "subtle", ] [[package]] -name = "derive_more" -version = "0.13.0" +name = "ctr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher", ] [[package]] -name = "digest" -version = "0.8.1" +name = "dbl" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" dependencies = [ - "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] -name = "dirs" -version = "1.0.4" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", ] [[package]] -name = "elf" -version = "0.0.10" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array", ] [[package]] -name = "error-chain" -version = "0.11.0" +name = "dirs-next" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "dirs-sys-next", ] [[package]] -name = "error-chain" -version = "0.12.0" +name = "dirs-sys-next" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_users", + "winapi", ] [[package]] -name = "failure" -version = "0.1.5" +name = "dlv-list" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand", ] [[package]] -name = "failure_derive" -version = "0.1.5" +name = "doc-comment" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] -name = "fake-simd" -version = "0.1.2" +name = "elf" +version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5" +dependencies = [ + "byteorder 0.5.3", +] [[package]] -name = "fuchsia-zircon" -version = "0.3.3" +name = "failure" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ - "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "failure_derive", ] [[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +name = "failure_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] name = "generic-array" -version = "0.12.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", + "version_check", ] [[package]] name = "getrandom" -version = "0.1.14" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "wasi", ] [[package]] -name = "glob" -version = "0.2.11" +name = "gimli" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "goblin" -version = "0.0.17" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884" dependencies = [ - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "plain", + "scroll", ] [[package]] -name = "heck" -version = "0.3.0" +name = "hashbrown" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" dependencies = [ - "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", ] [[package]] -name = "hex" -version = "0.3.2" +name = "heck" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] [[package]] -name = "idna" -version = "0.1.5" +name = "hermit-abi" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" + [[package]] name = "itoa" -version = "0.4.4" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "lazy_static" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "spin", ] [[package]] name = "libc" -version = "0.2.68" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libm" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[package]] name = "linkle" -version = "0.2.7" -dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-modes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blz-nx 0.1.0 (git+https://github.com/Thog/blz-nx-rs)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cargo_metadata 0.6.0 (git+https://github.com/roblabla/cargo_metadata)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lz4 1.23.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pem 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rsa 0.2.0 (git+https://github.com/RustCrypto/RSA?rev=11500ed)", - "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "yasna 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +version = "0.2.10" +dependencies = [ + "aes", + "bincode", + "bit_field", + "block-modes", + "blz-nx", + "byteorder 1.4.3", + "cargo-toml2", + "cargo_metadata", + "clap", + "cmac", + "ctr", + "derive_more", + "dirs-next", + "elf", + "goblin", + "hex", + "lz4", + "num-traits", + "pem 0.6.1", + "rand", + "rsa", + "rust-ini", + "scroll", + "semver 0.11.0", + "serde", + "serde_derive", + "serde_json", + "sha2", + "snafu", + "structopt", + "yasna", ] [[package]] name = "log" -version = "0.4.5" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "lz4" -version = "1.23.1" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edcb94251b1c375c459e5abe9fb0168c1c826c3370172684844f8f3f8d1a885" dependencies = [ - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "lz4-sys 1.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "lz4-sys", ] [[package]] name = "lz4-sys" -version = "1.8.3" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7be8908e2ed6f31c02db8a9fa962f03e36c53fbfde437363eae3306b85d7e17" dependencies = [ - "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", ] [[package]] -name = "matches" -version = "0.1.8" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "memchr" -version = "2.2.1" +name = "miniz_oxide" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] [[package]] -name = "nodrop" -version = "0.1.13" +name = "num-bigint" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] [[package]] name = "num-bigint" -version = "0.2.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.1.0", + "num-integer", + "num-traits", ] [[package]] name = "num-bigint-dig" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.8", + "byteorder 1.4.3", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", ] [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.1.0", + "num-traits", ] [[package]] name = "num-iter" -version = "0.1.39" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.1.0", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.6" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg 1.1.0", + "libm", +] + +[[package]] +name = "object" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "opaque-debug" -version = "0.2.1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "ordered-multimap" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +dependencies = [ + "dlv-list", + "hashbrown", +] [[package]] name = "pem" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39eb474073dfddbf7156515344266245d91ce698ddbf15e0498cef22b836f45a" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1", + "failure", + "lazy_static", + "regex", ] [[package]] name = "pem" -version = "0.7.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" dependencies = [ - "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.13.0", + "once_cell", + "regex", ] [[package]] -name = "percent-encoding" -version = "1.0.1" +name = "pest" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] -name = "proc-macro2" -version = "0.4.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "1.0.10" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", ] [[package]] -name = "pulldown-cmark" -version = "0.1.2" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "version_check", ] [[package]] -name = "quote" -version = "0.6.4" +name = "proc-macro2" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.4.3" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" -version = "0.7.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand_chacha", + "rand_core", ] [[package]] name = "rand_chacha" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core", ] [[package]] name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "redox_syscall" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "redox_users" -version = "0.2.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "redox_syscall", + "thiserror", ] [[package]] name = "regex" -version = "1.2.1" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.11" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] -name = "remove_dir_all" -version = "0.5.1" +name = "rsa" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0aeddcca1082112a6eeb43bf25fd7820b066aaf6eaef776e19d0a1febe38fe" dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rsa" -version = "0.2.0" -source = "git+https://github.com/RustCrypto/RSA?rev=11500ed#11500ed5e920b916a147107b89eaeced6dba0617" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint-dig 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.4.3", + "digest", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem 0.8.3", + "rand", + "simple_asn1", + "subtle", + "zeroize", ] [[package]] name = "rust-ini" -version = "0.13.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b134767a87e0b086f73a4ce569ac9ce7d202f39c8eab6caa266e2617e73ac6" +dependencies = [ + "cfg-if 0.1.10", + "ordered-multimap", +] [[package]] name = "rustc-demangle" -version = "0.1.9" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "rustc_version" -version = "0.2.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 1.0.10", ] [[package]] name = "ryu" -version = "0.2.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] -name = "same-file" -version = "1.0.3" +name = "scroll" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" dependencies = [ - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll_derive", ] [[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scroll" -version = "0.9.0" +name = "scroll_derive" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" dependencies = [ - "scroll_derive 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "scroll_derive" -version = "0.9.4" +name = "semver" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", + "serde", ] [[package]] name = "semver" -version = "0.9.0" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" [[package]] name = "semver-parser" -version = "0.7.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] [[package]] name = "serde" -version = "1.0.91" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.91" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_json" -version = "1.0.39" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha2" -version = "0.8.0" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer", + "cfg-if 1.0.0", + "cpufeatures", + "digest", + "opaque-debug", ] [[package]] name = "simple_asn1" -version = "0.4.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80" dependencies = [ - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", + "num-bigint 0.4.3", + "num-traits", + "thiserror", ] [[package]] -name = "skeptic" -version = "0.13.3" +name = "smallvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "snafu" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7" dependencies = [ - "bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "doc-comment", + "snafu-derive", ] [[package]] -name = "smallvec" -version = "1.2.0" +name = "snafu-derive" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.2.13" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "lazy_static", + "structopt-derive", ] [[package]] name = "structopt-derive" -version = "0.2.13" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "subtle" -version = "2.2.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "0.14.7" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "0.15.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "synstructure" -version = "0.10.1" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", + "unicode-xid", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "thiserror" -version = "1.0.14" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ - "thiserror-impl 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.6" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "time" -version = "0.1.42" +name = "toml" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "typenum" -version = "1.10.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] -name = "unicode-bidi" -version = "0.3.4" +name = "ucd-trie" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] -name = "unicode-normalization" -version = "0.1.7" +name = "unicode-ident" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-segmentation" -version = "1.2.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-width" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.1.0" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "url" -version = "1.7.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] -name = "walkdir" -version = "2.2.5" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "yasna" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de7bff972b4f2a06c85f6d8454b09df153af7e3a4ec2aac81db1b105b684ddb" dependencies = [ - "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.6", ] [[package]] name = "zeroize" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" dependencies = [ - "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.0.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" -"checksum aes-soft 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "acdc19c789666840bb86d1df8ee1f458418f74a6b9c8f10538fb700de5829cb8" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -"checksum arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f405cc4c21cd8b784f6c8fc2adf9bc00f59558f0049b5ec21517f875963040cc" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" -"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" -"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-modes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "283fa06a14026feac8912bf35328fc074f5d68907fd4b9cccad5658a3fc62a30" -"checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3" -"checksum blz-nx 0.1.0 (git+https://github.com/Thog/blz-nx-rs)" = "" -"checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182" -"checksum bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f861d9ce359f56dbcb6e0c2a1cb84e52ad732cadb57b806adeb3c7668caccbd8" -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1efca0b863ca03ed4c109fb1c55e0bc4bbeb221d3e103d86251046b06a526bd0" -"checksum cargo_metadata 0.6.0 (git+https://github.com/roblabla/cargo_metadata)" = "" -"checksum cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "d01c69d08ff207f231f07196e30f84c70f1c815b04f980f8b7b01ff01f05eb92" -"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" -"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" -"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum cmac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4a435124bcc292eba031f1f725d7abacdaf13cbf9f935450e8c45aa9e96cad" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -"checksum dbl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c40b13b561e11560d7b12785e74113a3163df617e2fbce60ce1764e0b270eaa" -"checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" -"checksum elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5" -"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" -"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" -"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5911d7df7b8f65ab676c5327b50acea29d3c6a1a4ad05e444cf5dce321b26db2" -"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" -"checksum libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" -"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" -"checksum lz4 1.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43c94a9f09a60017f373020cc93d4291db4cd92b0db64ff25927f27d09dc23d5" -"checksum lz4-sys 1.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "20ab022822e9331c58d373acdd6b98085bace058ac6837b8266f213a2fccdafe" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" -"checksum num-bigint-dig 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3d03c330f9f7a2c19e3c0b42698e48141d0809c78cd9b6219f85bd7d7e892aa" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" -"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum opaque-debug 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51ecbcb821e1bd256d456fe858aaa7f380b63863eab2eb86eee1bd9f33dd6682" -"checksum pem 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39eb474073dfddbf7156515344266245d91ce698ddbf15e0498cef22b836f45a" -"checksum pem 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1581760c757a756a41f0ee3ff01256227bdf64cb752839779b95ffb01c59793" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" -"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" -"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" -"checksum quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b71f9f575d55555aa9c06188be9d4e2bfc83ed02537948ac0d520c24d0419f1a" -"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" -"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" -"checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26" -"checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rsa 0.2.0 (git+https://github.com/RustCrypto/RSA?rev=11500ed)" = "" -"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" -"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" -"checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" -"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" -"checksum scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66f024a8cc5e456eb870f688dbd899c84f61190c82c7a911e40f926941969074" -"checksum scroll_derive 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f9a353f5dd99e42ff097d5a61db3257aa2c7127d76a3fa8287b642ef9ae0f7c5" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" -"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum simple_asn1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" -"checksum skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4474d6da9593171bcb086890fc344a3a12783cb24e5b141f8a5d0e43561f4b6" -"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum structopt 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "41c4a2479a078509940d82773d90ff824a8c89533ab3b59cd3ce8b0c0e369c02" -"checksum structopt-derive 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5352090cfae7a2c85e1a31146268b53396106c88ca5d6ccee2e3fae83b6e35c2" -"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" -"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" -"checksum syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e13df71f29f9440b50261a5882c86eac334f1badb3134ec26f0de2f1418e44" -"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7" -"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" -"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" -"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f0570dc61221295909abdb95c739f2e74325e14293b2026b0a7e195091ec54ae" -"checksum thiserror-impl 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "227362df41d566be41a28f64401e07a043157c21c14b9785a0d8e256f940a8fd" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" -"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum yasna 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a563d10ead87e2d798e357d44f40f495ad70bcee4d5c0d3f77a5b1b7376645d9" -"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" -"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml index ce2b14b..30800a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "linkle" -version = "0.2.7" +version = "0.2.10" authors = ["Thog "] license = "MIT/Apache-2.0" homepage = "https://github.com/MegatonHammer/linkle/" repository = "https://github.com/MegatonHammer/linkle/" edition = "2018" +description = "Nintendo file format manipulation library and tools." [[bin]] name = "linkle" @@ -22,31 +23,33 @@ elf = "0.0.10" byteorder = "1" lz4 = "1.23.1" clap = { version = "2", optional = true } -structopt = { version = "0.2", optional = true } -sha2 = "0.8.0" -scroll = { version = "0.9.0", optional = true } +structopt = { version = "0.3", optional = true } +sha2 = "0.9" +scroll = { version = "0.10", optional = true } serde = "1" serde_derive = "1" serde_json = "1" -cargo_metadata = { git = "https://github.com/roblabla/cargo_metadata", optional = true } -url = "1.7.1" -goblin = { version = "0.0.17", optional = true } -rust-ini = "0.13" -failure = "0.1" -dirs = "1.0" -block-modes = "0.2" -aes = "0.3" +cargo_metadata = { version = "0.12.0", optional = true } +semver = {version = "0.11", optional = true } +goblin = { version = "0.2", optional = true } +rust-ini = "0.16" +snafu = { version = "0.6", features = ["backtraces"] } +dirs-next = "2.0.0" +block-modes = "0.7" +aes = "0.6" num-traits = "0.2" -derive_more = "0.13" -cmac = "0.2.0" -blz-nx = { git = "https://github.com/Thog/blz-nx-rs" } -bit_field = "0.10.0" +derive_more = "0.99" +ctr = "0.6" +cmac = "0.5" +blz-nx = "1.0" +bit_field = "0.10" bincode = "1.1.4" yasna = { version = "0.3", features = ["num-bigint"] } pem = "0.6" -rand = "0.7" -rsa = { git = "https://github.com/RustCrypto/RSA", rev = "11500ed" } +rand = "0.8" +rsa = "0.4" hex = "0.3" +cargo-toml2 = { version = "1.3.2", optional = true } [features] -binaries = ["structopt", "cargo_metadata", "scroll", "goblin", "clap"] +binaries = ["structopt", "cargo_metadata", "semver", "scroll", "goblin", "clap", "cargo-toml2"] diff --git a/README.md b/README.md index cbe71bf..a48f5e0 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,16 @@ [![Apache 2 license](https://img.shields.io/badge/license-Apache-blue.svg)](https://raw.githubusercontent.com/MegatonHammer/linkle/master/LICENSE-APACHE) [![Discord](https://img.shields.io/discord/439418034130780182.svg)]( https://discord.gg/MZJbNZY) -# Introduction +# linkle This program permits to convert or create various formats used on the Nintendo Switch. -For now, it only supports the creation of PFS0/NSP and 64 bits NRO/NSO. It can also + +It can also be used with cargo through `cargo nro` to simplify the build process of Megaton-Hammer homebrew. +Supported formats: PFS0/NSP, NSO, NRO, NPDM, NACP, RomFs + # Installation Assuming you have `cargo` installed, `cargo install --features=binaries --git https://github.com/MegatonHammer/linkle` @@ -30,7 +33,11 @@ Creating a PFS0/NSP file: Creating a NACP file: - linkle ncap input.json output.nacp + linkle nacp input.json output.nacp + +Creating a NPDM file: + + linkle npdm input.json output.npdm Creating a RomFs file: @@ -45,7 +52,7 @@ Compiling and creating an NRO file (requires xargo from https://github.com/robla When compiling a project with `cargo nro`, a special `[package.metadata.linkle.BINARY_NAME]` key is used to allow customizing the build. This is an example Cargo.toml: -``` +```toml [package] name = "link" version = "0.1.0" @@ -57,9 +64,10 @@ icon = "icon.jpeg" titleid = "0100000000819" [package.metadata.linkle.megaton-example.nacp] -name = "Link" +default_name = "Link" +default_author = "Linkle" -[package.metadata.linkle.megaton-example.nacp.lang.ja] +[package.metadata.linkle.megaton-example.nacp.titles.ja] "name": "リンク", "author": "リンクル" ``` @@ -76,20 +84,25 @@ Every field has a sane default: The `[package.metadata.linkle.BINARY_NAME.nacp]` key follows the [NACP input format](#nacp-input-format) -# NACP input format +# NACP input format (JSON) This is an example of a compatible JSON: ```json { - "name": "Link", - "author": "Linkle", + "default_name": "Link", + "default_author": "Linkle", "version": "1.0.0", - "title_id": "0400000000020000", - "lang": { + "application_id": "0100AAAABBBBCCCC", + "startup_user_account": "Required", + "titles": { "ja": { "name": "リンク", "author": "リンクル" + }, + "Spanish": { + "name": "Link (es)", + "author": "Linkle (es)" } } } @@ -97,31 +110,48 @@ This is an example of a compatible JSON: ## Fields -NOTE: Every fields are optional - -| Field | Description | Default value | -| ----------------- |:------------------------------------------------:| -------------------:| -| name | The application name. | Unknown Application | -| author | The application author. | Unknown Author | -| version | The application version. | 1.0.0 | -| title_id | The application title id. | 0000000000000000 | -| dlc_base_title_id | The base id of all the title DLC. | title_id + 0x1000 | -| lang (object) | Different name/author depending of the language | use name and author | - -| Supported Languages| -|:------------------:| -| en-US | -| en-UK | -| ja | -| fr | -| de | -| es-419 | -| es | -| it | -| nl | -| fr-CA | -| pt | -| ru | -| ko | -| zh-TW | -| zh-CN | +| Field | Value | Description | Default value | +|------------------------|------------------------------------------------------------------|-----------------------------------------------------------|-------------------------| +| default_name | String (max size 0x200) | Default title name | Unknown application | +| default_author | String (max size 0x100) | Default application author | Unknown author | +| version | String (max size 0x10) | Application version | | +| application_id | Hex-String u64 | Application ID | 0000000000000000 | +| add_on_content_base_id | Hex-String u64 | Base ID for add-on content (DLC) | application_id + 0x1000 | +| titles | Object of language titles | Language-specific application name/author values | Default values above | +| presence_group_id | Hex-String u64 | Presence group ID | application_id | +| save_data_owner_id | Hex-String u64 | Save-data owner ID | application_id | +| isbn | String (max size 0x25) | ISBN | Empty string | +| startup_user_account | "None", "Required", "RequiredWithNetworkServiceAccountAvailable" | Whether the application requires a user account on launch | "None" | +| attribute | "None", "Demo", "RetailInteractiveDisplay" | Application attribute | "None" | +| screenshot | "Allow", "Deny" | Screenshot control | "Allow" | +| video_capture | "Disabled", "Enabled", "Automatic" | Video capture control | "Disabled" | +| logo_type | "LicensedByNintendo", "Nintendo" | Logo type | "LicensedByNintendo" | +| logo_handling | "Auto", "Manual" | Logo handling | "Auto" | +| crash_report | "Deny", "Allow" | Crash report control | "Allow" | +| bcat_passphrase | String (max size 0x41) | BCAT passphrase | Empty string | +| program_index | u8 | Program index | 0 | + +Note: default name/author and application ID are not actual NACP fields, but they are used as the default value for various fields, as the table shows. + +### Available languages + +| Language names | Language codes | +|----------------------|----------------| +| AmericanEnglish | en-US | +| BritishEnglish | en-UK | +| Japanese | ja | +| French | fr | +| German | de | +| LatinAmericanSpanish | es-419 | +| Spanish | es | +| Italian | it | +| Dutch | nl | +| CanadianFrench | fr-CA | +| Portuguese | pt | +| Russian | ru | +| Korean | ko | +| TraditionalChinese | zh-TW | +| SimplifiedChinese | zh-CN | +| BrazilianPortuguese | pt-BR | + +Note: languages in the titles object can be specified by their names or their codes, as the JSON example above shows. \ No newline at end of file diff --git a/src/bin/cargo-nro.rs b/src/bin/cargo-nro.rs index 163f1ab..eaf175a 100644 --- a/src/bin/cargo-nro.rs +++ b/src/bin/cargo-nro.rs @@ -1,48 +1,47 @@ #[macro_use] extern crate clap; -extern crate url; extern crate linkle; extern crate serde; extern crate serde_json; #[macro_use] extern crate serde_derive; extern crate cargo_metadata; +extern crate cargo_toml2; extern crate goblin; extern crate scroll; +use scroll::IOwrite; use std::env::{self, VarError}; -use std::process::{Command, Stdio}; -use std::path::{Path, PathBuf}; use std::fs::File; -use std::io::{Write, Read}; -use scroll::IOwrite; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; -use linkle::format::{romfs::RomFs, nxo::NxoFile, nacp::NacpFile}; -use cargo_metadata::{Package, Message}; -use clap::{Arg, App}; -use url::Url; +use cargo_metadata::{Message, Package}; +use cargo_toml2::CargoConfig; +use clap::{App, Arg}; +use goblin::elf::section_header::{SHT_NOBITS, SHT_STRTAB, SHT_SYMTAB}; use goblin::elf::{Elf, Header as ElfHeader, ProgramHeader}; -use goblin::elf::section_header::{SHT_NOBITS, SHT_SYMTAB, SHT_STRTAB}; -use failure::Fail; -use derive_more::Display; +use linkle::format::{nacp::Nacp, nxo::Nxo, romfs::RomFs}; +use snafu::Snafu; -#[derive(Debug, Fail, Display)] +#[derive(Debug, Snafu)] enum Error { - #[display(fmt = "{}", _0)] - Goblin(#[cause] goblin::error::Error), - #[display(fmt = "{}", _0)] - Linkle(#[cause] linkle::error::Error) + #[snafu(display("{}", error))] + Goblin { error: goblin::error::Error }, + #[snafu(display("{}", error))] + Linkle { error: linkle::error::Error }, } impl From for Error { - fn from(from: goblin::error::Error) -> Error { - Error::Goblin(from) + fn from(error: goblin::error::Error) -> Error { + Error::Goblin { error } } } impl From for Error { - fn from(from: linkle::error::Error) -> Error { - Error::Linkle(from) + fn from(error: linkle::error::Error) -> Error { + Error::Linkle { error } } } @@ -52,15 +51,6 @@ impl From for Error { } } -fn find_project_root(path: &Path) -> Option<&Path> { - for parent in path.ancestors() { - if parent.join("Cargo.toml").is_file() { - return Some(parent); - } - } - None -} - // TODO: Run cargo build --help to get the list of options! const CARGO_OPTIONS: &str = "CARGO OPTIONS: -p, --package ... Package to build @@ -95,19 +85,14 @@ const CARGO_OPTIONS: &str = "CARGO OPTIONS: -Z ... Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details -h, --help Prints help information"; - -fn get_metadata(manifest_path: &Path, package_id: &str, target_name: &str) -> (Package, PackageMetadata) { - let metadata = cargo_metadata::metadata(Some(&manifest_path)).unwrap(); - let package = metadata.packages.into_iter().find(|v| v.id == package_id).unwrap(); - let package_metadata = serde_json::from_value(package.metadata.pointer(&format!("linkle/{}", target_name)).cloned().unwrap_or(serde_json::Value::Null)).unwrap_or_default(); - (package, package_metadata) -} - trait BetterIOWrite: IOwrite { - fn iowrite_with_try + scroll::ctx::TryIntoCtx>(&mut self, n: N, ctx: Ctx) - -> Result<(), N::Error> + fn iowrite_with_try + scroll::ctx::TryIntoCtx>( + &mut self, + n: N, + ctx: Ctx, + ) -> Result<(), N::Error> where - N::Error: From + N::Error: From, { let mut buf = [0u8; 256]; let size = N::size_with(&ctx); @@ -120,7 +105,10 @@ trait BetterIOWrite: IOwrite { impl + ?Sized> BetterIOWrite for W {} -fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) -> Result { +fn generate_debuginfo_romfs>( + elf_path: &Path, + romfs: Option

, +) -> Result { let mut elf_file = File::open(elf_path)?; let mut buffer = Vec::new(); elf_file.read_to_end(&mut buffer)?; @@ -139,12 +127,23 @@ fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) - } = elf; let ctx = goblin::container::Ctx { - container: if is_64 { goblin::container::Container::Big } else { goblin::container::Container::Little }, - le: if little_endian { goblin::container::Endian::Little } else { goblin::container::Endian::Big } + container: if is_64 { + goblin::container::Container::Big + } else { + goblin::container::Container::Little + }, + le: if little_endian { + goblin::container::Endian::Little + } else { + goblin::container::Endian::Big + }, }; for section in section_headers.iter_mut() { - if section.sh_type == SHT_NOBITS || section.sh_type == SHT_SYMTAB || section.sh_type == SHT_STRTAB { + if section.sh_type == SHT_NOBITS + || section.sh_type == SHT_SYMTAB + || section.sh_type == SHT_STRTAB + { continue; } if let Some(Ok(s)) = elf.shdr_strtab.get(section.sh_name) { @@ -155,14 +154,18 @@ fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) - } // Calculate section data length + elf/program headers - let data_off = ElfHeader::size(&ctx) + ProgramHeader::size(&ctx) * program_headers.len(); - let shoff = data_off as u64 + section_headers.iter().map(|v| { - if v.sh_type != SHT_NOBITS { - v.sh_size - } else { - 0 - } - }).sum::(); + let data_off = ElfHeader::size(ctx) + ProgramHeader::size(ctx) * program_headers.len(); + let shoff = data_off as u64 + + section_headers + .iter() + .map(|v| { + if v.sh_type != SHT_NOBITS { + v.sh_size + } else { + 0 + } + }) + .sum::(); // Write ELF header // TODO: Anything else? @@ -177,8 +180,13 @@ fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) - // Write section data let mut cur_idx = data_off; - for section in section_headers.iter_mut().filter(|v| v.sh_type != SHT_NOBITS) { - file.write_all(&buffer[section.sh_offset as usize..(section.sh_offset + section.sh_size) as usize])?; + for section in section_headers + .iter_mut() + .filter(|v| v.sh_type != SHT_NOBITS) + { + file.write_all( + &buffer[section.sh_offset as usize..(section.sh_offset + section.sh_size) as usize], + )?; section.sh_offset = cur_idx as u64; cur_idx += section.sh_size as usize; } @@ -206,9 +214,32 @@ fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) - #[derive(Debug, Serialize, Deserialize, Default)] struct PackageMetadata { romfs: Option, - nacp: Option, + nacp: Option, icon: Option, - title_id: Option + title_id: Option, +} + +trait WorkspaceMember { + fn part(&self, n: usize) -> &str; + + fn name(&self) -> &str { + self.part(0) + } + + fn version(&self) -> semver::Version { + semver::Version::parse(self.part(1)).expect("bad version in cargo metadata") + } + + fn url(&self) -> &str { + let url = self.part(2); + &url[1..url.len() - 1] + } +} + +impl WorkspaceMember for cargo_metadata::PackageId { + fn part(&self, n: usize) -> &str { + self.repr.splitn(3, ' ').nth(n).unwrap() + } } fn main() { @@ -221,25 +252,41 @@ fn main() { let matches = App::new(crate_name!()) .about("Compile rust switch homebrews with ease!") - .arg(Arg::with_name("CARGO_OPTIONS") - .raw(true) - .help("Options that will be passed to cargo build")) + .arg( + Arg::with_name("CARGO_OPTIONS") + .raw(true) + .help("Options that will be passed to cargo build"), + ) .after_help(CARGO_OPTIONS) .get_matches_from(args); + let metadata = cargo_metadata::MetadataCommand::new().exec().unwrap(); let rust_target_path = match env::var("RUST_TARGET_PATH") { - Err(VarError::NotPresent) => { - // TODO: Handle workspace - find_project_root(&env::current_dir().unwrap()).unwrap().into() - }, + Err(VarError::NotPresent) => metadata.workspace_root.clone(), s => PathBuf::from(s.unwrap()), }; let mut command = Command::new("xargo"); + let config_path = Path::new("./.cargo/config"); + let target = if config_path.exists() { + let config: Option = cargo_toml2::from_path(config_path).ok(); + config + .map(|config| config.build.map(|build| build.target).flatten()) + .flatten() + } else { + None + }; + + let target = target.as_deref().unwrap_or("aarch64-roblabla-switch"); + command - .args(&["build", "--target=aarch64-roblabla-switch", "--message-format=json"]) + .args(&[ + "build", + &format!("--target={}", target), + "--message-format=json-diagnostic-rendered-ansi", + ]) .stdout(Stdio::piped()) .env("RUST_TARGET_PATH", rust_target_path.as_os_str()); @@ -247,33 +294,34 @@ fn main() { command.args(cargo_opts); } - let command = command.spawn().unwrap(); + let mut command = command.spawn().unwrap(); + let stdout_reader = std::io::BufReader::new(command.stdout.take().unwrap()); - let iter = cargo_metadata::parse_message_stream(command.stdout.unwrap()); + let iter = cargo_metadata::Message::parse_stream(stdout_reader); for message in iter { match message { - Ok(Message::CompilerArtifact(ref artifact)) if artifact.target.kind[0] == "bin" => { - // Find the artifact's source. This is not going to be pretty. - // For whatever reason, cargo thought it'd be a *great idea* to make file URLs use - // the non-standard "path+file:///" scheme, instead of, y'know, the ""file:///" everyone - // knows. - // - // So we check if it starts with path+file, and if it does, we skip the path+ part when - // parsing it. - let url = if artifact.package_id.url().starts_with("path+file") { - &artifact.package_id.url()["path+".len()..] - } else { - artifact.package_id.url() + Ok(Message::CompilerArtifact(ref artifact)) + if artifact.target.kind.contains(&"bin".into()) + || artifact.target.kind.contains(&"cdylib".into()) => + { + let package: &Package = match metadata + .packages + .iter() + .find(|v| v.id == artifact.package_id) + { + Some(v) => v, + None => continue, }; - let url = Url::parse(url).unwrap(); - if url.scheme() != "file" { - continue; - } - - let root = url.to_file_path().unwrap(); - let manifest = root.join("Cargo.toml"); - let (package, target_metadata) = get_metadata(&manifest, &artifact.package_id.raw, &artifact.target.name); + let root = package.manifest_path.parent().unwrap(); + let target_metadata: PackageMetadata = serde_json::from_value( + package + .metadata + .pointer(&format!("linkle/{}", artifact.target.name)) + .cloned() + .unwrap_or(serde_json::Value::Null), + ) + .unwrap_or_default(); let romfs = if let Some(romfs) = target_metadata.romfs { let romfs_path = root.join(romfs); @@ -305,37 +353,41 @@ fn main() { let icon_file = icon_file.as_ref().map(|v| v.as_ref()); let mut nacp = target_metadata.nacp.unwrap_or_default(); - nacp.name.get_or_insert(package.name); - nacp.author.get_or_insert(package.authors[0].clone()); - nacp.version.get_or_insert(package.version); - if nacp.title_id.is_none() { - nacp.title_id = target_metadata.title_id; + nacp.default_name.get_or_insert(package.name.clone()); + nacp.default_author.get_or_insert(package.authors[0].clone()); + nacp.version = package.version.to_string(); + if nacp.application_id.is_none() { + nacp.application_id = target_metadata.title_id; } - let romfs = generate_debuginfo_romfs(Path::new(&artifact.filenames[0]), romfs).unwrap(); + let romfs = + generate_debuginfo_romfs(Path::new(&artifact.filenames[0]), romfs).unwrap(); - let mut new_name = PathBuf::from(artifact.filenames[0].clone()); + let mut new_name = artifact.filenames[0].clone(); assert!(new_name.set_extension("nro")); - NxoFile::from_elf(&artifact.filenames[0]).unwrap() - .write_nro(&mut File::create(new_name.clone()).unwrap(), - Some(romfs), - icon_file, - Some(nacp) - ).unwrap(); + Nxo::from_elf(artifact.filenames[0].to_str().unwrap()) + .unwrap() + .write_nro( + &mut File::create(new_name.clone()).unwrap(), + Some(romfs), + icon_file, + Some(nacp), + ) + .unwrap(); println!("Built {}", new_name.to_string_lossy()); - }, + } Ok(Message::CompilerArtifact(_artifact)) => { //println!("{:#?}", artifact); - }, + } Ok(Message::CompilerMessage(msg)) => { if let Some(msg) = msg.message.rendered { println!("{}", msg); } else { println!("{:?}", msg); } - }, + } Ok(_) => (), Err(err) => { panic!("{:?}", err); diff --git a/src/bin/linkle_clap.rs b/src/bin/linkle_clap.rs index 8d784a8..f587bb9 100644 --- a/src/bin/linkle_clap.rs +++ b/src/bin/linkle_clap.rs @@ -2,11 +2,11 @@ extern crate structopt; extern crate linkle; -use std::fs::{OpenOptions, File}; +use linkle::error::ResultExt; +use std::fs::{File, OpenOptions}; use std::path::{Path, PathBuf}; use std::process; use structopt::StructOpt; -use linkle::error::ResultExt; #[derive(StructOpt)] #[structopt(name = "linkle", about = "The legendary hero")] @@ -72,9 +72,9 @@ enum Opt { /// Sets the input file to use. input_file: String, /// Sets the output file to use. - output_file: String + output_file: String, }, - /// Create a RomFS file from a directory. + /// Create a RomFs file from a directory. #[structopt(name = "romfs")] Romfs { /// Sets the input directory to use. @@ -95,7 +95,7 @@ enum Opt { #[structopt(parse(from_os_str), short = "k", long = "keyset")] keyfile: Option, }, - /// Create an NPDM from a JSON-NPDM formatted file. + /// Create a NPDM from a JSON-NPDM formatted file. #[structopt(name = "npdm")] Npdm { /// Sets the input JSON file to use. @@ -116,45 +116,74 @@ enum Opt { } } -fn create_nxo(format: &str, input_file: &str, output_file: &str, icon_file: Option<&str>, romfs_dir: Option<&str>, nacp_file: Option<&str>) -> Result<(), linkle::error::Error> { +fn create_nxo( + format: &str, + input_file: &str, + output_file: &str, + icon_file: Option<&str>, + romfs_dir: Option<&str>, + nacp_input: Option<&str>, +) -> Result<(), linkle::error::Error> { let romfs_dir = if let Some(romfs_path) = romfs_dir { - Some(linkle::format::romfs::RomFs::from_directory(Path::new(&romfs_path))?) + Some(linkle::format::romfs::RomFs::from_directory(Path::new( + &romfs_path, + ))?) } else { None }; - let nacp_file = if let Some(nacp_path) = nacp_file { - Some(linkle::format::nacp::NacpFile::from_file(&nacp_path).map_err(|err| (err, &nacp_path))?) + let nacp_input = if let Some(nacp_path) = nacp_input { + Some( + linkle::format::nacp::Nacp::from_json(&nacp_path) + .map_err(|err| (err, &nacp_path))?, + ) } else { None }; - let mut nxo = linkle::format::nxo::NxoFile::from_elf(&input_file).map_err(|err| (err, &input_file))?; + let mut nxo = + linkle::format::nxo::Nxo::from_elf(&input_file).map_err(|err| (err, &input_file))?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); match format { "nro" => { - let mut out_file = output_option.open(output_file).map_err(|err| (err, output_file))?; - nxo.write_nro(&mut out_file, romfs_dir, icon_file.as_ref().map(|v| &**v), nacp_file) + let mut out_file = output_option + .open(output_file) .map_err(|err| (err, output_file))?; - }, + nxo.write_nro(&mut out_file, romfs_dir, icon_file.as_deref(), nacp_input) + .map_err(|err| (err, output_file))?; + } "nso" => { - let mut out_file = output_option.open(output_file).map_err(|err| (err, output_file))?; - nxo.write_nso(&mut out_file).map_err(|err| (err, output_file))?; + let mut out_file = output_option + .open(output_file) + .map_err(|err| (err, output_file))?; + nxo.write_nso(&mut out_file) + .map_err(|err| (err, output_file))?; } _ => process::exit(1), } Ok(()) } -fn create_kip(input_file: &str, npdm_file: &str, output_file: &str) -> Result<(), linkle::error::Error> { - let mut nxo = linkle::format::nxo::NxoFile::from_elf(&input_file).map_err(|err| (err, &input_file))?; +fn create_kip( + input_file: &str, + npdm_file: &str, + output_file: &str, +) -> Result<(), linkle::error::Error> { + let mut nxo = + linkle::format::nxo::Nxo::from_elf(&input_file).map_err(|err| (err, &input_file))?; let npdm = serde_json::from_reader(File::open(npdm_file).map_err(|err| (err, npdm_file))?)?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); output_option.open(output_file)?; - nxo.write_kip1(&mut output_option.open(output_file).map_err(|err| (err, output_file))?, &npdm).map_err(|err| (err, output_file))?; + nxo.write_kip1( + &mut output_option + .open(output_file) + .map_err(|err| (err, output_file))?, + &npdm, + ) + .map_err(|err| (err, output_file))?; Ok(()) } @@ -162,7 +191,12 @@ fn create_pfs0(input_directory: &str, output_file: &str) -> Result<(), linkle::e let mut pfs0 = linkle::format::pfs0::Pfs0::from_directory(&input_directory)?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); - pfs0.write_pfs0(&mut output_option.open(output_file).map_err(|err| (err, output_file))?).map_err(|err| (err, output_file))?; + pfs0.write_pfs0( + &mut output_option + .open(output_file) + .map_err(|err| (err, output_file))?, + ) + .map_err(|err| (err, output_file))?; Ok(()) } @@ -175,7 +209,7 @@ fn extract_pfs0(input_path: &str, output_directory: &str) -> Result<(), linkle:: match std::fs::create_dir(path) { Ok(()) => (), Err(ref err) if err.kind() == std::io::ErrorKind::AlreadyExists => (), - Err(err) => Err((err, path))? + Err(err) => return Err((err, path).into()), } for file in pfs0.files() { let mut file = file?; @@ -188,11 +222,14 @@ fn extract_pfs0(input_path: &str, output_directory: &str) -> Result<(), linkle:: } fn create_nacp(input_file: &str, output_file: &str) -> Result<(), linkle::error::Error> { - let mut nacp = linkle::format::nacp::NacpFile::from_file(&input_file)?; + let mut nacp = linkle::format::nacp::Nacp::from_json(&input_file)?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); - let mut out_file = output_option.open(output_file).map_err(|err| (err, output_file))?; - nacp.write(&mut out_file).map_err(|err| (err, output_file))?; + let mut out_file = output_option + .open(output_file) + .map_err(|err| (err, output_file))?; + nacp.write(&mut out_file) + .map_err(|err| (err, output_file))?; Ok(()) } @@ -200,22 +237,36 @@ fn create_romfs(input_directory: &Path, output_file: &Path) -> Result<(), linkle let romfs = linkle::format::romfs::RomFs::from_directory(&input_directory)?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); - let mut out_file = output_option.open(output_file).map_err(|err| (err, output_file))?; - romfs.write(&mut out_file).map_err(|err| (err, output_file))?; + let mut out_file = output_option + .open(output_file) + .map_err(|err| (err, output_file))?; + romfs + .write(&mut out_file) + .map_err(|err| (err, output_file))?; Ok(()) } -fn print_keys(is_dev: bool, key_path: Option<&Path>) -> Result<(), linkle::error::Error> { - let keys = linkle::pki::Keys::new_retail(key_path).unwrap(); +fn print_keys( + is_dev: bool, + key_path: Option<&Path>, + console_unique: bool, + minimal: bool, +) -> Result<(), linkle::error::Error> { + let keys = if is_dev { + linkle::pki::Keys::new_dev(key_path).unwrap() + } else { + linkle::pki::Keys::new_retail(key_path).unwrap() + }; - keys.write(&mut std::io::stdout()).unwrap(); + keys.write(&mut std::io::stdout(), console_unique, minimal) + .unwrap(); Ok(()) } fn create_npdm(input_file: &Path, input_acid: Option<&Path>, sign_acid: Option<&Path>, output_file: &Path) -> Result<(), linkle::error::Error> { - use linkle::format::npdm::{NpdmJson, ACIDBehavior}; + use linkle::format::npdm::{Npdm, AcidBehavior}; - let npdm = NpdmJson::from_file(&input_file)?; + let npdm = Npdm::from_json(&input_file)?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); let mut out_file = output_option.open(output_file).map_err(|err| (err, output_file))?; @@ -223,11 +274,11 @@ fn create_npdm(input_file: &Path, input_acid: Option<&Path>, sign_acid: Option<& // They are set as conflicting in clap, so we can't reach here. unreachable!("Can't pass both sign_acid and input_acid."); } else if let Some(input_pem) = sign_acid { - ACIDBehavior::Sign { pem_file_path: input_pem } + AcidBehavior::Sign { pem_file_path: input_pem } } else if let Some(input_acid) = input_acid { - ACIDBehavior::Use { acid_file_path: input_acid } + AcidBehavior::Use { acid_file_path: input_acid } } else { - ACIDBehavior::Empty + AcidBehavior::Empty }; npdm.into_npdm(&mut out_file, behavior)?; Ok(()) @@ -239,15 +290,55 @@ fn to_opt_ref>(s: &Option) -> Option<&U> { fn process_args(app: &Opt) { let res = match app { - Opt::Nro { ref input_file, ref output_file, ref icon, ref romfs, ref nacp } => create_nxo("nro", input_file, output_file, to_opt_ref(icon), to_opt_ref(romfs), to_opt_ref(nacp)), - Opt::Nso { ref input_file, ref output_file } => create_nxo("nso", input_file, output_file, None, None, None), - Opt::Kip { ref input_file, ref npdm_file, ref output_file } => create_kip(input_file, npdm_file, output_file), - Opt::Pfs0 { ref input_directory, ref output_file } => create_pfs0(input_directory, output_file), - Opt::Pfs0Extract { ref input_file, ref output_directory } => extract_pfs0(input_file, output_directory), - Opt::Nacp { ref input_file, ref output_file } => create_nacp(input_file, output_file), - Opt::Romfs { ref input_directory, ref output_file } => create_romfs(input_directory, output_file), - Opt::Keygen { dev, ref keyfile } => print_keys(*dev, to_opt_ref(keyfile)), - Opt::Npdm { ref input_file, ref pem_file, ref acid_file, ref output_file } => create_npdm(input_file, to_opt_ref(acid_file), to_opt_ref(pem_file), output_file) + Opt::Nro { + ref input_file, + ref output_file, + ref icon, + ref romfs, + ref nacp, + } => create_nxo( + "nro", + input_file, + output_file, + to_opt_ref(icon), + to_opt_ref(romfs), + to_opt_ref(nacp), + ), + Opt::Nso { + ref input_file, + ref output_file, + } => create_nxo("nso", input_file, output_file, None, None, None), + Opt::Kip { + ref input_file, + ref npdm_file, + ref output_file, + } => create_kip(input_file, npdm_file, output_file), + Opt::Pfs0 { + ref input_directory, + ref output_file, + } => create_pfs0(input_directory, output_file), + Opt::Pfs0Extract { + ref input_file, + ref output_directory, + } => extract_pfs0(input_file, output_directory), + Opt::Nacp { + ref input_file, + ref output_file, + } => create_nacp(input_file, output_file), + Opt::Romfs { + ref input_directory, + ref output_file, + } => create_romfs(input_directory, output_file), + Opt::Keygen { + dev, + ref keyfile + } => print_keys(*dev, to_opt_ref(keyfile), true, false), + Opt::Npdm { + ref input_file, + ref pem_file, + ref acid_file, + ref output_file + } => create_npdm(input_file, to_opt_ref(acid_file), to_opt_ref(pem_file), output_file) }; if let Err(e) = res { diff --git a/src/error.rs b/src/error.rs index 8cde0da..435bcf6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,54 +1,105 @@ +use block_modes::BlockModeError; +use snafu::Backtrace; +use snafu::GenerateBacktrace; +use snafu::Snafu; use std::io; +use std::borrow::Cow; +use std::path::{Path, PathBuf}; use std::str::Utf8Error; use std::string::FromUtf8Error; -use std::path::{Path, PathBuf}; -use ini; -use failure::Backtrace; -use block_modes::BlockModeError; -use failure::Fail; -use derive_more::Display; -use std::borrow::Cow; -#[derive(Debug, Fail, Display)] +#[derive(Debug, Snafu)] pub enum Error { - #[display(fmt = "Failed to deserialize: {}", _0)] - Deserialization(#[cause] serde_json::error::Error), - #[display(fmt = "{}: {}", "_1.display()", _0)] - Io(#[cause] io::Error, PathBuf, Backtrace), - #[display(fmt = "Internal IO Error (please submit a bug report with the backtrace): {}", _0)] - IoInternal(#[cause] io::Error, Backtrace), - #[display(fmt = "Decryption failed")] - BlockMode(BlockModeError, Backtrace), - #[display(fmt = "Error parsing the INI file: {}", _0)] - Ini(#[cause] ini::ini::Error, Backtrace), - #[display(fmt = "Key derivation error: {}", _0)] - Crypto(String, Backtrace), - #[display(fmt = "Invalid keyblob {}: {}.", _1, _0)] - MacError(cmac::crypto_mac::MacError, usize, Backtrace), - #[display(fmt = "Invalid PFS0: {}.", _0)] - InvalidPfs0(&'static str, Backtrace), - #[display(fmt = "Failed to convert filename to UTF8: {}.", _0)] - Utf8Conversion(String, #[cause] Utf8Error, Backtrace), - #[display(fmt = "Can't handles symlinks in romfs: {}", "_0.display()")] - RomFsSymlink(PathBuf, Backtrace), - #[display(fmt = "Unknown file type at {}", "_0.display()")] - RomFsFiletype(PathBuf, Backtrace), - #[display(fmt = "Invalid NPDM value for field {}", "_0")] - InvalidNpdmValue(Cow<'static, str>, Backtrace), - #[display(fmt = "Failed to serialize NPDM.")] - BincodeError(#[cause] Box, Backtrace), - #[display(fmt = "Failed to sign NPDM.")] - RsaError(#[cause] rsa::errors::Error, Backtrace), - #[display(fmt = "Failed to sign NPDM, invalid PEM.")] - PemError(#[cause] pem::PemError, Backtrace), - #[display(fmt = "Failed to sign NPDM, invalid PEM.")] - Asn1Error(#[cause] yasna::ASN1Error, Backtrace), + #[snafu(display("Failed to deserialize: {}", error))] + Deserialization { error: serde_json::error::Error }, + #[snafu(display( "{}: {}", path.display(), error))] + Io { + error: io::Error, + path: PathBuf, + backtrace: Backtrace, + }, + #[snafu(display( + "Internal IO Error (please submit a bug report with the backtrace): {}", + error + ))] + IoInternal { + error: io::Error, + backtrace: Backtrace, + }, + #[snafu(display("Decryption failed"))] + BlockMode { + error: BlockModeError, + backtrace: Backtrace, + }, + #[snafu(display("Error parsing the INI file: {}", error))] + Ini { + error: ini::Error, + backtrace: Backtrace, + }, + #[snafu(display("Key derivation error: {}", error))] + Crypto { error: String, backtrace: Backtrace }, + #[snafu(display("Invalid keyblob {}: {}.", id, error))] + MacError { + error: cmac::crypto_mac::MacError, + id: usize, + backtrace: Backtrace, + }, + #[snafu(display("Invalid PFS0: {}.", error))] + InvalidPfs0 { + error: &'static str, + backtrace: Backtrace, + }, + #[snafu(display("Failed to convert filename to UTF8: {}.", filename))] + Utf8Conversion { + filename: String, + error: Utf8Error, + backtrace: Backtrace, + }, + #[snafu(display("Can't handles symlinks in romfs: {}", error.display()))] + RomFsSymlink { + error: PathBuf, + backtrace: Backtrace, + }, + #[snafu(display("Unknown file type at {}", error.display()))] + RomFsFiletype { + error: PathBuf, + backtrace: Backtrace, + }, + #[snafu(display("Invalid NPDM value for field {}", "_0"))] + InvalidNpdmValue { + error: Cow<'static, str>, + backtrace: Backtrace, + }, + #[snafu(display("Failed to serialize NPDM."))] + BincodeError { + error: Box, + backtrace: Backtrace + }, + #[snafu(display("Failed to sign NPDM."))] + RsaError { + error: rsa::errors::Error, + backtrace: Backtrace + }, + #[snafu(display("Failed to sign NPDM, invalid PEM."))] + PemError { + error: pem::PemError, + backtrace: Backtrace + }, + #[snafu(display("Failed to sign NPDM, invalid PEM."))] + Asn1Error { + error: yasna::ASN1Error, + backtrace: Backtrace + }, } impl Error { fn with_path>(self, path: T) -> Error { - if let Error::IoInternal(err, backtrace) = self { - Error::Io(err, path.as_ref().to_owned(), backtrace) + if let Error::IoInternal { error, backtrace } = self { + Error::Io { + error, + path: path.as_ref().to_owned(), + backtrace, + } } else { self } @@ -67,67 +118,100 @@ impl ResultExt for Result { impl From for Error { fn from(err: io::Error) -> Error { - Error::IoInternal(err, Backtrace::new()) + Error::IoInternal { + error: err, + backtrace: Backtrace::generate(), + } } } impl> From<(io::Error, T)> for Error { - fn from((err, path): (io::Error, T)) -> Error { - Error::Io(err, path.as_ref().to_owned(), Backtrace::new()) + fn from((error, path): (io::Error, T)) -> Error { + Error::Io { + error, + path: path.as_ref().to_owned(), + backtrace: Backtrace::generate(), + } } } -impl From for Error { - fn from(err: ini::ini::Error) -> Error { - Error::Ini(err, Backtrace::new()) +impl From for Error { + fn from(error: ini::Error) -> Error { + Error::Ini { + error, + backtrace: Backtrace::generate(), + } } } impl From for Error { - fn from(err: BlockModeError) -> Error { - Error::BlockMode(err, Backtrace::new()) + fn from(error: BlockModeError) -> Error { + Error::BlockMode { + error, + backtrace: Backtrace::generate(), + } } } impl From for Error { - fn from(err: serde_json::error::Error) -> Error { - Error::Deserialization(err) + fn from(error: serde_json::error::Error) -> Error { + Error::Deserialization { error } } } impl From for Error { - fn from(err: FromUtf8Error) -> Error { + fn from(error: FromUtf8Error) -> Error { // Why the heck does OsStr not have display()? - Error::Utf8Conversion(String::from_utf8_lossy(err.as_bytes()).into_owned(), err.utf8_error(), Backtrace::new()) + Error::Utf8Conversion { + filename: String::from_utf8_lossy(error.as_bytes()).into_owned(), + error: error.utf8_error(), + backtrace: Backtrace::generate(), + } } } impl From<(usize, cmac::crypto_mac::MacError)> for Error { - fn from((id, err): (usize, cmac::crypto_mac::MacError)) -> Error { - Error::MacError(err, id, Backtrace::new()) + fn from((id, error): (usize, cmac::crypto_mac::MacError)) -> Error { + Error::MacError { + error, + id, + backtrace: Backtrace::generate(), + } } } impl From> for Error { fn from(err: Box) -> Error { - Error::BincodeError(err, Backtrace::new()) + Error::BincodeError { + error: err, + backtrace: Backtrace::generate() + } } } impl From for Error { fn from(err: rsa::errors::Error) -> Error { - Error::RsaError(err, Backtrace::new()) + Error::RsaError { + error: err, + backtrace: Backtrace::generate() + } } } impl From for Error { fn from(err: pem::PemError) -> Error { - Error::PemError(err, Backtrace::new()) + Error::PemError { + error: err, + backtrace: Backtrace::generate() + } } } impl From for Error { fn from(err: yasna::ASN1Error) -> Error { - Error::Asn1Error(err, Backtrace::new()) + Error::Asn1Error { + error: err, + backtrace: Backtrace::generate() + } } } \ No newline at end of file diff --git a/src/format/mod.rs b/src/format.rs similarity index 86% rename from src/format/mod.rs rename to src/format.rs index 6ec7630..4389b0a 100644 --- a/src/format/mod.rs +++ b/src/format.rs @@ -3,4 +3,5 @@ pub mod nxo; pub mod pfs0; pub mod romfs; pub mod npdm; +pub mod svc; mod utils; diff --git a/src/format/nacp.rs b/src/format/nacp.rs index 9632293..6cebafd 100644 --- a/src/format/nacp.rs +++ b/src/format/nacp.rs @@ -1,63 +1,100 @@ +use crate::format::utils::{HexOrNum, check_string_or_truncate}; use byteorder::{LittleEndian, WriteBytesExt}; -use crate::format::utils; -use std; +use serde_derive::{Deserialize, Serialize}; use std::fs::File; use std::io::Write; -use serde_derive::{Serialize, Deserialize}; -use serde_json; + +mod fmt; +use fmt::*; #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct NacpLangEntry { +pub struct ApplicationTitle { pub name: String, pub author: String, } #[derive(Serialize, Deserialize, Debug)] -pub struct NacpLangEntries { - #[serde(rename = "en-US")] - pub en_us: Option, +pub struct ApplicationTitles { + #[serde(alias = "en-US", alias = "AmericanEnglish")] + pub en_us: Option, + + #[serde(alias = "en-GB", alias = "BritishEnglish")] + pub en_gb: Option, + + #[serde(alias = "Japanese")] + pub ja: Option, + + #[serde(alias = "French")] + pub fr: Option, + + #[serde(alias = "German")] + pub de: Option, - #[serde(rename = "en-GB")] - pub en_gb: Option, + #[serde(alias = "es-419", alias = "LatinAmericanSpanish")] + pub es_419: Option, - pub ja: Option, - pub fr: Option, - pub de: Option, + #[serde(alias = "Spanish")] + pub es: Option, - #[serde(rename = "es-419")] - pub es_419: Option, + #[serde(alias = "Italian")] + pub it: Option, - pub es: Option, - pub it: Option, - pub nl: Option, + #[serde(alias = "Dutch")] + pub nl: Option, - #[serde(rename = "fr-CA")] - pub fr_ca: Option, - pub pt: Option, + #[serde(alias = "fr-CA", alias = "CanadianFrench")] + pub fr_ca: Option, - pub ru: Option, - pub ko: Option, + #[serde(alias = "Portuguese")] + pub pt: Option, - #[serde(rename = "zh-TW")] - pub zh_tw: Option, + #[serde(alias = "Russian")] + pub ru: Option, - #[serde(rename = "zh-CN")] - pub zh_cn: Option, + #[serde(alias = "Korean")] + pub ko: Option, + + #[serde(alias = "zh-TW", alias = "TraditionalChinese")] + pub zh_tw: Option, // 4.0.0+ + + #[serde(alias = "zh-CN", alias = "SimplifiedChinese")] + pub zh_cn: Option, // 4.0.0+ + + #[serde(alias = "pt-BR", alias = "BrazilianPortuguese")] + pub pt_br: Option, // 10.0.0+ } #[derive(Serialize, Deserialize, Debug, Default)] -pub struct NacpFile { - pub name: Option, - pub author: Option, - pub version: Option, - pub title_id: Option, - pub dlc_base_title_id: Option, - pub lang: Option, +pub struct Nacp { + #[serde(alias = "name")] + pub default_name: Option, + #[serde(alias = "author")] + pub default_author: Option, + pub version: String, + #[serde(alias = "title_id")] + pub application_id: Option, + pub presence_group_id: Option, + #[serde(alias = "dlc_base_title_id")] + pub add_on_content_base_id: Option, + #[serde(alias = "lang")] + pub titles: Option, + pub isbn: Option, + pub startup_user_account: Option, + pub attribute: Option, + pub screenshot: Option, + pub video_capture: Option, + pub logo_type: Option, + pub logo_handling: Option, + pub crash_report: Option, + pub bcat_passphrase: Option, + pub program_index: Option, + pub save_data_owner_id: Option, + // TODO: support more NACP fields } #[allow(clippy::len_without_is_empty)] -impl NacpFile { - pub fn from_file(input: &str) -> std::io::Result { +impl Nacp { + pub fn from_json(input: &str) -> std::io::Result { let file = File::open(input)?; match serde_json::from_reader(file) { Ok(res) => Ok(res), @@ -67,21 +104,21 @@ impl NacpFile { fn write_lang_entry( &self, - output_writter: &mut T, - lang_entry: &NacpLangEntry, + out_writer: &mut T, + lang_entry: &ApplicationTitle, ) -> std::io::Result<()> where T: Write, { let name = &lang_entry.name; let name_padding = 0x200 - name.len(); - output_writter.write_all(name.as_bytes())?; - output_writter.write_all(&vec![0; name_padding])?; + out_writer.write_all(name.as_bytes())?; + out_writer.write_all(&vec![0; name_padding])?; let author = &lang_entry.author; let author_padding = 0x100 - author.len(); - output_writter.write_all(author.as_bytes())?; - output_writter.write_all(&vec![0; author_padding])?; + out_writer.write_all(author.as_bytes())?; + out_writer.write_all(&vec![0; author_padding])?; Ok(()) } @@ -90,194 +127,375 @@ impl NacpFile { 0x4000 } - pub fn write(&mut self, output_writter: &mut T) -> std::io::Result<()> + pub fn write(&mut self, out_writer: &mut T) -> std::io::Result<()> where T: Write, { - let mut name = self - .name + let mut def_name = self + .default_name .clone() - .unwrap_or_else(|| "Unknown Application".to_string()); - let mut version = self.version.clone().unwrap_or_else(|| "1.0.0".to_string()); - let mut author = self.author.clone().unwrap_or_else(|| "Unknown Author".to_string()); - - let title_id = match &self.title_id { - None => 0, - Some(title_string) => { - u64::from_str_radix(title_string.as_str(), 16).expect("Invalid title_id provided!") - } - }; - - let dlc_base_title_id = match &self.dlc_base_title_id { - None => title_id + 0x1000, - Some(title_string) => { - u64::from_str_radix(title_string.as_str(), 16).expect("Invalid title_id provided!") - } - }; + .unwrap_or_else(|| "Unknown application".to_string()); + let mut version = self.version.clone(); + let mut def_author = self + .default_author + .clone() + .unwrap_or_else(|| "Unknown author".to_string()); - let lang_entries = &self.lang; + // Truncate default names if needed + check_string_or_truncate(&mut def_name, "default_name", 0x200); + check_string_or_truncate(&mut def_author, "default_author", 0x100); - // Truncate names if needed - utils::check_string_or_truncate(&mut name, "name", 0x200); - utils::check_string_or_truncate(&mut version, "version", 0x10); - utils::check_string_or_truncate(&mut author, "author", 0x100); + let application_id = self.application_id.map(|h| h.0).unwrap_or(0); + let presence_group_id = self.presence_group_id.map(|h| h.0).unwrap_or(application_id); + let add_on_content_base_id = self.add_on_content_base_id.map(|h| h.0).unwrap_or(application_id + 0x1000); + let save_data_owner_id = self.save_data_owner_id.map(|h| h.0).unwrap_or(application_id); - // fallback entry if lang entry isn't defined - let default_lang_entry = NacpLangEntry { name, author }; - match lang_entries { + // Fallback default entry for each entry that wasn't provided + let default_title = ApplicationTitle { name: def_name, author: def_author }; + match &self.titles { None => { for _ in 0..16 { - self.write_lang_entry(output_writter, &default_lang_entry)?; + self.write_lang_entry(out_writer, &default_title)?; } } - Some(data) => { - let lang_entries = data; - - // Write every langs in order + Some(app_titles_ref) => { + // Write languages in order self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .en_us .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .en_gb .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .ja .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .fr .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .de .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .es_419 .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .es .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .it .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .nl .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .fr_ca .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .pt .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .ru .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .ko .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .zh_tw .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), )?; self.write_lang_entry( - output_writter, - &lang_entries + out_writer, + &app_titles_ref .zh_cn .clone() - .unwrap_or_else(|| default_lang_entry.clone()), + .unwrap_or_else(|| default_title.clone()), + )?; + self.write_lang_entry( + out_writer, + &app_titles_ref + .pt_br + .clone() + .unwrap_or_else(|| default_title.clone()), )?; - // There are 16 entries. One is missing :eyes: - self.write_lang_entry(output_writter, &default_lang_entry)?; } } - // 0x3000 - 0x3038: Unknown - output_writter.write_all(&[0; 0x38])?; + // 0x3000: u8 isbn[0x25] + let isbn_max_len = 0x25usize; + let mut isbn_pad = isbn_max_len; + if let Some(mut isbn) = self.isbn.as_mut() { + check_string_or_truncate(&mut isbn, "isbn", isbn_max_len); + isbn_pad = isbn_max_len - version.len(); + out_writer.write_all(version.as_bytes())?; + } + out_writer.write_all(&vec![0; isbn_pad])?; + + // 0x3025: u8 startup_user_account + let startup_user_account = self.startup_user_account.unwrap_or(StartupUserAccount::None); + out_writer.write_u8(startup_user_account as u8)?; + + // 0x3026: u8 user_account_switch_lock? + out_writer.write_u8(0)?; + + // 0x3027: u8 add_on_content_registration_type? + out_writer.write_u8(0)?; + + // 0x3028: u32 attribute + let attribute = self.attribute.unwrap_or(Attribute::None); + out_writer.write_u32::(attribute as u32)?; + + // 0x302C: u32 supported_language? + out_writer.write_u32::(0)?; + + // 0x3030: u32 parental_control? + out_writer.write_u32::(0)?; + + // 0x3034: u8 screenshot + let screenshot = self.screenshot.unwrap_or(Screenshot::Allow); + out_writer.write_u8(screenshot as u8)?; + + // 0x3035: u8 video_capture + let video_capture = self.video_capture.unwrap_or(VideoCapture::Disabled); + out_writer.write_u8(video_capture as u8)?; + + // 0x3036: u8 data_loss_confirmation? + out_writer.write_u8(0)?; + + // 0x3037: u8 play_log_policy? + out_writer.write_u8(0)?; + + // 0x3038: u64 presence_group_id + out_writer.write_u64::(presence_group_id)?; + + // 0x3040: u8 rating_age[0x20]? + let mut unk_rating_age = Vec::new(); + unk_rating_age.resize(0x20, 0xFF); + out_writer.write_all(&unk_rating_age)?; + + // 0x3060: u8 display_version[0x10] + let display_version_max_len = 0x10usize; + check_string_or_truncate(&mut version, "version", display_version_max_len); + let display_version_pad = display_version_max_len - version.len(); + out_writer.write_all(version.as_bytes())?; + out_writer.write_all(&vec![0; display_version_pad])?; + + // 0x3070: u64 add_on_content_base_id + out_writer.write_u64::(add_on_content_base_id)?; + + // 0x3078: save_data_owner_id + out_writer.write_u64::(save_data_owner_id)?; + + // 0x3080: u64 user_account_save_data_size? + out_writer.write_u64::(0)?; + + // 0x3088: u64 user_account_save_data_journal_size? + out_writer.write_u64::(0)?; + + // 0x3090: u64 device_save_data_size? + out_writer.write_u64::(0)?; + + // 0x3098: u64 device_save_data_journal_size? + out_writer.write_u64::(0)?; + + // 0x30A0: u64 bcat_delivery_cache_storage_size? + out_writer.write_u64::(0)?; + + // 0x30A8: u64 application_error_code_category? + out_writer.write_u64::(0)?; + + // 0x30B0: u64 local_communication_id[8] + for _ in 0..8 { + out_writer.write_u64::(0)?; + } + + // 0x30F0: u8 logo_type + let logo_type = self.logo_type.unwrap_or(LogoType::LicensedByNintendo); + out_writer.write_u8(logo_type as u8)?; + + // 0x30F1: u8 logo_handling + let logo_handling = self.logo_handling.unwrap_or(LogoHandling::Auto); + out_writer.write_u8(logo_handling as u8)?; + + // 0x30F2: u8 runtime_add_on_content_install? + out_writer.write_u8(0)?; + + // 0x30F3: u8 runtime_parameter_delivery? + out_writer.write_u8(0)?; + + // 0x30F4: u8 reserved[2] + out_writer.write_all(&vec![0; 2])?; + + // 0x30F6: u8 crash_report + let crash_report = self.crash_report.unwrap_or(CrashReport::Allow); + out_writer.write_u8(crash_report as u8)?; + + // 0x30F7: u8 hdcp? + out_writer.write_u8(0)?; + + // 0x30F8: u64 seed_for_pseudo_device_id? + out_writer.write_u64::(0)?; + + // 0x3100: u8 bcat_passphrase[0x41] + let bcat_passphrase_max_len = 0x41usize; + let mut bcat_passphrase_pad = bcat_passphrase_max_len; + if let Some(mut bcat_passphrase) = self.bcat_passphrase.as_mut() { + check_string_or_truncate(&mut bcat_passphrase, "bcat_passphrase", bcat_passphrase_max_len); + bcat_passphrase_pad = bcat_passphrase_max_len - version.len(); + out_writer.write_all(bcat_passphrase.as_bytes())?; + } + out_writer.write_all(&vec![0; bcat_passphrase_pad])?; + + // 0x3141: u8 startup_user_account_option? + out_writer.write_u8(0)?; + + // 0x3142: u8 reserved_for_user_account_save_data_operation[6]? + out_writer.write_all(&vec![0; 6])?; + + // 0x3148: u64 user_account_save_data_size_max? + out_writer.write_u64::(0)?; + + // 0x3150: u64 user_account_save_data_journal_size_max? + out_writer.write_u64::(0)?; + + // 0x3158: u64 device_save_data_size_max? + out_writer.write_u64::(0)?; + + // 0x3160: u64 device_save_data_journal_size_max? + out_writer.write_u64::(0)?; - output_writter.write_u64::(title_id)?; + // 0x3168: u64 temporary_storage_size? + out_writer.write_u64::(0)?; - // Unknown 0x20 bytes - let mut unknown = Vec::new(); - unknown.resize(0x20, 0xFF); - output_writter.write_all(&unknown)?; + // 0x3170: u64 cache_storage_size? + out_writer.write_u64::(0)?; - // Version string part (probably UTF8) - let version_padding = 0x10 - version.len(); - output_writter.write_all(version.as_bytes())?; - output_writter.write_all(&vec![0; version_padding])?; + // 0x3178: u64 cache_storage_journal_size? + out_writer.write_u64::(0)?; - output_writter.write_u64::(dlc_base_title_id)?; - output_writter.write_u64::(title_id)?; + // 0x3180: u64 cache_storage_data_and_journal_size_max? + out_writer.write_u64::(0)?; - // 0x3080 - 0x30B0: Unknown - output_writter.write_all(&[0; 0x30])?; + // 0x3188: u16 cache_storage_index_max? + out_writer.write_u16::(0)?; - output_writter.write_u64::(title_id)?; + // 0x318A: u8 reserved + out_writer.write_u8(0)?; - // title id array (0x7 entries), only write the base, other entries seems to be for update titles - output_writter.write_u64::(title_id)?; - output_writter.write_all(&[0; 0x30])?; + // 0x318B: u8 runtime_upgrade? + out_writer.write_u8(0)?; - // 0x30F0 - 0x30F8: Unknown - output_writter.write_u64::(0)?; - output_writter.write_u64::(title_id)?; + // 0x318C: u32 supporting_limited_licenses? + out_writer.write_u32::(0)?; + + // 0x3190: u64 play_log_queryable_application_id[16]? + for _ in 0..16 { + out_writer.write_u64::(0)?; + } + + // 0x3210: u8 play_log_query_capability? + out_writer.write_u8(0)?; + + // 0x3211: u8 repair? + out_writer.write_u8(0)?; + + // 0x3212: u8 program_index + let program_index = self.program_index.map(|h| h.0 as u8).unwrap_or(0); + out_writer.write_u8(program_index)?; + + // 0x3213: u8 required_network_service_license_on_launch? + out_writer.write_u8(0)?; + + // 0x3214: u8 reserved[4] + out_writer.write_all(&vec![0; 4])?; + + // 0x3218: u8 neighbor_detection_client_configuration[0x198]? + out_writer.write_all(&vec![0; 0x198])?; + + // 0x33B0: u8 jit_configuration[0x10]? + out_writer.write_all(&vec![0; 0x10])?; + + // 0x33C0: u16 required_add_on_contents_set_binary_descriptor[32]? + for _ in 0..32 { + out_writer.write_u16::(0)?; + } + + // 0x3400: u8 play_report_permission? + out_writer.write_u8(0)?; + + // 0x3401: u8 crash_screenshot_for_prod? + out_writer.write_u8(0)?; + + // 0x3402: u8 crash_screenshot_for_dev? + out_writer.write_u8(0)?; + + // 0x3403: u8 contents_availability_transition_policy? + out_writer.write_u8(0)?; + + // 0x3404: u8 reserved[4] + out_writer.write_all(&vec![0; 4])?; + + // 0x3408: u64 accessible_launch_required_version[8]? + for _ in 0..8 { + out_writer.write_u64::(0)?; + } - let mut end_of_file = Vec::new(); - end_of_file.resize(0xF00, 0); - output_writter.write_all(&end_of_file)?; + // 0x3448: u8 reserved[0xBB8] + out_writer.write_all(&vec![0; 0xBB8])?; Ok(()) } @@ -288,9 +506,9 @@ mod test { use super::*; #[test] - fn nacp_is_4000_size() { - let mut buf = Vec::new(); - NacpFile::default().write(&mut buf).unwrap(); - assert_eq!(buf.len(), 0x4000, "Nacp length is wrong"); + fn generated_nacp_has_expected_size() { + let mut gen_nacp_buf = Vec::new(); + Nacp::default().write(&mut gen_nacp_buf).unwrap(); + assert_eq!(gen_nacp_buf.len(), 0x4000, "NACP length is wrong"); } } diff --git a/src/format/nacp/fmt.rs b/src/format/nacp/fmt.rs new file mode 100644 index 0000000..ed8ac4d --- /dev/null +++ b/src/format/nacp/fmt.rs @@ -0,0 +1,53 @@ +use serde_derive::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u8)] +pub enum StartupUserAccount { + None = 0, + Required = 1, + RequiredWithNetworkServiceAccountAvailable = 2, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u32)] +pub enum Attribute { + None = 0, + Demo = 1, + RetailInteractiveDisplay = 2, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u8)] +pub enum Screenshot { + Allow = 0, + Deny = 1, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u8)] +pub enum VideoCapture { + Disabled = 0, + Enabled = 1, + Automatic = 2, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u8)] +pub enum LogoType { + LicensedByNintendo = 0, + Nintendo = 2, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u8)] +pub enum LogoHandling { + Auto = 0, + Manual = 1, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[repr(u8)] +pub enum CrashReport { + Deny = 0, + Allow = 1, +} \ No newline at end of file diff --git a/src/format/npdm.rs b/src/format/npdm.rs index 41b136c..79b34a9 100644 --- a/src/format/npdm.rs +++ b/src/format/npdm.rs @@ -1,54 +1,133 @@ -use std; -use std::io::Write; -use std::collections::HashMap; -use crate::format::utils::{SigOrPubKey, Reserved64, HexOrNum}; +use crate::format::utils; +use crate::format::utils::HexOrNum; +use crate::format::svc; use crate::error::Error; -use serde_derive::{Serialize, Deserialize}; -use serde_json; use bit_field::BitField; +use bincode::Options; +use serde_derive::{Deserialize, Serialize}; +use snafu::GenerateBacktrace; +use std::collections::HashMap; use std::convert::TryFrom; use std::path::Path; use std::mem::size_of; -use failure::Backtrace; -use sha2::{Digest, Sha256}; +use std::io::Write; +use snafu::Backtrace; use rsa::{BigUint, RSAPrivateKey}; -pub mod syscalls; +mod fmt; +use fmt::*; // TODO: Pretty errors if the user messes up. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(untagged)] -pub enum Syscalls { - /// Accepts the standard svcName: 0xsvc_hex +pub enum SystemCalls { + /// Accepts the standard svcName: 0x KeyValue(HashMap), /// Accepts syscall names. Those must be correctly spelled. - Name(Vec), + Name(Vec), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum ProgramType { + Value(HexOrNum), + Name(String) +} + +impl ProgramType { + pub fn get_value(&self) -> Option { + match self { + ProgramType::Value(prog_type_val) => { + if prog_type_val.0 > 2 { + None + } + else { + Some(prog_type_val.0 as u16) + } + } + ProgramType::Name(prog_type_str) => { + match prog_type_str.to_lowercase().as_str() { + "system" => Some(0), + "application" => Some(1), + "applet" => Some(2), + _ => None + } + } + } + } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum KernelVersion { + Value(HexOrNum), + Version(String) +} + +impl KernelVersion { + pub fn get_value(&self) -> Option { + match self { + KernelVersion::Value(ver_val) => { + if ver_val.0 < 0x030 { + None + } + else { + Some(ver_val.0 as u16) + } + }, + KernelVersion::Version(ver_str) => { + let ver_strs: Vec<&str> = ver_str.split('.').collect(); + if ver_strs.len() == 2 { + if let Ok(major) = u32::from_str_radix(ver_strs[0], 10) { + if let Ok(minor) = u32::from_str_radix(ver_strs[1], 10) { + return Some(*0u16.set_bits(0..4, minor as u16).set_bits(4..16, major as u16)) + } + } + } + + return None; + } + } + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "type", content = "value")] #[serde(rename_all = "snake_case")] pub enum KernelCapability { - KernelFlags { - highest_thread_priority: u8, - lowest_thread_priority: u8, - highest_cpu_id: u8, - lowest_cpu_id: u8, + #[serde(alias = "kernel_flags")] + ThreadInfo { + #[serde(alias = "highest_thread_priority")] + highest_priority: u8, + #[serde(alias = "lowest_thread_priority")] + lowest_priority: u8, + #[serde(alias = "highest_cpu_id")] + max_core_number: u8, + #[serde(alias = "lowest_cpu_id")] + min_core_number: u8, }, - Syscalls(Syscalls), - Map { + #[serde(alias = "syscalls")] + EnableSystemCalls(SystemCalls), + #[serde(alias = "map")] + MemoryMap { address: HexOrNum, size: HexOrNum, is_ro: bool, is_io: bool, }, - MapPage(HexOrNum), - IrqPair([u16; 2]), - ApplicationType(u16), - MinKernelVersion(HexOrNum), + #[serde(alias = "map_page")] + IoMemoryMap(HexOrNum), + #[serde(alias = "irq_pair")] + EnableInterrupts([u16; 2]), + #[serde(alias = "application_type")] + MiscParams(ProgramType), + #[serde(alias = "min_kernel_version")] + KernelVersion(KernelVersion), HandleTableSize(u16), - DebugFlags { - allow_debug: bool, + #[serde(alias = "debug_flags")] + MiscFlags { + #[serde(alias = "allow_debug")] + enable_debug: bool, force_debug: bool, }, } @@ -72,70 +151,68 @@ fn encode_syscalls>(syscalls: I) -> Vec { } impl KernelCapability { - pub fn encode(&self) -> Vec { + pub fn encode(&self) -> Result, Error> { match self { - KernelCapability::KernelFlags { - highest_thread_priority, - lowest_thread_priority, - highest_cpu_id, - lowest_cpu_id, + KernelCapability::ThreadInfo { + highest_priority, + lowest_priority, + max_core_number, + min_core_number, } => { - vec![*0b111u32 - .set_bits(04..10, u32::from(*lowest_thread_priority)) - .set_bits(10..16, u32::from(*highest_thread_priority)) - .set_bits(16..24, u32::from(*lowest_cpu_id)) - .set_bits(24..32, u32::from(*highest_cpu_id))] + Ok(vec![*0b111u32 + .set_bits(04..10, u32::from(*highest_priority)) + .set_bits(10..16, u32::from(*lowest_priority)) + .set_bits(16..24, u32::from(*min_core_number)) + .set_bits(24..32, u32::from(*max_core_number))]) }, - KernelCapability::Syscalls(Syscalls::Name(syscalls)) => { - encode_syscalls(syscalls.iter().map(|v| *v as u32)) + KernelCapability::EnableSystemCalls(SystemCalls::Name(syscalls)) => { + Ok(encode_syscalls(syscalls.iter().map(|v| *v as u32))) }, - KernelCapability::Syscalls(Syscalls::KeyValue(syscalls)) => { - encode_syscalls(syscalls.iter().map(|(_, v)| v.0 as u32)) + KernelCapability::EnableSystemCalls(SystemCalls::KeyValue(syscalls)) => { + Ok(encode_syscalls(syscalls.iter().map(|(_, v)| v.0 as u32))) }, - KernelCapability::Map { + KernelCapability::MemoryMap { address, size, is_ro, is_io, } => { - let mut val = vec![0b111111u32, 0b111111u32]; + let mut val = vec![0b11_1111u32, 0b11_1111u32]; val[0] .set_bits(7..31, u32::try_from(address.0).unwrap()) .set_bit(31, *is_ro); val[1] .set_bits(7..31, u32::try_from(size.0).unwrap()) .set_bit(31, *is_io); - val - }, - KernelCapability::MapPage(page) => { - vec![*0b1111111u32 - .set_bits(8..32, u32::try_from(page.0).unwrap())] - }, - KernelCapability::IrqPair(irq_pair) => { - vec![*0b11111111111u32 - .set_bits(12..22, u32::from(irq_pair[0])) - .set_bits(22..32, u32::from(irq_pair[1]))] - }, - KernelCapability::ApplicationType(app_type) => { - vec![*0b1111111111111u32 - .set_bits(14..17, u32::from(*app_type))] - }, - KernelCapability::MinKernelVersion(min_kernel) => { - vec![*0b11111111111111u32 - .set_bits(15..32, u32::try_from(min_kernel.0).unwrap())] - }, + Ok(val) + } + KernelCapability::IoMemoryMap(page) => { + Ok(vec![*0b111_1111u32.set_bits(8..32, u32::try_from(page.0).unwrap())]) + } + KernelCapability::EnableInterrupts(irq_pair) => Ok(vec![*0b111_1111_1111u32 + .set_bits(12..22, u32::from(irq_pair[0])) + .set_bits(22..32, u32::from(irq_pair[1]))]), + KernelCapability::MiscParams(prog_type) => { + match prog_type.get_value() { + None => Err(Error::InvalidNpdmValue { error: "misc_params (program_type)".into(), backtrace: Backtrace::generate() }), + Some(prog_type_val) => Ok(vec![*0b1_1111_1111_1111u32.set_bits(14..17, prog_type_val as u32)]) + } + } + KernelCapability::KernelVersion(kern_ver) => { + match kern_ver.get_value() { + None => Err(Error::InvalidNpdmValue { error: "kernel_version".into(), backtrace: Backtrace::generate() }), + Some(kern_ver_val) => Ok(vec![*0b11_1111_1111_1111u32.set_bits(15..32, kern_ver_val as u32)]) + } + } KernelCapability::HandleTableSize(handle_table_size) => { - vec![*0b111111111111111u32 - .set_bits(16..26, u32::from(*handle_table_size))] - }, - KernelCapability::DebugFlags { - allow_debug, + Ok(vec![*0b111_1111_1111_1111u32.set_bits(16..26, u32::from(*handle_table_size))]) + } + KernelCapability::MiscFlags { + enable_debug, force_debug, - } => { - vec![*0b1111111111111111u32 - .set_bit(17, *allow_debug) - .set_bit(18, *force_debug)] - }, + } => Ok(vec![*0b1111_1111_1111_1111u32 + .set_bit(17, *enable_debug) + .set_bit(18, *force_debug)]), } } } @@ -145,52 +222,199 @@ fn sac_encoded_len(sacs: &[String]) -> usize { } #[derive(Debug, Serialize, Deserialize)] -pub struct NPDMFilesystemAccess { - permissions: HexOrNum, +#[serde(untagged)] +pub enum EnabledSystemCall { + Value(HexOrNum), + Name(svc::SystemCallId) +} + +impl EnabledSystemCall { + #[inline] + pub fn get_id(&self) -> svc::SystemCallId { + match self { + EnabledSystemCall::Value(raw_val) => unsafe { + std::mem::transmute(raw_val.0 as u32) + }, + EnabledSystemCall::Name(id) => *id + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct MemoryMap { + address: HexOrNum, + size: HexOrNum, + is_ro: bool, + is_io: bool +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct KernelCapabilityValues { + highest_priority: u8, + lowest_priority: u8, + max_core_number: u8, + min_core_number: u8, + enable_system_calls: Vec, + memory_maps: Option>, + io_memory_maps: Option>, + enable_interrupts: Option>, + program_type: Option, + kernel_version: Option, + enable_debug: Option, + force_debug: Option +} + +impl KernelCapabilityValues { + pub fn to_list_format(&self) -> Vec { + let mut kern_caps: Vec = Vec::new(); + + let thread_info_kcap = KernelCapability::ThreadInfo { + highest_priority: self.highest_priority, + lowest_priority: self.lowest_priority, + max_core_number: self.max_core_number, + min_core_number: self.min_core_number + }; + kern_caps.push(thread_info_kcap); + + let enable_svc_ids: Vec = self.enable_system_calls.iter().map(|svc| svc.get_id()).collect(); + let enable_svcs_kcap = KernelCapability::EnableSystemCalls(SystemCalls::Name(enable_svc_ids)); + kern_caps.push(enable_svcs_kcap); + + if let Some(mem_maps) = &self.memory_maps { + for mem_map in mem_maps.iter() { + let mem_map_kcap = KernelCapability::MemoryMap { + address: mem_map.address, + size: mem_map.size, + is_ro: mem_map.is_ro, + is_io: mem_map.is_io + }; + kern_caps.push(mem_map_kcap); + } + } + + if let Some(io_mem_maps) = &self.io_memory_maps { + for io_mem_map in io_mem_maps.iter() { + let io_mem_map_kcap = KernelCapability::IoMemoryMap(*io_mem_map); + kern_caps.push(io_mem_map_kcap); + } + } + + if let Some(enable_ints) = &self.enable_interrupts { + for enable_int in enable_ints.iter() { + let enable_int_kcap = KernelCapability::EnableInterrupts(*enable_int); + kern_caps.push(enable_int_kcap); + } + } + + if let Some(program_type) = &self.program_type { + let misc_params_kcap = KernelCapability::MiscParams(program_type.clone()); + kern_caps.push(misc_params_kcap); + } + + if let Some(kernel_version) = &self.kernel_version { + let kern_version_kcap = KernelCapability::KernelVersion(kernel_version.clone()); + kern_caps.push(kern_version_kcap); + } + + if self.enable_debug.is_some() || self.force_debug.is_some() { + let misc_flags_kcap = KernelCapability::MiscFlags { + enable_debug: self.enable_debug.unwrap_or(false), + force_debug: self.force_debug.unwrap_or(false) + }; + kern_caps.push(misc_flags_kcap); + } + + kern_caps + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct FsAccessControl { + #[serde(alias = "permissions")] + flags: HexOrNum +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ServiceAccessControl { + accessed_services: Vec, + hosted_services: Vec +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum KernelCapabilities { + TypeValueList(Vec), + Struct(KernelCapabilityValues) +} + +impl KernelCapabilities { + pub fn get_list(&self) -> Vec { + match self { + KernelCapabilities::TypeValueList(list) => list.to_vec(), + KernelCapabilities::Struct(kern_caps) => kern_caps.to_list_format() + } + } } #[derive(Debug, Serialize, Deserialize)] -pub struct NpdmJson { - // META fields. +pub struct Npdm { + // META fields name: String, + product_code: Option, + signature_key_generation: Option, main_thread_stack_size: HexOrNum, main_thread_priority: u8, - default_cpu_id: u8, - // We thought this field was the process_category. We were wrong. 🤦 - #[serde(alias = "process_category")] - version: u32, + #[serde(alias = "default_cpu_id")] + main_thread_core_number: u8, + system_resource_size: Option, + #[serde(alias = "process_category")] // We thought this field was the process_category. We were wrong. 🤦 + version: Option, address_space_type: u8, is_64_bit: bool, + optimize_memory_allocation: Option, + disable_device_address_space_merge: Option, // ACID fields - is_retail: bool, - pool_partition: u32, - title_id_range_min: Option, - title_id_range_max: Option, - developer_key: Option, - - // ACI0 - title_id: HexOrNum, + #[serde(alias = "is_retail")] + is_production: Option, + unqualified_approval: Option, + #[serde(alias = "pool_partition")] + memory_region: u32, + #[serde(alias = "title_id_range_min")] + program_id_range_min: Option, + #[serde(alias = "title_id_range_max")] + program_id_range_max: Option, + + // ACI0 fields + #[serde(alias = "title_id")] + program_id: HexOrNum, // FAC - filesystem_access: NPDMFilesystemAccess, + #[serde(alias = "filesystem_access")] + fs_access_control: FsAccessControl, // SAC - service_access: Vec, - service_host: Vec, + #[serde(alias = "service_access")] + accessed_services: Option>, + #[serde(alias = "service_host")] + hosted_services: Option>, + service_access_control: Option, // KAC - kernel_capabilities: Vec, + kernel_capabilities: KernelCapabilities, + + // Other + developer_key: Option } -pub enum ACIDBehavior<'a> { +pub enum AcidBehavior<'a> { Sign { pem_file_path: &'a Path }, Empty, Use { acid_file_path: &'a Path } } -impl NpdmJson { - pub fn from_file(file: &Path) -> Result { +impl Npdm { + pub fn from_json(file: &Path) -> Result { let file = std::fs::File::open(file)?; match serde_json::from_reader(file) { Ok(res) => Ok(res), @@ -199,120 +423,158 @@ impl NpdmJson { } // TODO: Optionally pass a (signed) ACID here. - pub fn into_npdm(&self, mut file: W, acid_behavior: ACIDBehavior) -> Result<(), Error> { - let mut meta: RawMeta = RawMeta::default(); + pub fn into_npdm(&self, mut file: W, acid_behavior: AcidBehavior) -> Result<(), Error> { + let mut meta: Meta = Meta::default(); meta.magic = *b"META"; + meta.signature_key_generation = self.signature_key_generation.unwrap_or(0); if self.address_space_type & !3 != 0 { - return Err(Error::InvalidNpdmValue("address_space_type".into(), Backtrace::new())); + return Err(Error::InvalidNpdmValue { + error: "address_space_type".into(), + backtrace: Backtrace::generate() + }); + } + + meta.flags = (self.address_space_type & 3) << 1; + if self.is_64_bit { + meta.flags |= 1 << 0; + } + if self.optimize_memory_allocation.unwrap_or(false) { + meta.flags |= 1 << 4; + } + if self.disable_device_address_space_merge.unwrap_or(false) { + meta.flags |= 1 << 5; } - meta.mmu_flags = (self.address_space_type & 3) << 1; - if self.is_64_bit { meta.mmu_flags |= 1; } - meta.main_thread_prio = self.main_thread_priority; - meta.main_thread_core_num = self.default_cpu_id; + meta.main_thread_priority = self.main_thread_priority; + meta.main_thread_core_number = self.main_thread_core_number; - meta.system_resources = 0; - meta.version = 0; + meta.system_resource_size = self.system_resource_size.unwrap_or(0); + meta.version = self.version.unwrap_or(0); meta.main_thread_stack_size = self.main_thread_stack_size.0 as _; - let title_name_len = std::cmp::min(self.name.as_bytes().len(), 12); - meta.title_name = [0; 16]; - meta.title_name[..title_name_len].copy_from_slice(&self.name.as_bytes()[..title_name_len]); + let name_len = std::cmp::min(self.name.as_bytes().len(), 12); + meta.name = [0; 0x10]; + meta.name[..name_len].copy_from_slice(&self.name.as_bytes()[..name_len]); meta.product_code = [0; 0x10]; - meta.acid_offset = size_of::() as u32; + let accessed_services = if let Some(sac) = self.service_access_control.as_ref() { + &sac.accessed_services + } + else if let Some(accessed_srvs) = self.accessed_services.as_ref() { + accessed_srvs + } + else { + panic!("No accessed srvs!"); + }; + let hosted_services = if let Some(sac) = self.service_access_control.as_ref() { + &sac.hosted_services + } + else if let Some(hosted_srvs) = self.hosted_services.as_ref() { + hosted_srvs + } + else { + panic!("No hosted srvs!"); + }; + + let kern_caps = self.kernel_capabilities.get_list(); + + meta.acid_offset = size_of::() as u32; meta.acid_size = match acid_behavior { - ACIDBehavior::Sign { .. } | ACIDBehavior::Empty => { - (0x100 + size_of::() + size_of::() + - sac_encoded_len(&self.service_host) + sac_encoded_len(&self.service_access) + - self.kernel_capabilities.iter().map(|v| v.encode().len() * 4).sum::()) as u32 + AcidBehavior::Sign { .. } | AcidBehavior::Empty => { + (0x100 + size_of::() + size_of::() + + sac_encoded_len(&hosted_services) + sac_encoded_len(&accessed_services) + + kern_caps.iter().map(|v| v.encode().unwrap().len() * 4).sum::()) as u32 }, - ACIDBehavior::Use { acid_file_path } => std::fs::metadata(acid_file_path)?.len() as u32, + AcidBehavior::Use { acid_file_path } => std::fs::metadata(acid_file_path)?.len() as u32, }; meta.aci_offset = meta.acid_offset + meta.acid_size; - meta.aci_size = (size_of::() + size_of::() + - sac_encoded_len(&self.service_host) + sac_encoded_len(&self.service_access) + - self.kernel_capabilities.iter().map(|v| v.encode().len() * 4).sum::()) as u32; + meta.aci_size = (size_of::() + size_of::() + + sac_encoded_len(&hosted_services) + sac_encoded_len(&accessed_services) + + kern_caps.iter().map(|v| v.encode().unwrap().len() * 4).sum::()) as u32; - bincode::config().little_endian().serialize_into(&mut file, &meta)?; + bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialize_into(&mut file, &meta)?; match acid_behavior { - ACIDBehavior::Sign { pem_file_path } => { + AcidBehavior::Sign { pem_file_path } => { // Parse PEM file let pkey = get_pkey_from_pem(pem_file_path)?; let mut v = Vec::new(); - write_acid(&mut v, self, &meta)?; + write_acid(&mut v, self, &meta, accessed_services, hosted_services, &kern_caps)?; println!("Signing over {:02x?}", v); // calculate signature. - let mut hash = Sha256::new(); - write_acid(&mut hash, self, &meta)?; - let hash = hash.result(); + let hash = utils::calculate_sha256(v.as_slice())?; println!("Signing over {:02x?}", hash); let sig = pkey.sign(rsa::PaddingScheme::new_pss::(rand::thread_rng()), &hash)?; assert_eq!(sig.len(), 0x100, "Signature of wrong length generated"); file.write_all(&sig)?; - write_acid(&mut file, self, &meta)?; + write_acid(&mut file, self, &meta, accessed_services, hosted_services, &kern_caps)?; }, - ACIDBehavior::Empty => { + AcidBehavior::Empty => { file.write_all(&[0; 0x100])?; - write_acid(&mut file, self, &meta)?; + write_acid(&mut file, self, &meta, accessed_services, hosted_services, &kern_caps)?; } - ACIDBehavior::Use { acid_file_path } => { + AcidBehavior::Use { acid_file_path } => { let mut acid_file = std::fs::File::open(acid_file_path)?; std::io::copy(&mut acid_file, &mut file)?; } } // ACI0 - let mut aci0 = RawAci::default(); + let mut aci0 = Aci::default(); aci0.magic = *b"ACI0"; - aci0.titleid = self.title_id.0; - aci0.fs_access_header_offset = size_of::() as u32; - aci0.fs_access_header_size = size_of::() as u32; - aci0.service_access_control_offset = aci0.fs_access_header_offset + aci0.fs_access_header_size; - aci0.service_access_control_size = (sac_encoded_len(&self.service_host) + sac_encoded_len(&self.service_access)) as u32; + aci0.program_id = self.program_id.0; + aci0.fs_access_control_offset = size_of::() as u32; + aci0.fs_access_control_size = size_of::() as u32; + aci0.service_access_control_offset = aci0.fs_access_control_offset + aci0.fs_access_control_size; + aci0.service_access_control_size = (sac_encoded_len(&hosted_services) + sac_encoded_len(&accessed_services)) as u32; aci0.kernel_access_control_offset = aci0.service_access_control_offset + aci0.service_access_control_size; - aci0.kernel_access_control_size = self.kernel_capabilities.iter().map(|v| v.encode().len() * 4).sum::() as u32; + aci0.kernel_access_control_size = kern_caps.iter().map(|v| v.encode().unwrap().len() * 4).sum::() as u32; - bincode::config().little_endian().serialize_into(&mut file, &aci0)?; + bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialize_into(&mut file, &aci0)?; - let mut fah = RawFileSystemAccessHeader::default(); - fah.version = 1; - fah.padding = [0; 3]; - fah.permissions_bitmask.copy_from_slice(&self.filesystem_access.permissions.0.to_le_bytes()); - fah.data_size = 0x1C; // Always 0x1C - fah.size_of_content_owner_id = 0; - fah.data_size_plus_content_owner_size = 0x1C; - fah.size_of_save_data_owners = 0; + let mut aci0_fac = AciFsAccessControl::default(); + aci0_fac.version = 1; + aci0_fac.padding = [0; 3]; + aci0_fac.fs_access_flags_bitmask.copy_from_slice(&self.fs_access_control.flags.0.to_le_bytes()); + aci0_fac.content_owner_info_offset = 0x1C; // Always 0x1C + aci0_fac.content_owner_info_size = 0; + aci0_fac.save_data_owner_info_offset = 0x1C; + aci0_fac.save_data_owner_info_size = 0; - bincode::config().little_endian().serialize_into(&mut file, &fah)?; + bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialize_into(&mut file, &aci0_fac)?; - for elem in &self.service_access { + for elem in accessed_services { if elem.len() & !7 != 0 || elem.len() == 0 { - return Err(Error::InvalidNpdmValue(format!("service_access.{}", elem).into(), Backtrace::new())) + return Err(Error::InvalidNpdmValue { + error: format!("accessed_services.{}", elem).into(), + backtrace: Backtrace::generate() + }); } file.write_all(&[elem.len() as u8 - 1])?; file.write_all(elem.as_bytes())?; } - for elem in &self.service_host { + for elem in hosted_services { if elem.len() & !7 != 0 || elem.len() == 0 { - return Err(Error::InvalidNpdmValue(format!("service_host.{}", elem).into(), Backtrace::new())) + return Err(Error::InvalidNpdmValue { + error: format!("hosted_services.{}", elem).into(), + backtrace: Backtrace::generate() + }); } file.write_all(&[0x80 | (elem.len() as u8 - 1)])?; file.write_all(elem.as_bytes())?; } - for elem in &self.kernel_capabilities { - let encoded = elem.encode().iter().map(|v| v.to_le_bytes().to_vec()).flatten().collect::>(); + for elem in &kern_caps { + let encoded = elem.encode()?.iter().map(|v| v.to_le_bytes().to_vec()).flatten().collect::>(); file.write_all(&encoded)?; } @@ -360,8 +622,8 @@ fn get_pkey_from_pem(path: &Path) -> Result { Ok(pkey) } -fn write_acid(mut writer: &mut T, npdm: &NpdmJson, meta: &RawMeta) -> Result<(), Error> { - let mut acid = RawAcid::default(); +fn write_acid(mut writer: &mut T, npdm: &Npdm, meta: &Meta, accessed_services: &Vec, hosted_services: &Vec, kern_caps: &Vec) -> Result<(), Error> { + let mut acid = Acid::default(); if let Some(devkey) = &npdm.developer_key { acid.rsa_nca_pubkey.0.copy_from_slice(&hex::decode(devkey).unwrap()); @@ -371,42 +633,58 @@ fn write_acid(mut writer: &mut T, npdm: &NpdmJson, meta: &RawMeta) -> acid.signed_size = meta.acid_size - 0x100; acid.flags = 0u32; - if npdm.is_retail { acid.flags |= 1; } + if npdm.is_production.unwrap_or(true) { + acid.flags |= 1 << 0; + } + if npdm.unqualified_approval.unwrap_or(false) { + acid.flags |= 1 << 1; + } - if npdm.pool_partition & !3 != 0 { - return Err(Error::InvalidNpdmValue("pool_partition".into(), Backtrace::new())); + if npdm.memory_region & !3 != 0 { + return Err(Error::InvalidNpdmValue { + error: format!("memory_region").into(), + backtrace: Backtrace::generate() + }); } - acid.flags |= (npdm.pool_partition & 3) << 2; - // TODO: Unqualified approval. Zefuk is this? + acid.flags |= (npdm.memory_region & 3) << 2; - acid.titleid_range_min = npdm.title_id_range_min.as_ref().unwrap_or(&npdm.title_id).0; - acid.titleid_range_max = npdm.title_id_range_max.as_ref().unwrap_or(&npdm.title_id).0; + acid.program_id_range_min = npdm.program_id_range_min.as_ref().unwrap_or(&npdm.program_id).0; + acid.program_id_range_max = npdm.program_id_range_max.as_ref().unwrap_or(&npdm.program_id).0; - acid.fs_access_control_offset = 0x100 + size_of::() as u32; - acid.fs_access_control_size = size_of::() as u32; + acid.fs_access_control_offset = 0x100 + size_of::() as u32; + acid.fs_access_control_size = size_of::() as u32; acid.service_access_control_offset = acid.fs_access_control_offset + acid.fs_access_control_size; - acid.service_access_control_size = (sac_encoded_len(&npdm.service_host) + sac_encoded_len(&npdm.service_access)) as u32; + acid.service_access_control_size = (sac_encoded_len(hosted_services) + sac_encoded_len(accessed_services)) as u32; acid.kernel_access_control_offset = acid.service_access_control_offset + acid.service_access_control_size; - acid.kernel_access_control_size = npdm.kernel_capabilities.iter().map(|v| v.encode().len() * 4).sum::() as u32; - - let mut fac = RawFileSystemAccessControl::default(); - fac.version = 1; - fac.padding = [0; 3]; - fac.permissions_bitmask.copy_from_slice(&npdm.filesystem_access.permissions.0.to_le_bytes()); - - let mut final_size = bincode::config().little_endian().serialized_size(&acid)?; - assert_eq!(final_size as usize, size_of::(), "Serialized ACID has wrong size"); - bincode::config().little_endian().serialize_into(&mut writer, &acid)?; - - final_size += bincode::config().little_endian().serialized_size(&fac)?; - assert_eq!(final_size as usize, size_of::() + size_of::(), "Serialized FAC has wrong size"); - bincode::config().little_endian().serialize_into(&mut writer, &fac)?; - - for elem in &npdm.service_access { + acid.kernel_access_control_size = kern_caps.iter().map(|v| v.encode().unwrap().len() * 4).sum::() as u32; + + let mut acid_fac = AcidFsAccessControl::default(); + acid_fac.version = 1; + acid_fac.content_owner_id_count = 0; + acid_fac.save_data_owner_id_count = 0; + acid_fac.padding = 0; + acid_fac.fs_access_flags_bitmask.copy_from_slice(&npdm.fs_access_control.flags.0.to_le_bytes()); + acid_fac.content_owner_id_min = 0; + acid_fac.content_owner_id_max = 0; + acid_fac.save_data_owner_id_min = 0; + acid_fac.save_data_owner_id_max = 0; + + let mut final_size = bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialized_size(&acid)?; + assert_eq!(final_size as usize, size_of::(), "Serialized ACID has wrong size"); + bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialize_into(&mut writer, &acid)?; + + final_size += bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialized_size(&acid_fac)?; + assert_eq!(final_size as usize, size_of::() + size_of::(), "Serialized FAC has wrong size"); + bincode::DefaultOptions::new().with_fixint_encoding().allow_trailing_bytes().with_no_limit().with_little_endian().serialize_into(&mut writer, &acid_fac)?; + + for elem in accessed_services { if elem.len() & !7 != 0 || elem.len() == 0 { - return Err(Error::InvalidNpdmValue(format!("service_access.{}", elem).into(), Backtrace::new())) + return Err(Error::InvalidNpdmValue { + error: format!("accessed_services.{}", elem).into(), + backtrace: Backtrace::generate() + }); } final_size += 1; writer.write_all(&[elem.len() as u8 - 1])?; @@ -414,9 +692,12 @@ fn write_acid(mut writer: &mut T, npdm: &NpdmJson, meta: &RawMeta) -> writer.write_all(elem.as_bytes())?; } - for elem in &npdm.service_host { + for elem in hosted_services { if elem.len() & !7 != 0 || elem.len() == 0 { - return Err(Error::InvalidNpdmValue(format!("service_host.{}", elem).into(), Backtrace::new())) + return Err(Error::InvalidNpdmValue { + error: format!("hosted_services.{}", elem).into(), + backtrace: Backtrace::generate() + }); } final_size += 1; writer.write_all(&[0x80 | (elem.len() as u8 - 1)])?; @@ -424,122 +705,18 @@ fn write_acid(mut writer: &mut T, npdm: &NpdmJson, meta: &RawMeta) -> writer.write_all(elem.as_bytes())?; } - assert_eq!(final_size as usize, size_of::() + size_of::() - + sac_encoded_len(&npdm.service_access) + sac_encoded_len(&npdm.service_host), "Serialized SAC has wrong size"); + assert_eq!(final_size as usize, size_of::() + size_of::() + + sac_encoded_len(accessed_services) + sac_encoded_len(hosted_services), "Serialized SAC has wrong size"); - for elem in &npdm.kernel_capabilities { - let encoded = elem.encode().iter().map(|v| v.to_le_bytes().to_vec()).flatten().collect::>(); + for elem in kern_caps { + let encoded = elem.encode()?.iter().map(|v| v.to_le_bytes().to_vec()).flatten().collect::>(); final_size += encoded.len() as u64; writer.write_all(&encoded)?; } - assert_eq!(final_size as usize, size_of::() + size_of::() - + sac_encoded_len(&npdm.service_access) + sac_encoded_len(&npdm.service_host) - + npdm.kernel_capabilities.iter().map(|v| v.encode().len() * 4).sum::(), "Serialized KAC has wrong size"); + assert_eq!(final_size as usize, size_of::() + size_of::() + + sac_encoded_len(accessed_services) + sac_encoded_len(hosted_services) + + kern_caps.iter().map(|v| v.encode().unwrap().len() * 4).sum::(), "Serialized KAC has wrong size"); Ok(()) -} - -#[repr(C)] -#[derive(Default, Clone, Copy, Serialize)] -struct RawFileSystemAccessControl { - version: u8, - padding: [u8; 3], - // Work around broken alignment. It sucks. - permissions_bitmask: [u8; 8], - reserved: [u8; 0x20] -} - -#[repr(C)] -#[derive(Default, Clone, Copy, Serialize)] -struct RawFileSystemAccessHeader { - version: u8, - padding: [u8; 3], - // Work around broken alignment. It sucks. - permissions_bitmask: [u8; 8], - data_size: u32, // Always 0x1C - size_of_content_owner_id: u32, - data_size_plus_content_owner_size: u32, - size_of_save_data_owners: u32, - // TODO: there's more optional stuff afterwards. -} - -#[repr(C)] -#[derive(Default, Clone, Copy, Serialize)] -struct RawMeta { - magic: [u8; 4], - #[doc(hidden)] - reserved4: u32, - reserved8: u32, - mmu_flags: u8, - #[doc(hidden)] - reserved13: u8, - main_thread_prio: u8, - main_thread_core_num: u8, - #[doc(hidden)] - reserved16: u32, - system_resources: u32, - version: u32, - main_thread_stack_size: u32, - title_name: [u8; 16], - product_code: [u8; 16], - #[doc(hidden)] - reserved64: Reserved64, - aci_offset: u32, - aci_size: u32, - acid_offset: u32, - acid_size: u32, -} - -/// Restriced Access Controls, signed by Nintendo. -#[repr(C)] -#[derive(Default, Clone, Copy, Serialize)] -struct RawAcid { - // RSA-2048 Signature starting from `rsa_nca_pubkey` and spanning - // `signed_size` bytes, using a fixed key owned by Nintendo. The pubkey - // part can be found in hactool, `acid_fixed_key_modulus`. - // - // Written separately. - // rsa_acid_sig: SigOrPubKey, // [u8; 0x100], - /// RSA-2048 public key for the second NCA signature - rsa_nca_pubkey: SigOrPubKey, // [u8; 0x100], - /// Magic identifying a valid ACID. Should be `b"ACID"`. - magic: [u8; 4], - signed_size: u32, - #[doc(hidden)] - reserved: u32, - flags: u32, - titleid_range_min: u64, - titleid_range_max: u64, - fs_access_control_offset: u32, - fs_access_control_size: u32, - service_access_control_offset: u32, - service_access_control_size: u32, - kernel_access_control_offset: u32, - kernel_access_control_size: u32, - #[doc(hidden)] - reserved38: u64 -} - -/// Access Control Information. -/// -/// Protected by the NCA signature, which devs control via their pubkey. -#[repr(C)] -#[derive(Default, Clone, Copy, Serialize)] -struct RawAci { - /// Magic identifying a valid ACI. Should be `ACI0`. - magic: [u8; 4], - #[doc(hidden)] - reserved4: [u8; 0xC], - titleid: u64, - #[doc(hidden)] - reserved24: u64, - fs_access_header_offset: u32, - fs_access_header_size: u32, - service_access_control_offset: u32, - service_access_control_size: u32, - kernel_access_control_offset: u32, - kernel_access_control_size: u32, - #[doc(hidden)] - reserved38: u64 } \ No newline at end of file diff --git a/src/format/npdm/fmt.rs b/src/format/npdm/fmt.rs new file mode 100644 index 0000000..352e261 --- /dev/null +++ b/src/format/npdm/fmt.rs @@ -0,0 +1,110 @@ +use crate::format::utils::{Reserved64, SigOrPubKey}; +use serde_derive::Serialize; + +#[repr(C)] +#[derive(Default, Clone, Copy, Serialize)] +pub struct AciFsAccessControl { + pub version: u8, + pub padding: [u8; 3], + pub fs_access_flags_bitmask: [u8; 8], // Work around broken alignment. It sucks. + pub content_owner_info_offset: u32, // Always 0x1C + pub content_owner_info_size: u32, + pub save_data_owner_info_offset: u32, + pub save_data_owner_info_size: u32, + // TODO: more variable stuff afterwards? +} + +#[repr(C, packed)] +#[derive(Default, Clone, Copy, Serialize)] +pub struct AcidFsAccessControl { + pub version: u8, + pub content_owner_id_count: u8, // 5.0.0+ + pub save_data_owner_id_count: u8, // 5.0.0+ + pub padding: u8, + pub fs_access_flags_bitmask: [u8; 8], // Work around broken alignment. It sucks. + pub content_owner_id_min: u64, + pub content_owner_id_max: u64, + pub save_data_owner_id_min: u64, + pub save_data_owner_id_max: u64, + // TODO: more variable stuff afterwards? +} + +#[repr(C)] +#[derive(Default, Clone, Copy, Serialize)] +pub struct Meta { + pub magic: [u8; 4], + pub signature_key_generation: u32, // 9.0.0+ + #[doc(hidden)] + reserved8: u32, + pub flags: u8, + #[doc(hidden)] + reserved13: u8, + pub main_thread_priority: u8, + pub main_thread_core_number: u8, + #[doc(hidden)] + reserved16: u32, + pub system_resource_size: u32, + pub version: u32, + pub main_thread_stack_size: u32, + pub name: [u8; 16], + pub product_code: [u8; 16], + #[doc(hidden)] + reserved64: Reserved64, + pub aci_offset: u32, + pub aci_size: u32, + pub acid_offset: u32, + pub acid_size: u32, +} + +/// Restriced Access Controls, signed by Nintendo. +#[repr(C)] +#[derive(Default, Clone, Copy, Serialize)] +pub struct Acid { + // RSA-2048 Signature starting from `rsa_nca_pubkey` and spanning + // `signed_size` bytes, using a fixed key owned by Nintendo. The pubkey + // part can be found in hactool, `acid_fixed_key_modulus`. + // + // Written separately. + // rsa_acid_sig: SigOrPubKey, // [u8; 0x100], + /// RSA-2048 public key for the second NCA signature + pub rsa_nca_pubkey: SigOrPubKey, // [u8; 0x100], + /// Magic identifying a valid ACID. Should be `b"ACID"`. + pub magic: [u8; 4], + pub signed_size: u32, + #[doc(hidden)] + reserved: u32, + pub flags: u32, + pub program_id_range_min: u64, + pub program_id_range_max: u64, + pub fs_access_control_offset: u32, + pub fs_access_control_size: u32, + pub service_access_control_offset: u32, + pub service_access_control_size: u32, + pub kernel_access_control_offset: u32, + pub kernel_access_control_size: u32, + #[doc(hidden)] + reserved38: u64 +} + +/// Access Control Information. +/// +/// Protected by the NCA signature, which devs control via their pubkey. +#[repr(C)] +#[derive(Default, Clone, Copy, Serialize)] +pub struct Aci { + /// Magic identifying a valid ACI. Should be `ACI0`. + pub magic: [u8; 4], + #[doc(hidden)] + reserved4: [u8; 0xC], + pub program_id: u64, + #[doc(hidden)] + reserved24: u64, + pub fs_access_control_offset: u32, + pub fs_access_control_size: u32, + pub service_access_control_offset: u32, + pub service_access_control_size: u32, + pub kernel_access_control_offset: u32, + pub kernel_access_control_size: u32, + #[doc(hidden)] + reserved38: u64 +} \ No newline at end of file diff --git a/src/format/nxo.rs b/src/format/nxo.rs index 6dd9d21..7b8b9db 100644 --- a/src/format/nxo.rs +++ b/src/format/nxo.rs @@ -1,24 +1,27 @@ +use crate::format::utils::HexOrNum; +use crate::format::{nacp::Nacp, npdm::KernelCapability, romfs::RomFs, utils}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use elf; -use elf::types::{EM_ARM, EM_AARCH64, ProgramHeader, PT_LOAD, SHT_NOTE, Machine}; -use crate::format::{utils, romfs::RomFs, nacp::NacpFile, npdm::KernelCapability}; -use std; +use elf::types::{Machine, ProgramHeader, SectionHeader, EM_AARCH64, EM_ARM, PT_LOAD, SHT_NOTE}; +use serde_derive::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::convert::{TryFrom, TryInto}; use std::fs::File; use std::io::{self, Cursor, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process; -use serde_derive::{Serialize, Deserialize}; -use crate::format::utils::HexOrNum; -use std::convert::TryFrom; // TODO: Support switchbrew's embedded files for NRO -pub struct NxoFile { +pub struct Nxo { file: File, machine: Machine, - text_section: ProgramHeader, - rodata_section: ProgramHeader, - data_section: ProgramHeader, - bss_section: Option, + text_segment: ProgramHeader, + rodata_segment: ProgramHeader, + data_segment: ProgramHeader, + bss_segment: Option, + eh_frame_hdr_section: Option, + dynamic_section: Option, + dynstr_section: Option, + dynsym_section: Option, build_id: Option>, } @@ -34,17 +37,23 @@ pub struct KipNpdm { kernel_capabilities: Vec, } -fn pad_segment(previous_segment_data: &mut Vec, offset: usize, section: &ProgramHeader) { - let section_vaddr = section.vaddr as usize; - let section_supposed_start = previous_segment_data.len() + offset; +fn pad_segment(previous_segment_data: &mut Vec, offset: usize, segment: &ProgramHeader) { + let segment_vaddr = segment.vaddr as usize; + let segment_supposed_start = previous_segment_data.len() + offset; - if section_vaddr > section_supposed_start { + if segment_vaddr > segment_supposed_start { let real_size = previous_segment_data.len(); - previous_segment_data.resize(real_size + (section_vaddr - section_supposed_start), 0); + previous_segment_data.resize(real_size + (segment_vaddr - segment_supposed_start), 0); } } -fn write_build_id(build_id: &Option>, output_writter: &mut T) -> std::io::Result<()> +fn write_build_id( + build_id: &Option>, + output_writter: &mut T, + text_data: &[u8], + rodata: &[u8], + data: &[u8], +) -> std::io::Result<()> where T: Write, { @@ -62,13 +71,61 @@ where output_writter.write_all(&build_id_data[0x10..])?; } None => { - output_writter.write_all(&[0; 0x20])?; + let mut hasher = Sha256::default(); + hasher.update(text_data); + hasher.update(rodata); + hasher.update(data); + + output_writter.write_all(&hasher.finalize().as_slice()[..0x20])?; } } Ok(()) } -impl NxoFile { +fn write_mod0( + nxo_file: &Nxo, + offset: u32, + output_writter: &mut T, + bss_addr: u32, + bss_size: u32, +) -> std::io::Result<()> +where + T: Write, +{ + // MOD magic + output_writter.write_all(b"MOD0")?; + // Dynamic Offset + output_writter.write_u32::( + nxo_file + .dynamic_section + .as_ref() + .map(|v| v.addr as u32 - offset) + .unwrap_or(0), + )?; + + // BSS Start Offset + output_writter.write_u32::(bss_addr - offset)?; + // BSS End Offset + output_writter.write_u32::(bss_addr + bss_size - offset)?; + + let (eh_frame_hdr_addr, eh_frame_hdr_size) = nxo_file + .eh_frame_hdr_section + .as_ref() + .map(|v| (v.addr, v.size)) + .unwrap_or((0, 0)); + // EH Frame Header Start + output_writter.write_u32::(eh_frame_hdr_addr as u32 - offset)?; + // EH Frame Header End + output_writter + .write_u32::(eh_frame_hdr_addr as u32 + eh_frame_hdr_size as u32 - offset)?; + + // RTLD ptr - written at runtime by RTLD + output_writter.write_u32::(0)?; + + Ok(()) +} + +impl Nxo { pub fn from_elf(input: &str) -> std::io::Result { let path = PathBuf::from(input); let mut file = File::open(path)?; @@ -82,22 +139,22 @@ impl NxoFile { let sections = &elf_file.sections; let phdrs: Vec = elf_file.phdrs.to_vec(); - let text_section = phdrs.get(0).unwrap_or_else(|| { + let text_segment = phdrs.get(0).unwrap_or_else(|| { println!("Error: .text not found in ELF file"); process::exit(1) }); - let rodata_section = phdrs.get(1).unwrap_or_else(|| { + let rodata_segment = phdrs.get(1).unwrap_or_else(|| { println!("Error: .rodata not found in ELF file"); process::exit(1) }); - let data_section = phdrs.get(2).unwrap_or_else(|| { + let data_segment = phdrs.get(2).unwrap_or_else(|| { println!("Error: .data not found in ELF file"); process::exit(1) }); - let bss_section = match phdrs.get(3) { + let bss_segment = match phdrs.get(3) { Some(s) => { if s.progtype == PT_LOAD { Some(*s) @@ -107,47 +164,67 @@ impl NxoFile { } None => None, }; - let build_id = sections - .into_iter() - .filter(|&x| { - if x.shdr.shtype == SHT_NOTE { - let mut data = Cursor::new(x.data.clone()); - // Ignore the two first offset of nhdr32 - data.seek(SeekFrom::Start(0x8)).unwrap(); - let n_type = data.read_u32::().unwrap(); - - // BUILD_ID - n_type == 0x3 - } else { - false + + let mut build_id = None; + let mut dynamic_section = None; + let mut dynstr_section = None; + let mut dynsym_section = None; + let mut eh_frame_hdr_section = None; + + for section in sections { + if section.shdr.shtype == SHT_NOTE { + let mut data = Cursor::new(section.data.clone()); + // Ignore the two first offset of nhdr32 + data.seek(SeekFrom::Start(0x8)).unwrap(); + let n_type = data.read_u32::().unwrap(); + + // BUILD_ID + if n_type == 0x3 { + build_id = Some(data.into_inner()); } - }) - .map(|section| section.data.clone()) - .next(); + } + match &*section.shdr.name { + ".dynamic" => dynamic_section = Some(section.shdr.clone()), + ".dynstr" => dynstr_section = Some(section.shdr.clone()), + ".dynsym" => dynsym_section = Some(section.shdr.clone()), + ".eh_frame_hdr" => eh_frame_hdr_section = Some(section.shdr.clone()), + _ => (), + } + } - Ok(NxoFile { + Ok(Nxo { file, machine: elf_file.ehdr.machine, - text_section: *text_section, - rodata_section: *rodata_section, - data_section: *data_section, - bss_section, + text_segment: *text_segment, + rodata_segment: *rodata_segment, + data_segment: *data_segment, + bss_segment, build_id, + dynamic_section, + dynstr_section, + dynsym_section, + eh_frame_hdr_section, }) } - pub fn write_nro(&mut self, output_writter: &mut T, romfs: Option, icon: Option<&str>, nacp: Option) -> std::io::Result<()> + pub fn write_nro( + &mut self, + output_writter: &mut T, + romfs: Option, + icon: Option<&str>, + nacp: Option, + ) -> std::io::Result<()> where T: Write, { - let text_section = &self.text_section; - let rodata_section = &self.rodata_section; - let data_section = &self.data_section; + let text_segment = &self.text_segment; + let rodata_segment = &self.rodata_segment; + let data_segment = &self.data_segment; // Get segments data - let mut code = utils::get_section_data(&mut self.file, text_section)?; - let mut rodata = utils::get_section_data(&mut self.file, rodata_section)?; - let mut data = utils::get_section_data(&mut self.file, data_section)?; + let mut code = utils::get_segment_data(&mut self.file, text_segment)?; + let mut rodata = utils::get_segment_data(&mut self.file, rodata_segment)?; + let mut data = utils::get_segment_data(&mut self.file, data_segment)?; // First correctly align to be conform to the NRO standard utils::add_padding(&mut code, 0xFFF); @@ -155,11 +232,11 @@ impl NxoFile { utils::add_padding(&mut data, 0xFFF); // Finally fix possible misalign of vaddr because NRO only have one base - pad_segment(&mut code, 0, rodata_section); - pad_segment(&mut rodata, code.len(), data_section); + pad_segment(&mut code, 0, rodata_segment); + pad_segment(&mut rodata, code.len(), data_segment); - if let Some(section) = self.bss_section { - pad_segment(&mut data, code.len() + rodata.len(), §ion); + if let Some(segment) = self.bss_segment { + pad_segment(&mut data, code.len() + rodata.len(), &segment); } let total_len: u32 = (code.len() + rodata.len() + data.len()) as u32; @@ -169,11 +246,11 @@ impl NxoFile { // NRO magic output_writter.write_all(b"NRO0")?; - // Unknown + // Version output_writter.write_u32::(0)?; // Total size output_writter.write_u32::(total_len)?; - // Unknown + // Flags output_writter.write_u32::(0)?; // Segment Header (3 entries) @@ -186,59 +263,156 @@ impl NxoFile { file_offset += code_size; // .rodata segment + let rodata_offset = file_offset; let rodata_size = rodata.len() as u32; output_writter.write_u32::(file_offset)?; output_writter.write_u32::(rodata_size)?; file_offset += rodata_size; // .data segment + let data_offset = file_offset; let data_size = data.len() as u32; output_writter.write_u32::(file_offset)?; output_writter.write_u32::(data_size)?; file_offset += data_size; // BSS size - match self.bss_section { - Some(section) => { - if section.vaddr != u64::from(file_offset) { + let (bss_start, bss_size) = match self.bss_segment { + Some(segment) => { + if segment.vaddr != u64::from(file_offset) { println!( "Warning: possible misalign bss\n.bss addr: 0x{:x}\nexpected offset: 0x{:x}", - section.vaddr, file_offset); + segment.vaddr, file_offset); } output_writter - .write_u32::(((section.memsz + 0xFFF) & !0xFFF) as u32)?; + .write_u32::(((segment.memsz + 0xFFF) & !0xFFF) as u32)?; + ( + segment.vaddr as u32, + ((segment.memsz + 0xFFF) & !0xFFF) as u32, + ) } _ => { // in this case the bss is missing or is embedeed in .data. libnx does that, let's support it - let data_section_size = (data_section.filesz + 0xFFF) & !0xFFF; - let bss_size = if data_section.memsz > data_section_size { - (((data_section.memsz - data_section_size) + 0xFFF) & !0xFFF) as u32 + let data_segment_size = (data_segment.filesz + 0xFFF) & !0xFFF; + let bss_size = if data_segment.memsz > data_segment_size { + (((data_segment.memsz - data_segment_size) + 0xFFF) & !0xFFF) as u32 } else { 0 }; output_writter.write_u32::(bss_size)?; + ( + data_segment.vaddr as u32 + data_segment.memsz as u32, + bss_size, + ) } - } - // Unknown + }; + + // Reserved output_writter.write_u32::(0)?; - write_build_id(&self.build_id, output_writter)?; + write_build_id(&self.build_id, output_writter, &code, &rodata, &data)?; - // Padding - output_writter.write_u64::(0)?; - output_writter.write_u64::(0)?; + // TODO: DSO Module Offset (unused) + output_writter.write_u32::(0)?; + // Reserved (unused) + output_writter.write_u32::(0)?; - // Unknown - output_writter.write_u64::(0)?; + // TODO: apiInfo output_writter.write_u64::(0)?; - output_writter.write_all(&code[0x80..])?; - output_writter.write_all(&rodata)?; - output_writter.write_all(&data)?; + // .dynstr section info + output_writter.write_u32::( + self.dynstr_section + .as_ref() + .map(|v| u32::try_from(v.addr).unwrap()) + .unwrap_or(0), + )?; + output_writter.write_u32::( + self.dynstr_section + .as_ref() + .map(|v| u32::try_from(v.size).unwrap()) + .unwrap_or(0), + )?; + + // .dynsym section info + output_writter.write_u32::( + self.dynsym_section + .as_ref() + .map(|v| u32::try_from(v.addr).unwrap()) + .unwrap_or(0), + )?; + output_writter.write_u32::( + self.dynsym_section + .as_ref() + .map(|v| u32::try_from(v.size).unwrap()) + .unwrap_or(0), + )?; + + let module_offset = u32::from_le_bytes(code[4..8].try_into().unwrap()) as usize; + if module_offset != 0 + && !((0x80..code_size).contains(&(module_offset as u32)) + || (rodata_offset..data_offset).contains(&(module_offset as u32)) + || (data_offset..file_offset).contains(&(module_offset as u32))) + { + panic!("Invalid module offset {}", module_offset) + } + + if (0x80..code_size).contains(&(module_offset as u32)) + && &code[module_offset..module_offset + 4] != b"MOD0" + { + output_writter.write_all(&code[0x80..module_offset])?; + write_mod0( + self, + module_offset as u32, + output_writter, + bss_start, + bss_size, + )?; + output_writter.write_all(&code[module_offset + 0x1C..])?; + } else { + output_writter.write_all(&code[0x80..])?; + } + + if (rodata_offset..data_offset).contains(&(module_offset as u32)) + && &rodata + [module_offset - rodata_offset as usize..module_offset - rodata_offset as usize + 4] + != b"MOD0" + { + let rodata_module_offset = module_offset - rodata_offset as usize; + output_writter.write_all(&rodata[..rodata_module_offset])?; + write_mod0( + self, + module_offset as u32, + output_writter, + bss_start, + bss_size, + )?; + output_writter.write_all(&rodata[rodata_module_offset + 0x1C..])?; + } else { + output_writter.write_all(&rodata)?; + } - // Early return if there's no need for an ASET section. + if (data_offset..file_offset).contains(&(module_offset as u32)) + && &data[module_offset - data_offset as usize..module_offset - data_offset as usize + 4] + != b"MOD0" + { + let data_module_offset = module_offset - data_offset as usize; + output_writter.write_all(&data[..data_module_offset])?; + write_mod0( + self, + module_offset as u32, + output_writter, + bss_start, + bss_size, + )?; + output_writter.write_all(&data[data_module_offset + 0x1C..])?; + } else { + output_writter.write_all(&data)?; + } + + // Early return if there's no need for an ASET segment. if let (None, None, None) = (&icon, &romfs, &nacp) { - return Ok(()) + return Ok(()); } // Aset handling @@ -284,7 +458,11 @@ impl NxoFile { }; if let Some(icon) = icon { - assert_eq!(io::copy(&mut File::open(icon)?, output_writter)?, icon_len, "Icon changed while building."); + assert_eq!( + io::copy(&mut File::open(icon)?, output_writter)?, + icon_len, + "Icon changed while building." + ); } if let Some(mut nacp) = nacp { @@ -301,13 +479,13 @@ impl NxoFile { where T: Write, { - let text_section = &self.text_section; - let rodata_section = &self.rodata_section; - let data_section = &self.data_section; + let text_segment = &self.text_segment; + let rodata_segment = &self.rodata_segment; + let data_segment = &self.data_segment; - let mut code = utils::get_section_data(&mut self.file, text_section)?; - let mut rodata = utils::get_section_data(&mut self.file, rodata_section)?; - let mut data = utils::get_section_data(&mut self.file, data_section)?; + let mut code = utils::get_segment_data(&mut self.file, text_segment)?; + let mut rodata = utils::get_segment_data(&mut self.file, rodata_segment)?; + let mut data = utils::get_segment_data(&mut self.file, data_segment)?; // First correctly align to avoid possible compression issues utils::add_padding(&mut code, 0xFFF); @@ -315,15 +493,15 @@ impl NxoFile { utils::add_padding(&mut data, 0xFFF); // Because bss doesn't have it's own segment in NSO, we need to pad .data to the .bss vaddr - if let Some(section) = self.bss_section { - pad_segment(&mut data, data_section.vaddr as usize, §ion); + if let Some(segment) = self.bss_segment { + pad_segment(&mut data, data_segment.vaddr as usize, &segment); } // NSO magic output_writter.write_all(b"NSO0")?; - // Unknown + // Version output_writter.write_u32::(0)?; - // Unknown + // Reserved output_writter.write_u32::(0)?; // Flags, set compression + sum check @@ -337,10 +515,10 @@ impl NxoFile { let compressed_code = utils::compress_lz4(&mut code)?; let compressed_code_size = compressed_code.len() as u32; output_writter.write_u32::(file_offset as u32)?; - output_writter.write_u32::(text_section.vaddr as u32)?; + output_writter.write_u32::(text_segment.vaddr as u32)?; output_writter.write_u32::(code_size as u32)?; - // Module offset (TODO: SUPPORT THAT) + // TODO: Module Name Offset output_writter.write_u32::(0)?; file_offset += compressed_code_size; @@ -350,10 +528,10 @@ impl NxoFile { let compressed_rodata = utils::compress_lz4(&mut rodata)?; let compressed_rodata_size = compressed_rodata.len() as u32; output_writter.write_u32::(file_offset as u32)?; - output_writter.write_u32::(rodata_section.vaddr as u32)?; + output_writter.write_u32::(rodata_segment.vaddr as u32)?; output_writter.write_u32::(rodata_size as u32)?; - // Module file size (TODO: SUPPORT THAT) + // TODO: Module Name Size output_writter.write_u32::(0)?; file_offset += compressed_rodata_size; @@ -364,46 +542,70 @@ impl NxoFile { let compressed_data_size = compressed_data.len() as u32; let uncompressed_data_size = data.len() as u64; output_writter.write_u32::(file_offset as u32)?; - output_writter.write_u32::(data_section.vaddr as u32)?; + output_writter.write_u32::(data_segment.vaddr as u32)?; output_writter.write_u32::(data_size as u32)?; // BSS size - match self.bss_section { - Some(section) => { - let memory_offset = data_section.vaddr + uncompressed_data_size; - if section.vaddr != memory_offset { + match self.bss_segment { + Some(segment) => { + let memory_offset = data_segment.vaddr + uncompressed_data_size; + if segment.vaddr != memory_offset { println!( "Warning: possible misalign bss\n.bss addr: 0x{:x}\nexpected offset: 0x{:x}", - section.vaddr, memory_offset); + segment.vaddr, memory_offset); } // (bss_segment['p_memsz'] + 0xFFF) & ~0xFFF output_writter - .write_u32::(((section.memsz + 0xFFF) & !0xFFF) as u32)?; + .write_u32::(((segment.memsz + 0xFFF) & !0xFFF) as u32)?; } _ => { // in this case the bss is missing or is embedeed in .data. libnx does that, let's support it output_writter - .write_u32::((data_section.memsz - data_section.filesz) as u32)?; + .write_u32::((data_segment.memsz - data_segment.filesz) as u32)?; } } - write_build_id(&self.build_id, output_writter)?; + write_build_id(&self.build_id, output_writter, &code, &rodata, &data)?; // Compressed size output_writter.write_u32::(compressed_code_size)?; output_writter.write_u32::(compressed_rodata_size)?; output_writter.write_u32::(compressed_data_size)?; - // Padding (0x24) - output_writter.write_u64::(0)?; + // Reserved (0x1C) + output_writter.write_u32::(0)?; output_writter.write_u64::(0)?; output_writter.write_u64::(0)?; output_writter.write_u64::(0)?; - output_writter.write_u32::(0)?; - // Unknown - output_writter.write_u64::(0)?; + // TODO: SegmentHeaderRelative for .api_info output_writter.write_u64::(0)?; + // SegmentHeaderRelative for .dynstr + output_writter.write_u32::( + self.dynstr_section + .as_ref() + .map(|v| u32::try_from(v.addr).unwrap()) + .unwrap_or(0), + )?; + output_writter.write_u32::( + self.dynstr_section + .as_ref() + .map(|v| u32::try_from(v.size).unwrap()) + .unwrap_or(0), + )?; + // SegmentHeaderRelative for .dynsym + output_writter.write_u32::( + self.dynsym_section + .as_ref() + .map(|v| u32::try_from(v.addr).unwrap()) + .unwrap_or(0), + )?; + output_writter.write_u32::( + self.dynsym_section + .as_ref() + .map(|v| u32::try_from(v.size).unwrap()) + .unwrap_or(0), + )?; // .text sha256 let text_sum = utils::calculate_sha256(&code)?; @@ -429,7 +631,7 @@ impl NxoFile { T: Write, { output_writer.write_all(b"KIP1")?; - let mut name : Vec = npdm.name.clone().into(); + let mut name: Vec = npdm.name.clone().into(); name.resize(12, 0); output_writer.write_all(&name[..])?; output_writer.write_u64::(npdm.title_id.0)?; // TitleId @@ -441,44 +643,56 @@ impl NxoFile { output_writer.write_u8(flags)?; } else if self.machine == EM_AARCH64 { // Compression enable, Is64Bit, IsAddrSpace32Bit, UseSystemPoolPartition - output_writer.write_u8(0b00111111)?; + output_writer.write_u8(0b0011_1111)?; } else if self.machine == EM_ARM { // Compression enable, UseSystemPoolPartition - output_writer.write_u8(0b00100111)?; + output_writer.write_u8(0b0010_0111)?; } else { unimplemented!("Unknown machine type"); } - let mut section_data = utils::get_section_data(&mut self.file, &self.text_section)?; - let text_data = utils::compress_blz(&mut section_data).unwrap(); - let mut section_data = utils::get_section_data(&mut self.file, &self.rodata_section)?; - let rodata_data = utils::compress_blz(&mut section_data).unwrap(); - let mut section_data = utils::get_section_data(&mut self.file, &self.data_section)?; - let data_data = utils::compress_blz(&mut section_data).unwrap(); - - write_kip_section_header(output_writer, &self.text_section, 0, text_data.len() as u32)?; - write_kip_section_header(output_writer, &self.rodata_section, u32::try_from(npdm.main_thread_stack_size.0).expect("Exected main_thread_stack_size to be an u32"), rodata_data.len() as u32)?; - write_kip_section_header(output_writer, &self.data_section, 0, data_data.len() as u32)?; - - if let Some(section) = self.bss_section { - output_writer.write_u32::(u32::try_from(section.vaddr).expect("BSS vaddr too big"))?; - output_writer.write_u32::(u32::try_from(section.memsz).expect("BSS memsize too big"))?; + let mut segment_data = utils::get_segment_data(&mut self.file, &self.text_segment)?; + let text_data = utils::compress_blz(&mut segment_data).unwrap(); + let mut segment_data = utils::get_segment_data(&mut self.file, &self.rodata_segment)?; + let rodata_data = utils::compress_blz(&mut segment_data).unwrap(); + let mut segment_data = utils::get_segment_data(&mut self.file, &self.data_segment)?; + let data_data = utils::compress_blz(&mut segment_data).unwrap(); + + write_kip_segment_header(output_writer, &self.text_segment, 0, text_data.len() as u32)?; + write_kip_segment_header( + output_writer, + &self.rodata_segment, + u32::try_from(npdm.main_thread_stack_size.0) + .expect("Exected main_thread_stack_size to be an u32"), + rodata_data.len() as u32, + )?; + write_kip_segment_header(output_writer, &self.data_segment, 0, data_data.len() as u32)?; + + if let Some(segment) = self.bss_segment { + output_writer.write_u32::( + u32::try_from(segment.vaddr).expect("BSS vaddr too big"), + )?; + output_writer.write_u32::( + u32::try_from(segment.memsz).expect("BSS memsize too big"), + )?; } else { // in this case the bss is missing or is embedeed in .data. libnx does that, let's support it - let data_section_size = (self.data_section.filesz + 0xFFF) & !0xFFF; - let bss_size = if self.data_section.memsz > data_section_size { - (((self.data_section.memsz - data_section_size) + 0xFFF) & !0xFFF) as u32 + let data_segment_size = (self.data_segment.filesz + 0xFFF) & !0xFFF; + let bss_size = if self.data_segment.memsz > data_segment_size { + (((self.data_segment.memsz - data_segment_size) + 0xFFF) & !0xFFF) as u32 } else { 0 }; - output_writer.write_u32::(u32::try_from(self.data_section.vaddr + data_section_size).unwrap())?; + output_writer.write_u32::( + u32::try_from(self.data_segment.vaddr + data_segment_size).unwrap(), + )?; output_writer.write_u32::(bss_size)?; } output_writer.write_u32::(0)?; output_writer.write_u32::(0)?; // Empty Sections: - for i in 4..6 { + for _ in 4..6 { output_writer.write_u32::(0)?; output_writer.write_u32::(0)?; output_writer.write_u32::(0)?; @@ -486,36 +700,51 @@ impl NxoFile { } // Kernel caps: - let caps = npdm.kernel_capabilities.iter() - .map(|v| v.encode()) + let caps = npdm + .kernel_capabilities + .iter() + .map(|v| v.encode().unwrap()) .flatten() .collect::>(); - assert!(caps.len() < 0x20, "kernel_capabilities should have less than 0x20 entries!"); + assert!( + caps.len() < 0x20, + "kernel_capabilities should have less than 0x20 entries!" + ); unsafe { // Safety: This is safe. I'm just casting a slice of u32 to a slice of u8 // for fuck's sake. - output_writer.write_all(std::slice::from_raw_parts(caps.as_ptr() as *const u8, caps.len() * 4))?; + output_writer.write_all(std::slice::from_raw_parts( + caps.as_ptr() as *const u8, + caps.len() * 4, + ))?; } output_writer.write_all(&vec![0xFF; (0x20 - caps.len()) * 4])?; // Section data - output_writer.write_all(&text_data); - output_writer.write_all(&rodata_data); - output_writer.write_all(&data_data); + output_writer.write_all(&text_data)?; + output_writer.write_all(&rodata_data)?; + output_writer.write_all(&data_data)?; Ok(()) } } -pub fn write_kip_section_header(output_writer: &mut T, section: &ProgramHeader, attributes: u32, compressed_size: u32) -> std::io::Result<()> +pub fn write_kip_segment_header( + output_writer: &mut T, + segment: &ProgramHeader, + attributes: u32, + compressed_size: u32, +) -> std::io::Result<()> where T: Write, { - output_writer.write_u32::(u32::try_from(section.vaddr).expect("vaddr too big"))?; - output_writer.write_u32::(u32::try_from(section.filesz).expect("memsz too big"))?; - output_writer.write_u32::(u32::try_from(compressed_size).expect("Compressed size too big"))?; + output_writer + .write_u32::(u32::try_from(segment.vaddr).expect("vaddr too big"))?; + output_writer + .write_u32::(u32::try_from(segment.filesz).expect("memsz too big"))?; + output_writer.write_u32::(compressed_size)?; output_writer.write_u32::(attributes)?; Ok(()) diff --git a/src/format/pfs0.rs b/src/format/pfs0.rs index e018e44..d718296 100644 --- a/src/format/pfs0.rs +++ b/src/format/pfs0.rs @@ -1,11 +1,11 @@ -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use crate::format::utils; use crate::error::Error; -use crate::utils::{ReadRange, TryClone, align_up}; -use failure::Backtrace; -use std; +use crate::format::utils; +use crate::utils::{align_up, ReadRange, TryClone}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use snafu::Backtrace; +use snafu::GenerateBacktrace; use std::fs::File; -use std::io::{self, Read, Seek, SeekFrom, Write, BufRead}; +use std::io::{self, BufRead, Read, Seek, SeekFrom, Write}; use std::path::PathBuf; pub trait ReadSeek: Read + Seek {} @@ -15,10 +15,10 @@ impl ReadSeek for T {} enum Pfs0Meta { HostPath(PathBuf), SubFile { - file: Box, + file: Box, name: String, - size: u64 - } + size: u64, + }, } impl Pfs0Meta { @@ -56,7 +56,10 @@ impl Pfs0 { let mut magic = [0; 4]; f.read_exact(&mut magic)?; if &magic != b"PFS0" { - return Err(Error::InvalidPfs0("magic is wrong", Backtrace::new())) + return Err(Error::InvalidPfs0 { + error: "magic is wrong", + backtrace: Backtrace::generate(), + }); } let filecount = f.read_u32::()?; @@ -67,7 +70,7 @@ impl Pfs0 { let string_table_offset = 0x10 + filecount as u64 * 0x18; let data_offset = string_table_offset + string_table_size as u64; - for file in 0..filecount { + for _ in 0..filecount { let offset = data_offset + f.read_u64::()?; let size = f.read_u64::()?; let filename_offset = string_table_offset + f.read_u32::()? as u64; @@ -85,12 +88,10 @@ impl Pfs0 { finalfiles.push(Pfs0Meta::SubFile { file: Box::new(ReadRange::new(f.get_ref().try_clone()?, offset, size)), name: filename, - size + size, }); } - Ok(Pfs0 { - files: finalfiles - }) + Ok(Pfs0 { files: finalfiles }) } pub fn write_pfs0(&mut self, output_writter: &mut T) -> std::io::Result<()> @@ -104,13 +105,8 @@ impl Pfs0 { // Header output_writter.write_all(b"PFS0")?; output_writter.write_u32::(file_count)?; - let string_table_size = utils::align( - files - .iter() - .map(|x| x.file_name().len() + 1) - .sum(), - 0x1F, - ); + let string_table_size = + utils::align(files.iter().map(|x| x.file_name().len() + 1).sum(), 0x1F); output_writter.write_u32::(string_table_size as u32)?; output_writter.write_u32::(0)?; @@ -127,10 +123,13 @@ impl Pfs0 { let mut string_offset = 0; let mut data_offset = 0; - for (file_index, file) in files.iter_mut().enumerate().map(|(idx, path)| (idx as u64, path)) { + for (file_index, file) in files + .iter_mut() + .enumerate() + .map(|(idx, path)| (idx as u64, path)) + { // Seek and write file name to string table - output_writter - .seek(SeekFrom::Start(string_table_pos + string_offset))?; + output_writter.seek(SeekFrom::Start(string_table_pos + string_offset))?; println!( "Writing {}... [{}/{}]", @@ -150,25 +149,23 @@ impl Pfs0 { let file_size = host_file.metadata()?.len(); let name = path.file_name().unwrap().to_str().unwrap(); (&mut host_file as &mut dyn ReadSeek, file_size, name) - }, - Pfs0Meta::SubFile { file, name, size, .. } => { - (file as &mut dyn ReadSeek, *size, &**name) } + Pfs0Meta::SubFile { + file, name, size, .. + } => (file as &mut dyn ReadSeek, *size, &**name), }; // Write file entry to the file entry table - output_writter - .seek(SeekFrom::Start(0x10 + (file_index * 0x18)))?; + output_writter.seek(SeekFrom::Start(0x10 + (file_index * 0x18)))?; output_writter.write_u64::(data_offset)?; output_writter.write_u64::(file_size)?; output_writter.write_u64::(string_offset)?; // Write the actual file content - output_writter - .seek(SeekFrom::Start(data_pos + data_offset))?; + output_writter.seek(SeekFrom::Start(data_pos + data_offset))?; - file.seek(SeekFrom::Start(0)); + file.seek(SeekFrom::Start(0))?; let size = io::copy(file, output_writter)?; assert_eq!(size, file_size); @@ -180,15 +177,13 @@ impl Pfs0 { } pub fn files(self) -> impl Iterator> + 'static { - Pfs0FileIterator { - pfs0: self, - } + Pfs0FileIterator { pfs0: self } } } pub struct Pfs0File { name: String, - file: Box + file: Box, } impl Pfs0File { @@ -221,17 +216,11 @@ impl Iterator for Pfs0FileIterator { let name = meta.file_name().into(); let file = match meta { Pfs0Meta::HostPath(path) => { - File::open(path) - .map(|v| Box::new(v) as Box) - }, - Pfs0Meta::SubFile { mut file, .. } => { - file.seek(SeekFrom::Start(0)) - .map(|_| file) + File::open(path).map(|v| Box::new(v) as Box) } + Pfs0Meta::SubFile { mut file, .. } => file.seek(SeekFrom::Start(0)).map(|_| file), }; - Some(file.map(|file| Pfs0File { - name, file - })) + Some(file.map(|file| Pfs0File { name, file })) } else { None } diff --git a/src/format/romfs.rs b/src/format/romfs.rs index 4cc7d0e..838a123 100644 --- a/src/format/romfs.rs +++ b/src/format/romfs.rs @@ -1,12 +1,13 @@ -use std::mem; -use std::rc::{Rc, Weak}; -use std::io::{self, Write, Cursor}; -use std::fs::{self, File}; +use crate::error::Error; +use byteorder::{WriteBytesExt, LE}; +use snafu::Backtrace; +use snafu::GenerateBacktrace; use std::cell::RefCell; +use std::fs::{self, File}; +use std::io::{self, Cursor, Write}; +use std::mem; use std::path::{Path, PathBuf}; -use byteorder::{WriteBytesExt, LE}; -use crate::error::Error; -use failure::Backtrace; +use std::rc::{Rc, Weak}; #[derive(Debug)] struct RomFsDirEntCtx { @@ -58,7 +59,7 @@ struct RomFsDirEntryHdr { child: u32, file: u32, hash: u32, - name_size: u32 + name_size: u32, } #[repr(C)] @@ -69,20 +70,25 @@ struct RomFsFileEntryHdr { offset: u64, size: u64, hash: u32, - name_size: u32 + name_size: u32, } impl RomFsDirEntCtx { #[allow(clippy::new_ret_no_self)] fn new(parent: Weak>, path: PathBuf) -> Rc> { - let filename = path.file_name().expect("Path to terminate properly").to_str().expect("Path to contain non-unicode chars").into(); + let filename = path + .file_name() + .expect("Path to terminate properly") + .to_str() + .expect("Path to contain non-unicode chars") + .into(); Rc::new(RefCell::new(RomFsDirEntCtx { system_path: path, name: filename, entry_offset: 0, parent, child: vec![], - file: vec![] + file: vec![], })) } @@ -96,7 +102,7 @@ impl RomFsDirEntCtx { entry_offset: 0, parent: Weak::new(), child: vec![], - file: vec![] + file: vec![], })); let weak = Rc::downgrade(&root); root.borrow_mut().parent = weak; @@ -115,10 +121,14 @@ fn romfs_get_hash_table_count(mut num_entries: usize) -> usize { } else if num_entries < 19 { num_entries | 1 } else { - while num_entries % 2 == 0 || num_entries % 3 == 0 || - num_entries % 5 == 0 || num_entries % 7 == 0 || - num_entries % 11 == 0 || num_entries % 13 == 0 || - num_entries % 17 == 0 { + while num_entries % 2 == 0 + || num_entries % 3 == 0 + || num_entries % 5 == 0 + || num_entries % 7 == 0 + || num_entries % 11 == 0 + || num_entries % 13 == 0 + || num_entries % 17 == 0 + { num_entries += 1; } num_entries @@ -179,15 +189,24 @@ impl RomFs { })); self.files.push(file_to_add.clone()); parent.borrow_mut().file.push(file_to_add.clone()); - parent.borrow_mut().file.sort_by_key(|v| v.borrow().name.clone()); + parent + .borrow_mut() + .file + .sort_by_key(|v| v.borrow().name.clone()); - self.file_table_size += mem::size_of::() as u64 + align64(file_to_add.borrow().name.len() as u64, 4); + self.file_table_size += mem::size_of::() as u64 + + align64(file_to_add.borrow().name.len() as u64, 4); } else { // Handling a parent component. Find the directory, create if it doesn't exist. if component == "" { continue; } - let new_parent = if let Some(child) = parent.borrow().child.iter().find(|v| v.borrow().name == component) { + let new_parent = if let Some(child) = parent + .borrow() + .child + .iter() + .find(|v| v.borrow().name == component) + { child.clone() } else { // system_path is not used outside from_directory. It's okay if it doesn't @@ -198,13 +217,17 @@ impl RomFs { entry_offset: 0, parent: Rc::downgrade(&parent), child: vec![], - file: vec![] + file: vec![], })); self.dirs.push(child.clone()); parent.borrow_mut().child.push(child.clone()); - parent.borrow_mut().child.sort_by_key(|v| v.borrow().name.clone()); + parent + .borrow_mut() + .child + .sort_by_key(|v| v.borrow().name.clone()); - self.dir_table_size += mem::size_of::() as u64 + align64(child.borrow().name.len() as u64, 4); + self.dir_table_size += mem::size_of::() as u64 + + align64(child.borrow().name.len() as u64, 4); child }; parent = new_parent; @@ -272,32 +295,50 @@ impl RomFs { // Update the context. We want to keep track of the number of directories, and // the size of the dir_table. - ctx.dir_table_size += mem::size_of::() as u64 + align64(new_dir.borrow().name.len() as u64, 4); - + ctx.dir_table_size += mem::size_of::() as u64 + + align64(new_dir.borrow().name.len() as u64, 4); } else if file_type.is_file() { let file = Rc::new(RefCell::new(RomFsFileEntCtx { system_path: entry.path(), - name: entry.path().file_name().expect("Path to terminate properly").to_str().expect("Path to contain non-unicode chars").into(), + name: entry + .path() + .file_name() + .expect("Path to terminate properly") + .to_str() + .expect("Path to contain non-unicode chars") + .into(), entry_offset: 0, offset: 0, size: entry.metadata().map_err(|err| (err, entry.path()))?.len(), - parent: Rc::downgrade(&parent_dir) + parent: Rc::downgrade(&parent_dir), })); ctx.files.push(file.clone()); parent_dir.borrow_mut().file.push(file.clone()); - ctx.file_table_size += mem::size_of::() as u64 + align64(file.borrow().name.len() as u64, 4); - + ctx.file_table_size += mem::size_of::() as u64 + + align64(file.borrow().name.len() as u64, 4); } else if file_type.is_symlink() { - Err(Error::RomFsSymlink(entry.path(), Backtrace::new()))?; + return Err(Error::RomFsSymlink { + error: entry.path(), + backtrace: Backtrace::generate(), + }); } else { - Err(Error::RomFsFiletype(entry.path(), Backtrace::new()))?; + return Err(Error::RomFsFiletype { + error: entry.path(), + backtrace: Backtrace::generate(), + }); } } - parent_dir.borrow_mut().child.sort_by_key(|v| v.borrow().name.clone()); - parent_dir.borrow_mut().file.sort_by_key(|v| v.borrow().name.clone()); + parent_dir + .borrow_mut() + .child + .sort_by_key(|v| v.borrow().name.clone()); + parent_dir + .borrow_mut() + .file + .sort_by_key(|v| v.borrow().name.clone()); } ctx.files.sort_by_key(|v| v.borrow().internal_path()); @@ -309,11 +350,11 @@ impl RomFs { } pub fn len(&self) -> usize { - (align64(ROMFS_FILEPARTITION_OFS + self.file_partition_size, 4) + - romfs_get_hash_table_count(self.dirs.len() * mem::size_of::()) as u64 + - self.dir_table_size + - romfs_get_hash_table_count(self.files.len() * mem::size_of::()) as u64 + - self.file_table_size) as usize + (align64(ROMFS_FILEPARTITION_OFS + self.file_partition_size, 4) + + romfs_get_hash_table_count(self.dirs.len() * mem::size_of::()) as u64 + + self.dir_table_size + + romfs_get_hash_table_count(self.files.len() * mem::size_of::()) as u64 + + self.file_table_size) as usize } fn calculate_offsets(&mut self) { @@ -332,23 +373,26 @@ impl RomFs { // Set the file offset in the file table section. file.borrow_mut().entry_offset = entry_offset; - entry_offset += mem::size_of::() as u32 + align32(file.borrow().name.len() as u32, 4); + entry_offset += mem::size_of::() as u32 + + align32(file.borrow().name.len() as u32, 4); } // Calculate directory offsets. let mut entry_offset = 0; for dir in self.dirs.iter_mut() { dir.borrow_mut().entry_offset = entry_offset; - entry_offset += mem::size_of::() as u32 + align32(dir.borrow().name.len() as u32, 4); + entry_offset += mem::size_of::() as u32 + + align32(dir.borrow().name.len() as u32, 4); } } - pub fn write(&self, to: &mut dyn Write) -> io::Result<()> { const ROMFS_ENTRY_EMPTY: u32 = 0xFF_FF_FF_FF; - let mut dir_hash_table = vec![ROMFS_ENTRY_EMPTY; romfs_get_hash_table_count(self.dirs.len())]; - let mut file_hash_table = vec![ROMFS_ENTRY_EMPTY; romfs_get_hash_table_count(self.files.len())]; + let mut dir_hash_table = + vec![ROMFS_ENTRY_EMPTY; romfs_get_hash_table_count(self.dirs.len())]; + let mut file_hash_table = + vec![ROMFS_ENTRY_EMPTY; romfs_get_hash_table_count(self.files.len())]; let mut dir_table = vec![0u8; self.dir_table_size as usize]; let mut file_table = vec![0u8; self.file_table_size as usize]; @@ -359,7 +403,11 @@ impl RomFs { let file = file.borrow(); let parent = file.parent.upgrade().unwrap(); let parent = parent.borrow(); - let sibling = parent.file.windows(2).find(|window| Rc::ptr_eq(&window[0], orig_file)).map(|window| window[1].borrow().entry_offset); + let sibling = parent + .file + .windows(2) + .find(|window| Rc::ptr_eq(&window[0], orig_file)) + .map(|window| window[1].borrow().entry_offset); let hash = calc_path_hash(parent.entry_offset, &file.name); let mut cursor = Cursor::new(&mut file_table[file.entry_offset as usize..]); @@ -380,14 +428,28 @@ impl RomFs { let dir = dir.borrow(); let parent = dir.parent.upgrade().unwrap(); let parent = parent.borrow(); - let sibling = parent.child.windows(2).find(|window| window[0].borrow().internal_path() == dir.internal_path()).map(|window| window[1].borrow().entry_offset); + let sibling = parent + .child + .windows(2) + .find(|window| window[0].borrow().internal_path() == dir.internal_path()) + .map(|window| window[1].borrow().entry_offset); let hash = calc_path_hash(parent.entry_offset, &dir.name); let mut cursor = Cursor::new(&mut dir_table[dir.entry_offset as usize..]); cursor.write_u32::(parent.entry_offset)?; cursor.write_u32::(sibling.unwrap_or(ROMFS_ENTRY_EMPTY))?; - cursor.write_u32::(dir.child.first().map(|v| v.borrow().entry_offset).unwrap_or(ROMFS_ENTRY_EMPTY))?; - cursor.write_u32::(dir.file.first().map(|v| v.borrow().entry_offset).unwrap_or(ROMFS_ENTRY_EMPTY))?; + cursor.write_u32::( + dir.child + .first() + .map(|v| v.borrow().entry_offset) + .unwrap_or(ROMFS_ENTRY_EMPTY), + )?; + cursor.write_u32::( + dir.file + .first() + .map(|v| v.borrow().entry_offset) + .unwrap_or(ROMFS_ENTRY_EMPTY), + )?; cursor.write_u32::(dir_hash_table[hash as usize % dir_hash_table.len()])?; cursor.write_u32::(dir.name.len() as u32)?; cursor.write_all(dir.name.as_bytes())?; @@ -429,11 +491,14 @@ impl RomFs { to.write_all(&vec![0; (new_cur_ofs - cur_ofs) as usize])?; cur_ofs = new_cur_ofs; - println!("Writing {} to RomFS image...", file.borrow().system_path.to_string_lossy()); + println!( + "Writing {} to RomFs image...", + file.borrow().system_path.to_string_lossy() + ); assert_eq!(file.borrow().offset, cur_ofs - 0x200, "Wrong offset"); let len = io::copy(&mut File::open(&file.borrow().system_path)?, to)?; - assert_eq!(len, file.borrow().size, "File changed while building romfs"); + assert_eq!(len, file.borrow().size, "File changed while building RomFs"); cur_ofs += file.borrow().size; } @@ -443,7 +508,10 @@ impl RomFs { let cur_ofs = new_cur_ofs; // Write dir hash table - assert_eq!(cur_ofs, align64(ROMFS_FILEPARTITION_OFS + self.file_partition_size, 4)); + assert_eq!( + cur_ofs, + align64(ROMFS_FILEPARTITION_OFS + self.file_partition_size, 4) + ); for hash in dir_hash_table { to.write_u32::(hash)?; } diff --git a/src/format/npdm/syscalls.rs b/src/format/svc.rs similarity index 90% rename from src/format/npdm/syscalls.rs rename to src/format/svc.rs index 75fad1e..9f30a50 100644 --- a/src/format/npdm/syscalls.rs +++ b/src/format/svc.rs @@ -1,9 +1,8 @@ -//! Straight from SunriseOS' libkern use serde_derive::{Serialize, Deserialize}; -#[repr(u32)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)] -pub enum SyscallNames { +#[repr(u32)] +pub enum SystemCallId { SetHeapSize = 0x01, SetMemoryPermission = 0x02, SetMemoryAttribute = 0x03, @@ -49,7 +48,7 @@ pub enum SyscallNames { FlushDataCache = 0x2B, MapPhysicalMemory = 0x2C, UnmapPhysicalMemory = 0x2D, - GetFutureThreadInfo = 0x2E, + GetDebugFutureThreadInfo = 0x2E, // GetFutureThreadInfo before 6.0.0 GetLastThreadInfo = 0x2F, GetResourceLimitLimitValue = 0x30, GetResourceLimitCurrentValue = 0x31, @@ -57,14 +56,20 @@ pub enum SyscallNames { GetThreadContext3 = 0x33, WaitForAddress = 0x34, SignalToAddress = 0x35, - DumpInfo = 0x3C, - DumpInfoNew = 0x3D, + SynchronizePreemptionState = 0x36, + GetResourceLimitPeakValue = 0x37, + CreateIoPool = 0x39, + CreateIoRegion = 0x3A, + KernelDebug = 0x3C, // DumpInfo before 4.0.0 + ChangeKernelTraceState = 0x3D, CreateSession = 0x40, AcceptSession = 0x41, ReplyAndReceiveLight = 0x42, ReplyAndReceive = 0x43, ReplyAndReceiveWithUserBuffer = 0x44, CreateEvent = 0x45, + MapIoRegion = 0x46, + UnmapIoRegion = 0x47, MapPhysicalMemoryUnsafe = 0x48, UnmapPhysicalMemoryUnsafe = 0x49, SetUnsafeLimit = 0x4A, @@ -119,11 +124,5 @@ pub enum SyscallNames { GetProcessInfo = 0x7C, CreateResourceLimit = 0x7D, SetResourceLimitLimitValue = 0x7E, - CallSecureMonitor = 0x7F, - - // Sunrise extensions - MapFramebuffer = 0x80, - StartProcessEntrypoint = 0x81, - MapMmioRegion = 0x82, - SetThreadArea = 0x83, + CallSecureMonitor = 0x7F } \ No newline at end of file diff --git a/src/format/utils.rs b/src/format/utils.rs index ee880c3..39180c4 100644 --- a/src/format/utils.rs +++ b/src/format/utils.rs @@ -1,13 +1,10 @@ -use elf; -use lz4; -use std; +use serde::de::{Unexpected, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::ser::{SerializeTuple}; +use sha2::{Digest, Sha256}; use std::fmt; use std::fs::File; use std::io::{Read, Seek, SeekFrom}; -use sha2::{Sha256, Digest}; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::ser::SerializeTuple; -use serde::de::{Visitor, Unexpected}; pub fn align(size: usize, padding: usize) -> usize { ((size as usize) + padding) & !padding @@ -25,7 +22,7 @@ pub fn check_string_or_truncate(string: &mut String, name: &str, size: usize) { } } -pub fn get_section_data( +pub fn get_segment_data( file: &mut File, header: &elf::types::ProgramHeader, ) -> std::io::Result> { @@ -40,7 +37,8 @@ pub fn compress_lz4(uncompressed_data: &mut Vec) -> std::io::Result> } pub fn compress_blz(uncompressed_data: &mut Vec) -> blz_nx::BlzResult> { - let mut compressed_data = vec![0; blz_nx::get_worst_compression_buffer_size(uncompressed_data.len())]; + let mut compressed_data = + vec![0; blz_nx::get_worst_compression_buffer_size(uncompressed_data.len())]; let res = blz_nx::compress_raw(&mut uncompressed_data[..], &mut compressed_data[..])?; compressed_data.resize(res, 0); Ok(compressed_data) @@ -48,11 +46,11 @@ pub fn compress_blz(uncompressed_data: &mut Vec) -> blz_nx::BlzResult std::io::Result> { let mut hasher = Sha256::default(); - hasher.input(data); - Ok(Vec::from(hasher.result().as_slice())) + hasher.update(data); + Ok(Vec::from(hasher.finalize().as_slice())) } -#[derive(Default)] +#[derive(Default, Copy, Clone)] pub struct HexOrNum(pub u64); impl fmt::Debug for HexOrNum { @@ -77,22 +75,24 @@ impl<'de> Deserialize<'de> for HexOrNum { fn visit_u64(self, v: u64) -> Result where - E: serde::de::Error + E: serde::de::Error, { Ok(v) } fn visit_str(self, v: &str) -> Result where - E: serde::de::Error + E: serde::de::Error, { - if v.starts_with("0x") { - u64::from_str_radix(&v[2..], 16).map_err(|_| { - E::invalid_value(Unexpected::Str(v), &"a hex-encoded string") - }) - } else { - Err(E::invalid_value(Unexpected::Str(v), &"a hex-encoded string")) + let hex_str = if v.starts_with("0x") { + &v[2..] } + else { + v + }; + + u64::from_str_radix(hex_str, 16) + .map_err(|_| E::invalid_value(Unexpected::Str(v), &"a hex-encoded string")) } } diff --git a/src/lib.rs b/src/lib.rs index e3e1991..07c78ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ +pub mod error; pub mod format; pub mod pki; -pub mod error; mod utils; diff --git a/src/pki.rs b/src/pki.rs index fbc8d2d..310d19f 100644 --- a/src/pki.rs +++ b/src/pki.rs @@ -1,17 +1,20 @@ -use std::fmt; -use ini::{self, ini::Properties}; -use failure::Backtrace; -use std::fs::File; -use std::io::{self, Write, ErrorKind}; -use std::path::Path; use crate::error::Error; +use aes::cipher::generic_array::GenericArray; +use aes::cipher::BlockCipher; use aes::Aes128; -use cmac::Cmac; +use aes::NewBlockCipher; +use block_modes::cipher::SyncStreamCipher; use cmac::crypto_mac::Mac; -use block_modes::{Ctr128, BlockModeIv, BlockMode}; -use block_modes::block_padding::ZeroPadding; -use aes::block_cipher_trait::generic_array::GenericArray; -use aes::block_cipher_trait::BlockCipher; +use cmac::{Cmac, NewMac}; +use ctr::cipher::stream::NewStreamCipher; +use ctr::Ctr128; +use ini::{self, Properties}; +use snafu::Backtrace; +use snafu::GenerateBacktrace; +use std::fmt; +use std::fs::File; +use std::io::{self, ErrorKind, Write}; +use std::path::Path; struct Aes128Key([u8; 0x10]); struct AesXtsKey([u8; 0x20]); @@ -37,7 +40,7 @@ macro_rules! impl_debug { Ok(()) } } - } + }; } impl_debug!(Aes128Key); @@ -47,31 +50,48 @@ impl_debug!(Keyblob); impl_debug!(Modulus); impl Keyblob { - fn encrypt(&self, key: &Aes128Key, mac_key: &Aes128Key, keyblob_id: usize) -> Result { + fn encrypt( + &self, + key: &Aes128Key, + mac_key: &Aes128Key, + _keyblob_id: usize, + ) -> Result { let mut encrypted_keyblob = [0; 0xB0]; encrypted_keyblob[0x20..].copy_from_slice(&self.0); - let mut crypter = Ctr128::::new_fixkey(GenericArray::from_slice(&key.0), GenericArray::from_slice(&encrypted_keyblob[0x10..0x20])); - crypter.encrypt_nopad(&mut encrypted_keyblob[0x20..])?; + let mut crypter = Ctr128::::new( + GenericArray::from_slice(&key.0), + GenericArray::from_slice(&encrypted_keyblob[0x10..0x20]), + ); + crypter.apply_keystream(&mut encrypted_keyblob[0x20..]); let mut cmac = Cmac::::new_varkey(&mac_key.0[..]).unwrap(); - cmac.input(&encrypted_keyblob[0x10..]); - encrypted_keyblob[..0x10].copy_from_slice(cmac.result().code().as_slice()); + cmac.update(&encrypted_keyblob[0x10..]); + encrypted_keyblob[..0x10].copy_from_slice(cmac.finalize().into_bytes().as_slice()); Ok(EncryptedKeyblob(encrypted_keyblob)) } } impl EncryptedKeyblob { - fn decrypt(&self, key: &Aes128Key, mac_key: &Aes128Key, keyblob_id: usize) -> Result { + fn decrypt( + &self, + key: &Aes128Key, + mac_key: &Aes128Key, + keyblob_id: usize, + ) -> Result { let mut keyblob = [0; 0x90]; keyblob.copy_from_slice(&self.0[0x20..]); let mut cmac = Cmac::::new_varkey(&mac_key.0[..]).unwrap(); - cmac.input(&self.0[0x10..]); - cmac.verify(&self.0[..0x10]).map_err(|err| (keyblob_id, err))?; + cmac.update(&self.0[0x10..]); + cmac.verify(&self.0[..0x10]) + .map_err(|err| (keyblob_id, err))?; - let mut crypter = Ctr128::::new_fixkey(GenericArray::from_slice(&key.0), GenericArray::from_slice(&self.0[0x10..0x20])); - crypter.decrypt_nopad(&mut keyblob)?; + let mut crypter = Ctr128::::new( + GenericArray::from_slice(&key.0), + GenericArray::from_slice(&self.0[0x10..0x20]), + ); + crypter.apply_keystream(&mut keyblob); Ok(Keyblob(keyblob)) } @@ -87,6 +107,15 @@ impl Aes128Key { Ok(Aes128Key(newkey)) } + fn generate_kek(&self, key: &[u8; 0x10]) -> Result { + let mut newkey = *key; + + let crypter = Aes128::new(GenericArray::from_slice(&self.0)); + crypter.encrypt_block(GenericArray::from_mut_slice(&mut newkey)); + + Ok(Aes128Key(newkey)) + } + fn derive_xts_key(&self, source: &[u8; 0x20]) -> Result { let mut newkey = *source; @@ -95,7 +124,6 @@ impl Aes128Key { crypter.decrypt_block(GenericArray::from_mut_slice(&mut newkey[0x10..0x20])); Ok(AesXtsKey(newkey)) - } } @@ -103,14 +131,21 @@ fn key_to_aes(keys: &Properties, name: &str, key: &mut [u8]) -> Result c - b'a' + 10, b'A'..=b'Z' => c - b'A' + 10, b'0'..=b'9' => c - b'0', - c => return Err(Error::Crypto(format!("Key {} contains invalid character {}. Each character should be a hexadecimal digit.", name, c as char), Backtrace::new())) + c => return Err(Error::Crypto { error: format!("Key {} contains invalid character {}. Each character should be a hexadecimal digit.", name, c as char), backtrace: Backtrace::generate()}) }; key[idx / 2] |= c << if idx % 2 == 0 { 4 } else { 0 }; } @@ -120,7 +155,12 @@ fn key_to_aes(keys: &Properties, name: &str, key: &mut [u8]) -> Result Result, Error> { +fn key_to_aes_array( + keys: &Properties, + name: &str, + idx: usize, + key: &mut [u8], +) -> Result, Error> { key_to_aes(keys, &format!("{}_{:02x}", name, idx), key) } @@ -137,23 +177,35 @@ impl OptionExt for Option { } #[derive(Default, Debug)] +#[allow(dead_code)] pub struct Keys { secure_boot_key: Option, tsec_key: Option, + device_key: Option, keyblob_keys: [Option; 0x20], keyblob_mac_keys: [Option; 0x20], encrypted_keyblobs: [Option; 0x20], + mariko_aes_class_keys: [Option; 0xC], + mariko_kek: Option, + mariko_bek: Option, keyblobs: [Option; 0x20], keyblob_key_sources: [Option; 0x20], keyblob_mac_key_source: Option, - tsec_root_key: Option, + tsec_root_kek: Option, + package1_mac_kek: Option, + package1_kek: Option, + tsec_auth_signatures: [Option; 0x20], + tsec_root_key: [Option; 0x20], master_kek_sources: [Option; 0x20], + mariko_master_kek_sources: [Option; 0x20], master_keks: [Option; 0x20], master_key_source: Option, master_keys: [Option; 0x20], + package1_mac_keys: [Option; 0x20], package1_keys: [Option; 0x20], package2_keys: [Option; 0x20], package2_key_source: Option, + per_console_key_source: Option, aes_kek_generation_source: Option, aes_key_generation_source: Option, key_area_key_application_source: Option, @@ -172,96 +224,195 @@ pub struct Keys { key_area_key_application: [Option; 0x20], key_area_key_ocean: [Option; 0x20], key_area_key_system: [Option; 0x20], + xci_header_key: Option, + save_mac_key: Option, sd_card_save_key: Option, sd_card_nca_key: Option, - nca_hdr_fixed_key_modulus: Option, - acid_fixed_key_modulus: Option, + nca_hdr_fixed_key_modulus: [Option; 2], + acid_fixed_key_modulus: [Option; 2], package2_fixed_key_modulus: Option, } macro_rules! make_key_macros_write { - ($self:ident, $w:ident) => { + ($d:tt, $self:ident, $w:ident, $show_console_unique:expr, $minimal:expr) => { macro_rules! single_key { - ($keyname:tt) => { - if let Some(key) = &$self.$keyname { - writeln!($w, "{} = {}", stringify!($keyname), key)?; + ($keyname:tt, $doc:expr, $console_unique:expr, [$d ($parent:expr),*]) => { + if $show_console_unique || !$console_unique { + #[allow(unused_mut)] + for key in &$self.$keyname { + if $minimal { + let mut count = 0; + let mut total = 0; + $d ( + total += 1; + if $parent.is_some() { + count += 1; + } + )* + if count == total && total != 0 { + continue; + } + } + for line in $doc.split('\n') { + writeln!($w, "; {}", line)?; + } + writeln!($w, "{} = {}", stringify!($keyname), key)?; + } } - } + }; } macro_rules! single_key_xts { - ($keyname:tt) => { - if let Some(key) = &$self.$keyname { - writeln!($w, "{} = {}", stringify!($keyname), key)?; + ($keyname:tt, $doc:expr, $console_unique:expr, [$d ($parent:expr),*]) => { + if $show_console_unique || !$console_unique { + #[allow(unused_mut)] + for key in &$self.$keyname { + if $minimal { + let mut count = 0; + let mut total = 0; + $d ( + total += 1; + if $parent.is_some() { + count += 1; + } + )* + if count == total && total != 0 { + continue; + //println!("Skipping {}", stringify!($keyname)); + } else if total != 0 { + //println!("Can't skip {}, need {}, have {}", stringify!($keyname), total, count); + } + } + for line in $doc.split('\n') { + writeln!($w, "; {}", line)?; + } + writeln!($w, "{} = {}", stringify!($keyname), key)?; + } } - } + }; } macro_rules! multi_key { - ($keyname:tt) => { - for (idx, v) in $self.$keyname.iter().enumerate() { - if let Some(key) = v { - // remove trailing s - let mut name = String::from(stringify!($keyname)); - if name.bytes().last() == Some(b's') { - name.pop(); + ($keyname:tt, $doc:expr, $console_unique:expr, $idx:ident => $d ([$d ($parent:expr),*]),*) => { + let mut first = true; + if $show_console_unique || !$console_unique { + #[allow(unused_mut)] + for ($idx, v) in $self.$keyname.iter().enumerate() { + if $minimal { + $d ( + let mut count = 0; + let mut total = 0; + $d ( + total += 1; + if $parent.is_some() { + count += 1; + } + )* + if count == total && total != 0 { + continue; + } + )* } - writeln!($w, "{}_{:02x} = {}", name, idx, key)?; + if let Some(key) = v { + // remove trailing s + let mut name = String::from(stringify!($keyname)); + if name.bytes().last() == Some(b's') { + name.pop(); + } + if first { + for line in $doc.split('\n') { + writeln!($w, "; {}", line)?; + } + first = false; + } + writeln!($w, "{}_{:02x} = {}", name, $idx, key)?; + } + } + if !first { + writeln!($w)?; } } - } + }; } macro_rules! multi_keyblob { - ($keyname:tt) => { - for (idx, v) in $self.$keyname.iter().enumerate() { - if let Some(key) = v { - // remove trailing s - let mut name = String::from(stringify!($keyname)); - if name.bytes().last() == Some(b's') { - name.pop(); + ($keyname:tt, $doc:expr, $console_unique:expr) => { + if $show_console_unique || !$console_unique { + let mut first = true; + for (idx, v) in $self.$keyname.iter().enumerate() { + if let Some(key) = v { + // remove trailing s + let mut name = String::from(stringify!($keyname)); + if name.bytes().last() == Some(b's') { + name.pop(); + } + if first { + for line in $doc.split('\n') { + writeln!($w, "; {}", line)?; + } + first = false; + } + writeln!($w, "{}_{:02x} = {}", name, idx, key)?; } - writeln!($w, "{}_{:02x} = {}", name, idx, key)?; + } + if !first { + writeln!($w)?; } } - } + }; } macro_rules! multi_encrypted_keyblob { - ($keyname:tt) => { - for (idx, v) in $self.$keyname.iter().enumerate() { - if let Some(key) = v { - // remove trailing s - let mut name = String::from(stringify!($keyname)); - if name.bytes().last() == Some(b's') { - name.pop(); + ($keyname:tt, $doc:expr, $console_unique:expr) => { + if $show_console_unique || !$console_unique { + let mut first = true; + for (idx, v) in $self.$keyname.iter().enumerate() { + if let Some(key) = v { + // remove trailing s + let mut name = String::from(stringify!($keyname)); + if name.bytes().last() == Some(b's') { + name.pop(); + } + if first { + for line in $doc.split('\n') { + writeln!($w, "; {}", line)?; + } + first = false; + } + writeln!($w, "{}_{:02x} = {}", name, idx, key)?; } - writeln!($w, "{}_{:02x} = {}", name, idx, key)?; + } + if !first { + writeln!($w)?; } } - } + }; } - } + }; } macro_rules! make_key_macros { - ($self:ident, $section:ident) => { + ($d:tt, $self:ident, $section:ident) => { macro_rules! single_key { - ($keyname:tt) => { + ($keyname:tt, $doc:expr, $console_unique:expr, [$d ($parent:expr),*]) => { let mut key = [0; 0x10]; - $self.$keyname.or_in(key_to_aes($section, stringify!($keyname), &mut key)?.map(|()| Aes128Key(key))); - } + $self.$keyname.or_in( + key_to_aes($section, stringify!($keyname), &mut key)?.map(|()| Aes128Key(key)), + ); + }; } macro_rules! single_key_xts { - ($keyname:tt) => { + ($keyname:tt, $doc:expr, $console_unique:expr, [$d ($parent:expr),*]) => { let mut key = [0; 0x20]; - $self.$keyname.or_in(key_to_aes($section, stringify!($keyname), &mut key)?.map(|()| AesXtsKey(key))); - } + $self.$keyname.or_in( + key_to_aes($section, stringify!($keyname), &mut key)?.map(|()| AesXtsKey(key)), + ); + }; } macro_rules! multi_key { - ($keyname:tt) => { + ($keyname:tt, $doc:expr, $console_unique:expr, $idx:ident => $d ([$d ($parent:expr),*]),*) => { for (idx, v) in $self.$keyname.iter_mut().enumerate() { let mut key = [0; 0x10]; // remove trailing s @@ -269,13 +420,15 @@ macro_rules! make_key_macros { if name.bytes().last() == Some(b's') { name.pop(); } - v.or_in(key_to_aes_array($section, &name, idx, &mut key)?.map(|()| Aes128Key(key))); + v.or_in( + key_to_aes_array($section, &name, idx, &mut key)?.map(|()| Aes128Key(key)), + ); } - } + }; } macro_rules! multi_keyblob { - ($keyname:tt) => { + ($keyname:tt, $doc:expr, $console_unique:expr) => { for (idx, v) in $self.$keyname.iter_mut().enumerate() { let mut key = [0; 0x90]; // remove trailing s @@ -283,13 +436,15 @@ macro_rules! make_key_macros { if name.bytes().last() == Some(b's') { name.pop(); } - v.or_in(key_to_aes_array($section, &name, idx, &mut key)?.map(|()| Keyblob(key))); + v.or_in( + key_to_aes_array($section, &name, idx, &mut key)?.map(|()| Keyblob(key)), + ); } - } + }; } macro_rules! multi_encrypted_keyblob { - ($keyname:tt) => { + ($keyname:tt, $doc:expr, $console_unique:expr) => { for (idx, v) in $self.$keyname.iter_mut().enumerate() { let mut key = [0; 0xB0]; // remove trailing s @@ -297,14 +452,141 @@ macro_rules! make_key_macros { if name.bytes().last() == Some(b's') { name.pop(); } - v.or_in(key_to_aes_array($section, &name, idx, &mut key)?.map(|()| EncryptedKeyblob(key))); + v.or_in( + key_to_aes_array($section, &name, idx, &mut key)? + .map(|()| EncryptedKeyblob(key)), + ); } - } + }; } + }; +} + +macro_rules! keys { + ($self:ident) => { + single_key!(secure_boot_key, "Dumpable using Fusee-Gelee and biskeydump. +Secure boot key of the console associated with given BOOT0. +Useful to derive master_key and package1_key from keyblobs. +NOTE: CONSOLE UNIQUE!", true, []); + single_key!(tsec_key, "Dumpable using Fusee-Gelee and biskeydump. +TSEC key of the console associated with given BOOT0. +Useful to derive master_key and package1_key from keyblobs. +NOTE: CONSOLE UNIQUE!", true, []); + single_key!(device_key, "Device key used to derive some FS keys. +Derived from per_console_key_source and keyblob_key_00 +NOTE: CONSOLE UNIQUE.", true, [$self.keyblob_keys[0], $self.per_console_key_source]); + single_key!(tsec_root_kek, "Used to generate TSEC root keys. +Can be found using [magic hax] on the TSEC.", false, []); + single_key!(package1_mac_kek, "Used to generate package1 validation keys.", false, []); + single_key!(package1_kek, "Used to generate package1 keys.", false, []); + + multi_key!(tsec_auth_signatures, "Auth signatures, seeds for TSEC Root Key, Package1 MAC KEK and Package1 Key on 6.2.0+.", false, i => []); + + multi_key!(tsec_root_key, "Key for master kek decryption, from TSEC firmware on 6.2.0+. +Can be dumped using [magic hax] on the TSEC. +Can be derived from tsec_root_kek and tsec_auth_signatures.", false, i => [$self.tsec_auth_signatures[i], $self.tsec_root_kek]); + + single_key!(keyblob_mac_key_source, "Seed for keyblob MAC key derivation.", false, []); + multi_key!(keyblob_key_sources, "Seeds for keyblob keys.", false, i => []); + + multi_key!(keyblob_keys, "Actual keys used to decrypt keyblobs. NOTE: CONSOLE UNIQUE.", true, i => [$self.keyblob_key_sources[i], $self.tsec_key, $self.secure_boot_key]); + + multi_key!(keyblob_mac_keys, "Keys used to validate keyblobs. NOTE: CONSOLE UNIQUE.", true, i => [$self.keyblob_keys[i], $self.keyblob_mac_key_source]); + + multi_encrypted_keyblob!(encrypted_keyblobs, "Actual encrypted keyblobs (EKS). NOTE: CONSOLE UNIQUE.", true); + + multi_keyblob!(keyblobs, "Actual decrypted keyblobs (EKS).", false); + + multi_key!(master_kek_sources, "Seeds for firmware master keks.", false, i => []); + + single_key!(mariko_kek, "Key Encryption Key for mariko.", false, []); + single_key!(mariko_bek, "Boot Encryption Key for mariko.", false, []); + multi_key!(mariko_aes_class_keys, "AES Class Keys set by mariko bootrom.", false, i => []); + multi_key!(mariko_master_kek_sources, "Seeds for firmware master keks (Mariko).", false, i => []); + multi_key!(master_keks, "Firmware master keks, stored in keyblob prior to 6.2.0.", false, i => + [$self.keyblobs[i]], + [if i >= 6 { &$self.tsec_root_key[i - 6] } else { &None }, $self.master_kek_sources[i]], + [$self.mariko_kek, $self.mariko_master_kek_sources[i]] + ); + single_key!(master_key_source, "Seed for master key derivation.", false, []); + multi_key!(master_keys, "Firmware master keys.", false, i => [$self.master_key_source, $self.master_keks[i]]); + multi_key!(package1_keys, "Package1 keys.", false, i => + [$self.keyblobs[i]], + [$self.package1_kek, if i >= 6 { &$self.tsec_auth_signatures[i - 6] } else { &None }] + ); + multi_key!(package1_mac_keys, "Package1 MAC Keys.", false, i => [$self.package1_mac_kek, if i >= 6 { &$self.tsec_auth_signatures[i - 6] } else { &None }]); + single_key!(package2_key_source, "Seed for Package2 key.", false, []); + multi_key!(package2_keys, "Package2 keys.", false, i => [$self.master_keys[i], $self.package2_key_source]); + single_key!(per_console_key_source, "Seed for Device key.", false, []); + single_key!(aes_kek_generation_source, "Seed for GenerateAesKek, usecase + generation 0.", false, []); + single_key!(aes_key_generation_source, "Seed for GenerateAesKek.", false, []); + single_key!(titlekek_source, "Seed for titlekeks.", false, []); + multi_key!(titlekeks, "Title key encryption keys.", false, i => [$self.master_keys[i], $self.titlekek_source]); + single_key!(key_area_key_application_source, "Seed for kaek 0.", false, []); + single_key!(key_area_key_ocean_source, "Seed for kaek 1.", false, []); + single_key!(key_area_key_system_source, "Seed for kaek 2.", false, []); + single_key!(sd_card_kek_source, "Seed for SD card kek.", false, []); + single_key_xts!(sd_card_save_key_source, "Seed for SD card save encryption key.", false, []); + single_key_xts!(sd_card_nca_key_source, "Seed for SD card NCA encryption key.", false, []); + single_key!(save_mac_kek_source, "Seed for save kek.", false, []); + single_key!(save_mac_key_source, "Seed for save key.", false, []); + // Derived from save_mac_key_source, save_mac_kek_source and device_key + single_key!(save_mac_key, "Key used to sign savedata. NOTE: CONSOLE UNIQUE!", true, [ + $self.device_key, + $self.save_mac_kek_source, + $self.aes_kek_generation_source, + $self.save_mac_key_source + ]); + + single_key!(header_kek_source, "Seed for header kek.", false, []); + single_key_xts!(header_key_source, "Seed for NCA header key.", false, []); + single_key_xts!(header_key, "NCA header key.", false, [ + $self.master_keys[0], + $self.key_area_key_application_source, + $self.aes_kek_generation_source, + $self.aes_key_generation_source + ]); + multi_key!(key_area_key_application, "Key area encryption key 0.", true, i => [ + $self.master_keys[i], + $self.key_area_key_application_source, + $self.aes_kek_generation_source, + $self.aes_key_generation_source + ]); + multi_key!(key_area_key_ocean, "Key area encryption key 1.", true, i => [ + $self.master_keys[i], + $self.key_area_key_ocean_source, + $self.aes_kek_generation_source, + $self.aes_key_generation_source + ]); + multi_key!(key_area_key_system, "Key area encryption key 2.", true, i => [ + $self.master_keys[i], + $self.key_area_key_system_source, + $self.aes_kek_generation_source, + $self.aes_key_generation_source + ]); + single_key_xts!(sd_card_save_key, "Encryption key for SD card save.", true, [ + $self.master_keys[0], + $self.sd_card_save_key_source, + $self.aes_kek_generation_source, + $self.aes_key_generation_source + ]); + single_key_xts!(sd_card_nca_key, "Encryption key for SD card NCA.", true, [ + $self.master_keys[0], + $self.sd_card_nca_key_source, + $self.aes_kek_generation_source, + $self.aes_key_generation_source + ]); + + single_key!(xci_header_key, "Key for XCI partially encrypted header.", false, []); } } -fn generate_kek(src: &Aes128Key, master_key: &Aes128Key, kek_seed: &Aes128Key, key_seed: &Aes128Key) -> Result { +fn generate_kek( + src: &Aes128Key, + master_key: &Aes128Key, + kek_seed: &Aes128Key, + key_seed: &Aes128Key, +) -> Result { let kek = master_key.derive_key(&kek_seed.0)?; let src_kek = kek.derive_key(&src.0)?; src_kek.derive_key(&key_seed.0) @@ -312,22 +594,35 @@ fn generate_kek(src: &Aes128Key, master_key: &Aes128Key, kek_seed: &Aes128Key, k impl Keys { #[allow(clippy::new_ret_no_self)] - fn new(key_path: Option<&Path>, default_key_name: &Path, modulus: (Modulus, Modulus, Modulus)) -> Result { + fn new( + key_path: Option<&Path>, + default_key_name: &Path, + modulus: ([Modulus; 2], [Modulus; 2], Modulus), + ) -> Result { let (modulus0, modulus1, modulus2) = modulus; + let [modulus00, modulus01] = modulus0; + let [modulus10, modulus11] = modulus1; let mut keys = Keys { - nca_hdr_fixed_key_modulus: Some(modulus0), - acid_fixed_key_modulus: Some(modulus1), + nca_hdr_fixed_key_modulus: [Some(modulus00), Some(modulus01)], + acid_fixed_key_modulus: [Some(modulus10), Some(modulus11)], package2_fixed_key_modulus: Some(modulus2), ..Default::default() }; - let paths = if let Some(key_path) = key_path { vec![Some(key_path.into())] } else { vec![ - dirs::config_dir().map(|mut v| { v.push("switch"); v.push(default_key_name); v }), - dirs::home_dir().map(|mut v| { v.push(".switch"); v.push(default_key_name); v }), + dirs_next::config_dir().map(|mut v| { + v.push("switch"); + v.push(default_key_name); + v + }), + dirs_next::home_dir().map(|mut v| { + v.push(".switch"); + v.push(default_key_name); + v + }), ] }; @@ -339,7 +634,7 @@ impl Keys { keys.read_from_ini(file)?; succeed = true; break; - }, + } Err(ref err) if err.kind() == ErrorKind::NotFound => (), Err(err) => println!("Failed to open {}: {}", path.display(), err), } @@ -347,7 +642,7 @@ impl Keys { } if !succeed { - Err(io::Error::new(ErrorKind::NotFound, "Keyfile not found."))?; + return Err(io::Error::new(ErrorKind::NotFound, "Keyfile not found.").into()); } keys.derive_keys()?; @@ -355,178 +650,353 @@ impl Keys { } pub fn new_retail(key_path: Option<&Path>) -> Result { - Keys::new(key_path, Path::new("prod.keys"), ( - /* nca_hdr_fixed_key_modulus: */ Modulus([ - 0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F, - 0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58, - 0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16, - 0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2, - 0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF, - 0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67, - 0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26, - 0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C, - 0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE, - 0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86, - 0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35, - 0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B, - 0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29, - 0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40, - 0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81, - 0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03 - ]), - /* acid_fixed_key_modulus: */ Modulus([ - 0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D, - 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, - 0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, - 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20, - 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, - 0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, - 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4, - 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, - 0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, - 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53, - 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, - 0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, - 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A, - 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, - 0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, - 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD - ]), - /* package2_fixed_key_modulus: */ Modulus([ - 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, - 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, - 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, - 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, - 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, - 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, - 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, - 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, - 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, - 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, - 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, - 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, - 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, - 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, - 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, - 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F - ]) - )) + Keys::new( + key_path, + Path::new("prod.keys"), + ( + /* nca_hdr_fixed_key_modulus: */ + [ + Modulus([ + 0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, + 0x61, 0x1D, 0x77, 0x2F, 0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, + 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58, 0x34, 0xB0, 0x05, 0xA3, + 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16, + 0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, + 0xF6, 0x00, 0x07, 0xB2, 0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, + 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF, 0xA7, 0x82, 0xB5, 0xCF, + 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67, + 0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, + 0x8A, 0x68, 0xC9, 0x26, 0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, + 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C, 0xCE, 0x02, 0x6F, 0x16, + 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE, + 0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, + 0x43, 0xF3, 0xFC, 0x86, 0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, + 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35, 0xF2, 0xF6, 0x56, 0x56, + 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B, + 0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, + 0xFD, 0x13, 0x63, 0x29, 0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, + 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40, 0x7E, 0x32, 0x8C, 0x27, + 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81, + 0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, + 0x9D, 0xE4, 0xF4, 0x03, + ]), + Modulus([ + 0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, + 0x29, 0xB1, 0xFF, 0xB6, 0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, + 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48, 0x70, 0x58, 0x33, 0xA2, + 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24, + 0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, + 0xF9, 0x7D, 0x45, 0x16, 0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, + 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6, 0x3F, 0x91, 0xAE, 0x44, + 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2, + 0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, + 0x15, 0x8A, 0x2F, 0x94, 0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, + 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B, 0x17, 0x5F, 0x58, 0xF6, + 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37, + 0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, + 0x09, 0xCC, 0x4C, 0xF4, 0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, + 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76, 0x1E, 0xE3, 0x6C, 0x8C, + 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9, + 0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, + 0x9E, 0xB0, 0x15, 0xB2, 0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, + 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B, 0xB9, 0x22, 0x96, 0x4B, + 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA, + 0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, + 0x98, 0x68, 0x5A, 0xAD, + ]), + ], + /* acid_fixed_key_modulus: */ + [ + Modulus([ + 0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, + 0x7B, 0xAD, 0xFE, 0x7D, 0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, + 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50, 0x2E, 0xFC, 0x9D, 0x94, + 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57, + 0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, + 0x1B, 0xFE, 0x30, 0x20, 0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, + 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21, 0xE7, 0x8B, 0xE9, 0x75, + 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2, + 0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, + 0x5E, 0x01, 0x30, 0xA4, 0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, + 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE, 0x1C, 0x82, 0x78, 0xB5, + 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10, + 0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, + 0x73, 0xB0, 0x31, 0x53, 0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, + 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD, 0x40, 0x9A, 0xA2, 0x8B, + 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08, + 0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, + 0x69, 0x2B, 0x31, 0x6A, 0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, + 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA, 0x37, 0xEA, 0xE8, 0x1E, + 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B, + 0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, + 0x7D, 0x35, 0x22, 0xFD, + ]), + Modulus([ + 0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, + 0x96, 0x5A, 0xBF, 0x90, 0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, + 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5, 0x20, 0x65, 0xB3, 0xB9, + 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF, + 0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, + 0x0C, 0xB0, 0x4E, 0x8E, 0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, + 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A, 0x9A, 0x7D, 0x41, 0x56, + 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D, + 0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, + 0xD0, 0x1D, 0x60, 0x01, 0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, + 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8, 0xFE, 0x7D, 0x50, 0xCB, + 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43, + 0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, + 0xF6, 0xF3, 0xE1, 0xF6, 0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, + 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4, 0x0B, 0xB8, 0x3D, 0x7A, + 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C, + 0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, + 0xD4, 0x02, 0xE0, 0x3D, 0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, + 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83, 0xC3, 0x43, 0x03, 0xEE, + 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03, + 0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, + 0x26, 0x4F, 0x49, 0x03, + ]), + ], + /* package2_fixed_key_modulus: */ + Modulus([ + 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, + 0x20, 0x69, 0x59, 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, + 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, + 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, 0x6C, 0x39, 0x7F, 0x41, + 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, 0x8B, + 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, + 0xD3, 0x48, 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, + 0xBD, 0xC3, 0x29, 0x90, 0xBE, 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, + 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, 0x9A, 0x42, 0x9D, 0x09, 0x8B, + 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, 0xD7, 0xE1, + 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, + 0x0B, 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, + 0x9E, 0xF4, 0x46, 0xA3, 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, + 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, + 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, 0xFD, 0xFA, 0x4C, + 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, + 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, + 0x9A, 0x3F, 0xF2, 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, + 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, + 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F, + ]), + ), + ) } + pub fn new_dev(key_path: Option<&Path>) -> Result { + Keys::new( + key_path, + Path::new("dev.keys"), + ( + /* nca_hdr_fixed_key_modulus: */ + [ + Modulus([ + 0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, + 0xB3, 0x04, 0xA8, 0xA4, 0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, + 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49, 0x85, 0xE5, 0x8A, 0x9B, + 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C, + 0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, + 0x0A, 0x50, 0xF2, 0x2B, 0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, + 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4, 0x49, 0x2F, 0xCF, 0x03, + 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47, + 0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, + 0x0E, 0x17, 0x3F, 0xFE, 0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, + 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D, 0xA1, 0xD9, 0x66, 0x20, + 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E, + 0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, + 0x75, 0x76, 0xBF, 0xB0, 0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, + 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D, 0x7A, 0xA4, 0x21, 0x55, + 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E, + 0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, + 0xDE, 0xD8, 0x1B, 0x4B, 0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, + 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E, 0xEA, 0x7F, 0x86, 0xF3, + 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62, + 0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, + 0x8F, 0xB9, 0x4A, 0xA9, + ]), + Modulus([ + 0x9A, 0xBC, 0x88, 0xBD, 0x0A, 0xBE, 0xD7, 0x0C, 0x9B, 0x42, 0x75, 0x65, + 0x38, 0x5E, 0xD1, 0x01, 0xCD, 0x12, 0xAE, 0xEA, 0xE9, 0x4B, 0xDB, 0xB4, + 0x5E, 0x36, 0x10, 0x96, 0xDA, 0x3D, 0x2E, 0x66, 0xD3, 0x99, 0x13, 0x8A, + 0xBE, 0x67, 0x41, 0xC8, 0x93, 0xD9, 0x3E, 0x42, 0xCE, 0x34, 0xCE, 0x96, + 0xFA, 0x0B, 0x23, 0xCC, 0x2C, 0xDF, 0x07, 0x3F, 0x3B, 0x24, 0x4B, 0x12, + 0x67, 0x3A, 0x29, 0x36, 0xA3, 0xAA, 0x06, 0xF0, 0x65, 0xA5, 0x85, 0xBA, + 0xFD, 0x12, 0xEC, 0xF1, 0x60, 0x67, 0xF0, 0x8F, 0xD3, 0x5B, 0x01, 0x1B, + 0x1E, 0x84, 0xA3, 0x5C, 0x65, 0x36, 0xF9, 0x23, 0x7E, 0xF3, 0x26, 0x38, + 0x64, 0x98, 0xBA, 0xE4, 0x19, 0x91, 0x4C, 0x02, 0xCF, 0xC9, 0x6D, 0x86, + 0xEC, 0x1D, 0x41, 0x69, 0xDD, 0x56, 0xEA, 0x5C, 0xA3, 0x2A, 0x58, 0xB4, + 0x39, 0xCC, 0x40, 0x31, 0xFD, 0xFB, 0x42, 0x74, 0xF8, 0xEC, 0xEA, 0x00, + 0xF0, 0xD9, 0x28, 0xEA, 0xFA, 0x2D, 0x00, 0xE1, 0x43, 0x53, 0xC6, 0x32, + 0xF4, 0xA2, 0x07, 0xD4, 0x5F, 0xD4, 0xCB, 0xAC, 0xCA, 0xFF, 0xDF, 0x84, + 0xD2, 0x86, 0x14, 0x3C, 0xDE, 0x22, 0x75, 0xA5, 0x73, 0xFF, 0x68, 0x07, + 0x4A, 0xF9, 0x7C, 0x2C, 0xCC, 0xDE, 0x45, 0xB6, 0x54, 0x82, 0x90, 0x36, + 0x1F, 0x2C, 0x51, 0x96, 0xC5, 0x0A, 0x53, 0x5B, 0xF0, 0x8B, 0x4A, 0xAA, + 0x3B, 0x68, 0x97, 0x19, 0x17, 0x1F, 0x01, 0xB8, 0xED, 0xB9, 0x9A, 0x5E, + 0x08, 0xC5, 0x20, 0x1E, 0x6A, 0x09, 0xF0, 0xE9, 0x73, 0xA3, 0xBE, 0x10, + 0x06, 0x02, 0xE9, 0xFB, 0x85, 0xFA, 0x5F, 0x01, 0xAC, 0x60, 0xE0, 0xED, + 0x7D, 0xB9, 0x49, 0xA8, 0x9E, 0x98, 0x7D, 0x91, 0x40, 0x05, 0xCF, 0xF9, + 0x1A, 0xFC, 0x40, 0x22, 0xA8, 0x96, 0x5B, 0xB0, 0xDC, 0x7A, 0xF5, 0xB7, + 0xE9, 0x91, 0x4C, 0x49, + ]), + ], + /* acid_fixed_key_modulus: */ + [ + Modulus([ + 0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, + 0x82, 0x45, 0xC6, 0x89, 0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, + 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87, 0x81, 0x25, 0xF4, 0x30, + 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C, + 0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, + 0xA6, 0x1C, 0xA0, 0x8B, 0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, + 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5, 0x9A, 0xBF, 0xEC, 0x0E, + 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32, + 0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, + 0xC7, 0xC6, 0x2E, 0x53, 0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, + 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4, 0x7C, 0x3D, 0x34, 0x2A, + 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA, + 0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, + 0x1F, 0x11, 0x67, 0x4B, 0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, + 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F, 0xFD, 0x19, 0xBF, 0xED, + 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33, + 0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, + 0x9F, 0xCE, 0x09, 0x5C, 0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, + 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3, 0xD8, 0x6C, 0xA2, 0x69, + 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0, + 0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, + 0xAE, 0x64, 0x88, 0x69, + ]), + Modulus([ + 0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, + 0xE1, 0x63, 0x7B, 0xF0, 0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, + 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E, 0x8A, 0x98, 0x76, 0x47, + 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70, + 0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, + 0xE8, 0x64, 0x42, 0x9A, 0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, + 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76, 0x90, 0xD2, 0x4B, 0x30, + 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD, + 0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, + 0x07, 0xD9, 0xDE, 0x4F, 0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, + 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE, 0x37, 0xA2, 0xB6, 0x8B, + 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D, + 0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, + 0x89, 0xBA, 0x0B, 0xE7, 0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, + 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03, 0xFA, 0xC4, 0xA6, 0x31, + 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62, + 0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, + 0x46, 0xD6, 0xA7, 0x1A, 0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, + 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2, 0x70, 0x2A, 0xA1, 0xE5, + 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36, + 0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, + 0x2E, 0xC6, 0x32, 0x6B, + ]), + ], + /* package2_fixed_key_modulus: */ + Modulus([ + 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, + 0xBA, 0x96, 0x99, 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, + 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, + 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, 0xF3, 0xBB, 0x77, 0x87, + 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, 0x06, + 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, + 0x51, 0x83, 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, + 0x1E, 0x68, 0x11, 0x50, 0xA0, 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, + 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, 0xC8, 0x26, 0xE9, 0xFE, 0x06, + 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, 0x1A, 0xA4, + 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, + 0xB7, 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, + 0x69, 0x79, 0x31, 0x42, 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, + 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, + 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, 0x6B, 0xAE, 0xC7, + 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, + 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, + 0x90, 0x0C, 0xB4, 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, + 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, + 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B, + ]), + ), + ) + } + + #[allow(clippy::cognitive_complexity)] fn read_from_ini(&mut self, mut file: File) -> Result<(), Error> { let config = ini::Ini::read_from(&mut file)?; let section = config.general_section(); - make_key_macros!(self, section); - single_key!(secure_boot_key); - single_key!(tsec_key); - multi_key!(keyblob_keys); - multi_key!(keyblob_mac_keys); - multi_key!(keyblob_key_sources); - multi_encrypted_keyblob!(encrypted_keyblobs); - multi_keyblob!(keyblobs); - single_key!(keyblob_mac_key_source); - single_key!(tsec_root_key); - multi_key!(master_kek_sources); - multi_key!(master_keks); - single_key!(master_key_source); - multi_key!(master_keys); - multi_key!(package1_keys); - multi_key!(package2_keys); - single_key!(package2_key_source); - single_key!(aes_kek_generation_source); - single_key!(aes_key_generation_source); - single_key!(key_area_key_application_source); - single_key!(key_area_key_ocean_source); - single_key!(key_area_key_system_source); - single_key!(titlekek_source); - single_key!(header_kek_source); - single_key!(sd_card_kek_source); - single_key_xts!(sd_card_save_key_source); - single_key_xts!(sd_card_nca_key_source); - single_key!(save_mac_kek_source); - single_key!(save_mac_key_source); - single_key_xts!(header_key_source); - single_key_xts!(header_key); - multi_key!(titlekeks); - multi_key!(key_area_key_application); - multi_key!(key_area_key_ocean); - multi_key!(key_area_key_system); - single_key_xts!(sd_card_save_key); - single_key_xts!(sd_card_nca_key); + make_key_macros!($, self, section); + keys!(self); Ok(()) } - pub fn write(&self, w: &mut W) -> io::Result<()> { - make_key_macros_write!(self, w); - single_key!(secure_boot_key); - single_key!(tsec_key); - multi_key!(keyblob_keys); - multi_key!(keyblob_mac_keys); - multi_key!(keyblob_key_sources); - multi_encrypted_keyblob!(encrypted_keyblobs); - multi_keyblob!(keyblobs); - single_key!(keyblob_mac_key_source); - single_key!(tsec_root_key); - multi_key!(master_kek_sources); - multi_key!(master_keks); - single_key!(master_key_source); - multi_key!(master_keys); - multi_key!(package1_keys); - multi_key!(package2_keys); - single_key!(package2_key_source); - single_key!(aes_kek_generation_source); - single_key!(aes_key_generation_source); - single_key!(key_area_key_application_source); - single_key!(key_area_key_ocean_source); - single_key!(key_area_key_system_source); - single_key!(titlekek_source); - single_key!(header_kek_source); - single_key!(sd_card_kek_source); - single_key_xts!(sd_card_save_key_source); - single_key_xts!(sd_card_nca_key_source); - single_key!(save_mac_kek_source); - single_key!(save_mac_key_source); - single_key_xts!(header_key_source); - single_key_xts!(header_key); - multi_key!(titlekeks); - multi_key!(key_area_key_application); - multi_key!(key_area_key_ocean); - multi_key!(key_area_key_system); - single_key_xts!(sd_card_save_key); - single_key_xts!(sd_card_nca_key); + #[allow(clippy::cognitive_complexity)] + pub fn write( + &self, + w: &mut W, + console_unique: bool, + minimal: bool, + ) -> io::Result<()> { + make_key_macros_write!($, self, w, console_unique, minimal); + keys!(self); Ok(()) } + #[allow(clippy::cognitive_complexity)] + #[allow(clippy::single_match)] pub fn derive_keys(&mut self) -> Result<(), Error> { for i in 0..6 { /* Derive the keyblob_keys */ - match (&self.secure_boot_key, &self.tsec_key, &self.keyblob_key_sources[i]) { + match ( + &self.secure_boot_key, + &self.tsec_key, + &self.keyblob_key_sources[i], + ) { (Some(sbk), Some(tsec_key), Some(keyblob_key_source)) => { let tmp = tsec_key.derive_key(&keyblob_key_source.0)?; self.keyblob_keys[i] = Some(sbk.derive_key(&tmp.0)?); - }, - _ => continue + } + _ => continue, } } for i in 0..6 { /* Derive the keyblob mac keys */ match (&self.keyblob_keys[i], &self.keyblob_mac_key_source) { (Some(keyblob_key), Some(keyblob_mac_key_source)) => { - self.keyblob_mac_keys[i] = Some(keyblob_key.derive_key(&keyblob_mac_key_source.0)?); - }, - _ => continue + self.keyblob_mac_keys[i] = + Some(keyblob_key.derive_key(&keyblob_mac_key_source.0)?); + } + _ => continue, + } + } + /* Derive the device key */ + match (&self.keyblob_keys[0], &self.per_console_key_source) { + (Some(keyblob_key), Some(per_console_key_source)) => { + self.device_key = Some(keyblob_key.derive_key(&per_console_key_source.0)?); } + _ => (), } for i in 0..6 { - match (&self.keyblob_keys[i], &self.keyblob_mac_keys[i], &mut self.encrypted_keyblobs[i], &mut self.keyblobs[i]) { - (Some(keyblob_key), Some(keyblob_mac_key), Some(encrypted_keyblob), ref mut keyblob @ None) => { + match ( + &self.keyblob_keys[i], + &self.keyblob_mac_keys[i], + &mut self.encrypted_keyblobs[i], + &mut self.keyblobs[i], + ) { + ( + Some(keyblob_key), + Some(keyblob_mac_key), + Some(encrypted_keyblob), + ref mut keyblob @ None, + ) => { **keyblob = Some(encrypted_keyblob.decrypt(keyblob_key, keyblob_mac_key, i)?); - }, - (Some(keyblob_key), Some(keyblob_mac_key), ref mut encrypted_keyblob @ None, Some(keyblob)) => { + } + ( + Some(keyblob_key), + Some(keyblob_mac_key), + ref mut encrypted_keyblob @ None, + Some(keyblob), + ) => { **encrypted_keyblob = Some(keyblob.encrypt(keyblob_key, keyblob_mac_key, i)?); - }, - _ => continue + } + _ => continue, } } for i in 0..6 { @@ -540,13 +1010,52 @@ impl Keys { self.master_keks[i] = Some(Aes128Key(keysource)); } } + for i in 6..0x20 { + // Derive new 6.2.0+ keks + match (&self.tsec_root_kek, &self.tsec_auth_signatures[i - 6]) { + (Some(tsec_root_kek), Some(tsec_auth_signature)) => { + self.tsec_root_key[i - 6] = + Some(tsec_root_kek.derive_key(&tsec_auth_signature.0)?); + } + _ => (), + } + match (&self.package1_mac_kek, &self.tsec_auth_signatures[i - 6]) { + (Some(package1_mac_kek), Some(tsec_auth_signature)) => { + self.package1_mac_keys[i] = + Some(package1_mac_kek.derive_key(&tsec_auth_signature.0)?); + } + _ => (), + } + match (&self.package1_kek, &self.tsec_auth_signatures[i - 6]) { + (Some(package1_kek), Some(tsec_auth_signature)) => { + self.package1_keys[i] = Some(package1_kek.derive_key(&tsec_auth_signature.0)?); + } + _ => (), + } + } for i in 6..0x20 { /* Do keygen for 6.2.0+ */ - match (&self.tsec_root_key, &self.master_kek_sources[i]) { + match (&self.tsec_root_key[i - 6], &self.master_kek_sources[i]) { (Some(tsec_root_key), Some(master_kek_source)) => { self.master_keks[i] = Some(tsec_root_key.derive_key(&master_kek_source.0)?); - }, - _ => continue + } + _ => continue, + } + } + for i in 0..0x20 { + /* Derive master keks with mariko keydata */ + match ( + &self.mariko_kek, + &mut self.mariko_master_kek_sources[i], + &mut self.master_keks[i], + ) { + (Some(mariko_kek), Some(mariko_master_kek_sources), ref mut master_kek @ None) => { + **master_kek = Some(mariko_kek.derive_key(&mariko_master_kek_sources.0)?) + } + (Some(mariko_kek), ref mut mariko_master_kek_sources @ None, Some(master_kek)) => { + **mariko_master_kek_sources = Some(mariko_kek.generate_kek(&master_kek.0)?) + } + _ => (), } } for i in 0..0x20 { @@ -554,30 +1063,69 @@ impl Keys { match (&self.master_key_source, &self.master_keks[i]) { (Some(master_key_source), Some(master_kek)) => { self.master_keys[i] = Some(master_kek.derive_key(&master_key_source.0)?); - }, - _ => continue + } + _ => continue, } } for i in 0..0x20 { if let Some(master_key) = &self.master_keys[i] { /* Derive key area encryption key */ - match (&self.key_area_key_application_source, &self.aes_kek_generation_source, &self.aes_key_generation_source) { - (Some(key_area_key_application_source), Some(aes_kek_generation_source), Some(aes_key_generation_source)) => { - self.key_area_key_application[i] = Some(generate_kek(key_area_key_application_source, master_key, aes_kek_generation_source, aes_key_generation_source)?); - }, - _ => continue + match ( + &self.key_area_key_application_source, + &self.aes_kek_generation_source, + &self.aes_key_generation_source, + ) { + ( + Some(key_area_key_application_source), + Some(aes_kek_generation_source), + Some(aes_key_generation_source), + ) => { + self.key_area_key_application[i] = Some(generate_kek( + key_area_key_application_source, + master_key, + aes_kek_generation_source, + aes_key_generation_source, + )?); + } + _ => continue, } - match (&self.key_area_key_ocean_source, &self.aes_kek_generation_source, &self.aes_key_generation_source) { - (Some(key_area_key_ocean_source), Some(aes_kek_generation_source), Some(aes_key_generation_source)) => { - self.key_area_key_ocean[i] = Some(generate_kek(key_area_key_ocean_source, master_key, aes_kek_generation_source, aes_key_generation_source)?); - }, - _ => continue + match ( + &self.key_area_key_ocean_source, + &self.aes_kek_generation_source, + &self.aes_key_generation_source, + ) { + ( + Some(key_area_key_ocean_source), + Some(aes_kek_generation_source), + Some(aes_key_generation_source), + ) => { + self.key_area_key_ocean[i] = Some(generate_kek( + key_area_key_ocean_source, + master_key, + aes_kek_generation_source, + aes_key_generation_source, + )?); + } + _ => continue, } - match (&self.key_area_key_system_source, &self.aes_kek_generation_source, &self.aes_key_generation_source) { - (Some(key_area_key_system_source), Some(aes_kek_generation_source), Some(aes_key_generation_source)) => { - self.key_area_key_system[i] = Some(generate_kek(key_area_key_system_source, master_key, aes_kek_generation_source, aes_key_generation_source)?); - }, - _ => continue + match ( + &self.key_area_key_system_source, + &self.aes_kek_generation_source, + &self.aes_key_generation_source, + ) { + ( + Some(key_area_key_system_source), + Some(aes_kek_generation_source), + Some(aes_key_generation_source), + ) => { + self.key_area_key_system[i] = Some(generate_kek( + key_area_key_system_source, + master_key, + aes_kek_generation_source, + aes_key_generation_source, + )?); + } + _ => continue, } /* Derive titlekek */ if let Some(titlekek_source) = &self.titlekek_source { @@ -588,29 +1136,86 @@ impl Keys { if let Some(package2_key_source) = &self.package2_key_source { self.package2_keys[i] = Some(master_key.derive_key(&package2_key_source.0)?); } + } - /* Derive Header Key */ - #[allow(clippy::single_match)] - match (i, &self.header_kek_source, &self.header_key_source, &self.aes_kek_generation_source, &self.aes_key_generation_source) { - (0, Some(header_kek_source), Some(header_key_source), Some(aes_kek_generation_source), Some(aes_key_generation_source)) => { - let header_kek = generate_kek(&header_kek_source, master_key, &aes_kek_generation_source, &aes_key_generation_source)?; - self.header_key = Some(header_kek.derive_xts_key(&header_key_source.0)?); - }, - _ => () + /* Derive Header Key */ + #[allow(clippy::single_match)] + match ( + &self.master_keys[0], + &self.header_kek_source, + &self.header_key_source, + &self.aes_kek_generation_source, + &self.aes_key_generation_source, + ) { + ( + Some(master_key), + Some(header_kek_source), + Some(header_key_source), + Some(aes_kek_generation_source), + Some(aes_key_generation_source), + ) => { + let header_kek = generate_kek( + &header_kek_source, + master_key, + &aes_kek_generation_source, + &aes_key_generation_source, + )?; + self.header_key = Some(header_kek.derive_xts_key(&header_key_source.0)?); } - /* Derive SD Card key */ - match (&self.sd_card_kek_source, &self.aes_kek_generation_source, &self.aes_key_generation_source) { - (Some(sd_card_kek_source), Some(aes_kek_generation_source), Some(aes_key_generation_source)) => { - let sd_kek = generate_kek(sd_card_kek_source, master_key, aes_kek_generation_source, aes_key_generation_source)?; - if let Some(sd_card_save_key_source) = &self.sd_card_save_key_source { - self.sd_card_save_key = Some(sd_kek.derive_xts_key(&sd_card_save_key_source.0)?); - } - if let Some(sd_card_nca_key_source) = &self.sd_card_nca_key_source { - self.sd_card_nca_key = Some(sd_kek.derive_xts_key(&sd_card_nca_key_source.0)?); - } - }, - _ => continue + _ => (), + } + /* Derive SD Card key */ + match ( + &self.master_keys[0], + &self.sd_card_kek_source, + &self.aes_kek_generation_source, + &self.aes_key_generation_source, + ) { + ( + Some(master_key), + Some(sd_card_kek_source), + Some(aes_kek_generation_source), + Some(aes_key_generation_source), + ) => { + let sd_kek = generate_kek( + sd_card_kek_source, + master_key, + aes_kek_generation_source, + aes_key_generation_source, + )?; + if let Some(sd_card_save_key_source) = &self.sd_card_save_key_source { + self.sd_card_save_key = + Some(sd_kek.derive_xts_key(&sd_card_save_key_source.0)?); + } + if let Some(sd_card_nca_key_source) = &self.sd_card_nca_key_source { + self.sd_card_nca_key = + Some(sd_kek.derive_xts_key(&sd_card_nca_key_source.0)?); + } + } + _ => (), + } + + // Derive Save MAC key + match ( + &self.device_key, + &self.save_mac_kek_source, + &self.aes_kek_generation_source, + &self.save_mac_key_source, + ) { + ( + Some(device_key), + Some(save_mac_kek_source), + Some(aes_kek_generation_source), + Some(save_mac_key_source), + ) => { + self.save_mac_key = Some(generate_kek( + save_mac_kek_source, + device_key, + aes_kek_generation_source, + save_mac_key_source, + )?); } + _ => (), } } Ok(()) diff --git a/src/utils.rs b/src/utils.rs index 3a62d02..3b1b3f4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,14 +1,12 @@ +use core::ops::{BitAnd, Not}; use num_traits::Num; -use core::ops::{Not, BitAnd}; use std::io; -pub fn align_down + BitAnd + Copy>(addr: T, align: T) -> T -{ +pub fn align_down + BitAnd + Copy>(addr: T, align: T) -> T { addr & !(align - T::one()) } -pub fn align_up + BitAnd + Copy>(addr: T, align: T) -> T -{ +pub fn align_up + BitAnd + Copy>(addr: T, align: T) -> T { align_down(addr + (align - T::one()), align) } @@ -18,7 +16,9 @@ pub trait TryClone: Sized { } impl TryClone for std::fs::File { - fn try_clone(&self) -> std::io::Result { std::fs::File::try_clone(&self) } + fn try_clone(&self) -> std::io::Result { + std::fs::File::try_clone(&self) + } } pub struct ReadRange { @@ -32,12 +32,13 @@ impl ReadRange { pub fn new(stream: R, start_from: u64, max_size: u64) -> ReadRange { ReadRange { inner: stream, - start_from: start_from, + start_from, size: max_size, - inner_pos: 0 + inner_pos: 0, } } + #[allow(unused)] pub fn pos_in_stream(&self) -> u64 { self.start_from + self.inner_pos } @@ -64,18 +65,24 @@ impl io::Seek for ReadRange { if let Some(s) = self.inner_pos.checked_sub(-val as u64) { s } else { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "Seek before position 0")); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Seek before position 0", + )); } } else { self.inner_pos + val as u64 } - }, + } io::SeekFrom::End(val) => { if val < 0 { if let Some(s) = self.size.checked_sub(-val as u64) { s } else { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "Seek before position 0")); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Seek before position 0", + )); } } else { self.size + val as u64 @@ -83,7 +90,9 @@ impl io::Seek for ReadRange { } }; - let newpos = self.inner.seek(io::SeekFrom::Start(self.start_from + new_inner_pos))?; + let newpos = self + .inner + .seek(io::SeekFrom::Start(self.start_from + new_inner_pos))?; self.inner_pos = newpos - self.start_from; Ok(self.inner_pos) }