gmd5_native.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gmd5_native.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gmd5.h"
23 #include "gstr.h"
24 #include "gstrings.h"
25 #include "gassert.h"
26 #include "md5.h"
27 #include <sstream>
28 
29 namespace
30 {
31  typedef md5::big_t big_t ;
32  typedef md5::digest_stream md5_state_t ;
33  typedef md5::digest::state_type state_type ;
34  typedef md5::format format ;
35 
36  void init( md5_state_t & )
37  {
38  }
39  void update( md5_state_t & context , const std::string & input )
40  {
41  context.add( input ) ;
42  }
43  std::string final( md5_state_t & context )
44  {
45  context.close() ;
46  return format::raw( context.state().d ) ;
47  }
48  std::string writeOut( const md5_state_t & context )
49  {
50  G_ASSERT( context.state().s.length() == 0U ) ;
51  G_ASSERT( context.state().n == 64U ) ; // ie. magic number below
52  std::ostringstream ss ;
53  ss <<
54  context.state().d.a << "." <<
55  context.state().d.b << "." <<
56  context.state().d.c << "." <<
57  context.state().d.d ;
58  return ss.str() ;
59  }
60  big_t toUnsigned( const std::string & s , bool limited = true )
61  {
62  // big_t can be bigger than unsigned long but long long might not
63  // be supported by the compiler, so do it oldschool
64  if( s.empty() ) throw G::Md5::Error( s ) ;
65  big_t result = 0U ;
66  for( std::string::const_iterator p = s.begin() ; p != s.end() ; ++p )
67  {
68  result *= 10U ;
69  unsigned int n =
70  ( *p == '0' ) ? 0U : (
71  ( *p == '1' ) ? 1U : (
72  ( *p == '2' ) ? 2U : (
73  ( *p == '3' ) ? 3U : (
74  ( *p == '4' ) ? 4U : (
75  ( *p == '5' ) ? 5U : (
76  ( *p == '6' ) ? 6U : (
77  ( *p == '7' ) ? 7U : (
78  ( *p == '8' ) ? 8U : (
79  ( *p == '9' ) ? 9U : 10U ))))))))) ;
80  if( n == 10U ) throw G::Md5::Error( s ) ;
81  if( limited && (result+n) < result ) throw G::Md5::Error( s ) ;
82  result += n ;
83  }
84  return result ;
85  }
86  void readIn( md5_state_t & context , G::Strings & s )
87  {
88  big_t a = toUnsigned( s.front() ) ; s.pop_front() ;
89  big_t b = toUnsigned( s.front() ) ; s.pop_front() ;
90  big_t c = toUnsigned( s.front() ) ; s.pop_front() ;
91  big_t d = toUnsigned( s.front() ) ; s.pop_front() ;
92  state_type state = { a , b , c , d } ;
93  md5::small_t magic_number = 64U ;
94  context = md5_state_t( state , magic_number ) ;
95  }
96 }
97 
98 std::string G::Md5::xor_( const std::string & s1 , const std::string & s2 )
99 {
100  G_ASSERT( s1.length() == s2.length() ) ;
101  std::string::const_iterator p1 = s1.begin() ;
102  std::string::const_iterator p2 = s2.begin() ;
103  std::string result ;
104  for( ; p1 != s1.end() ; ++p1 , ++p2 )
105  {
106  unsigned char c1 = static_cast<unsigned char>(*p1) ;
107  unsigned char c2 = static_cast<unsigned char>(*p2) ;
108  unsigned char c = static_cast<unsigned char>( c1 ^ c2 ) ;
109  result.append( 1U , static_cast<char>(c) ) ;
110  }
111  return result ;
112 }
113 
114 std::string G::Md5::key64( std::string k )
115 {
116  const size_t B = 64U ;
117  if( k.length() > B )
118  k = digest(k) ;
119  if( k.length() < B )
120  k.append( std::string(B-k.length(),'\0') ) ;
121  G_ASSERT( k.length() == B ) ;
122  return k ;
123 }
124 
125 std::string G::Md5::ipad()
126 {
127  const size_t B = 64U ;
128  return std::string( B , '\066' ) ; // 00110110 = 00,110,110
129 }
130 
131 std::string G::Md5::opad()
132 {
133  const size_t B = 64U ;
134  return std::string( B , '\134' ) ; // 01011100 = 01,011,100
135 }
136 
137 std::string G::Md5::mask( const std::string & k )
138 {
139  std::string k64 = key64( k ) ;
140  return mask( k64 , ipad() ) + "." + mask( k64 , opad() ) ;
141 }
142 
143 std::string G::Md5::mask( const std::string & k64 , const std::string & pad )
144 {
145  md5_state_t context ;
146  init( context ) ;
147  update( context , xor_(k64,pad) ) ;
148  return writeOut( context ) ;
149 }
150 
151 std::string G::Md5::hmac( const std::string & masked_key , const std::string & input , Masked )
152 {
153  G::Strings part_list ;
154  G::Str::splitIntoTokens( masked_key , part_list , "." ) ;
155  if( part_list.size() != 8U )
156  throw InvalidMaskedKey( masked_key ) ;
157 
158  md5_state_t inner_context ;
159  md5_state_t outer_context ;
160  readIn( inner_context , part_list ) ;
161  readIn( outer_context , part_list ) ;
162  update( inner_context , input ) ;
163  update( outer_context , final(inner_context) ) ;
164  return final( outer_context ) ;
165 }
166 
167 std::string G::Md5::hmac( const std::string & k , const std::string & input )
168 {
169  std::string k64 = key64( k ) ;
170  return digest( xor_(k64,opad()) , digest(xor_(k64,ipad()),input) ) ;
171 }
172 
173 std::string G::Md5::digest( const std::string & input )
174 {
175  return digest( input , NULL ) ;
176 }
177 
178 std::string G::Md5::digest( const std::string & input_1 , const std::string & input_2 )
179 {
180  return digest( input_1 , &input_2 ) ;
181 }
182 
183 std::string G::Md5::digest( const std::string & input_1 , const std::string * input_2 )
184 {
185  md5_state_t context ;
186  init( context ) ;
187  update( context , input_1 ) ;
188  if( input_2 != NULL )
189  update( context , *input_2 ) ;
190  return final( context ) ;
191 }
192 
193 std::string G::Md5::printable( const std::string & input )
194 {
195  G_ASSERT( input.length() == 16U ) ;
196 
197  std::string result ;
198  const char * hex = "0123456789abcdef" ;
199  const size_t n = input.length() ;
200  for( size_t i = 0U ; i < n ; i++ )
201  {
202  unsigned char c = static_cast<unsigned char>(input.at(i)) ;
203  result.append( 1U , hex[(c>>4U)&0x0F] ) ;
204  result.append( 1U , hex[(c>>0U)&0x0F] ) ;
205  }
206 
207  return result ;
208 }
209 
A class that calculates an md5 digest from a data stream using the algorithm described by RFC 1321...
Definition: md5.h:238
An overload discriminator for G::Md5::hmac()
Definition: gmd5.h:43
size_type big_t
To hold at least 32 bits, maybe more. Try unsigned long on small systems.
Definition: md5.h:44
size_type small_t
To hold at least a size_t. Must fit in a big_t.
Definition: md5.h:45
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
static void splitIntoTokens(const std::string &in, Strings &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:714
static std::string hmac(const std::string &key, const std::string &input)
Computes a Hashed Message Authentication Code using MD5 as the hash function.
#define G_ASSERT(test)
Definition: gassert.h:30
Holds the md5 algorithm state. Used by md5::digest.
Definition: md5.h:80
A static string-formatting class for the output of md5::digest.
Definition: md5.h:141
static std::string mask(const std::string &key)
Masks an HMAC key so that it can be stored more safely.
static std::string digest(const std::string &input)
Creates an MD5 digest.
static std::string printable(const std::string &input)
Converts a binary string into a printable form, using a lowercase hexadecimal encoding.