|
|
|
|
using ICSharpCode.SharpZipLib.Zip;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace ndpack
|
|
|
|
|
{
|
|
|
|
|
class Program
|
|
|
|
|
{
|
|
|
|
|
static Task<string> ReadPassword(string prompt = null, CancellationToken token = default)
|
|
|
|
|
{
|
|
|
|
|
TaskCompletionSource<string> comp = new TaskCompletionSource<string>();
|
|
|
|
|
List<char> pwd = new List<char>();
|
|
|
|
|
|
|
|
|
|
bool set = false;
|
|
|
|
|
_ = Task.Run(() =>
|
|
|
|
|
{
|
|
|
|
|
if (prompt != null)
|
|
|
|
|
Console.Write(prompt);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using var _c = token.Register(() =>
|
|
|
|
|
{
|
|
|
|
|
if (!set)
|
|
|
|
|
{
|
|
|
|
|
set = true;
|
|
|
|
|
comp.SetException(new OperationCanceledException());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
ConsoleKeyInfo i = Console.ReadKey(true);
|
|
|
|
|
if (token.IsCancellationRequested) break;
|
|
|
|
|
if (i.Key == ConsoleKey.Enter)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (i.Key == ConsoleKey.Backspace)
|
|
|
|
|
{
|
|
|
|
|
if (pwd.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
pwd.RemoveAt(pwd.Count - 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (i.KeyChar != '\u0000')
|
|
|
|
|
{
|
|
|
|
|
pwd.Add(i.KeyChar);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!set)
|
|
|
|
|
{
|
|
|
|
|
set = true;
|
|
|
|
|
if (token.IsCancellationRequested)
|
|
|
|
|
comp.SetException(new OperationCanceledException());
|
|
|
|
|
else
|
|
|
|
|
comp.SetResult(new string(pwd.ToArray()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
pwd.Clear();
|
|
|
|
|
if (!set)
|
|
|
|
|
{
|
|
|
|
|
set = true;
|
|
|
|
|
comp.SetException(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return comp.Task;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void usage()
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Usage: ndpack -c <dump file> <images directory> <output file>");
|
|
|
|
|
Console.WriteLine("Usage: ndpack -e <dump file> <images directory> <key> <output file>");
|
|
|
|
|
Console.WriteLine("Usage: ndpack -u <archive> <output dir>");
|
|
|
|
|
Console.WriteLine("Usage: ndpack -d <archive encrypted> <key file> <output dir>");
|
|
|
|
|
}
|
|
|
|
|
static async Task CopyDirectory(string SourcePath, string DestinationPath)
|
|
|
|
|
{
|
|
|
|
|
Exception bad = null;
|
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//Now Create all of the directories
|
|
|
|
|
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*",
|
|
|
|
|
SearchOption.AllDirectories))
|
|
|
|
|
Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
|
|
|
|
|
|
|
|
|
|
//Copy all the files & Replaces any files with the same name
|
|
|
|
|
foreach (string newPath in Directory.GetFiles(SourcePath, "*",
|
|
|
|
|
SearchOption.AllDirectories))
|
|
|
|
|
File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
|
|
|
|
|
}catch(Exception ex)
|
|
|
|
|
{
|
|
|
|
|
bad = ex;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (bad != null) throw bad;
|
|
|
|
|
}
|
|
|
|
|
static async Task create(string output, string dumpf, string dirf)
|
|
|
|
|
{
|
|
|
|
|
FastZip fs = new FastZip();
|
|
|
|
|
using var tdir = new TempDirectory();
|
|
|
|
|
await Task.Yield();
|
|
|
|
|
Console.WriteLine("Copying dump...");
|
|
|
|
|
File.Copy(dumpf, Path.Combine(tdir.Directory.FullName, Path.GetFileName(dumpf)));
|
|
|
|
|
Console.WriteLine($"Copying images...");
|
|
|
|
|
var ipath = Path.Combine(tdir.Directory.FullName, new DirectoryInfo(dirf).Name);
|
|
|
|
|
Directory.CreateDirectory(ipath);
|
|
|
|
|
await CopyDirectory(dirf, ipath);
|
|
|
|
|
|
|
|
|
|
await Task.Yield();
|
|
|
|
|
fs.CreateZip(output, tdir.Directory.FullName, true, null, null);
|
|
|
|
|
}
|
|
|
|
|
static async Task unpack(string inputArchive, string outputDir)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(outputDir))
|
|
|
|
|
Directory.CreateDirectory(outputDir);
|
|
|
|
|
|
|
|
|
|
var fs = new FastZip();
|
|
|
|
|
|
|
|
|
|
await Task.Yield();
|
|
|
|
|
fs.ExtractZip(inputArchive, outputDir, null);
|
|
|
|
|
}
|
|
|
|
|
static async Task decrypt(string inf, string of, string kf, Func<Task<string>> password)
|
|
|
|
|
{
|
|
|
|
|
using var infs = new FileStream(inf, FileMode.Open, FileAccess.Read);
|
|
|
|
|
using var enc = new encaes.AesEncryptor(infs);
|
|
|
|
|
using (var kfs = new FileStream(kf, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
|
|
|
|
enc.Key = await encaes.AesEncryptor.LoadKey(kfs, () =>
|
|
|
|
|
{
|
|
|
|
|
var tsk = password();
|
|
|
|
|
tsk.Wait();
|
|
|
|
|
if (tsk.IsFaulted)
|
|
|
|
|
throw tsk.Exception;
|
|
|
|
|
else return tsk.Result;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using var ofs = new FileStream(of, FileMode.Create);
|
|
|
|
|
await enc.Decrypt(ofs);
|
|
|
|
|
}
|
|
|
|
|
static async Task encrypt(string inf, string outf, string kf, Func<Task<string>> password)
|
|
|
|
|
{
|
|
|
|
|
using var infs = new FileStream(inf, FileMode.Open, FileAccess.Read);
|
|
|
|
|
using var enc = new encaes.AesEncryptor(infs);
|
|
|
|
|
using (var kfs = new FileStream(kf, FileMode.Open, FileAccess.Read))
|
|
|
|
|
{
|
|
|
|
|
enc.Key = await encaes.AesEncryptor.LoadKey(kfs, () =>
|
|
|
|
|
{
|
|
|
|
|
var tsk = password();
|
|
|
|
|
tsk.Wait();
|
|
|
|
|
if (tsk.IsFaulted)
|
|
|
|
|
throw tsk.Exception;
|
|
|
|
|
else return tsk.Result;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
using (var ofs = new FileStream(outf, FileMode.Create))
|
|
|
|
|
{
|
|
|
|
|
await enc.Encrypt(ofs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static async Task Main(string[] args)
|
|
|
|
|
{
|
|
|
|
|
if (args.Length < 3)
|
|
|
|
|
{
|
|
|
|
|
usage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (args[0].ToLower())
|
|
|
|
|
{
|
|
|
|
|
case "-c":
|
|
|
|
|
if (args.Length < 4)
|
|
|
|
|
{
|
|
|
|
|
usage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await create(args[3], args[1], args[2]);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error creating: " + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "-e":
|
|
|
|
|
if(args.Length<5)
|
|
|
|
|
{
|
|
|
|
|
usage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string realOutput = args[4];
|
|
|
|
|
using var tf = TempFile.WithNoStream();
|
|
|
|
|
await create(tf.File.FullName, args[1], args[2]);
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Encrypting...");
|
|
|
|
|
await encrypt(tf.File.FullName, realOutput, args[3], async () =>
|
|
|
|
|
{
|
|
|
|
|
return await ReadPassword("Password: ");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error creating: " + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "-u":
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Unpacking...");
|
|
|
|
|
await unpack(args[1], args[2]);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error unpacking: " + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "-d":
|
|
|
|
|
if (args.Length < 4)
|
|
|
|
|
{
|
|
|
|
|
usage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using var arcfs = TempFile.WithNoStream();
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Decrypting...");
|
|
|
|
|
await decrypt(args[1], arcfs.File.FullName, args[2], async () =>
|
|
|
|
|
{
|
|
|
|
|
return await ReadPassword("Password: ");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("Unpacking...");
|
|
|
|
|
await unpack(arcfs.File.FullName, args[3]);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("Error unpacking: " + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
usage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|