Passwords - password entry at main menu, stored and checked by server

This commit is contained in:
Ciaran Gultnieks 2011-05-20 20:28:03 +01:00
parent b5ceaf445a
commit d4d49ee8f4
17 changed files with 534 additions and 34 deletions

View File

@ -95,6 +95,8 @@ set(minetest_SRCS
tile.cpp
game.cpp
main.cpp
sha1.cpp
base64.cpp
)
# Server sources

123
src/base64.cpp Normal file
View File

@ -0,0 +1,123 @@
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}

4
src/base64.h Normal file
View File

@ -0,0 +1,4 @@
#include <string>
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

View File

@ -68,6 +68,7 @@ void * MeshUpdateThread::Thread()
Client::Client(
IrrlichtDevice *device,
const char *playername,
std::string password,
MapDrawControl &control):
m_mesh_update_thread(),
m_env(
@ -83,7 +84,9 @@ Client::Client(
m_server_ser_ver(SER_FMT_VER_INVALID),
m_inventory_updated(false),
m_time_of_day(0),
m_map_seed(0)
m_map_seed(0),
m_password(password),
m_access_denied(false)
{
m_packetcounter_timer = 0.0;
m_delete_unused_sectors_timer = 0.0;
@ -299,11 +302,14 @@ void Client::step(float dtime)
// [0] u16 TOSERVER_INIT
// [2] u8 SER_FMT_VER_HIGHEST
// [3] u8[20] player_name
SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE);
// [23] u8[28] password
SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
writeU16(&data[0], TOSERVER_INIT);
writeU8(&data[2], SER_FMT_VER_HIGHEST);
memset((char*)&data[3], 0, PLAYERNAME_SIZE);
snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
// Send as unreliable
Send(0, data, false);
}
@ -597,7 +603,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
return;
}
if(command == TOCLIENT_ACCESS_DENIED)
{
// The server didn't like our password. Note, this needs
// to be processed even if the serialisation format has
// not been agreed yet, the same as TOCLIENT_INIT.
m_access_denied = true;
return;
}
if(ser_version == SER_FMT_VER_INVALID)
{
dout_client<<DTIME<<"WARNING: Client: Server serialization"

View File

@ -207,6 +207,7 @@ public:
Client(
IrrlichtDevice *device,
const char *playername,
std::string password,
MapDrawControl &control
);
@ -377,6 +378,11 @@ public:
// Get event from queue. CE_NONE is returned if queue is empty.
ClientEvent getClientEvent();
inline bool accessDenied()
{
return m_access_denied;
}
private:
// Virtual methods from con::PeerHandler
@ -430,6 +436,9 @@ private:
// The seed returned by the server in TOCLIENT_INIT is stored here
u64 m_map_seed;
std::string m_password;
bool m_access_denied;
InventoryContext m_inventory_context;
Queue<ClientEvent> m_client_event_queue;

View File

@ -150,6 +150,11 @@ enum ToClientCommand
f1000 player pitch
f1000 player yaw
*/
TOCLIENT_ACCESS_DENIED = 0x35,
/*
u16 command
*/
};
enum ToServerCommand
@ -161,6 +166,7 @@ enum ToServerCommand
[0] u16 TOSERVER_INIT
[2] u8 SER_FMT_VER_HIGHEST
[3] u8[20] player_name
[23] u8[28] password
*/
TOSERVER_INIT2 = 0x11,

View File

@ -634,6 +634,7 @@ void the_game(
gui::IGUIFont* font,
std::string map_dir,
std::string playername,
std::string password,
std::string address,
u16 port,
std::wstring &error_message
@ -689,7 +690,7 @@ void the_game(
*/
std::cout<<DTIME<<"Creating client"<<std::endl;
Client client(device, playername.c_str(), draw_control);
Client client(device, playername.c_str(), password, draw_control);
Address connect_address(0,0,0,0, port);
try{
@ -728,6 +729,10 @@ void the_game(
could_connect = true;
break;
}
if(client.accessDenied())
{
break;
}
// Wait for 10 seconds
if(time_counter >= 10.0)
{
@ -756,8 +761,16 @@ void the_game(
if(could_connect == false)
{
std::cout<<DTIME<<"Timed out."<<std::endl;
error_message = L"Connection timed out.";
if(client.accessDenied())
{
error_message = L"Access denied. Check your password and try again.";
std::cout<<DTIME<<"Access denied."<<std::endl;
}
else
{
error_message = L"Connection timed out.";
std::cout<<DTIME<<"Timed out."<<std::endl;
}
gui_loadingtext->remove();
return;
}

View File

@ -67,6 +67,7 @@ void the_game(
gui::IGUIFont* font,
std::string map_dir,
std::string playername,
std::string password,
std::string address,
u16 port,
std::wstring &error_message

View File

@ -164,30 +164,38 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
// Nickname
// Nickname + password
{
core::rect<s32> rect(0, 0, 100, 20);
rect += topleft_client + v2s32(40, 50+6);
const wchar_t *text = L"Nickname";
core::rect<s32> rect(0, 0, 110, 20);
rect += topleft_client + v2s32(35, 50+6);
const wchar_t *text = L"Name/Password";
Environment->addStaticText(text, rect, false, true, this, -1);
}
{
core::rect<s32> rect(0, 0, 250, 30);
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, 50);
gui::IGUIElement *e =
Environment->addEditBox(text_name.c_str(), rect, true, this, 258);
if(text_name == L"")
Environment->setFocus(e);
}
{
core::rect<s32> rect(0, 0, 120, 30);
rect += topleft_client + v2s32(size_client.X-60-100, 50);
gui::IGUIEditBox *e =
Environment->addEditBox(L"", rect, true, this, 264);
e->setPasswordBox(true);
}
// Address + port
{
core::rect<s32> rect(0, 0, 100, 20);
rect += topleft_client + v2s32(40, 100+6);
const wchar_t *text = L"Address + Port";
core::rect<s32> rect(0, 0, 110, 20);
rect += topleft_client + v2s32(35, 100+6);
const wchar_t *text = L"Address/Port";
Environment->addStaticText(text, rect, false, true, this, -1);
}
{
core::rect<s32> rect(0, 0, 250, 30);
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, 100);
gui::IGUIElement *e =
Environment->addEditBox(text_address.c_str(), rect, true, this, 256);
@ -195,9 +203,9 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
Environment->setFocus(e);
}
{
core::rect<s32> rect(0, 0, 100, 30);
core::rect<s32> rect(0, 0, 120, 30);
//rect += topleft_client + v2s32(160+250+20, 125);
rect += topleft_client + v2s32(size_client.X-40-100, 100);
rect += topleft_client + v2s32(size_client.X-60-100, 100);
Environment->addEditBox(text_port.c_str(), rect, true, this, 257);
}
{
@ -208,13 +216,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
}
{
core::rect<s32> rect(0, 0, 250, 30);
rect += topleft_client + v2s32(40, 150);
rect += topleft_client + v2s32(35, 150);
Environment->addCheckBox(fancy_trees, rect, this, 263,
L"Fancy trees");
}
{
core::rect<s32> rect(0, 0, 250, 30);
rect += topleft_client + v2s32(40, 150+30);
rect += topleft_client + v2s32(35, 150+30);
Environment->addCheckBox(smooth_lighting, rect, this, 262,
L"Smooth Lighting");
}
@ -245,12 +253,12 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
// Server parameters
{
core::rect<s32> rect(0, 0, 250, 30);
rect += topleft_server + v2s32(40, 30);
rect += topleft_server + v2s32(35, 30);
Environment->addCheckBox(creative_mode, rect, this, 259, L"Creative Mode");
}
{
core::rect<s32> rect(0, 0, 250, 30);
rect += topleft_server + v2s32(40, 60);
rect += topleft_server + v2s32(35, 60);
Environment->addCheckBox(enable_damage, rect, this, 261, L"Enable Damage");
}
// Map delete button
@ -296,6 +304,11 @@ void GUIMainMenu::acceptInput()
if(e != NULL)
m_data->name = e->getText();
}
{
gui::IGUIElement *e = getElementFromId(264);
if(e != NULL)
m_data->password = e->getText();
}
{
gui::IGUIElement *e = getElementFromId(256);
if(e != NULL)
@ -380,7 +393,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
{
switch(event.GUIEvent.Caller->getID())
{
case 256: case 257: case 258:
case 256: case 257: case 258: case 264:
acceptInput();
quitMenu();
return true;

View File

@ -46,6 +46,7 @@ struct MainMenuData
std::wstring address;
std::wstring port;
std::wstring name;
std::wstring password;
bool fancy_trees;
bool smooth_lighting;
// Server options

View File

@ -325,6 +325,8 @@ Making it more portable:
#include "materials.h"
#include "game.h"
#include "keycode.h"
#include "sha1.h"
#include "base64.h"
// This makes textures
ITextureSource *g_texturesource = NULL;
@ -1170,6 +1172,7 @@ int main(int argc, char *argv[])
return 0;
}
/*
More parameters
*/
@ -1324,11 +1327,15 @@ int main(int argc, char *argv[])
*/
std::wstring error_message = L"";
// The password entered during the menu screen,
std::string password;
/*
Menu-game loop
*/
while(device->run() && kill == false)
{
// This is used for catching disconnects
try
{
@ -1428,6 +1435,25 @@ int main(int argc, char *argv[])
}
playername = wide_to_narrow(menudata.name);
// Get an sha-1 hash of the player's name combined with
// the password entered. That's what the server uses as
// their password. (Exception : if the password field is
// blank, we send a blank password - this is for backwards
// compatibility with password-less players).
if(menudata.password.length() > 0)
{
std::string slt=playername + wide_to_narrow(menudata.password);
SHA1 *sha1 = new SHA1();
sha1->addBytes(slt.c_str(), slt.length());
unsigned char *digest = sha1->getDigest();
password = base64_encode(digest, 20);
}
else
{
password = "";
}
address = wide_to_narrow(menudata.address);
int newport = stoi(wide_to_narrow(menudata.port));
if(newport != 0)
@ -1474,6 +1500,7 @@ int main(int argc, char *argv[])
font,
map_dir,
playername,
password,
address,
port,
error_message

View File

@ -87,6 +87,7 @@ Player::Player():
m_position(0,0,0)
{
updateName("<not set>");
updatePassword("");
resetInventory();
}
@ -145,6 +146,7 @@ void Player::serialize(std::ostream &os)
Settings args;
args.setS32("version", 1);
args.set("name", m_name);
args.set("password", m_password);
args.setFloat("pitch", m_pitch);
args.setFloat("yaw", m_yaw);
args.setV3F("position", m_position);
@ -179,6 +181,10 @@ void Player::deSerialize(std::istream &is)
//args.getS32("version");
std::string name = args.get("name");
updateName(name.c_str());
std::string password = "";
if(args.exists("password"))
password = args.get("password");
updatePassword(password.c_str());
m_pitch = args.getFloat("pitch");
m_yaw = args.getFloat("yaw");
m_position = args.getV3F("position");

View File

@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "collision.h"
#define PLAYERNAME_SIZE 20
#define PASSWORD_SIZE 28 // Maximum password length. Allows for
// base64-encoded SHA-1.
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,"
@ -121,6 +123,16 @@ public:
return m_name;
}
virtual void updatePassword(const char *password)
{
snprintf(m_password, PASSWORD_SIZE, "%s", password);
}
const char * getPassword()
{
return m_password;
}
virtual bool isLocal() const = 0;
virtual void updateLight(u8 light_at_pos) {};
@ -157,6 +169,7 @@ public:
protected:
char m_name[PLAYERNAME_SIZE];
char m_password[PASSWORD_SIZE];
f32 m_pitch;
f32 m_yaw;
v3f m_speed;

View File

@ -1734,8 +1734,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// [0] u16 TOSERVER_INIT
// [2] u8 SER_FMT_VER_HIGHEST
// [3] u8[20] player_name
// [23] u8[28] password <--- can be sent without this, from old versions
if(datasize < 3)
if(datasize < 2+1+PLAYERNAME_SIZE)
return;
derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
@ -1767,17 +1768,41 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
*/
// Get player name
const u32 playername_size = 20;
char playername[playername_size];
for(u32 i=0; i<playername_size-1; i++)
char playername[PLAYERNAME_SIZE];
for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
{
playername[i] = data[3+i];
}
playername[playername_size-1] = 0;
playername[PLAYERNAME_SIZE-1] = 0;
// Get password
char password[PASSWORD_SIZE];
if(datasize == 2+1+PLAYERNAME_SIZE)
{
// old version - assume blank password
*password = 0;
}
else
{
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
{
password[i] = data[23+i];
}
password[PASSWORD_SIZE-1] = 0;
}
Player *checkplayer = m_env.getPlayer(playername);
if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
{
derr_server<<DTIME<<"Server: peer_id="<<peer_id
<<": supplied invalid password for "
<<playername<<std::endl;
SendAccessDenied(m_con, peer_id);
return;
}
// Get player
Player *player = emergePlayer(playername, "", peer_id);
//Player *player = m_env.getPlayer(peer_id);
Player *player = emergePlayer(playername, password, peer_id);
/*{
// DEBUG: Test serialization
@ -3138,6 +3163,20 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
con.Send(peer_id, 0, data, true);
}
void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_ACCESS_DENIED);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
con.Send(peer_id, 0, data, true);
}
/*
Non-static send methods
*/
@ -4052,8 +4091,7 @@ v3f findSpawnPos(ServerMap &map)
), BS);
}
Player *Server::emergePlayer(const char *name, const char *password,
u16 peer_id)
Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
{
/*
Try to get an existing player
@ -4099,6 +4137,7 @@ Player *Server::emergePlayer(const char *name, const char *password,
//player->peer_id = PEER_ID_INEXISTENT;
player->peer_id = peer_id;
player->updateName(name);
player->updatePassword(password);
/*
Set player position

View File

@ -436,6 +436,7 @@ private:
*/
static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id);
/*
Non-static send methods
@ -476,11 +477,12 @@ private:
/*
Get a player from memory or creates one.
If player is already connected, return NULL
The password is not checked here - it is only used to
set the password if a new player is created.
Call with env and con locked.
*/
Player *emergePlayer(const char *name, const char *password,
u16 peer_id);
Player *emergePlayer(const char *name, const char *password, u16 peer_id);
/*
Update water pressure.

191
src/sha1.cpp Normal file
View File

@ -0,0 +1,191 @@
/* sha1.cpp
Copyright (c) 2005 Michael D. Leonhard
http://tamale.net/
This file is licensed under the terms described in the
accompanying LICENSE file.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "sha1.h"
// print out memory in hexadecimal
void SHA1::hexPrinter( unsigned char* c, int l )
{
assert( c );
assert( l > 0 );
while( l > 0 )
{
printf( " %02x", *c );
l--;
c++;
}
}
// circular left bit rotation. MSB wraps around to LSB
Uint32 SHA1::lrot( Uint32 x, int bits )
{
return (x<<bits) | (x>>(32 - bits));
};
// Save a 32-bit unsigned integer to memory, in big-endian order
void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num )
{
assert( byte );
byte[0] = (unsigned char)(num>>24);
byte[1] = (unsigned char)(num>>16);
byte[2] = (unsigned char)(num>>8);
byte[3] = (unsigned char)num;
}
// Constructor *******************************************************
SHA1::SHA1()
{
// make sure that the data type is the right size
assert( sizeof( Uint32 ) * 5 == 20 );
// initialize
H0 = 0x67452301;
H1 = 0xefcdab89;
H2 = 0x98badcfe;
H3 = 0x10325476;
H4 = 0xc3d2e1f0;
unprocessedBytes = 0;
size = 0;
}
// Destructor ********************************************************
SHA1::~SHA1()
{
// erase data
H0 = H1 = H2 = H3 = H4 = 0;
for( int c = 0; c < 64; c++ ) bytes[c] = 0;
unprocessedBytes = size = 0;
}
// process ***********************************************************
void SHA1::process()
{
assert( unprocessedBytes == 64 );
//printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" );
int t;
Uint32 a, b, c, d, e, K, f, W[80];
// starting values
a = H0;
b = H1;
c = H2;
d = H3;
e = H4;
// copy and expand the message block
for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24)
+(bytes[t*4 + 1] << 16)
+(bytes[t*4 + 2] << 8)
+ bytes[t*4 + 3];
for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 );
/* main loop */
Uint32 temp;
for( t = 0; t < 80; t++ )
{
if( t < 20 ) {
K = 0x5a827999;
f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~
} else if( t < 40 ) {
K = 0x6ed9eba1;
f = b ^ c ^ d;
} else if( t < 60 ) {
K = 0x8f1bbcdc;
f = (b & c) | (b & d) | (c & d);
} else {
K = 0xca62c1d6;
f = b ^ c ^ d;
}
temp = lrot(a,5) + f + e + W[t] + K;
e = d;
d = c;
c = lrot(b,30);
b = a;
a = temp;
//printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e );
}
/* add variables */
H0 += a;
H1 += b;
H2 += c;
H3 += d;
H4 += e;
//printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 );
/* all bytes have been processed */
unprocessedBytes = 0;
}
// addBytes **********************************************************
void SHA1::addBytes( const char* data, int num )
{
assert( data );
assert( num > 0 );
// add these bytes to the running total
size += num;
// repeat until all data is processed
while( num > 0 )
{
// number of bytes required to complete block
int needed = 64 - unprocessedBytes;
assert( needed > 0 );
// number of bytes to copy (use smaller of two)
int toCopy = (num < needed) ? num : needed;
// Copy the bytes
memcpy( bytes + unprocessedBytes, data, toCopy );
// Bytes have been copied
num -= toCopy;
data += toCopy;
unprocessedBytes += toCopy;
// there is a full block
if( unprocessedBytes == 64 ) process();
}
}
// digest ************************************************************
unsigned char* SHA1::getDigest()
{
// save the message size
Uint32 totalBitsL = size << 3;
Uint32 totalBitsH = size >> 29;
// add 0x80 to the message
addBytes( "\x80", 1 );
unsigned char footer[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// block has no room for 8-byte filesize, so finish it
if( unprocessedBytes > 56 )
addBytes( (char*)footer, 64 - unprocessedBytes);
assert( unprocessedBytes <= 56 );
// how many zeros do we need
int neededZeros = 56 - unprocessedBytes;
// store file size (in bits) in big-endian format
storeBigEndianUint32( footer + neededZeros , totalBitsH );
storeBigEndianUint32( footer + neededZeros + 4, totalBitsL );
// finish the final block
addBytes( (char*)footer, neededZeros + 8 );
// allocate memory for the digest bytes
unsigned char* digest = (unsigned char*)malloc( 20 );
// copy the digest bytes
storeBigEndianUint32( digest, H0 );
storeBigEndianUint32( digest + 4, H1 );
storeBigEndianUint32( digest + 8, H2 );
storeBigEndianUint32( digest + 12, H3 );
storeBigEndianUint32( digest + 16, H4 );
// return the digest
return digest;
}

35
src/sha1.h Normal file
View File

@ -0,0 +1,35 @@
/* sha1.h
Copyright (c) 2005 Michael D. Leonhard
http://tamale.net/
This file is licensed under the terms described in the
accompanying LICENSE file.
*/
#ifndef SHA1_HEADER
typedef unsigned int Uint32;
class SHA1
{
private:
// fields
Uint32 H0, H1, H2, H3, H4;
unsigned char bytes[64];
int unprocessedBytes;
Uint32 size;
void process();
public:
SHA1();
~SHA1();
void addBytes( const char* data, int num );
unsigned char* getDigest();
// utility methods
static Uint32 lrot( Uint32 x, int bits );
static void storeBigEndianUint32( unsigned char* byte, Uint32 num );
static void hexPrinter( unsigned char* c, int l );
};
#define SHA1_HEADER
#endif