E-MailRelay
gsecret.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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/// \file gsecret.cpp
19///
20
21#include "gdef.h"
22#include "gsecret.h"
23#include "gstr.h"
24#include "gstringfield.h"
25#include "gbase64.h"
26#include "gxtext.h"
27#include "gmd5.h"
28#include "gassert.h"
29#include <sstream>
30
31GAuth::Secret::Secret( Value id , Value secret , std::string_view hash_function ,
32 std::string_view context ) :
33 m_hash_function(G::Str::lower(hash_function)) ,
34 m_context(G::sv_to_string(context))
35{
36 std::string reason = check( id , secret , m_hash_function ) ;
37 if( !reason.empty() )
38 throw context.empty() ? Error(reason) : Error(m_context,reason) ;
39
40 m_id = decode( id ) ;
41 m_secret = decode( secret ) ;
42}
43
44GAuth::Secret::Secret() // private -- see Secret::none()
45{
46 G_ASSERT( !valid() ) ;
47}
48
49std::string GAuth::Secret::check( Value id , Value secret , std::string_view hash_function )
50{
51 if( id.first.empty() )
52 return "empty id" ;
53
54 if( secret.first.empty() )
55 return "empty secret" ;
56
57 if( !validEncodingType(id) )
58 return "invalid encoding type for id" ;
59
60 if( !validEncodingType(secret) )
61 return "invalid encoding type for secret" ;
62
63 if( !validEncoding(id) )
64 return "invalid " + G::sv_to_string(id.second) + " encoding of id" ;
65
66 if( !validEncoding(secret) )
67 return "invalid " + G::sv_to_string(id.second) + " encoding of secret" ;
68
69 if( encoding(secret) == Encoding::dotted && !G::Str::imatch(hash_function,"md5"_sv) )
70 return "invalid use of dotted format" ;
71
72 return {} ;
73}
74
76{
77 return {} ;
78}
79
81{
82 return !m_secret.empty() ;
83}
84
85std::string GAuth::Secret::secret() const
86{
87 if( !valid() ) throw Error() ;
88 return m_secret ;
89}
90
92{
93 return !m_hash_function.empty() ;
94}
95
96std::string GAuth::Secret::id() const
97{
98 if( !valid() ) throw Error() ;
99 return m_id ;
100}
101
103{
104 if( !valid() ) throw Error() ;
105 return m_hash_function ;
106}
107
108std::string GAuth::Secret::info( const std::string & id_in ) const
109{
110 std::ostringstream ss ;
111 ss << (valid()?(masked()?maskHashFunction():std::string("plaintext")):std::string("missing")) << " secret" ;
112 std::string id_ = ( id_in.empty() && valid() ) ? m_id : id_in ;
113 if( !id_.empty() )
114 {
115 ss << " for [" << G::Str::printable(id_) << "]" ;
116 }
117 if( !m_context.empty() )
118 {
119 ss << " from " << m_context ;
120 }
121 return ss.str() ;
122}
123
124bool GAuth::Secret::isDotted( std::string_view s )
125{
126 return
127 s.size() >= 15U &&
128 s.find_first_not_of("0123456789."_sv) == std::string::npos &&
129 G::StringFieldView(s,'.').count() == 8U ;
130}
131
132std::string GAuth::Secret::undotted( std::string_view s )
133{
134 std::string result ;
135 for( G::StringFieldView decimal(s,".",1U) ; decimal ; ++decimal )
136 {
137 G::Md5::big_t n = 0U ;
138 std::string_view d = decimal() ;
139 for( const char & c : d )
140 {
141 n *= 10U ;
142 n += (G::Md5::big_t(c)-'0') ;
143 }
144 for( int j = 0 ; j < 4 ; j++ )
145 {
146 unsigned char uc = ( n & 0xffU ) ;
147 n >>= 8 ;
148 result.push_back( static_cast<char>(uc) ) ;
149 }
150 }
151 return result ;
152}
153
154bool GAuth::Secret::validEncodingType( Value value )
155{
156 return
157 value.second.empty() ||
158 G::Str::imatch( value.second , "xtext"_sv ) ||
159 G::Str::imatch( value.second , "base64"_sv ) ||
160 G::Str::imatch( value.second , "dotted"_sv ) ;
161}
162
163GAuth::Secret::Encoding GAuth::Secret::encoding( Value value )
164{
165 if( value.second.empty() )
166 return Encoding::raw ;
167 else if( G::Str::imatch( value.second , "xtext"_sv ) )
168 return Encoding::xtext ;
169 else if( G::Str::imatch( value.second , "dotted"_sv ) )
170 return Encoding::dotted ;
171 else
172 return Encoding::base64 ;
173}
174
175bool GAuth::Secret::validEncoding( Value value )
176{
177 if( encoding(value) == Encoding::raw )
178 return true ;
179 else if( encoding(value) == Encoding::xtext )
180 return G::Xtext::valid( value.first ) ;
181 else if( encoding(value) == Encoding::dotted )
182 return isDotted( value.first ) ;
183 else
184 return G::Base64::valid( value.first ) ;
185}
186
187std::string GAuth::Secret::decode( Value value )
188{
189 if( encoding(value) == Encoding::raw )
190 return G::sv_to_string( value.first ) ;
191 else if( encoding(value) == Encoding::xtext )
192 return G::Xtext::decode( value.first ) ;
193 else if( encoding(value) == Encoding::dotted )
194 return undotted( value.first ) ;
195 else
196 return G::Base64::decode( value.first ) ;
197}
198
Encapsulates a userid/shared-secret/hash-function tuple from the secrets file.
Definition: gsecret.h:44
std::string secret() const
Returns the secret shared key. Throws if not valid().
Definition: gsecret.cpp:85
bool masked() const
Returns true if a non-empty hash function was passed to the ctor.
Definition: gsecret.cpp:91
static Secret none()
Factory function that returns a secret that is not valid().
Definition: gsecret.cpp:75
Secret(Value id, Value secret, std::string_view masking_hash_function={}, std::string_view context={})
Constructor used by the SecretsFile class.
Definition: gsecret.cpp:31
std::string maskHashFunction() const
Returns the masking function name as passed to the ctor, such as "md5", or the empty string if not ma...
Definition: gsecret.cpp:102
bool valid() const
Returns true if the secret is valid.
Definition: gsecret.cpp:80
static bool isDotted(std::string_view)
Returns true if the given secret string looks like it is in the old dotted format rather than base64.
Definition: gsecret.cpp:124
std::string info(const std::string &id={}) const
Returns information for logging, excluding anything sensitive.
Definition: gsecret.cpp:108
static std::string decode(Value)
Decodes a value.
Definition: gsecret.cpp:187
std::string id() const
Returns the associated identity. Throws if not valid().
Definition: gsecret.cpp:96
static std::string check(Value id, Value secret, std::string_view masking_hash_function)
Does a non-throwing check of the constructor parameters, returning an error message or the empty stri...
Definition: gsecret.cpp:49
static std::string decode(std::string_view, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
Definition: gbase64.cpp:84
static bool valid(std::string_view, bool strict=true)
Returns true if the string is a valid base64 encoding, possibly allowing for embedded newlines,...
Definition: gbase64.cpp:89
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
Definition: gstr.cpp:1415
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
Definition: gstr.cpp:913
A zero-copy string field iterator where the field separators are short fixed strings.
Definition: gstringfield.h:53
std::size_t count() const noexcept
Returns the number of fields.
Definition: gstringfield.h:243
static std::string decode(std::string_view)
Decodes the given string.
Definition: gxtext.cpp:119
static bool valid(std::string_view, bool strict=false)
Returns true if a valid encoding, or empty.
Definition: gxtext.cpp:77
Low-level classes.
Definition: garg.h:36