From 29d9a262f37384a6abea42e3222e1fe8433a86e2 Mon Sep 17 00:00:00 2001 From: morkt Date: Wed, 6 Jul 2016 08:31:30 +0400 Subject: [PATCH] implemented RC4-encrypted PNG images. --- ArcFormats/ArcFormats.csproj | 1 + ArcFormats/ImageRSA.cs | 160 +++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 ArcFormats/ImageRSA.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index a9c56bdd..479e1aaa 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -117,6 +117,7 @@ + diff --git a/ArcFormats/ImageRSA.cs b/ArcFormats/ImageRSA.cs new file mode 100644 index 00000000..3d77635a --- /dev/null +++ b/ArcFormats/ImageRSA.cs @@ -0,0 +1,160 @@ +//! \file ImageRSA.cs +//! \date Tue Jul 05 20:38:47 2016 +//! \brief RSA-encrypted PNG image. +// +// Copyright (C) 2016 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace GameRes.Formats.Dogenzaka +{ + internal class Rc4PngMetaData : ImageMetaData + { + public byte[] Key; + } + + [Export(typeof(ImageFormat))] + public class Rc4PngFormat : PngFormat + { + public override string Tag { get { return "PNG/RC4"; } } + public override string Description { get { return "RC4 encrypted PNG image"; } } + public override uint Signature { get { return 0xC4F7F61A; } } + + public Rc4PngFormat () + { + Extensions = new string[] { "a" }; + } + + public static readonly byte[] KnownKey = Encoding.ASCII.GetBytes ("Hlk9D28p"); + + public override ImageMetaData ReadMetaData (Stream stream) + { + using (var sha = SHA1.Create()) + { + var key = sha.ComputeHash (KnownKey).Take (16).ToArray(); + using (var proxy = new InputProxyStream (stream, true)) + using (var input = new CryptoStream (proxy, new Rc4Transform (key), CryptoStreamMode.Read)) + { + var info = base.ReadMetaData (input); + if (null == info) + return null; + return new Rc4PngMetaData + { + Width = info.Width, + Height = info.Height, + OffsetX = info.OffsetX, + OffsetY = info.OffsetY, + BPP = info.BPP, + Key = key, + }; + } + } + } + + public override ImageData Read (Stream stream, ImageMetaData info) + { + var rc4 = (Rc4PngMetaData)info; + using (var sha = SHA1.Create()) + using (var proxy = new InputProxyStream (stream, true)) + using (var input = new CryptoStream (proxy, new Rc4Transform (rc4.Key), CryptoStreamMode.Read)) + return base.Read (input, info); + } + + public override void Write (Stream file, ImageData image) + { + throw new NotImplementedException ("Rc4PngFormat.Write not implemented"); + } + } + + public sealed class Rc4Transform : ICryptoTransform + { + private const int StateLength = 256; + private const int BlockSize = 256; + private byte[] m_state = new byte[StateLength]; + private int x; + private int y; + private byte[] m_key; + + public bool CanReuseTransform { get { return false; } } + public bool CanTransformMultipleBlocks { get { return true; } } + public int InputBlockSize { get { return BlockSize; } } + public int OutputBlockSize { get { return BlockSize; } } + + public Rc4Transform (byte[] key) + { + m_key = key; + x = 0; + y = 0; + + for (int i = 0; i < StateLength; ++i) + { + m_state[i] = (byte)i; + } + + int i1 = 0; + int i2 = 0; + + for (int i = 0; i < StateLength; ++i) + { + i2 = ((m_key[i1] & 0xFF) + m_state[i] + i2) & 0xFF; + byte t = m_state[i]; + m_state[i] = m_state[i2]; + m_state[i2] = t; + i1 = (i1+1) % m_key.Length; + } + } + + public int TransformBlock (byte[] inBuf, int inOffset, int inCount, + byte[] outBuf, int outOffset) + { + for (int i = 0; i < inCount; i++) + { + x = (x + 1) & 0xFF; + y = (m_state[x] + y) & 0xFF; + + byte t = m_state[x]; + m_state[x] = m_state[y]; + m_state[y] = t; + + outBuf[i+outOffset] = (byte)(inBuf[i + inOffset] ^ m_state[(m_state[x] + m_state[y]) & 0xFF]); + } + return inCount; + } + + public byte[] TransformFinalBlock (byte[] inBuf, int inOffset, int inCount) + { + byte[] output = new byte[inCount]; + TransformBlock (inBuf, inOffset, inCount, output, 0); + return output; + } + + public void Dispose () + { + GC.SuppressFinalize (this); + } + } +}