VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gmd5_native.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
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 finish( 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 old-school
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  if( *p < '0' || *p > '9' ) throw G::Md5::Error( s ) ;
70  const unsigned int n = static_cast<unsigned int>(*p) - static_cast<unsigned int>('0') ;
71  if( limited && (result+n) < result ) throw G::Md5::Error( s ) ;
72  result += n ;
73  }
74  return result ;
75  }
76  G::StringArray::iterator readIn( md5_state_t & context , G::StringArray::iterator p )
77  {
78  big_t a = toUnsigned( *p++ ) ;
79  big_t b = toUnsigned( *p++ ) ;
80  big_t c = toUnsigned( *p++ ) ;
81  big_t d = toUnsigned( *p++ ) ;
82  state_type state = { a , b , c , d } ;
83  md5::small_t magic_number = 64U ;
84  context = md5_state_t( state , magic_number ) ;
85  return p ;
86  }
87 }
88 
89 std::string G::Md5::xor_( const std::string & s1 , const std::string & s2 )
90 {
91  G_ASSERT( s1.length() == s2.length() ) ;
92  std::string::const_iterator p1 = s1.begin() ;
93  std::string::const_iterator p2 = s2.begin() ;
94  std::string result ;
95  result.reserve( s1.length() ) ;
96  for( ; p1 != s1.end() ; ++p1 , ++p2 )
97  {
98  unsigned char c1 = static_cast<unsigned char>(*p1) ;
99  unsigned char c2 = static_cast<unsigned char>(*p2) ;
100  unsigned char c = static_cast<unsigned char>( c1 ^ c2 ) ;
101  result.append( 1U , static_cast<char>(c) ) ;
102  }
103  return result ;
104 }
105 
106 std::string G::Md5::key64( std::string k )
107 {
108  const size_t B = 64U ;
109  if( k.length() > B )
110  k = digest(k) ;
111  if( k.length() < B )
112  k.append( std::string(B-k.length(),'\0') ) ;
113  G_ASSERT( k.length() == B ) ;
114  return k ;
115 }
116 
117 std::string G::Md5::ipad()
118 {
119  const size_t B = 64U ;
120  return std::string( B , '\066' ) ; // 00110110 = 00,110,110
121 }
122 
123 std::string G::Md5::opad()
124 {
125  const size_t B = 64U ;
126  return std::string( B , '\134' ) ; // 01011100 = 01,011,100
127 }
128 
129 std::string G::Md5::mask( const std::string & k )
130 {
131  std::string k64 = key64( k ) ;
132  return mask( k64 , ipad() ) + "." + mask( k64 , opad() ) ;
133 }
134 
135 std::string G::Md5::mask( const std::string & k64 , const std::string & pad )
136 {
137  md5_state_t context ;
138  init( context ) ;
139  update( context , xor_(k64,pad) ) ;
140  return writeOut( context ) ;
141 }
142 
143 std::string G::Md5::hmac( const std::string & masked_key , const std::string & input , Masked )
144 {
145  G::StringArray part_list ;
146  part_list.reserve( 8U ) ;
147  G::Str::splitIntoTokens( masked_key , part_list , "." ) ;
148  if( part_list.size() != 8U )
149  throw InvalidMaskedKey( masked_key ) ;
150 
151  md5_state_t inner_context ;
152  md5_state_t outer_context ;
153  readIn( outer_context , readIn( inner_context , part_list.begin() ) ) ;
154  update( inner_context , input ) ;
155  update( outer_context , finish(inner_context) ) ;
156  return finish( outer_context ) ;
157 }
158 
159 std::string G::Md5::hmac( const std::string & k , const std::string & input )
160 {
161  std::string k64 = key64( k ) ;
162  return digest( xor_(k64,opad()) , digest(xor_(k64,ipad()),input) ) ;
163 }
164 
165 std::string G::Md5::digest( const std::string & input )
166 {
167  return digest( input , nullptr ) ;
168 }
169 
170 std::string G::Md5::digest( const std::string & input_1 , const std::string & input_2 )
171 {
172  return digest( input_1 , &input_2 ) ;
173 }
174 
175 std::string G::Md5::digest( const std::string & input_1 , const std::string * input_2 )
176 {
177  md5_state_t context ;
178  init( context ) ;
179  update( context , input_1 ) ;
180  if( input_2 != nullptr )
181  update( context , *input_2 ) ;
182  return finish( context ) ;
183 }
184 
185 std::string G::Md5::printable( const std::string & input )
186 {
187  G_ASSERT( input.length() == 16U ) ;
188 
189  std::string result ;
190  const char * hex = "0123456789abcdef" ;
191  const size_t n = input.length() ;
192  for( size_t i = 0U ; i < n ; i++ )
193  {
194  unsigned char c = static_cast<unsigned char>(input.at(i)) ;
195  result.append( 1U , hex[(c>>4U)&0x0F] ) ;
196  result.append( 1U , hex[(c>>0U)&0x0F] ) ;
197  }
198 
199  return result ;
200 }
201 
202 /// \file gmd5_native.cpp
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:41
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::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:868
static std::string hmac(const std::string &key, const std::string &input)
Computes a Hashed Message Authentication Code using MD5 as the hash function.
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)
Computes a masked key for hmac() from the given shared key, returning a printable string...
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.