Beispiel #1
0
        public static ProtectedPrivateKey Protect(PrivateKey privateKey, string passphrase)
        {
            var salt = new byte[32];

            using RandomNumberGenerator rng = RandomNumberGenerator.Create();
            rng.GetBytes(salt);
            var kdf = new Pbkdf2 <Sha256Digest>(10240, salt, 32);
            ImmutableArray <byte> derivedKey = kdf.Derive(passphrase);
            ImmutableArray <byte> encKey     = MakeEncryptionKey(derivedKey);
            var iv = new byte[16];

            rng.GetBytes(iv);
            var cipher = new Aes128Ctr(iv);
            ImmutableArray <byte> ciphertext = cipher.Encrypt(encKey, privateKey.ByteArray);
            ImmutableArray <byte> mac        = CalculateMac(derivedKey, ciphertext);
            Address address = privateKey.ToAddress();

            return(new ProtectedPrivateKey(address, kdf, mac, cipher, ciphertext));
        }
        /// <summary>
        /// Loads a <see cref="ProtectedPrivateKey"/> from a JSON, according to Ethereum's
        /// <a href="https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition">Web3
        /// Secret Storage Definition</a>.
        /// </summary>
        /// <param name="json">A JSON string that encodes a <see cref="ProtectedPrivateKey"/>.
        /// </param>
        /// <returns>A protected private key loaded from the given <paramref name="json"/>.
        /// </returns>
        /// <exception cref="JsonException">Thrown when the given <paramref name="json"/> is not
        /// a valid JSON.</exception>
        /// <exception cref="InvalidKeyJsonException">Thrown when the given key data lacks some
        /// required fields or consists of wrong types.</exception>
        /// <exception cref="UnsupportedKeyJsonException">Thrown when the given key data depends on
        /// an unsupported features (e.g., KDF).</exception>
        public static ProtectedPrivateKey FromJson(string json)
        {
            var options = new JsonDocumentOptions
            {
                AllowTrailingCommas = true,
                CommentHandling     = JsonCommentHandling.Skip,
            };

            using JsonDocument doc = JsonDocument.Parse(json, options);
            JsonElement rootElement = doc.RootElement;

            if (rootElement.ValueKind != JsonValueKind.Object)
            {
                throw new InvalidKeyJsonException(
                          "The root of the key JSON must be an object, but it is a/an " +
                          $"{rootElement.ValueKind}."
                          );
            }

            if (!rootElement.TryGetProperty("version", out JsonElement versionElement))
            {
                throw new InvalidKeyJsonException(
                          "The key JSON must contain \"version\" field, but it lacks."
                          );
            }

            if (versionElement.ValueKind != JsonValueKind.Number ||
                !versionElement.TryGetDecimal(out decimal versionNum))
            {
                throw new InvalidKeyJsonException("The \"version\" field must be a number.");
            }
            else if (versionNum != 3)
            {
                throw new UnsupportedKeyJsonException(
                          $"The key JSON format version {versionNum} is unsupported; " +
                          "Only version 3 is supported."
                          );
            }

            string GetStringProperty(JsonElement element, string fieldName)
            {
                if (!element.TryGetProperty(fieldName, out JsonElement fieldElement))
                {
                    throw new InvalidKeyJsonException(
                              $"The key JSON must contain \"{fieldName}\" field, but it lacks."
                              );
                }

                string str;

                try
                {
                    str = fieldElement.GetString();
                }
                catch (InvalidOperationException)
                {
                    throw new InvalidKeyJsonException(
                              $"The \"{fieldName}\" field must be a string."
                              );
                }

                if (str is null)
                {
                    throw new InvalidKeyJsonException(
                              $"The \"{fieldName}\" field must not be null, but a string."
                              );
                }

                return(str);
            }

            JsonElement GetObjectProperty(JsonElement element, string fieldName)
            {
                if (!element.TryGetProperty(fieldName, out var fieldElement))
                {
                    throw new InvalidKeyJsonException(
                              $"The key JSON must contain \"{fieldName}\" field, but it lacks."
                              );
                }
                else if (fieldElement.ValueKind != JsonValueKind.Object)
                {
                    throw new InvalidKeyJsonException(
                              $"The \"{fieldName}\" field must be an object, but it is a/an " +
                              $"{fieldElement.ValueKind}."
                              );
                }

                return(fieldElement);
            }

            byte[] GetHexProperty(JsonElement element, string fieldName)
            {
                string str = GetStringProperty(element, fieldName);

                byte[] bytes;
                try
                {
                    bytes = ByteUtil.ParseHex(str);
                }
                catch (Exception e)
                {
                    throw new InvalidKeyJsonException(
                              $"The \"{fieldName}\" field must be a hexadecimal string.\n{e}"
                              );
                }

                return(bytes);
            }

            JsonElement crypto              = GetObjectProperty(rootElement, "crypto");
            string      cipherType          = GetStringProperty(crypto, "cipher");
            JsonElement cipherParamsElement = GetObjectProperty(crypto, "cipherparams");

            byte[]      ciphertext       = GetHexProperty(crypto, "ciphertext");
            byte[]      mac              = GetHexProperty(crypto, "mac");
            string      kdfType          = GetStringProperty(crypto, "kdf");
            JsonElement kdfParamsElement = GetObjectProperty(crypto, "kdfparams");

            byte[]  addressBytes = GetHexProperty(rootElement, "address");
            Address address;

            try
            {
                address = new Address(addressBytes);
            }
            catch (ArgumentException e)
            {
                throw new InvalidKeyJsonException(
                          "The \"address\" field must contain an Ethereum-style address which " +
                          "consists of 40 hexadecimal letters: " + e
                          );
            }

            var cipher = cipherType switch
            {
                "aes-128-ctr" => Aes128Ctr.FromJson(cipherParamsElement),
                _ =>
                throw new UnsupportedKeyJsonException(
                          $"Unsupported cipher type: \"{cipherType}\".")
            };

            IKdf kdf;

            try
            {
                kdf = kdfType switch
                {
                    "pbkdf2" => Pbkdf2.FromJson(kdfParamsElement),
                    "scrypt" => Scrypt.FromJson(kdfParamsElement),
                    _ =>
                    throw new UnsupportedKeyJsonException(
                              $"Unsupported cipher type: \"{kdfType}\".")
                };
            }
            catch (ArgumentException e)
            {
                throw new InvalidKeyJsonException(e.Message);
            }

            return(new ProtectedPrivateKey(address, kdf, mac, cipher, ciphertext));
        }
Beispiel #3
0
        public void Activate(string filePath, int type, object[] values)
        {
            switch ((CXIActivation)type)
            {
                case CXIActivation.RomFS:
                case CXIActivation.ExeFS:
                    var isRom = (CXIActivation)type == CXIActivation.RomFS;
                    var saveFileDialog = new SaveFileDialog() { Filter = "Binary files (*.bin)|*.bin"};
                    if (saveFileDialog.ShowDialog() == DialogResult.OK)
                    {
                        var strKey = InputBox.ShowDialog("Please Enter Key:\nPress OK with empty key to save encrypted");
                        if (strKey != null) //Cancel wasn't pressed
                        {
                            // returns (null if error, byte[0] on Empty, byte[16] on valid)
                            var key = StringUtil.ParseKeyStringToByteArray(strKey);

                            if (key == null)
                                MessageBox.Show(@"Error parsing key string (must be a multiple of 2 and made of hex letters).");
                            else
                            {
                                var infs = File.OpenRead(filePath);
                                infs.Seek((OffsetInNCSD + (isRom ? Header.RomFSOffset :Header.ExeFSOffset)) * 0x200, SeekOrigin.Begin);
                                var buffer = new byte[(isRom ? Header.RomFSLength : Header.ExeFSLength) * 0x200];
                                infs.Read(buffer, 0, buffer.Length);
                                infs.Close();
                                if (key.Length > 0)
                                {
                                    var iv = new byte[0x10];
                                    for (var i = 0; i < 8; i++)
                                        iv[i] = 0;
                                    Buffer.BlockCopy(Header.ProgramID, 0, iv, 8, 8); //TODO: change to TitleID

                                    var aes = new Aes128Ctr(key,iv);
                                    aes.TransformBlock(buffer);
                                }
                                var outpath = saveFileDialog.FileName;
                                var outfs = File.OpenWrite(outpath);
                                outfs.Write(buffer, 0, buffer.Length);
                                outfs.Close();
                            }
                        }
                    }
                    break;
                case CXIActivation.ExHeader:
                    saveFileDialog = new SaveFileDialog() { Filter = "Binary files (*.bin)|*.bin" };
                    if (saveFileDialog.ShowDialog() == DialogResult.OK)
                    {
                        var strKey = InputBox.ShowDialog("Please Enter Key:\nPress OK with empty key to save encrypted");
                        if (strKey != null) //Cancel wasn't pressed
                        {
                            // returns (null if error, byte[0] on Empty, byte[16] on valid)
                            var key = StringUtil.ParseKeyStringToByteArray(strKey);

                            if (key == null)
                                MessageBox.Show(@"Error parsing key string (must be a multiple of 2 and made of hex letters).");
                            else
                            {
                                var infs = File.OpenRead(filePath);
                                infs.Seek(OffsetInNCSD + Marshal.SizeOf(Header), SeekOrigin.Begin); //right after the header
                                var buffer = new byte[Header.ExtendedHeaderSize];
                                infs.Read(buffer, 0, buffer.Length);
                                infs.Close();
                                if (key.Length > 0)
                                {
                                    var iv = new byte[0x10];
                                    for (var i = 0; i < 8; i++)
                                        iv[i] = 0;
                                    Buffer.BlockCopy(Header.ProgramID, 0, iv, 8, 8); //TODO: change to TitleID

                                    var aes = new Aes128Ctr(key, iv);
                                    aes.TransformBlock(buffer);
                                }
                                var outpath = saveFileDialog.FileName;
                                var outfs = File.OpenWrite(outpath);
                                outfs.Write(buffer, 0, buffer.Length);
                                outfs.Close();
                            }
                        }
                    }
                    break;
                case CXIActivation.Logo:
                    saveFileDialog = new SaveFileDialog() { Filter = "Binary files (*.bin)|*.bin" };
                    if (saveFileDialog.ShowDialog() == DialogResult.OK)
                    {
                        var infs = File.OpenRead(filePath);
                        infs.Seek(OffsetInNCSD + (Header.LogoRegionOffset * NcchInfo.media_unit), SeekOrigin.Begin); //right after the header
                        var buffer = new byte[Header.LogoRegionLength * NcchInfo.media_unit];
                        infs.Read(buffer, 0, buffer.Length);
                        var outpath = saveFileDialog.FileName;
                        var outfs = File.OpenWrite(outpath);
                        outfs.Write(buffer, 0, buffer.Length);
                        outfs.Close();

                    }
                    break;
            }
        }
Beispiel #4
0
        public void Activate(string filePath, int type, object[] values)
        {
            switch ((CXIActivation)type)
            {
            case CXIActivation.RomFS:
            case CXIActivation.ExeFS:
                var isRom          = (CXIActivation)type == CXIActivation.RomFS;
                var saveFileDialog = new SaveFileDialog()
                {
                    Filter = "Binary files (*.bin)|*.bin"
                };
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    var strKey = InputBox.ShowDialog("Please Enter Key:\nPress OK with empty key to save encrypted");
                    if (strKey != null)     //Cancel wasn't pressed
                    {
                        // returns (null if error, byte[0] on Empty, byte[16] on valid)
                        var key = StringUtil.ParseKeyStringToByteArray(strKey);

                        if (key == null)
                        {
                            MessageBox.Show(@"Error parsing key string (must be a multiple of 2 and made of hex letters).");
                        }
                        else
                        {
                            var infs = File.OpenRead(filePath);
                            infs.Seek((OffsetInNCSD + (isRom ? Header.RomFSOffset :Header.ExeFSOffset)) * 0x200, SeekOrigin.Begin);
                            var buffer = new byte[(isRom ? Header.RomFSLength : Header.ExeFSLength) * 0x200];
                            infs.Read(buffer, 0, buffer.Length);
                            infs.Close();
                            if (key.Length > 0)
                            {
                                var iv = new byte[0x10];
                                for (var i = 0; i < 8; i++)
                                {
                                    iv[i] = 0;
                                }
                                Buffer.BlockCopy(Header.ProgramID, 0, iv, 8, 8);     //TODO: change to TitleID

                                var aes = new Aes128Ctr(key, iv);
                                aes.TransformBlock(buffer);
                            }
                            var outpath = saveFileDialog.FileName;
                            var outfs   = File.OpenWrite(outpath);
                            outfs.Write(buffer, 0, buffer.Length);
                            outfs.Close();
                        }
                    }
                }
                break;

            case CXIActivation.ExHeader:
                saveFileDialog = new SaveFileDialog()
                {
                    Filter = "Binary files (*.bin)|*.bin"
                };
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    var strKey = InputBox.ShowDialog("Please Enter Key:\nPress OK with empty key to save encrypted");
                    if (strKey != null)     //Cancel wasn't pressed
                    {
                        // returns (null if error, byte[0] on Empty, byte[16] on valid)
                        var key = StringUtil.ParseKeyStringToByteArray(strKey);

                        if (key == null)
                        {
                            MessageBox.Show(@"Error parsing key string (must be a multiple of 2 and made of hex letters).");
                        }
                        else
                        {
                            var infs = File.OpenRead(filePath);
                            infs.Seek(OffsetInNCSD + Marshal.SizeOf(Header), SeekOrigin.Begin);     //right after the header
                            var buffer = new byte[Header.ExtendedHeaderSize];
                            infs.Read(buffer, 0, buffer.Length);
                            infs.Close();
                            if (key.Length > 0)
                            {
                                var iv = new byte[0x10];
                                for (var i = 0; i < 8; i++)
                                {
                                    iv[i] = 0;
                                }
                                Buffer.BlockCopy(Header.ProgramID, 0, iv, 8, 8);     //TODO: change to TitleID

                                var aes = new Aes128Ctr(key, iv);
                                aes.TransformBlock(buffer);
                            }
                            var outpath = saveFileDialog.FileName;
                            var outfs   = File.OpenWrite(outpath);
                            outfs.Write(buffer, 0, buffer.Length);
                            outfs.Close();
                        }
                    }
                }
                break;

            case CXIActivation.Logo:
                saveFileDialog = new SaveFileDialog()
                {
                    Filter = "Binary files (*.bin)|*.bin"
                };
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    var infs = File.OpenRead(filePath);
                    infs.Seek(OffsetInNCSD + (Header.LogoRegionOffset * NcchInfo.media_unit), SeekOrigin.Begin);     //right after the header
                    var buffer = new byte[Header.LogoRegionLength * NcchInfo.media_unit];
                    infs.Read(buffer, 0, buffer.Length);
                    var outpath = saveFileDialog.FileName;
                    var outfs   = File.OpenWrite(outpath);
                    outfs.Write(buffer, 0, buffer.Length);
                    outfs.Close();
                }
                break;
            }
        }