E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gcram.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 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 // gcram.cpp
19 //
20
21 #include "gdef.h"
22 #include "gcram.h"
23 #include "ghash.h"
24 #include "gmd5.h"
25 #include "gstr.h"
26 #include "gssl.h"
27 #include "gbase64.h"
28 #include "glocal.h"
29 #include "gtest.h"
30 #include "gdebug.h"
31 #include <cstdlib> // std::rand()
32 #include <algorithm>
33
34 namespace
35 {
36 GSsl::Library & lib()
37 {
38 GSsl::Library * p = GSsl::Library::instance() ;
39 if( p == nullptr ) throw std::runtime_error( "no tsl library" ) ;
40 return *p ;
41 }
42 struct DigesterAdaptor
43 {
44 explicit DigesterAdaptor( const std::string & name ) :
45 m_name(name) ,
46 m_digester(lib().digester(name)) ,
47 m_used(false)
48 {
49 }
50 std::string operator()( const std::string & data_1 , const std::string & data_2 )
51 {
52 // adaptor must be stateless
53 if( m_used )
54 m_digester = GSsl::Digester( lib().digester(m_name) ) ;
55 m_used = true ;
56
57 m_digester.add( data_1 ) ;
58 m_digester.add( data_2 ) ;
59 return m_digester.value() ;
60 }
61 size_t blocksize() const
62 {
63 return m_digester.blocksize() ;
64 }
65 std::string m_name ;
66 GSsl::Digester m_digester ;
67 bool m_used ;
68 } ;
69 struct PostDigesterAdaptor
70 {
71 explicit PostDigesterAdaptor( const std::string & name ) :
72 m_name(name)
73 {
74 GSsl::Digester x = lib().digester( m_name ) ;
75 if( x.statesize() == 0U )
76 throw GAuth::Cram::NoState( m_name ) ;
77 }
78 std::string operator()( const std::string & state_pair , const std::string & data )
79 {
80 GSsl::Digester xi = lib().digester( m_name , state_pair.substr(0U,state_pair.size()/2U) ) ;
81 xi.add( data ) ;
82 GSsl::Digester xo = lib().digester( m_name , state_pair.substr(state_pair.size()/2U) ) ;
83 xo.add( xi.value() ) ;
84 return xo.value() ;
85 }
86 std::string m_name ;
87 } ;
88 }
89
90 std::string GAuth::Cram::response( const std::string & hash_type , bool as_hmac ,
91 const Secret & secret , const std::string & challenge ,
92 const std::string & id_prefix )
93 {
94 try
95 {
96 G_DEBUG( "GAuth::Cram::response: [" << hash_type << "][" << as_hmac << "]"
97 "[" << G::Str::printable(secret.key()) << "][" << secret.maskType() << "][" << challenge << "]"
98 "[" << G::Str::printable(id_prefix) << "]"
99 "[" << responseImp(hash_type,as_hmac,secret,challenge) << "]" ) ;
100
101 return id_prefix + " " + responseImp(hash_type,as_hmac,secret,challenge) ;
102 }
103 catch( std::exception & e )
104 {
105 G_DEBUG( "GAuth::Cram::response: exception: " << e.what() ) ; G_IGNORE_VARIABLE( e ) ;
106 return std::string() ;
107 }
108 }
109
110 bool GAuth::Cram::validate( const std::string & hash_type , bool as_hmac ,
111 const Secret & secret , const std::string & challenge ,
112 const std::string & response_in )
113 {
114 try
115 {
116 G_DEBUG( "GAuth::Cram::validate: [" << hash_type << "][" << as_hmac << "]"
117 "[" << G::Str::printable(secret.key()) << "][" << secret.maskType() << "][" << challenge << "][" << response_in << "]"
118 "[" << responseImp(hash_type,as_hmac,secret,challenge) << "]" ) ;
119
120 std::string expectation = G::Str::tail( response_in , " " ) ;
121 return !expectation.empty() && responseImp(hash_type,as_hmac,secret,challenge) == expectation ;
122 }
123 catch( std::exception & e )
124 {
125 G_DEBUG( "GAuth::Cram::validate: exception: " << e.what() ) ; G_IGNORE_VARIABLE( e ) ;
126 return false ;
127 }
128 }
129
130 std::string GAuth::Cram::id( const std::string & response )
131 {
132 return G::Str::head( response , " " ) ;
133 }
134
135 std::string GAuth::Cram::responseImp( const std::string & mechanism_hash_type , bool as_hmac ,
136 const Secret & secret , const std::string & challenge )
137 {
138 G_DEBUG( "GAuth::Cram::responseImp: mechanism-hash=[" << mechanism_hash_type << "] secret-hash=[" << secret.maskType() << "] as-hmac=" << as_hmac ) ;
139 if( !as_hmac )
140 {
141 if( secret.masked() )
142 throw BadType( secret.maskType() ) ;
143
144 if( mechanism_hash_type == "MD5" )
145 {
146 return G::Hash::printable( G::Md5::digest(challenge,secret.key()) ) ;
147 }
148 else
149 {
150 DigesterAdaptor digest( mechanism_hash_type ) ;
151 return G::Hash::printable( digest(challenge,secret.key()) ) ;
152 }
153 }
154 else if( secret.masked() )
155 {
156 if( ! G::Str::imatch(secret.maskType(),mechanism_hash_type) )
157 throw Mismatch( secret.maskType() , mechanism_hash_type ) ;
158
159 if( mechanism_hash_type == "MD5" )
160 {
161 return G::Hash::printable( G::Hash::hmac(G::Md5::postdigest,secret.key(),challenge,G::Hash::Masked()) ) ;
162 }
163 else
164 {
165 PostDigesterAdaptor postdigest( mechanism_hash_type ) ;
166 return G::Hash::printable( G::Hash::hmac(postdigest,secret.key(),challenge,G::Hash::Masked()) ) ;
167 }
168 }
169 else
170 {
171 if( mechanism_hash_type == "MD5" )
172 {
173 return G::Hash::printable( G::Hash::hmac(G::Md5::digest2,G::Md5::blocksize(),secret.key(),challenge) ) ;
174 }
175 else
176 {
177 DigesterAdaptor digest( mechanism_hash_type ) ;
178 return G::Hash::printable( G::Hash::hmac(digest,digest.blocksize(),secret.key(),challenge) ) ;
179 }
180 }
181 }
182
183 G::StringArray GAuth::Cram::hashTypes( const std::string & prefix , bool require_state )
184 {
185 // we can do CRAM-X for all hash functions (X) provided by the TLS library
186 // but if we only have masked passwords (ie. require_state) then we only
187 // want hash functions that are capable of initialision with intermediate state
188 //
189 G::StringArray result = GSsl::Library::digesters( require_state ) ; // strongest first
190 if( G::Test::enabled("cram-fake-hash") )
191 result.push_back( "FAKE" ) ;
192
193 G_DEBUG( "GAuth::Cram::hashTypes: tls library [" << GSsl::Library::ids() << "]" ) ;
194 G_DEBUG( "GAuth::Cram::hashTypes: tls library hash types: [" << G::Str::join(",",result) << "] (" << (require_state?1:0) << ")" ) ;
195
196 // always include MD5 since we use G::Md5 code
197 if( std::find(result.begin(),result.end(),"MD5") == result.end() )
198 result.push_back( "MD5" ) ;
199
200 if( !prefix.empty() )
201 {
202 for( G::StringArray::iterator p = result.begin() ; p != result.end() ; ++p )
203 *p = prefix + *p ;
204 }
205 return result ;
206 }
207
208 std::string GAuth::Cram::challenge()
209 {
210 std::ostringstream ss ;
211 ss << "<" << std::rand() << "." << G::DateTime::now() << "@" << GNet::Local::canonicalName() << ">" ;
212 return ss.str() ;
213 }
214
215 /// \file gcram.cpp
GSsl::Library::instance
static Library * instance()
Returns a pointer to a library object, if any.
Definition: gssl.cpp:72
gssl.h
An interface to an underlying TLS library.
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
GNet::Local::canonicalName
static std::string canonicalName()
Returns the canonical network name assiciated with hostname().
Definition: glocal.cpp:54
G::Str::tail
static std::string tail(const std::string &in, size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1102
G::Hash::hmac
static std::string hmac(Fn2 digest, size_t blocksize, const std::string &key, const std::string &input)
Computes a Hashed Message Authentication Code using given hash function.
Definition: ghash.h:114
gmd5.h
GSsl::Library::digesters
static G::StringArray digesters(bool require_state=false)
Returns a list of hash function names (such as "MD5") that the TLS library can do, ordered roughly from strongest to weakest.
Definition: gssl.cpp:145
G::Str::head
static std::string head(const std::string &in, size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1088
gcram.h
G::Md5::blocksize
static size_t blocksize()
Returns the block size in bytes (64).
Definition: gmd5.cpp:564
G::Str::imatch
static bool imatch(const std::string &, const std::string &)
Returns true if the two strings are the same, ignoring Latin-1 case.
Definition: gstr.cpp:1220
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
GAuth::Secret::key
std::string key() const
Returns the key. Throws if not valid().
Definition: gsecret.cpp:100
gtest.h
GAuth::Cram::validate
static bool validate(const std::string &hash_type, bool hmac, const Secret &secret, const std::string &challenge, const std::string &response)
Validates the response with respect to the original challenge.
Definition: gcram.cpp:110
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime_unix.cpp:26
GAuth::Cram::challenge
static std::string challenge()
Returns a challenge string that incorporates a random number and the current time.
Definition: gcram.cpp:208
G::Hash::Masked
An overload discriminator for G::Hash::hmac()
Definition: ghash.h:40
GAuth::Cram::id
static std::string id(const std::string &response)
Returns the leading id part of the response.
Definition: gcram.cpp:130
gstr.h
GSsl::Digester
Returns an object that can perform a cryptographic hash.
Definition: gssl.h:194
G::Test::enabled
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:69
ghash.h
GSsl::Digester::value
std::string value()
Returns the hash value.
Definition: gssl.cpp:228
G::Hash::printable
static std::string printable(const std::string &input)
Converts a binary string into a printable form, using a lowercase hexadecimal encoding.
Definition: ghash.cpp:53
GAuth::Cram::response
static std::string response(const std::string &hash_type, bool hmac, const Secret &secret, const std::string &challenge, const std::string &response_prefix)
Constructs a response to a challenge comprising the response-prefix, space, and digest-or-hmac of key...
Definition: gcram.cpp:90
G::Md5::postdigest
static std::string postdigest(const std::string &state_pair, const std::string &message)
A convenience function that returns the value() from an outer digest that is initialised with the sec...
Definition: gmd5.cpp:549
GSsl::Digester::add
void add(const std::string &)
Adds data of arbitrary size.
Definition: gssl.cpp:223
G::Md5::digest
static std::string digest(const std::string &input)
A convenience function that returns a digest from one input.
Definition: gmd5.cpp:522
GSsl::Library::ids
static std::string ids()
Returns a concatenation of all available TLS library names and versions.
Definition: gssl_none.cpp:89
glocal.h
GSsl::Library
A singleton class for initialising the underlying TLS library.
Definition: gssl.h:234
gbase64.h
G::Str::join
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1023
GAuth::Secret::masked
bool masked() const
Returns true if key() is masked.
Definition: gsecret.cpp:106
GSsl::Digester::statesize
size_t statesize() const
Returns the size of the state() string in bytes, or zero if state() is not implemented.
Definition: gssl.cpp:248
GAuth::Secret::maskType
std::string maskType() const
Returns the masking function name, such as "MD5", or the empty string if not masked().
Definition: gsecret.cpp:117
G::Md5::digest2
static std::string digest2(const std::string &input_1, const std::string &input_2)
A non-overloaded name for the digest() overload taking two parameters.
Definition: gmd5.cpp:536
GAuth::Secret
Encapsulates a shared secret from the secrets file plus the associated userid.
Definition: gsecret.h:42
gdebug.h
GAuth::Cram::hashTypes
static G::StringArray hashTypes(const std::string &prefix=std::string(), bool require_state=false)
Returns a list of supported hash types, such as "MD5" and "SHA1", ordered with the strongest first...
Definition: gcram.cpp:183