From 24f6a1715a5485e007a18e999bba436c9676b417 Mon Sep 17 00:00:00 2001 From: Antidote Date: Fri, 15 Feb 2013 20:22:16 -0800 Subject: [PATCH] * Merge libwiisave with libzelda --- include/BinaryWriter.hpp | 2 +- include/WiiBanner.h | 82 +++++ include/WiiBanner.hpp | 82 +++++ include/WiiFile.h | 68 +++++ include/WiiFile.hpp | 68 +++++ include/WiiSave.h | 47 +++ include/WiiSave.hpp | 37 +++ include/WiiSaveReader.hpp | 26 ++ include/WiiSaveWriter.hpp | 27 ++ include/aes.h | 20 ++ include/bn.h | 13 + include/ec.h | 10 + include/md5.h | 241 +++++++++++++++ include/sha1.h | 46 +++ libzelda.layout | 14 + libzelda.pro | 31 +- src/WiiBanner.cpp | 165 +++++++++++ src/WiiFile.cpp | 115 +++++++ src/WiiSave.cpp | 66 +++++ src/WiiSaveReader.cpp | 276 +++++++++++++++++ src/WiiSaveWriter.cpp | 238 +++++++++++++++ src/aes.c | 403 +++++++++++++++++++++++++ src/bn.cpp | 111 +++++++ src/ec.cpp | 419 ++++++++++++++++++++++++++ src/md5.c | 610 ++++++++++++++++++++++++++++++++++++++ src/sha1.cpp | 396 +++++++++++++++++++++++++ 26 files changed, 3608 insertions(+), 5 deletions(-) create mode 100644 include/WiiBanner.h create mode 100644 include/WiiBanner.hpp create mode 100644 include/WiiFile.h create mode 100644 include/WiiFile.hpp create mode 100644 include/WiiSave.h create mode 100644 include/WiiSave.hpp create mode 100644 include/WiiSaveReader.hpp create mode 100644 include/WiiSaveWriter.hpp create mode 100644 include/aes.h create mode 100644 include/bn.h create mode 100644 include/ec.h create mode 100644 include/md5.h create mode 100644 include/sha1.h create mode 100644 libzelda.layout create mode 100644 src/WiiBanner.cpp create mode 100644 src/WiiFile.cpp create mode 100644 src/WiiSave.cpp create mode 100644 src/WiiSaveReader.cpp create mode 100644 src/WiiSaveWriter.cpp create mode 100644 src/aes.c create mode 100644 src/bn.cpp create mode 100644 src/ec.cpp create mode 100644 src/md5.c create mode 100644 src/sha1.cpp diff --git a/include/BinaryWriter.hpp b/include/BinaryWriter.hpp index 8e37ab5..3faa971 100644 --- a/include/BinaryWriter.hpp +++ b/include/BinaryWriter.hpp @@ -132,7 +132,7 @@ public: * \param str The string to write to the buffer */ void writeUnicode(const std::string& str); -private: +protected: Int8 readByte(); Int8* readBytes(Int64); bool isOpenForReading(); diff --git a/include/WiiBanner.h b/include/WiiBanner.h new file mode 100644 index 0000000..c38fdf6 --- /dev/null +++ b/include/WiiBanner.h @@ -0,0 +1,82 @@ +#ifndef WIIBANNER_H +#define WIIBANNER_H + + +#include +#include +#include + +class WiiImage +{ +public: + WiiImage(); + WiiImage(Uint32 width, Uint32 height, Uint8* data); + ~WiiImage(); + + void setWidth(const Uint32 width); + Uint32 width() const; + + void setHeight(const Uint32 height); + Uint32 height() const; + + void setData(const Uint8* data); + Uint8* data(); + + Uint8* toRGBA32(); + +private: + Uint32 m_width; + Uint32 m_height; + Uint8* m_data; +}; + +class WiiBanner +{ +public: + enum { NoCopy = 0x00000001, Bounce = 0x00000010, NoCopyBounce = NoCopy | Bounce }; + WiiBanner(); + WiiBanner(Uint32 gameId, const std::string& title, const std::string& subtitle, WiiImage* m_banner, std::vector icons); + virtual ~WiiBanner(); + + void setGameID(Uint64 id); + Uint64 gameID() const; + + void setBannerImage(WiiImage* banner); + WiiImage* bannerImage() const; + + void setBannerSize(Uint32 size); + Uint32 bannerSize() const; + + void setTitle(const std::string& title); + std::string title() const; + + void setSubtitle(const std::string& subtitle); + std::string subtitle() const; + + void addIcon(WiiImage* icon); + void setIcon(Uint32 id, WiiImage* icon); + WiiImage* getIcon(Uint32 id) const; + std::vector icons() const; + + void setAnimationSpeed(Uint16 animSpeed); + Uint16 animationSpeed() const; + + void setPermissions(Uint8 permissions); + Uint8 permissions() const; + + void setFlags(Uint32 flags); + Uint32 flags() const; +protected: +private: + Uint64 m_gameId; + WiiImage* m_banner; + Uint32 m_animSpeed; + Uint8 m_permissions; + Uint32 m_flags; + Uint32 m_bannerSize; + std::vector m_icons; + std::string m_title; + std::string m_subtitle; +}; + +#endif // WIIBANNER_H diff --git a/include/WiiBanner.hpp b/include/WiiBanner.hpp new file mode 100644 index 0000000..c38fdf6 --- /dev/null +++ b/include/WiiBanner.hpp @@ -0,0 +1,82 @@ +#ifndef WIIBANNER_H +#define WIIBANNER_H + + +#include +#include +#include + +class WiiImage +{ +public: + WiiImage(); + WiiImage(Uint32 width, Uint32 height, Uint8* data); + ~WiiImage(); + + void setWidth(const Uint32 width); + Uint32 width() const; + + void setHeight(const Uint32 height); + Uint32 height() const; + + void setData(const Uint8* data); + Uint8* data(); + + Uint8* toRGBA32(); + +private: + Uint32 m_width; + Uint32 m_height; + Uint8* m_data; +}; + +class WiiBanner +{ +public: + enum { NoCopy = 0x00000001, Bounce = 0x00000010, NoCopyBounce = NoCopy | Bounce }; + WiiBanner(); + WiiBanner(Uint32 gameId, const std::string& title, const std::string& subtitle, WiiImage* m_banner, std::vector icons); + virtual ~WiiBanner(); + + void setGameID(Uint64 id); + Uint64 gameID() const; + + void setBannerImage(WiiImage* banner); + WiiImage* bannerImage() const; + + void setBannerSize(Uint32 size); + Uint32 bannerSize() const; + + void setTitle(const std::string& title); + std::string title() const; + + void setSubtitle(const std::string& subtitle); + std::string subtitle() const; + + void addIcon(WiiImage* icon); + void setIcon(Uint32 id, WiiImage* icon); + WiiImage* getIcon(Uint32 id) const; + std::vector icons() const; + + void setAnimationSpeed(Uint16 animSpeed); + Uint16 animationSpeed() const; + + void setPermissions(Uint8 permissions); + Uint8 permissions() const; + + void setFlags(Uint32 flags); + Uint32 flags() const; +protected: +private: + Uint64 m_gameId; + WiiImage* m_banner; + Uint32 m_animSpeed; + Uint8 m_permissions; + Uint32 m_flags; + Uint32 m_bannerSize; + std::vector m_icons; + std::string m_title; + std::string m_subtitle; +}; + +#endif // WIIBANNER_H diff --git a/include/WiiFile.h b/include/WiiFile.h new file mode 100644 index 0000000..c98b348 --- /dev/null +++ b/include/WiiFile.h @@ -0,0 +1,68 @@ +#ifndef WIIFILE_H +#define WIIFILE_H + +#include +#include +#include + +class WiiFile +{ +public: + enum Permission + { + OtherRead = 0x01, + OtherWrite = 0x02, + GroupRead = 0x04, + GroupWrite = 0x08, + OwnerRead = 0x10, + OwnerWrite = 0x20, + + // Mask values; + OtherRW = (OtherRead|OtherWrite), + GroupRW = (GroupRead|GroupWrite), + OwnerRW = (OwnerRead|OwnerWrite) + }; + + enum Type + { + File = 0x01, + Directory = 0x02 + }; + + WiiFile(); + WiiFile(const std::string& filename); + WiiFile(const std::string& filename, Uint8 permissions, const Uint8* data, Uint32 length); + virtual ~WiiFile(); + + void setFilename(const std::string& filename); + std::string filename() const; + + void setData(const Uint8* data); + Uint8* data() const; + + void setLength(const int len); + int length() const; + + void setPermissions(const Uint8 permissions); + Uint8 permissions() const; + + void setAttributes(const Uint8 attr); + Uint8 attributes() const; + + void setType(Type type); + Type type() const; + + bool isDirectory() const; + bool isFile() const; + +protected: +private: + Uint8 m_permissions; + Uint8 m_attributes; + Type m_type; + std::string m_filename; + int m_fileLen; + Uint8* m_fileData; +}; + +#endif // WIIFILE_H diff --git a/include/WiiFile.hpp b/include/WiiFile.hpp new file mode 100644 index 0000000..c98b348 --- /dev/null +++ b/include/WiiFile.hpp @@ -0,0 +1,68 @@ +#ifndef WIIFILE_H +#define WIIFILE_H + +#include +#include +#include + +class WiiFile +{ +public: + enum Permission + { + OtherRead = 0x01, + OtherWrite = 0x02, + GroupRead = 0x04, + GroupWrite = 0x08, + OwnerRead = 0x10, + OwnerWrite = 0x20, + + // Mask values; + OtherRW = (OtherRead|OtherWrite), + GroupRW = (GroupRead|GroupWrite), + OwnerRW = (OwnerRead|OwnerWrite) + }; + + enum Type + { + File = 0x01, + Directory = 0x02 + }; + + WiiFile(); + WiiFile(const std::string& filename); + WiiFile(const std::string& filename, Uint8 permissions, const Uint8* data, Uint32 length); + virtual ~WiiFile(); + + void setFilename(const std::string& filename); + std::string filename() const; + + void setData(const Uint8* data); + Uint8* data() const; + + void setLength(const int len); + int length() const; + + void setPermissions(const Uint8 permissions); + Uint8 permissions() const; + + void setAttributes(const Uint8 attr); + Uint8 attributes() const; + + void setType(Type type); + Type type() const; + + bool isDirectory() const; + bool isFile() const; + +protected: +private: + Uint8 m_permissions; + Uint8 m_attributes; + Type m_type; + std::string m_filename; + int m_fileLen; + Uint8* m_fileData; +}; + +#endif // WIIFILE_H diff --git a/include/WiiSave.h b/include/WiiSave.h new file mode 100644 index 0000000..713eef7 --- /dev/null +++ b/include/WiiSave.h @@ -0,0 +1,47 @@ +#ifndef WIISAVE_H +#define WIISAVE_H + +#include +#include +#include + +class WiiFile; +class WiiBanner; +class WiiImage; +class BinaryReader; +class BinaryWriter; + +class WiiSave +{ +public: + typedef std::unordered_map::const_iterator FileIterator; + WiiSave(); + virtual ~WiiSave(); + + bool saveToFile(const std::string& filepath, Uint8* macAddress, Uint32 ngId, Uint8* ngPriv, Uint8* ngSig, Uint32 ngKeyId); + + void addFile(const std::string& filename, WiiFile* file); + WiiFile* getFile(const std::string& filename) const; + std::unordered_map& getFileList(); + + void setBanner(WiiBanner* banner); + WiiBanner* banner() const; + +protected: +private: + + void writeBanner(); + Uint32 writeFile(WiiFile* file); + void writeImage(WiiImage* image); + void writeCerts(Uint32 filesSize, Uint32 ngId, Uint8* ngPriv, Uint8* ngSig, Uint32 ngKeyId); + + std::string readNullTermString(); + + std::unordered_map m_files; + WiiBanner* m_banner; + + BinaryReader* m_reader; + BinaryWriter* m_writer; +}; + +#endif // WIISAVE_H diff --git a/include/WiiSave.hpp b/include/WiiSave.hpp new file mode 100644 index 0000000..5b59055 --- /dev/null +++ b/include/WiiSave.hpp @@ -0,0 +1,37 @@ +#ifndef WIISAVE_H +#define WIISAVE_H + +#include +#include +#include + +class WiiFile; +class WiiBanner; +class WiiImage; +class BinaryReader; +class BinaryWriter; + +class WiiSave +{ +public: + + typedef std::unordered_map::const_iterator FileIterator; + WiiSave(); + virtual ~WiiSave(); + + void addFile(const std::string& filename, WiiFile* file); + WiiFile* file(const std::string& filename) const; + std::unordered_map& fileList(); + + void setBanner(WiiBanner* banner); + WiiBanner* banner() const; + +protected: +private: + + std::unordered_map m_files; + WiiBanner* m_banner; + +}; + +#endif // WIISAVE_H diff --git a/include/WiiSaveReader.hpp b/include/WiiSaveReader.hpp new file mode 100644 index 0000000..16860e8 --- /dev/null +++ b/include/WiiSaveReader.hpp @@ -0,0 +1,26 @@ +#ifndef __WII_SAVE_READER_HPP__ +#define __WII_SAVE_READER_HPP__ + +#include +#include +#include + +class WiiSave; +class WiiBanner; +class WiiFile; +class WiiImage; + +class WiiSaveReader : public BinaryReader +{ +public: + WiiSaveReader(const Uint8*, Uint64); + WiiSaveReader(const std::string&); + + WiiSave* readSave(); +private: + WiiBanner* readBanner(); + WiiFile* readFile(); + WiiImage* readImage(Uint32 width, Uint32 height); + void readCerts(Uint32 totalSize); +}; +#endif // __WII_SAVE_READER_HPP__ diff --git a/include/WiiSaveWriter.hpp b/include/WiiSaveWriter.hpp new file mode 100644 index 0000000..910e51b --- /dev/null +++ b/include/WiiSaveWriter.hpp @@ -0,0 +1,27 @@ +#ifndef __WII_SAVE_WRITER_HPP__ +#define __WII_SAVE_WRITER_HPP__ + +#include +#include +#include + +class WiiSave; +class WiiBanner; +class WiiFile; +class WiiImage; + +class WiiSaveWriter : public BinaryWriter +{ +public: + WiiSaveWriter(const std::string&); + + bool writeSave(WiiSave* save, Uint8* macAddress, Uint32 ngId, Uint8* ngPriv, Uint8* ngSig, Uint32 ngKeyId, const std::string& filepath = ""); + +private: + void writeBanner(WiiBanner* banner); + Uint32 writeFile(WiiFile* file); + void writeImage(WiiImage* image); + void writeCerts(Uint32 filesSize, Uint32 ngId, Uint8* ngPriv, Uint8* ngSig, Uint32 ngKeyId); +}; + +#endif // __WII_SAVE_WRITER_HPP__ diff --git a/include/aes.h b/include/aes.h new file mode 100644 index 0000000..c87aae5 --- /dev/null +++ b/include/aes.h @@ -0,0 +1,20 @@ +#ifndef __AES_H_ +#define __AES_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void aes_encrypt(Uint8 *iv, const Uint8 *inbuf, Uint8 *outbuf, Uint64 len); +void aes_decrypt(Uint8 *iv, const Uint8 *inbuf, Uint8 *outbuf, Uint64 len); +void aes_set_key(const Uint8 *key ); + +#ifdef __cplusplus +} +#endif + +#endif //__AES_H_ + + diff --git a/include/bn.h b/include/bn.h new file mode 100644 index 0000000..4ac8e26 --- /dev/null +++ b/include/bn.h @@ -0,0 +1,13 @@ +#ifndef BN_H +#define BN_H + +#include + +int bn_compare(Uint8 *a, Uint8 *b, Uint32 n); +void bn_sub_modulus(Uint8 *a, Uint8 *N, Uint32 n); +void bn_add(Uint8 *d, Uint8 *a, Uint8 *b, Uint8 *N, Uint32 n); +void bn_mul(Uint8 *d, Uint8 *a, Uint8 *b, Uint8 *N, Uint32 n); +void bn_exp(Uint8 *d, Uint8 *a, Uint8 *N, Uint32 n, Uint8 *e, Uint32 en); +void bn_inv(Uint8 *d, Uint8 *a, Uint8 *N, Uint32 n); + +#endif // BN_H diff --git a/include/ec.h b/include/ec.h new file mode 100644 index 0000000..e39d889 --- /dev/null +++ b/include/ec.h @@ -0,0 +1,10 @@ +#ifndef EC_H +#define EC_H +#include + +bool check_ec ( Uint8 *ng, Uint8 *ap, Uint8 *sig, Uint8 *sig_hash ); +void make_ec_cert ( Uint8 *cert, Uint8 *sig, char *signer, char *name, Uint8 *priv, Uint32 key_id ); +void generate_ecdsa( Uint8 *R, Uint8 *S, Uint8 *k, Uint8 *hash ); + +#endif // EC_H + diff --git a/include/md5.h b/include/md5.h new file mode 100644 index 0000000..a7cb9fd --- /dev/null +++ b/include/md5.h @@ -0,0 +1,241 @@ +#ifndef MD5_H +#define MD5_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* ========================================================================== ** + * + * MD5.h + * + * Copyright: + * Copyright (C) 2003-2005 by Christopher R. Hertel + * + * Email: crh@ubiqx.mn.org + * + * $Id: MD5.h,v 0.6 2005/06/08 18:35:59 crh Exp $ + * + * Modifications and additions by dimok + * + * -------------------------------------------------------------------------- ** + * + * Description: + * Implements the MD5 hash algorithm, as described in RFC 1321. + * + * -------------------------------------------------------------------------- ** + * + * License: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -------------------------------------------------------------------------- ** + * + * Notes: + * + * None of this will make any sense unless you're studying RFC 1321 as you + * read the code. + * + * MD5 is described in RFC 1321. + * The MD*4* algorithm is described in RFC 1320 (that's 1321 - 1). + * MD5 is very similar to MD4, but not quite similar enough to justify + * putting the two into a single module. Besides, I wanted to add a few + * extra functions to this one to expand its usability. + * + * There are three primary motivations for this particular implementation. + * 1) Programmer's pride. I wanted to be able to say I'd done it, and I + * wanted to learn from the experience. + * 2) Portability. I wanted an implementation that I knew to be portable + * to a reasonable number of platforms. In particular, the algorithm is + * designed with little-endian platforms in mind, but I wanted an + * endian-agnostic implementation. + * 3) Compactness. While not an overriding goal, I thought it worth-while + * to see if I could reduce the overall size of the result. This is in + * keeping with my hopes that this library will be suitable for use in + * some embedded environments. + * Beyond that, cleanliness and clarity are always worth pursuing. + * + * As mentioned above, the code really only makes sense if you are familiar + * with the MD5 algorithm or are using RFC 1321 as a guide. This code is + * quirky, however, so you'll want to be reading carefully. + * + * Yeah...most of the comments are cut-and-paste from my MD4 implementation. + * + * -------------------------------------------------------------------------- ** + * + * References: + * IETF RFC 1321: The MD5 Message-Digest Algorithm + * Ron Rivest. IETF, April, 1992 + * + * ========================================================================== ** + */ + /* -------------------------------------------------------------------------- ** + * Typedefs: + */ + + typedef struct + { + unsigned int len; + unsigned int ABCD[4]; + int b_used; + unsigned char block[64]; + } auth_md5Ctx; + + /* -------------------------------------------------------------------------- ** + * Functions: + */ + + auth_md5Ctx *auth_md5InitCtx(auth_md5Ctx *ctx); + /* ------------------------------------------------------------------------ ** + * Initialize an MD5 context. + * + * Input: ctx - A pointer to the MD5 context structure to be initialized. + * Contexts are typically created thusly: + * ctx = (auth_md5Ctx *)malloc( sizeof(auth_md5Ctx) ); + * + * Output: A pointer to the initialized context (same as ). + * + * Notes: The purpose of the context is to make it possible to generate + * an MD5 Message Digest in stages, rather than having to pass a + * single large block to a single MD5 function. The context + * structure keeps track of various bits of state information. + * + * Once the context is initialized, the blocks of message data + * are passed to the function. Once the + * final bit of data has been handed to the + * context can be closed out by calling , + * which also calculates the final MD5 result. + * + * Don't forget to free an allocated context structure when + * you've finished using it. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ + + auth_md5Ctx *auth_md5SumCtx(auth_md5Ctx *ctx, const unsigned char *src, const int len); + /* ------------------------------------------------------------------------ ** + * Build an MD5 Message Digest within the given context. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * src - A chunk of source data. This will be used to drive + * the MD5 algorithm. + * len - The number of bytes in . + * + * Output: A pointer to the updated context (same as ). + * + * See Also: , , + * + * ------------------------------------------------------------------------ ** + */ + + auth_md5Ctx *auth_md5CloseCtx(auth_md5Ctx *ctx, unsigned char *dst); + /* ------------------------------------------------------------------------ ** + * Close an MD5 Message Digest context and generate the final MD5 sum. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * dst - A pointer to at least 16 bytes of memory, which will + * receive the finished MD5 sum. + * + * Output: A pointer to the closed context (same as ). + * You might use this to free a malloc'd context structure. :) + * + * Notes: The context () is returned in an undefined state. + * It must be re-initialized before re-use. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ + + unsigned char * MD5(unsigned char * hash, const unsigned char *src, const int len); + /* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - Source data block to be MD5'd. + * len - The length, in bytes, of the source block. + * (Note that the length is given in bytes, not bits.) + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ + + unsigned char * MD5fromFile(unsigned char *dst, const char *src); + /* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - filepath to the file to be MD5'd. + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ + + const char * MD5ToString(const unsigned char *hash, char *dst); + unsigned char * StringToMD5(const char * hash, unsigned char * dst); + +/* ========================================================================== */ + +#ifdef __cplusplus +} +#endif +#endif /* AUTH_MD5_H */ diff --git a/include/sha1.h b/include/sha1.h new file mode 100644 index 0000000..4f1bdfc --- /dev/null +++ b/include/sha1.h @@ -0,0 +1,46 @@ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, + const unsigned char *, + unsigned); + + + +Uint8* getSha1( Uint8 * stuff, Uint32 stuff_size ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libzelda.layout b/libzelda.layout new file mode 100644 index 0000000..dbda020 --- /dev/null +++ b/libzelda.layout @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/libzelda.pro b/libzelda.pro index 9453c34..2e9ae2d 100644 --- a/libzelda.pro +++ b/libzelda.pro @@ -7,6 +7,9 @@ INCLUDEPATH += include HEADERS += \ include/utility.hpp \ include/utf8.h \ + include/utf8/unchecked.h \ + include/utf8/core.h \ + include/utf8/checked.h \ include/Types.hpp \ include/TextStream.hpp \ include/Stream.hpp \ @@ -17,16 +20,36 @@ HEADERS += \ include/Exception.hpp \ include/BinaryWriter.hpp \ include/BinaryReader.hpp \ - include/utf8/unchecked.h \ - include/utf8/core.h \ - include/utf8/checked.h + include/WiiBanner.h \ + include/WiiBanner.hpp \ + include/WiiFile.h \ + include/WiiFile.hpp \ + include/WiiSave.h \ + include/WiiSave.hpp \ + include/WiiSaveReader.hpp \ + include/WiiSaveWriter.hpp \ + include/aes.h \ + include/bn.h \ + include/ec.h \ + include/md5.h \ + include/sha1.h SOURCES += \ src/utility.cpp \ src/TextStream.cpp \ src/Stream.cpp \ src/BinaryWriter.cpp \ - src/BinaryReader.cpp + src/BinaryReader.cpp \ + src/WiiBanner.cpp \ + src/WiiFile.cpp \ + src/WiiSave.cpp \ + src/WiiSaveReader.cpp \ + src/WiiSaveWriter.cpp \ + src/aes.c \ + src/bn.cpp \ + src/ec.cpp \ + src/md5.c \ + src/sha1.cpp system("exec doxygen libzelda.conf") system("cd doc/latex && make") diff --git a/src/WiiBanner.cpp b/src/WiiBanner.cpp new file mode 100644 index 0000000..8c3d9ec --- /dev/null +++ b/src/WiiBanner.cpp @@ -0,0 +1,165 @@ +#include "WiiBanner.h" +#include +#include + +WiiImage::WiiImage(Uint32 width, Uint32 height, Uint8* data) : + m_width(width), + m_height(height), + m_data(data) +{ +} +WiiImage::~WiiImage() +{ + if (m_data) + delete[] m_data; + m_data = NULL; +} + +Uint8* WiiImage::data() +{ + return m_data; +} + +Uint32 WiiImage::width() const +{ + return m_width; +} + +Uint32 WiiImage::height() const +{ + return m_height; +} + +WiiBanner::WiiBanner() : + m_gameId(0), + m_banner(NULL), + m_flags(0), + m_bannerSize(0), + m_title(""), + m_subtitle("") +{ +} + +WiiBanner::WiiBanner(Uint32 gameId, const std::string& title, + const std::string& subtitle, WiiImage* banner, std::vector icons) : + m_gameId(gameId), + m_banner(banner), + m_flags(0), + m_bannerSize(0), + m_icons(icons), + m_title(title), + m_subtitle(subtitle) +{ +} + +WiiBanner::~WiiBanner() +{ + delete m_banner; + m_icons.clear(); +} + +void WiiBanner::setGameID(Uint64 id) +{ + m_gameId = id; +} + +Uint64 WiiBanner::gameID() const +{ + return m_gameId; +} +void WiiBanner::setTitle(const std::string& title) +{ + m_title = title; +} + +std::string WiiBanner::title() const +{ + return m_title; +} + +void WiiBanner::setSubtitle(const std::string& subtitle) +{ + m_subtitle = subtitle; +} + +std::string WiiBanner::subtitle() const +{ + return m_subtitle; +} + +void WiiBanner::addIcon(WiiImage* icon) +{ + m_icons.push_back(icon); +} + +void WiiBanner::setIcon(Uint32 id, WiiImage* icon) +{ + if (m_icons[id] != NULL) + { + delete m_icons[id]; + m_icons[id] = icon; + } +} + +WiiImage* WiiBanner::getIcon(Uint32 id) const +{ + if (!m_icons[id]) + return NULL; + + return m_icons[id]; +} +std::vector WiiBanner::icons() const +{ + return m_icons; +} + +void WiiBanner::setBannerImage(WiiImage* banner) +{ + m_banner = banner; +} + +WiiImage* WiiBanner::bannerImage() const +{ + return m_banner; +} + +void WiiBanner::setAnimationSpeed(Uint16 animSpeed) +{ + m_animSpeed = animSpeed; +} + +Uint16 WiiBanner::animationSpeed() const +{ + return m_animSpeed; +} + +void WiiBanner::setPermissions(Uint8 permissions) +{ + m_permissions = permissions; +} + +Uint8 WiiBanner::permissions() const +{ + return m_permissions; +} + +void WiiBanner::setBannerSize(Uint32 size) +{ + m_bannerSize = size; +} + +Uint32 WiiBanner::bannerSize() const +{ + return m_bannerSize; +} + +void WiiBanner::setFlags(Uint32 flags) +{ + m_flags = flags; +} + +Uint32 WiiBanner::flags() const +{ + return m_flags; +} + diff --git a/src/WiiFile.cpp b/src/WiiFile.cpp new file mode 100644 index 0000000..fab668f --- /dev/null +++ b/src/WiiFile.cpp @@ -0,0 +1,115 @@ +#include "WiiFile.h" + +WiiFile::WiiFile() : + m_permissions(WiiFile::GroupRW|WiiFile::OtherRW|WiiFile::OwnerRW), + m_attributes(0), + m_type(WiiFile::File), + m_filename(""), + m_fileLen(0), + m_fileData(NULL) +{ + //ctor +} + +WiiFile::WiiFile(const std::string& filename) : + m_permissions(WiiFile::GroupRW|WiiFile::OtherRW|WiiFile::OwnerRW), + m_attributes(0), + m_type(WiiFile::File), + m_filename(filename), + m_fileLen(0), + m_fileData(NULL) +{ +} + +WiiFile::WiiFile(const std::string& filename, Uint8 permissions, const Uint8* data, Uint32 length) : + m_permissions(permissions), + m_attributes(0), + m_type(WiiFile::File), + m_filename(filename), + m_fileLen(length), + m_fileData((Uint8*)data) +{ +} + +WiiFile::~WiiFile() +{ + if (m_fileData) + delete[] m_fileData; +} + + +void WiiFile::setFilename(const std::string& filename) +{ + m_filename = filename; +} + +std::string WiiFile::filename() const +{ + return m_filename; +} + +void WiiFile::setPermissions(const Uint8 permissions) +{ + m_permissions = (Uint8)permissions; +} + +Uint8 WiiFile::permissions() const +{ + return m_permissions; +} + +void WiiFile::setData(const Uint8* data) +{ + if (m_fileData) + { + delete[] m_fileData; + m_fileData = NULL; + } + m_fileData = (Uint8*)data; +} + +Uint8* WiiFile::data() const +{ + return m_fileData; +} + +void WiiFile::setLength(const int len) +{ + m_fileLen = (int)len; +} + +int WiiFile::length() const +{ + return m_fileLen; +} + +void WiiFile::setAttributes(const Uint8 attr) +{ + m_attributes = attr; +} + +Uint8 WiiFile::attributes() const +{ + return m_attributes; +} + +void WiiFile::setType(WiiFile::Type type) +{ + m_type = type; +} + +WiiFile::Type WiiFile::type() const +{ + return m_type; +} + +bool WiiFile::isDirectory() const +{ + return (m_type == WiiFile::Directory); +} + +bool WiiFile::isFile() const +{ + return (m_type == WiiFile::File); +} + diff --git a/src/WiiSave.cpp b/src/WiiSave.cpp new file mode 100644 index 0000000..f81f4e2 --- /dev/null +++ b/src/WiiSave.cpp @@ -0,0 +1,66 @@ +#include "WiiSave.h" +#include "WiiFile.h" +#include "WiiBanner.h" +#include "BinaryReader.hpp" +#include "BinaryWriter.hpp" +#include "IOException.hpp" +#include "aes.h" +#include "ec.h" +#include +#include "md5.h" +#include "sha1.h" + +#include +#include +#include +#include +#include +#include + + +WiiSave::WiiSave() + : m_banner(NULL) +{ +} + +WiiSave::~WiiSave() +{ + m_files.clear(); + + delete m_banner; + m_banner = NULL; + +} + +void WiiSave::addFile(const std::string& filepath, WiiFile* file) +{ + m_files[filepath] = file; +} + +WiiFile* WiiSave::file(const std::string& filepath) const +{ + std::unordered_map::const_iterator iter = m_files.begin(); + + for (;iter != m_files.end(); ++iter) + { + if (iter->first == filepath) + return (WiiFile*)iter->second; + } + + return NULL; +} + +std::unordered_map& WiiSave::fileList() +{ + return m_files; +} + +void WiiSave::setBanner(WiiBanner* banner) +{ + m_banner = banner; +} + +WiiBanner* WiiSave::banner() const +{ + return m_banner; +} diff --git a/src/WiiSaveReader.cpp b/src/WiiSaveReader.cpp new file mode 100644 index 0000000..30439dc --- /dev/null +++ b/src/WiiSaveReader.cpp @@ -0,0 +1,276 @@ +#include "WiiSaveReader.hpp" +#include "WiiSave.h" +#include "WiiFile.h" +#include "WiiBanner.h" +#include "md5.h" +#include "aes.h" +#include "ec.h" +#include "sha1.h" +#include +#include +#include +#include +#include + + +const Uint8 SD_KEY[16] = {0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08, 0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d}; +const Uint8 SD_IV[16] = {0x21, 0x67, 0x12, 0xe6, 0xaa, 0x1f, 0x68, 0x9f, 0x95, 0xc5, 0xa2, 0x23, 0x24, 0xdc, 0x6a, 0x98}; +const Uint8 MD5_BLANKER[16] = {0x0e, 0x65, 0x37, 0x81, 0x99, 0xbe, 0x45, 0x17, 0xab, 0x06, 0xec, 0x22, 0x45, 0x1a, 0x57, 0x93}; + +WiiSaveReader::WiiSaveReader(const Uint8* data, Uint64 length) + : BinaryReader(data, length) +{ + setEndianess(BigEndian); +} + +WiiSaveReader::WiiSaveReader(const std::string& filename) + : BinaryReader(filename) +{ + std::cout << filename << std::endl; + setEndianess(BigEndian); +} + +WiiSave* WiiSaveReader::readSave() +{ + WiiSave* ret = new WiiSave; + if (length() < 0xF0C0) + throw IOException("Not a valid WiiSave"); + + WiiBanner* banner = this->readBanner(); + if (!banner) + throw IOException("Invalid banner"); + + ret->setBanner(banner); + Uint32 bkVer = this->readUInt32(); + + if (bkVer != 0x00000070) + throw IOException("Invalid BacKup header size"); + + Uint32 bkMagic = this->readUInt32(); + bkMagic = bkMagic; + if (bkMagic != 0x426B0001) + throw IOException("Invalid BacKup header magic"); + + Uint32 ngId = this->readUInt32(); + ngId = ngId; + + Uint32 numFiles = this->readUInt32(); + + /*int fileSize =*/ this->readUInt32(); + seek(8); // skip unknown data; + + Uint32 totalSize = this->readUInt32(); + this->seek(64); // Unknown (Most likely padding) + this->seek(8); + this->seek(6); + this->seek(2); + this->seek(0x10); + + WiiFile* file; + for (Uint32 i = 0; i < numFiles; ++i) + { + file = readFile(); + if (file) + ret->addFile("/" + file->filename(), file); + } + + readCerts(totalSize); + return ret; +} + +WiiBanner* WiiSaveReader::readBanner() +{ + Uint8* dec = new Uint8[0xf0c0]; + memset(dec, 0, 0xF0C0); + Uint8* data = (Uint8*)this->readBytes(0xF0C0); + Uint8* oldData = this->data(); + Uint64 oldPos = this->position(); + Uint64 oldLen = this->length(); + Uint64 gameId; + Uint32 bannerSize; + Uint8 permissions; + Uint8 md5[16]; + Uint8 md5Calc[16]; + Uint8 tmpIV[26]; + memcpy(tmpIV, SD_IV, 16); + + aes_set_key(SD_KEY); + aes_decrypt(tmpIV, data, dec, 0xF0C0); + memset(md5, 0, 16); + memset(md5Calc, 0, 16); + // Read in the MD5 sum + memcpy(md5, (dec + 0x0E), 0x10); + // Write the blanker to the buffer + memcpy((dec + 0x0E), MD5_BLANKER, 0x10); + MD5(md5Calc, dec, 0xF0C0); + + // Compare the Calculated MD5 to the one from the file. + // This needs to be done incase the file is corrupted. + if (memcmp(md5, md5Calc, 0x10)) + { + std::cerr << "MD5 Mismatch" << std::endl; + // Make sure to reset m_reader values back to the old ones. + std::cerr << "MD5 provided: "; + for (int i = 0; i < 16; ++i) + std::cerr << std::setw(2) << std::setfill('0') << std::hex << (int)(md5[i]); + std::cerr << std::endl; + + std::cerr << "MD5 Calculated: "; + for (int i = 0; i < 16; ++i) + std::cerr << std::hex << (int)(md5Calc[i]); + std::cerr << std::endl; + this->setData(oldData, oldLen); + this->seek(oldPos, Stream::Beginning); + throw IOException("MD5 Mismatch"); + } + // Set the binary reader buffer; + this->setData(dec, 0xF0C0); + // Start reading the header + gameId = this->readUInt64(); + bannerSize = this->readUInt32(); + permissions = this->readByte(); +/* unk =*/ this->readByte(); + this->seek(0x10); + // skip padding + this->seek(2); + + int magic; + int flags; + short animSpeed; + std::string gameTitle; + std::string subTitle; + + magic = this->readUInt32(); + + // Ensure that the header magic is valid. + if (magic != 0x5749424E) + { + // Make sure to reset m_reader values back to the old ones. + this->setData(oldData, oldLen); + this->seek(oldPos, Stream::Beginning); + throw IOException("Invalid Header Magic"); + } + + flags = this->readUInt32(); + animSpeed = this->readUInt16(); + this->seek(22); + + gameTitle = this->readUnicode(); + if (this->position() != 0x0080) + this->seek(0x0080, Stream::Beginning); + + subTitle = this->readUnicode(); + if (this->position() != 0x00C0) + this->seek(0x00C0, Stream::Beginning); + + WiiBanner* banner = new WiiBanner; + banner->setGameID(gameId); + banner->setTitle(gameTitle); + banner->setSubtitle(subTitle); + banner->setBannerSize(bannerSize); + WiiImage* bannerImage = readImage(192, 64); + banner->setBannerImage(bannerImage); + banner->setAnimationSpeed(animSpeed); + banner->setPermissions(permissions); + banner->setFlags(flags); + + + if (banner->bannerSize() == 0x72a0) + { + WiiImage* icon = readImage(48, 48); + if (icon) + banner->addIcon(icon); + else + std::cerr << "Warning: Icon empty, skipping" << std::endl; + } + else + { + for(int i = 0; i < 8; i++) + { + WiiImage* icon = readImage(48, 48); + if (icon) + banner->addIcon(icon); + else + std::cerr << "Warning: Icon empty, skipping" << std::endl; + } + } + + this->setData(oldData, oldLen); + this->seek(oldPos, Stream::Beginning); + return banner; +} + +WiiImage* WiiSaveReader::readImage(Uint32 width, Uint32 height) +{ + Uint8* image = (Uint8*)this->readBytes(width*height*2); + + if (!isEmpty((Int8*)image, width*height*2)) + return new WiiImage(width, height, image); + + return NULL; +} + + +WiiFile* WiiSaveReader::readFile() +{ + Uint32 fileLen; + Uint8 permissions; + Uint8 attributes; + Uint8 type; + std::string name; + Uint8* filedata; + WiiFile* ret; + + Uint32 magic = this->readUInt32(); + if (magic != 0x03adf17e) + { + std::cerr << "Not a valid File entry header: 0x" << std::hex << magic << std::endl; + return NULL; + } + + fileLen = this->readUInt32(); + permissions = this->readByte(); + attributes = this->readByte(); + type = (WiiFile::Type)this->readByte(); + name = std::string((const char*)this->readBytes(0x45)); + ret = new WiiFile(std::string(name)); + ret->setPermissions(permissions); + ret->setAttributes(attributes); + ret->setType((WiiFile::Type)type); + Uint8* iv = (Uint8*)this->readBytes(0x10); + this->seek(0x20); + + if (type == WiiFile::File) + { + // Read file data + int roundedLen = (fileLen + 63) & ~63; + filedata = (Uint8*)this->readBytes(roundedLen); + + // Decrypt file + Uint8* decData = new Uint8[roundedLen]; + aes_set_key(SD_KEY); + aes_decrypt(iv, filedata, decData, roundedLen); + delete filedata; + ret->setData(decData); + ret->setLength(fileLen); + } + + return ret; +} + + +void WiiSaveReader::readCerts(Uint32 totalSize) +{ + Uint32 dataSize = totalSize - 0x340; + Uint8* sig = (Uint8*)this->readBytes(0x40); + Uint8* ngCert = (Uint8*)this->readBytes(0x180); + Uint8* apCert = (Uint8*)this->readBytes(0x180); + this->seek(0xF0C0, Stream::Beginning); + Uint8* data = (Uint8*)this->readBytes(dataSize); + Uint8* hash; + + hash = getSha1(data, dataSize); + Uint8* hash2 = getSha1(hash, 20); + + check_ec(ngCert, apCert, sig, hash2); +} diff --git a/src/WiiSaveWriter.cpp b/src/WiiSaveWriter.cpp new file mode 100644 index 0000000..63b702f --- /dev/null +++ b/src/WiiSaveWriter.cpp @@ -0,0 +1,238 @@ +#include "WiiSaveWriter.hpp" + +#include "WiiSave.h" +#include "WiiFile.h" +#include "WiiBanner.h" + +#include "WiiSave.h" +#include "WiiFile.h" +#include "WiiBanner.h" +#include "BinaryWriter.hpp" +#include "IOException.hpp" +#include "aes.h" +#include "ec.h" +#include +#include "md5.h" +#include "sha1.h" + +#include +#include +#include +#include +#include +#include + + +const Uint8 SD_KEY[16] = {0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08, 0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d}; +const Uint8 SD_IV[16] = {0x21, 0x67, 0x12, 0xe6, 0xaa, 0x1f, 0x68, 0x9f, 0x95, 0xc5, 0xa2, 0x23, 0x24, 0xdc, 0x6a, 0x98}; +const Uint8 MD5_BLANKER[16] = {0x0e, 0x65, 0x37, 0x81, 0x99, 0xbe, 0x45, 0x17, 0xab, 0x06, 0xec, 0x22, 0x45, 0x1a, 0x57, 0x93}; + +WiiSaveWriter::WiiSaveWriter(const std::string &filename) + : BinaryWriter(filename) +{ + this->setAutoResizing(true); + this->setEndianess(Stream::BigEndian); +} + + +bool WiiSaveWriter::writeSave(WiiSave *save, Uint8 *macAddress, Uint32 ngId, Uint8 *ngPriv, Uint8 *ngSig, Uint32 ngKeyId,const std::string &filepath) +{ + if (filepath != "") + m_filepath = filepath; + + writeBanner(save->banner()); + + this->writeUInt32(0x70); + this->writeUInt32(0x426B0001); + this->writeUInt32(ngId); // NG-ID + this->writeUInt32(save->fileList().size()); + this->writeUInt32(0); // Size of files; + this->seek(8); + this->writeUInt32(0); // totalSize + this->seek(64); + this->writeUInt64(save->banner()->gameID()); + this->writeBytes((Int8*)macAddress, 6); + this->seek(2); // unknown; + this->seek(0x10); // padding; + Uint32 totalSize = 0; + for (std::unordered_map::const_iterator iter = save->fileList().begin(); iter != save->fileList().end(); ++iter) + { + totalSize += writeFile(iter->second); + } + int pos = this->position(); + // Write size data + this->seek(0xF0C0 + 0x10, Stream::Beginning); + this->writeUInt32(totalSize); + this->seek(0xF0C0 + 0x1C, Stream::Beginning); + this->writeUInt32(totalSize + 0x3c0); + this->seek(pos, Stream::Beginning); + + writeCerts(totalSize, ngId, ngPriv, ngSig, ngKeyId); + + this->save(); + + return true; +} + +void WiiSaveWriter::writeBanner(WiiBanner *banner) +{ + this->setEndianess(Stream::BigEndian); + this->setAutoResizing(true); + this->writeInt64(banner->gameID()); + this->writeInt32((0x60a0+0x1200)*banner->icons().size()); + this->writeByte((Int8)banner->permissions()); + this->seek(1); + this->writeBytes((Int8*)MD5_BLANKER, 16); + this->seek(2); + this->writeInt32(0x5749424E); // WIBN + this->writeInt32(banner->flags()); + this->writeInt16(banner->animationSpeed()); + this->seek(22); + + this->writeUnicode(banner->title()); + + if (this->position() != 0x0080) + this->seek(0x0080, Stream::Beginning); + + this->writeUnicode(banner->subtitle()); + + if (this->position() != 0x00C0) + this->seek(0x00C0, Stream::Beginning); + + WiiImage* bannerImage = banner->bannerImage(); + this->writeBytes((Int8*)bannerImage->data(), bannerImage->width()*bannerImage->height()*2); + + // For empty icons + Uint8* tmpIcon = new Uint8[48*48*2]; + memset(tmpIcon, 0, 48*48*2); + for (Uint32 i = 0; i < 8; ++i) + { + if (i < banner->icons().size()) + { + writeImage(banner->icons()[i]); + } + else + { + this->writeBytes((Int8*)tmpIcon, 48*48*2); + } + } + + delete[] tmpIcon; // delete tmp buffer; + + Uint8* hash = new Uint8[0x10]; + MD5(hash, (Uint8*)this->data(), 0xF0C0); + this->seek(0x0E, Stream::Beginning); + this->writeBytes((Int8*)hash, 0x10); + + aes_set_key(SD_KEY); + Uint8 data[0xF0C0]; + memcpy(data, this->data(), 0xF0C0); + Uint8 tmpIV[26]; + memcpy(tmpIV, SD_IV, 16); + aes_encrypt(tmpIV, data, data, 0xF0C0); + + this->seek(0, Stream::Beginning); + this->writeBytes((Int8*)data, 0xF0C0); + this->seek(0xF0C0, Stream::Beginning); +} + +Uint32 WiiSaveWriter::writeFile(WiiFile *file) +{ + Uint32 ret = 0x80; + + // Write the File magic + this->writeUInt32(0x03ADF17E); + this->writeUInt32(file->length()); + this->writeByte(file->permissions()); + this->writeByte(file->attributes()); + this->writeByte(file->type()); + + Uint8 name[0x45]; + fillRandom(name, 0x45); + memcpy(name, file->filename().c_str(), file->filename().size()); + name[file->filename().size()] = '\0'; + this->writeBytes((Int8*)name, 0x45); + Uint8 iv[16]; + fillRandom(iv, 0x10); + this->writeBytes((Int8*)iv, 0x10); + Uint8 crap[0x20]; + fillRandom(crap, 0x20); + this->writeBytes((Int8*)crap, 0x20); + + if (file->type() == WiiFile::File) + { + int roundedSize = (file->length() + 63) & ~63; + Uint8* data = new Uint8[roundedSize]; + memset(data, 0, roundedSize); + + aes_set_key(SD_KEY); + aes_encrypt(iv, file->data(), data, roundedSize); + + this->writeBytes((Int8*)data, roundedSize); + ret += roundedSize; + delete[] data; + } + + return ret; +} + + +void WiiSaveWriter::writeImage(WiiImage* image) +{ + Int8* data = (Int8*)image->data(); + this->writeBytes(data, image->width() * image->height() * 2); +} + +void WiiSaveWriter::writeCerts(Uint32 filesSize, Uint32 ngId, Uint8 *ngPriv, Uint8 *ngSig, Uint32 ngKeyId) +{ + Uint8 sig[0x40]; + Uint8 ngCert[0x180]; + Uint8 apCert[0x180]; + Uint8* hash; + Uint8 apPriv[30]; + Uint8 apSig[60]; + char signer[64]; + char name[64]; + Uint8* data; + Uint32 dataSize; + + sprintf(signer, "Root-CA00000001-MS00000002"); + sprintf(name, "NG%08x", ngId); + make_ec_cert(ngCert, ngSig, signer, name, ngPriv, ngKeyId); + + memset(apPriv, 0, 30); + apPriv[10] = 1; + + memset(apSig, 81, 30); + + sprintf(signer, "Root-CA00000001-MS00000002-NG%08x", ngId); + sprintf(name, "AP%08x%08x", 1, 2); + make_ec_cert(apCert, apSig, signer, name, apPriv, 0); + + hash = getSha1(apCert + 0x80, 0x100); + generate_ecdsa(apSig, apSig+30, ngPriv, hash); + make_ec_cert(apCert, apSig, signer, name, apPriv, 0); + delete[] hash; + + dataSize = filesSize + 0x80; + data = new Uint8[dataSize]; + Uint8* rawData = this->data(); + memcpy(data, rawData + 0xF0C0, dataSize); + + hash = getSha1(data, dataSize); + Uint8* hash2 = getSha1(hash, 20); + delete[] hash; + delete[] data; + + generate_ecdsa(sig, sig+30, apPriv, hash2); + int stuff = 0x2f536969; + if (!isSystemBigEndian()) + stuff = swap32(stuff); + + *(Uint32*)(sig+60) = stuff; + delete[] hash2; + + this->writeBytes((Int8*)sig, 0x40); + this->writeBytes((Int8*)ngCert, 0x180); + this->writeBytes((Int8*)apCert, 0x180); +} diff --git a/src/aes.c b/src/aes.c new file mode 100644 index 0000000..717c222 --- /dev/null +++ b/src/aes.c @@ -0,0 +1,403 @@ +/* Rijndael Block Cipher - aes.c + + Written by Mike Scott 21st April 1999 + mike@compapp.dcu.ie + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + +*/ + +#include +//#include +#include + +typedef unsigned char u8; /* 8 bits */ +typedef unsigned long u32; /* 32 bits */ +typedef unsigned long long u64; + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 byte */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4]={0xB,0xD,0x9,0xE}; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256],ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; + +/* Parameter-dependent data */ + +int Nk,Nb,Nr; +u8 fi[24],ri[24]; +u32 fkey[120]; +u32 rkey[120]; + +static u32 pack(u8 *b) +{ /* pack bytes into a 32-bit Word */ + return ((u32)b[3]<<24)|((u32)b[2]<<16)|((u32)b[1]<<8)|(u32)b[0]; +} + +static void unpack(u32 a,u8 *b) +{ /* unpack bytes from a word */ + b[0]=(u8)a; + b[1]=(u8)(a>>8); + b[2]=(u8)(a>>16); + b[3]=(u8)(a>>24); +} + +static u8 xtime(u8 a) +{ + u8 b; + if (a&0x80) b=0x1B; + else b=0; + a<<=1; + a^=b; + return a; +} + +static u8 bmul(u8 x,u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) return ptab[(ltab[x]+ltab[y])%255]; + else return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a,b); + b[0]=fbsub[b[0]]; + b[1]=fbsub[b[1]]; + b[2]=fbsub[b[2]]; + b[3]=fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x,u32 y) +{ /* dot product of two 4-byte arrays */ + u8 xb[4],yb[4]; + unpack(x,xb); + unpack(y,yb); + return bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y,m; + u8 b[4]; + + m=pack(InCo); + b[3]=product(m,x); + m=ROTL24(m); + b[2]=product(m,x); + m=ROTL24(m); + b[1]=product(m,x); + m=ROTL24(m); + b[0]=product(m,x); + y=pack(b); + return y; +} + +u8 ByteSub(u8 x) +{ + u8 y=ptab[255-ltab[x]]; /* multiplicative inverse */ + x=y; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; y^=0x63; + return y; +} + +void gentables(void) +{ /* generate tables */ + int i; + u8 y,b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0]=0; + ptab[0]=1; ltab[1]=0; + ptab[1]=3; ltab[3]=1; + for (i=2;i<256;i++) + { + ptab[i]=ptab[i-1]^xtime(ptab[i-1]); + ltab[ptab[i]]=i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0]=0x63; + rbsub[0x63]=0; + for (i=1;i<256;i++) + { + y=ByteSub((u8)i); + fbsub[i]=y; rbsub[y]=i; + } + + for (i=0,y=1;i<30;i++) + { + rco[i]=y; + y=xtime(y); + } + + /* calculate forward and reverse tables */ + for (i=0;i<256;i++) + { + y=fbsub[i]; + b[3]=y^xtime(y); b[2]=y; + b[1]=y; b[0]=xtime(y); + ftable[i]=pack(b); + + y=rbsub[i]; + b[3]=bmul(InCo[0],y); b[2]=bmul(InCo[1],y); + b[1]=bmul(InCo[2],y); b[0]=bmul(InCo[3],y); + rtable[i]=pack(b); + } +} + +void gkey(int nb,int nk,u8 *key) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int i,j,k,m,N; + int C1,C2,C3; + u32 CipherKey[8]; + + Nb=nb; Nk=nk; + + /* Nr is number of rounds */ + if (Nb>=Nk) Nr=6+Nb; + else Nr=6+Nk; + + C1=1; + if (Nb<8) { C2=2; C3=3; } + else { C2=3; C3=4; } + + /* pre-calculate forward and reverse increments */ + for (m=j=0;j>8)])^ + ROTL16(ftable[(u8)(x[fi[m+1]]>>16)])^ + ROTL24(ftable[(u8)(x[fi[m+2]]>>24)]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)fbsub[(u8)(x[fi[m+1]]>>16)])^ + ROTL24((u32)fbsub[(u8)(x[fi[m+2]]>>24)]); + } + for (i=j=0;i>8)])^ + ROTL16(rtable[(u8)(x[ri[m+1]]>>16)])^ + ROTL24(rtable[(u8)(x[ri[m+2]]>>24)]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)rbsub[(u8)(x[ri[m+1]]>>16)])^ + ROTL24((u32)rbsub[(u8)(x[ri[m+2]]>>24)]); + } + for (i=j=0;i +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include +#include + +#include "bn.h" + +static void bn_zero(Uint8 *d, Uint32 n) +{ + memset(d, 0, n); +} + +static void bn_copy(Uint8 *d, Uint8 *a, Uint32 n) +{ + memcpy(d, a, n); +} + +int bn_compare(Uint8 *a, Uint8 *b, Uint32 n) +{ + Uint32 i; + + for (i = 0; i < n; i++) { + if (a[i] < b[i]) + return -1; + if (a[i] > b[i]) + return 1; + } + + return 0; +} + +void bn_sub_modulus(Uint8 *a, Uint8 *N, Uint32 n) +{ + Uint32 i; + Uint32 dig; + Uint8 c; + + c = 0; + for (i = n - 1; i < n; i--) { + dig = N[i] + c; + c = (a[i] < dig); + a[i] -= dig; + } +} + +void bn_add(Uint8 *d, Uint8 *a, Uint8 *b, Uint8 *N, Uint32 n) +{ + Uint32 i; + Uint32 dig; + Uint8 c; + + c = 0; + for (i = n - 1; i < n; i--) { + dig = a[i] + b[i] + c; + c = (dig >= 0x100); + d[i] = dig; + } + + if (c) + bn_sub_modulus(d, N, n); + + if (bn_compare(d, N, n) >= 0) + bn_sub_modulus(d, N, n); +} + +void bn_mul(Uint8 *d, Uint8 *a, Uint8 *b, Uint8 *N, Uint32 n) +{ + Uint32 i; + Uint8 mask; + + bn_zero(d, n); + + for (i = 0; i < n; i++) + for (mask = 0x80; mask != 0; mask >>= 1) { + bn_add(d, d, d, N, n); + if ((a[i] & mask) != 0) + bn_add(d, d, b, N, n); + } +} + +void bn_exp(Uint8 *d, Uint8 *a, Uint8 *N, Uint32 n, Uint8 *e, Uint32 en) +{ + Uint8 t[512]; + Uint32 i; + Uint8 mask; + + bn_zero(d, n); + d[n-1] = 1; + for (i = 0; i < en; i++) + for (mask = 0x80; mask != 0; mask >>= 1) { + bn_mul(t, d, d, N, n); + if ((e[i] & mask) != 0) + bn_mul(d, t, a, N, n); + else + bn_copy(d, t, n); + } +} + +// only for prime N -- stupid but lazy, see if I care +void bn_inv(Uint8 *d, Uint8 *a, Uint8 *N, Uint32 n) +{ + Uint8 t[512], s[512]; + + bn_copy(t, N, n); + bn_zero(s, n); + s[n-1] = 2; + bn_sub_modulus(t, s, n); + bn_exp(d, a, N, n, t, n); +} diff --git a/src/ec.cpp b/src/ec.cpp new file mode 100644 index 0000000..3e3797a --- /dev/null +++ b/src/ec.cpp @@ -0,0 +1,419 @@ +// Copyright 2007,2008 Segher Boessenkool +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include +#include +#include +#include +#include + +#include "bn.h" +#include "ec.h" +#include "sha1.h" + +// y**2 + x*y = x**3 + x + b +/*static u8 ec_b[30] = { 0x00, 0x66, 0x64, 0x7e, 0xde, 0x6c, 0x33, 0x2c, 0x7f, 0x8c, 0x09, 0x23, 0xbb, 0x58, 0x21, + 0x3b, 0x33, 0x3b, 0x20, 0xe9, 0xce, 0x42, 0x81, 0xfe, 0x11, 0x5f, 0x7d, 0x8f, 0x90, 0xad }; +*/ +// order of the addition group of points +static Uint8 ec_N[30] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0xe9, 0x74, 0xe7, 0x2f, 0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7 }; + +// base point +static Uint8 ec_G[60] = { 0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1, 0xbb, 0x75, 0x5f, + 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, + 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, + 0x8a, 0x0b, 0xef, 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52 }; +/* +static void elt_print(char *name, u8 *a) +{ + u32 i; + + printf("%s = ", name); + + for (i = 0; i < 30; i++) + printf("%02x", a[i]); + + printf("\n"); +} +*/ +static void elt_copy(Uint8 *d, Uint8 *a) +{ + memcpy(d, a, 30); +} + +static void elt_zero(Uint8 *d) +{ + memset(d, 0, 30); +} + +static int elt_is_zero(Uint8 *d) +{ + Uint32 i; + + for (i = 0; i < 30; i++) + if (d[i] != 0) + return 0; + + return 1; +} + +static void elt_add(Uint8 *d, Uint8 *a, Uint8 *b) +{ + Uint32 i; + + for (i = 0; i < 30; i++) + d[i] = a[i] ^ b[i]; +} + +static void elt_mul_x(Uint8 *d, Uint8 *a) +{ + Uint8 carry, x, y; + Uint32 i; + + carry = a[0] & 1; + + x = 0; + for (i = 0; i < 29; i++) { + y = a[i + 1]; + d[i] = x ^ (y >> 7); + x = y << 1; + } + d[29] = x ^ carry; + + d[20] ^= carry << 2; +} + +static void elt_mul(Uint8 *d, Uint8 *a, Uint8 *b) +{ + Uint32 i, n; + Uint8 mask; + + elt_zero(d); + + i = 0; + mask = 1; + for (n = 0; n < 233; n++) { + elt_mul_x(d, d); + + if ((a[i] & mask) != 0) + elt_add(d, d, b); + + mask >>= 1; + if (mask == 0) { + mask = 0x80; + i++; + } + } +} + +static const Uint8 square[16] = { 0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15, 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55 }; + +static void elt_square_to_wide(Uint8 *d, Uint8 *a) +{ + Uint32 i; + + for (i = 0; i < 30; i++) { + d[2*i] = square[a[i] >> 4]; + d[2*i + 1] = square[a[i] & 15]; + } +} + +static void wide_reduce(Uint8 *d) +{ + Uint32 i; + Uint8 x; + + for (i = 0; i < 30; i++) { + x = d[i]; + + d[i + 19] ^= x >> 7; + d[i + 20] ^= x << 1; + + d[i + 29] ^= x >> 1; + d[i + 30] ^= x << 7; + } + + x = d[30] & ~1; + + d[49] ^= x >> 7; + d[50] ^= x << 1; + + d[59] ^= x >> 1; + + d[30] &= 1; +} + +static void elt_square(Uint8 *d, Uint8 *a) +{ + Uint8 wide[60]; + + elt_square_to_wide(wide, a); + wide_reduce(wide); + + elt_copy(d, wide + 30); +} + +static void itoh_tsujii(Uint8 *d, Uint8 *a, Uint8 *b, Uint32 j) +{ + Uint8 t[30]; + + elt_copy(t, a); + while (j--) { + elt_square(d, t); + elt_copy(t, d); + } + + elt_mul(d, t, b); +} + +static void elt_inv(Uint8 *d, Uint8 *a) +{ + Uint8 t[30]; + Uint8 s[30]; + + itoh_tsujii(t, a, a, 1); + itoh_tsujii(s, t, a, 1); + itoh_tsujii(t, s, s, 3); + itoh_tsujii(s, t, a, 1); + itoh_tsujii(t, s, s, 7); + itoh_tsujii(s, t, t, 14); + itoh_tsujii(t, s, a, 1); + itoh_tsujii(s, t, t, 29); + itoh_tsujii(t, s, s, 58); + itoh_tsujii(s, t, t, 116); + elt_square(d, s); +} +/* +static int point_is_on_curve(u8 *p) +{ + u8 s[30], t[30]; + u8 *x, *y; + + x = p; + y = p + 30; + + elt_square(t, x); + elt_mul(s, t, x); + + elt_add(s, s, t); + + elt_square(t, y); + elt_add(s, s, t); + + elt_mul(t, x, y); + elt_add(s, s, t); + + elt_add(s, s, ec_b); + + return elt_is_zero(s); +} +*/ +static int point_is_zero(Uint8 *p) +{ + return elt_is_zero(p) && elt_is_zero(p + 30); +} + +static void point_double(Uint8 *r, Uint8 *p) +{ + Uint8 s[30], t[30]; + Uint8 *px, *py, *rx, *ry; + + px = p; + py = p + 30; + rx = r; + ry = r + 30; + + if (elt_is_zero(px)) { + elt_zero(rx); + elt_zero(ry); + + return; + } + + elt_inv(t, px); + elt_mul(s, py, t); + elt_add(s, s, px); + + elt_square(t, px); + + elt_square(rx, s); + elt_add(rx, rx, s); + rx[29] ^= 1; + + elt_mul(ry, s, rx); + elt_add(ry, ry, rx); + elt_add(ry, ry, t); +} + +static void point_add(Uint8 *r, Uint8 *p, Uint8 *q) +{ + Uint8 s[30], t[30], u[30]; + Uint8 *px, *py, *qx, *qy, *rx, *ry; + + px = p; + py = p + 30; + qx = q; + qy = q + 30; + rx = r; + ry = r + 30; + + if (point_is_zero(p)) { + elt_copy(rx, qx); + elt_copy(ry, qy); + return; + } + + if (point_is_zero(q)) { + elt_copy(rx, px); + elt_copy(ry, py); + return; + } + + elt_add(u, px, qx); + + if (elt_is_zero(u)) { + elt_add(u, py, qy); + if (elt_is_zero(u)) + point_double(r, p); + else { + elt_zero(rx); + elt_zero(ry); + } + + return; + } + + elt_inv(t, u); + elt_add(u, py, qy); + elt_mul(s, t, u); + + elt_square(t, s); + elt_add(t, t, s); + elt_add(t, t, qx); + t[29] ^= 1; + + elt_mul(u, s, t); + elt_add(s, u, py); + elt_add(rx, t, px); + elt_add(ry, s, rx); +} + +static void point_mul(Uint8 *d, Uint8 *a, Uint8 *b) // a is bignum +{ + Uint32 i; + Uint8 mask; + + elt_zero(d); + elt_zero(d + 30); + + for (i = 0; i < 30; i++) + for (mask = 0x80; mask != 0; mask >>= 1) { + point_double(d, d); + if ((a[i] & mask) != 0) + point_add(d, d, b); + } +} + +void sillyRandom(Uint8 * rndArea, Uint8 count) +{ + for(Uint16 i = 0; i < count; i++) + rndArea[i]=rand(); +} + +void generate_ecdsa(Uint8 *R, Uint8 *S, Uint8 *k, Uint8 *hash) +{ + Uint8 e[30]; + Uint8 kk[30]; + Uint8 m[30]; + Uint8 minv[30]; + Uint8 mG[60]; + //FILE *fp; + + elt_zero(e); + memcpy(e + 10, hash, 20); + + sillyRandom(m, sizeof(m)); + m[0] = 0; + + // R = (mG).x + + point_mul(mG, m, ec_G); + elt_copy(R, mG); + if (bn_compare(R, ec_N, 30) >= 0) + bn_sub_modulus(R, ec_N, 30); + + // S = m**-1*(e + Rk) (mod N) + + elt_copy(kk, k); + if (bn_compare(kk, ec_N, 30) >= 0) + bn_sub_modulus(kk, ec_N, 30); + bn_mul(S, R, kk, ec_N, 30); + bn_add(kk, S, e, ec_N, 30); + bn_inv(minv, m, ec_N, 30); + bn_mul(S, minv, kk, ec_N, 30); +} + +bool check_ecdsa(Uint8 *Q, Uint8 *R, Uint8 *S, Uint8 *hash) +{ + Uint8 Sinv[30]; + Uint8 e[30]; + Uint8 w1[30], w2[30]; + Uint8 r1[60], r2[60]; + + bn_inv(Sinv, S, ec_N, 30); + + elt_zero(e); + memcpy(e + 10, hash, 20); + + bn_mul(w1, e, Sinv, ec_N, 30); + bn_mul(w2, R, Sinv, ec_N, 30); + + point_mul(r1, w1, ec_G); + point_mul(r2, w2, Q); + + point_add(r1, r1, r2); + + if (bn_compare(r1, ec_N, 30) >= 0) + bn_sub_modulus(r1, ec_N, 30); + + return (bn_compare(r1, R, 30) == 0); +} + +void ec_priv_to_pub(Uint8 *k, Uint8 *Q) +{ + point_mul(Q, k, ec_G); +} + +bool check_ec(Uint8 *ng, Uint8 *ap, Uint8 *sig, Uint8 *sig_hash) +{ + Uint8* ap_hash; + Uint8 *ng_Q, *ap_R, *ap_S; + Uint8 *ap_Q, *sig_R, *sig_S; + + ng_Q = ng + 0x0108; + ap_R = ap + 0x04; + ap_S = ap + 0x22; + + + ap_hash = getSha1(ap+0x80, 0x100); + ap_Q = ap + 0x0108; + sig_R = sig; + sig_S = sig + 30; + + return check_ecdsa(ng_Q, ap_R, ap_S, ap_hash) + && check_ecdsa(ap_Q, sig_R, sig_S, sig_hash); +} + +void make_ec_cert(Uint8 *cert, Uint8 *sig, char *signer, char *name, Uint8 *priv, Uint32 key_id ) +{ + memset(cert, 0, 0x180); + *(Uint32*)(cert) = swapU32(0x10002); + memcpy((char*)cert + 4, sig, 60); + strcpy((char*)cert + 0x80, signer); + *(Uint32*)(cert + 0xc0) = swapU32(2); + strcpy((char*)cert + 0xc4, name); + *(Uint32*)(cert + 0x104) = swapU32(key_id); + ec_priv_to_pub(priv, cert + 0x108); +} diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..0cfea4c --- /dev/null +++ b/src/md5.c @@ -0,0 +1,610 @@ +/* ========================================================================== ** + * + * MD5.c + * + * Copyright: + * Copyright (C) 2003-2005 by Christopher R. Hertel + * + * Email: crh@ubiqx.mn.org + * + * $Id: MD5.c,v 0.6 2005/06/08 18:35:59 crh Exp $ + * + * + * Modifications and additions by dimok + * + * -------------------------------------------------------------------------- ** + * + * Description: + * Implements the MD5 hash algorithm, as described in RFC 1321. + * + * -------------------------------------------------------------------------- ** + * + * License: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * -------------------------------------------------------------------------- ** + * + * Notes: + * + * None of this will make any sense unless you're studying RFC 1321 as you + * read the code. + * + * MD5 is described in RFC 1321. + * The MD*4* algorithm is described in RFC 1320 (that's 1321 - 1). + * MD5 is very similar to MD4, but not quite similar enough to justify + * putting the two into a single module. Besides, I wanted to add a few + * extra functions to this one to expand its usability. + * + * There are three primary motivations for this particular implementation. + * 1) Programmer's pride. I wanted to be able to say I'd done it, and I + * wanted to learn from the experience. + * 2) Portability. I wanted an implementation that I knew to be portable + * to a reasonable number of platforms. In particular, the algorithm is + * designed with little-endian platforms in mind, but I wanted an + * endian-agnostic implementation. + * 3) Compactness. While not an overriding goal, I thought it worth-while + * to see if I could reduce the overall size of the result. This is in + * keeping with my hopes that this library will be suitable for use in + * some embedded environments. + * Beyond that, cleanliness and clarity are always worth pursuing. + * + * As mentioned above, the code really only makes sense if you are familiar + * with the MD5 algorithm or are using RFC 1321 as a guide. This code is + * quirky, however, so you'll want to be reading carefully. + * + * Yeah...most of the comments are cut-and-paste from my MD4 implementation. + * + * -------------------------------------------------------------------------- ** + * + * References: + * IETF RFC 1321: The MD5 Message-Digest Algorithm + * Ron Rivest. IETF, April, 1992 + * + * ========================================================================== ** + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "md5.h" + +/* -------------------------------------------------------------------------- ** + * Static Constants: + * + * K[][] - In round one, the values of k (which are used to index + * particular four-byte sequences in the input) are simply + * sequential. In later rounds, however, they are a bit more + * varied. Rather than calculate the values of k (which may + * or may not be possible--I haven't though about it) the + * values are stored in this array. + * + * S[][] - In each round there is a left rotate operation performed as + * part of the 16 permutations. The number of bits varies in + * a repeating patter. This array keeps track of the patterns + * used in each round. + * + * T[][] - There are four rounds of 16 permutations for a total of 64. + * In each of these 64 permutation operations, a different + * constant value is added to the mix. The constants are + * based on the sine function...read RFC 1321 for more detail. + * In any case, the correct constants are stored in the T[][] + * array. They're divided up into four groups of 16. + */ + +static const uint8_t K[3][16] = { +/* Round 1: skipped (since it is simply sequential). */ +{ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 }, /* R2 */ +{ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 }, /* R3 */ +{ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 } /* R4 */ +}; + +static const uint8_t S[4][4] = { { 7, 12, 17, 22 }, /* Round 1 */ +{ 5, 9, 14, 20 }, /* Round 2 */ +{ 4, 11, 16, 23 }, /* Round 3 */ +{ 6, 10, 15, 21 } /* Round 4 */ +}; + +static const uint32_t T[4][16] = { { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, /* Round 1 */ +0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, + 0xa679438e, 0x49b40821 }, + +{ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, /* Round 2 */ +0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, + 0x676f02d9, 0x8d2a4c8a }, + +{ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, /* Round 3 */ +0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, + 0x1fa27cf8, 0xc4ac5665 }, + +{ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, /* Round 4 */ +0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, + 0x2ad7d2bb, 0xeb86d391 }, }; + +/* -------------------------------------------------------------------------- ** + * Macros: + * md5F(), md5G(), md5H(), and md5I() are described in RFC 1321. + * All of these operations are bitwise, and so not impacted by endian-ness. + * + * GetLongByte() + * Extract one byte from a (32-bit) longword. A value of 0 for + * indicates the lowest order byte, while 3 indicates the highest order + * byte. + * + */ + +#define md5F( X, Y, Z ) ( ((X) & (Y)) | ((~(X)) & (Z)) ) +#define md5G( X, Y, Z ) ( ((X) & (Z)) | ((Y) & (~(Z))) ) +#define md5H( X, Y, Z ) ( (X) ^ (Y) ^ (Z) ) +#define md5I( X, Y, Z ) ( (Y) ^ ((X) | (~(Z))) ) + +#define GetLongByte( L, idx ) ((unsigned char)(( L >> (((idx) & 0x03) << 3) ) & 0xFF)) + +#define STR2HEX(x) ((x >= 0x30) && (x <= 0x39)) ? x - 0x30 : toupper((int)x)-0x37 + +/* -------------------------------------------------------------------------- ** + * Static Functions: + */ + +static void Permute(uint32_t ABCD[4], const unsigned char block[64]) +/* ------------------------------------------------------------------------ ** + * Permute the ABCD "registers" using the 64-byte as a driver. + * + * Input: ABCD - Pointer to an array of four unsigned longwords. + * block - An array of bytes, 64 bytes in size. + * + * Output: none. + * + * Notes: The MD5 algorithm operates on a set of four longwords stored + * (conceptually) in four "registers". It is easy to imagine a + * simple MD4/5 chip that would operate this way. In any case, + * the mangling of the contents of those registers is driven by + * the input message. The message is chopped and finally padded + * into 64-byte chunks and each chunk is used to manipulate the + * contents of the registers. + * + * The MD5 Algorithm calls for padding the input to ensure that + * it is a multiple of 64 bytes in length. The last 16 bytes + * of the padding space are used to store the message length + * (the length of the original message, before padding, expressed + * in terms of bits). If there is not enough room for 16 bytes + * worth of bitcount (eg., if the original message was 122 bytes + * long) then the block is padded to the end with zeros and + * passed to this function. Then *another* block is filled with + * zeros except for the last 16 bytes which contain the length. + * + * Oh... and the algorithm requires that there be at least one + * padding byte. The first padding byte has a value of 0x80, + * and any others are 0x00. + * + * ------------------------------------------------------------------------ ** + */ +{ + int round; + int i, j; + uint8_t s; + uint32_t a, b, c, d; + uint32_t KeepABCD[4]; + uint32_t X[16]; + + /* Store the current ABCD values for later re-use. + */ + for (i = 0; i < 4; i++) + KeepABCD[i] = ABCD[i]; + + /* Convert the input block into an array of unsigned longs, taking care + * to read the block in Little Endian order (the algorithm assumes this). + * The uint32_t values are then handled in host order. + */ + for (i = 0, j = 0; i < 16; i++) + { + X[i] = (uint32_t) block[j++]; + X[i] |= ((uint32_t) block[j++] << 8); + X[i] |= ((uint32_t) block[j++] << 16); + X[i] |= ((uint32_t) block[j++] << 24); + } + + /* This loop performs the four rounds of permutations. + * The rounds are each very similar. The differences are in three areas: + * - The function (F, G, H, or I) used to perform bitwise permutations + * on the registers, + * - The order in which values from X[] are chosen. + * - Changes to the number of bits by which the registers are rotated. + * This implementation uses a switch statement to deal with some of the + * differences between rounds. Other differences are handled by storing + * values in arrays and using the round number to select the correct set + * of values. + * + * (My implementation appears to be a poor compromise between speed, size, + * and clarity. Ugh. [crh]) + */ + for (round = 0; round < 4; round++) + { + for (i = 0; i < 16; i++) + { + j = (4 - (i % 4)) & 0x3; /* handles the rotation of ABCD. */ + s = S[round][i % 4]; /* is the bit shift for this iteration. */ + + b = ABCD[(j + 1) & 0x3]; /* Copy the b,c,d values per ABCD rotation. */ + c = ABCD[(j + 2) & 0x3]; /* This isn't really necessary, it just looks */ + d = ABCD[(j + 3) & 0x3]; /* clean & will hopefully be optimized away. */ + + /* The actual perumation function. + * This is broken out to minimize the code within the switch(). + */ + switch (round) + { + case 0: + /* round 1 */ + a = md5F( b, c, d ) + X[i]; + break; + case 1: + /* round 2 */ + a = md5G( b, c, d ) + X[K[0][i]]; + break; + case 2: + /* round 3 */ + a = md5H( b, c, d ) + X[K[1][i]]; + break; + default: + /* round 4 */ + a = md5I( b, c, d ) + X[K[2][i]]; + break; + } + a = 0xFFFFFFFF & (ABCD[j] + a + T[round][i]); + ABCD[j] = b + (0xFFFFFFFF & ((a << s) | (a >> (32 - s)))); + } + } + + /* Use the stored original A, B, C, D values to perform + * one last convolution. + */ + for (i = 0; i < 4; i++) + ABCD[i] = 0xFFFFFFFF & (ABCD[i] + KeepABCD[i]); + +} /* Permute */ + +/* -------------------------------------------------------------------------- ** + * Functions: + */ + +auth_md5Ctx *auth_md5InitCtx(auth_md5Ctx *ctx) +/* ------------------------------------------------------------------------ ** + * Initialize an MD5 context. + * + * Input: ctx - A pointer to the MD5 context structure to be initialized. + * Contexts are typically created thusly: + * ctx = (auth_md5Ctx *)malloc( sizeof(auth_md5Ctx) ); + * + * Output: A pointer to the initialized context (same as ). + * + * Notes: The purpose of the context is to make it possible to generate + * an MD5 Message Digest in stages, rather than having to pass a + * single large block to a single MD5 function. The context + * structure keeps track of various bits of state information. + * + * Once the context is initialized, the blocks of message data + * are passed to the function. Once the + * final bit of data has been handed to the + * context can be closed out by calling , + * which also calculates the final MD5 result. + * + * Don't forget to free an allocated context structure when + * you've finished using it. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ +{ + ctx->len = 0; + ctx->b_used = 0; + + ctx->ABCD[0] = 0x67452301; /* The array ABCD[] contains the four 4-byte */ + ctx->ABCD[1] = 0xefcdab89; /* "registers" that are manipulated to */ + ctx->ABCD[2] = 0x98badcfe; /* produce the MD5 digest. The input acts */ + ctx->ABCD[3] = 0x10325476; /* upon the registers, not the other way */ + /* 'round. The initial values are those */ + /* given in RFC 1321 (pg. 4). Note, however, that RFC 1321 */ + /* provides these values as bytes, not as longwords, and the */ + /* bytes are arranged in little-endian order as if they were */ + /* the bytes of (little endian) 32-bit ints. That's */ + /* confusing as all getout (to me, anyway). The values given */ + /* here are provided as 32-bit values in C language format, */ + /* so they are endian-agnostic. */ + return (ctx); +} /* auth_md5InitCtx */ + +auth_md5Ctx *auth_md5SumCtx(auth_md5Ctx *ctx, const unsigned char *src, const int len) +/* ------------------------------------------------------------------------ ** + * Build an MD5 Message Digest within the given context. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * src - A chunk of source data. This will be used to drive + * the MD5 algorithm. + * len - The number of bytes in . + * + * Output: A pointer to the updated context (same as ). + * + * See Also: , , + * + * ------------------------------------------------------------------------ ** + */ +{ + int i; + + /* Add the new block's length to the total length. + */ + ctx->len += (uint32_t) len; + + /* Copy the new block's data into the context block. + * Call the Permute() function whenever the context block is full. + */ + for (i = 0; i < len; i++) + { + ctx->block[ctx->b_used] = src[i]; + (ctx->b_used)++; + if (64 == ctx->b_used) + { + Permute(ctx->ABCD, ctx->block); + ctx->b_used = 0; + } + } + + /* Return the updated context. + */ + return (ctx); +} /* auth_md5SumCtx */ + +auth_md5Ctx *auth_md5CloseCtx(auth_md5Ctx *ctx, unsigned char *dst) +/* ------------------------------------------------------------------------ ** + * Close an MD5 Message Digest context and generate the final MD5 sum. + * + * Input: ctx - Pointer to the context in which the MD5 sum is being + * built. + * dst - A pointer to at least 16 bytes of memory, which will + * receive the finished MD5 sum. + * + * Output: A pointer to the closed context (same as ). + * You might use this to free a malloc'd context structure. :) + * + * Notes: The context () is returned in an undefined state. + * It must be re-initialized before re-use. + * + * See Also: , + * + * ------------------------------------------------------------------------ ** + */ +{ + int i; + uint32_t l; + + /* Add the required 0x80 padding initiator byte. + * The auth_md5SumCtx() function always permutes and resets the context + * block when it gets full, so we know that there must be at least one + * free byte in the context block. + */ + ctx->block[ctx->b_used] = 0x80; + (ctx->b_used)++; + + /* Zero out any remaining free bytes in the context block. + */ + for (i = ctx->b_used; i < 64; i++) + ctx->block[i] = 0; + + /* We need 8 bytes to store the length field. + * If we don't have 8, call Permute() and reset the context block. + */ + if (56 < ctx->b_used) + { + Permute(ctx->ABCD, ctx->block); + for (i = 0; i < 64; i++) + ctx->block[i] = 0; + } + + /* Add the total length and perform the final perumation. + * Note: The 60'th byte is read from the *original* len> value + * and shifted to the correct position. This neatly avoids + * any MAXINT numeric overflow issues. + */ + l = ctx->len << 3; + for (i = 0; i < 4; i++) + ctx->block[56 + i] |= GetLongByte( l, i ); + ctx->block[60] = ((GetLongByte( ctx->len, 3 ) & 0xE0) >> 5); /* See Above! */ + Permute(ctx->ABCD, ctx->block); + + /* Now copy the result into the output buffer and we're done. + */ + for (i = 0; i < 4; i++) + { + dst[0 + i] = GetLongByte( ctx->ABCD[0], i ); + dst[4 + i] = GetLongByte( ctx->ABCD[1], i ); + dst[8 + i] = GetLongByte( ctx->ABCD[2], i ); + dst[12 + i] = GetLongByte( ctx->ABCD[3], i ); + } + + /* Return the context. + * This is done for compatibility with the other auth_md5*Ctx() functions. + */ + return (ctx); +} /* auth_md5CloseCtx */ + +unsigned char * MD5(unsigned char *dst, const unsigned char *src, const int len) +/* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - Source data block to be MD5'd. + * len - The length, in bytes, of the source block. + * (Note that the length is given in bytes, not bits.) + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ +{ + auth_md5Ctx ctx[1]; + + (void) auth_md5InitCtx(ctx); /* Open a context. */ + (void) auth_md5SumCtx(ctx, src, len); /* Pass only one block. */ + (void) auth_md5CloseCtx(ctx, dst); /* Close the context. */ + + return (dst); /* Makes life easy. */ +} /* auth_md5Sum */ + +unsigned char * MD5fromFile(unsigned char *dst, const char *src) +/* ------------------------------------------------------------------------ ** + * Compute an MD5 message digest. + * + * Input: dst - Destination buffer into which the result will be written. + * Must be 16 bytes, minimum. + * src - filepath of the file to be checked + * + * Output: A pointer to , which will contain the calculated 16-byte + * MD5 message digest. + * + * Notes: This function is a shortcut. It takes a single input block. + * For more drawn-out operations, see . + * + * This function is interface-compatible with the + * function in the MD4 module. + * + * The MD5 algorithm is designed to work on data with an + * arbitrary *bit* length. Most implementations, this one + * included, handle the input data in byte-sized chunks. + * + * The MD5 algorithm does much of its work using four-byte + * words, and so can be tuned for speed based on the endian-ness + * of the host. This implementation is intended to be + * endian-neutral, which may make it a teeny bit slower than + * others. ...maybe. + * + * See Also: + * + * ------------------------------------------------------------------------ ** + */ +{ + auth_md5Ctx ctx[1]; + + FILE * file; + unsigned int blksize = 0; + unsigned int read = 0; + unsigned int filesize; + unsigned char* buffer; + + file = fopen(src, "rb"); + + if (file == NULL) + { + return NULL; + } + + (void) auth_md5InitCtx(ctx); /* Open a context. */ + + fseek(file, 0, SEEK_END); + filesize = ftell(file); + rewind(file); + + if (filesize < 1048576) //1MB cache for files bigger than 1 MB + blksize = filesize; + else blksize = 1048576; + + buffer = malloc(blksize); + + if (buffer == NULL) + { + //no memory + fclose(file); + return NULL; + } + + do + { + read = fread(buffer, 1, blksize, file); + (void) auth_md5SumCtx(ctx, buffer, read); /* Pass only one block. */ + + } while (read > 0); + + fclose(file); + free(buffer); + + (void) auth_md5CloseCtx(ctx, dst); /* Close the context. */ + + return (dst); /* Makes life easy. */ +} /* auth_md5Sum */ + +const char * MD5ToString(const unsigned char * hash, char * dst) +{ + char hexchar[3]; + short i = 0, n = 0; + + for (i = 0; i < 16; i++) + { + sprintf(hexchar, "%02X", hash[i]); + + dst[n++] = hexchar[0]; + dst[n++] = hexchar[1]; + } + + dst[n] = 0x00; + + return dst; +} + +unsigned char * StringToMD5(const char * hash, unsigned char * dst) +{ + char hexchar[2]; + short i = 0, n = 0; + + for (i = 0; i < 16; i++) + { + hexchar[0] = hash[n++]; + hexchar[1] = hash[n++]; + + dst[i] = STR2HEX( hexchar[0] ); + dst[i] <<= 4; + dst[i] += STR2HEX( hexchar[1] ); + } + + return dst; +} + +/* ========================================================================== */ diff --git a/src/sha1.cpp b/src/sha1.cpp new file mode 100644 index 0000000..892d943 --- /dev/null +++ b/src/sha1.cpp @@ -0,0 +1,396 @@ +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking u8acters and u8acter + * arrays assume that only 8 bits of information are stored in each + * u8acter. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit u8acter. + * + */ + +#include "sha1.h" +#include +#include + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of u8acters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single u8acter names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} + +Uint8* getSha1( Uint8 * stuff, Uint32 stuff_size ) +{ + SHA1Context sha; + SHA1Reset( &sha ); + SHA1Input( &sha, (const Uint8*)stuff, stuff_size ); + if( !SHA1Result( &sha ) ) + return 0; + + Uint8* ret = new Uint8[20]; + memset(ret, 0, 20); + + for( int i = 0; i < 5 ; i++ ) + { + int val = sha.Message_Digest[ i ]; + if (!isSystemBigEndian()) + val = swap32(val); + + memcpy( (char*)ret + ( i * 4 ), &val, 4 ); + } + + return ret; +}