1 #include "httpauth.hpp"
13 std::string
encode_hex(const uint8_t* data
, size_t datasize
)
16 for(size_t i
= 0; i
< datasize
; i
++)
17 x
<< std::hex
<< std::setw(2) << std::setfill('0') << (int)data
[i
];
21 //Can only handle 9, 32-126, 128-255.
22 std::string
quote_field(const std::string
& field
)
25 for(size_t i
= 0; i
< field
.length(); i
++) {
28 else if(field
[i
] == '\"')
37 std::string
identity(const std::string
& field
)
43 //0 => Controls, except HT
44 //1 => Whitespace (HT and SP).
45 //2 => Token chars (!#$%&'*+.^_`|~0-9A-Za-z-)
46 //3 => Double quote (")
47 //4 => Other quoted characters.
52 uint8_t charclass
[] = {
53 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, //0
55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //1
56 1, 2, 3, 2, 2, 2, 2, 2, 4, 4, 2, 2, 5, 2, 2, 4, //2
57 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 6, 4, 4, //3
58 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //4
59 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 7, 4, 2, 2, //5
60 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //6
61 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 4, 2, 0, //7
62 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //8
63 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //9
64 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //A
65 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //B
66 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //C
67 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //D
68 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, //E
69 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 //F
72 #define A_INVALID 0x0000 //Give up.
73 #define A_NOOP 0x1000 //Do nothing, not even eat the character.
74 #define A_EAT 0x2000 //Eat the character.
75 #define A_COPY_PNAME 0x3000 //Eat and copy to pname.
76 #define A_COPY_PVAL 0x4000 //Eat and copy to pvalue.
77 #define A_EMIT 0x5000 //Emit (pname,pvalue) and zeroize.
79 #define A_STATE 0x0FFF
81 unsigned auth_param_parser
[] = {
82 //CTRL WS TOKN DBLQ OTHQ COMM EQLS BCKS EOS
83 // 0: Skip the initial whitespace.
84 0x0000, 0x2000, 0x1001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2000,
86 0x0000, 0x1002, 0x3001, 0x0000, 0x0000, 0x0000, 0x1002, 0x0000, 0x0000,
87 // 2: Parse whitespace after name (and =)
88 0x0000, 0x2002, 0x0000, 0x0000, 0x0000, 0x0000, 0x2003, 0x0000, 0x0000,
89 // 3: Parse whitespace before value.
90 0x0000, 0x2003, 0x1004, 0x2005, 0x0000, 0x5007, 0x0000, 0x0000, 0x5000,
92 0x0000, 0x5007, 0x4004, 0x0000, 0x0000, 0x5007, 0x0000, 0x0000, 0x5000,
93 // 5: Quoted-string value.
94 0x0000, 0x4005, 0x4005, 0x5009, 0x4005, 0x4005, 0x4005, 0x2006, 0x0000,
95 // 6: Quoted-string escape.
96 0x0000, 0x4005, 0x4005, 0x4005, 0x4005, 0x4005, 0x4005, 0x4005, 0x0000,
97 // 7: Whitespace after end of value.
98 0x0000, 0x2007, 0x0000, 0x0000, 0x0000, 0x2008, 0x0000, 0x0000, 0x2000,
99 // 8: Whitespace after comma.
100 0x0000, 0x2008, 0x1001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
101 // 9: Eat double quote at end of quoted string.
102 0x0000, 0x0000, 0x0000, 0x2007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
105 unsigned get_charclass(const std::string
& str
, size_t pos
) {
106 if(pos
< str
.length())
107 return charclass
[(uint8_t)str
[pos
]];
112 bool do_state_machine(const unsigned* machine
, const std::string
& input
, size_t start
,
113 std::map
<std::string
, std::string
>& params
)
115 std::string pname
, pvalue
;
117 while(start
<= input
.length()) {
118 unsigned act
= machine
[state
* 9 + get_charclass(input
, start
)];
119 switch(act
& A_MASK
) {
128 pname
= pname
+ std::string(1, input
[start
++]);
131 pvalue
= pvalue
+ std::string(1, input
[start
++]);
134 params
[pname
] = pvalue
;
139 state
= act
& A_STATE
;
145 inline uint8_t hparse(char _ch
)
148 uint8_t itbl
[] = {9,1,16,2,10,3,11,4,12,5,13,6,14,7,15,8,0};
149 return itbl
[(uint8_t)(2*ch
+ 22*(ch
>>5)) % 17];
153 void unhex(uint8_t* buf
, const std::string
& str
)
155 bool polarity
= false;
158 while(ptr
< str
.length()) {
159 val
= val
* 16 + hparse(str
[ptr
++]);
160 if(!(polarity
= !polarity
)) *(buf
++) = val
;
165 dh25519_http_auth::dh25519_http_auth(const uint8_t* _privkey
)
167 memcpy(privkey
, _privkey
, 32);
168 curve25519_clamp(privkey
);
169 curve25519(pubkey
, privkey
, curve25519_base
);
173 std::string
dh25519_http_auth::format_get_session_request()
175 return "dh25519 key="+encode_hex(pubkey
,32);
178 dh25519_http_auth::request_hash
dh25519_http_auth::start_request(const std::string
& url
, const std::string
& verb
)
181 std::string personalization
= verb
+ " " + url
;
185 throw std::runtime_error("Authenticator is not ready for request auth");
187 sprintf(buf
, "%u", _nonce
);
189 skein_hash
hp(skein_hash::PIPE_512
, 64);
190 hp
.write(ssecret
, 32, skein_hash::T_KEY
);
191 hp
.write((const uint8_t*)personalization
.c_str(), personalization
.length(), skein_hash::T_PERSONALIZATION
);
192 hp
.write(pubkey
, 32, skein_hash::T_PUBKEY
);
193 hp
.write((uint8_t*)buf
, strlen(buf
), skein_hash::T_NONCE
);
196 skein_hash
h(skein_hash::PIPE_512
, 256);
197 h
.write(ssecret
, 32, skein_hash::T_KEY
);
198 h
.write((const uint8_t*)personalization
.c_str(), personalization
.length(), skein_hash::T_PERSONALIZATION
);
199 h
.write(pubkey
, 32, skein_hash::T_PUBKEY
);
200 h
.write((uint8_t*)buf
, strlen(buf
), skein_hash::T_NONCE
);
201 return request_hash(id
, pubkey
, _nonce
, h
, prereq
);
204 std::string
dh25519_http_auth::request_hash::get_authorization()
207 uint8_t response
[32];
208 sprintf(buf
, "%u", nonce
);
210 return "dh25519 id="+quote_field(id
)+",key="+encode_hex(pubkey
,32)+",nonce="+identity(buf
)+
211 ",response="+encode_hex(response
,32)+",response2="+encode_hex(prereq
,8);
214 void dh25519_http_auth::parse_auth_response(const std::string
& response
)
216 std::map
<std::string
,std::string
> pparse
;
217 if(response
.substr(0, 7) != "dh25519") return;
219 if(!do_state_machine(auth_param_parser
, response
, 7, pparse
)) {
220 throw std::runtime_error("Response parse error: <"+response
+">");
223 //If there are id and challenge fields, use those to reseed.
224 bool stale
= (pparse
.count("error") && pparse
["error"] == "stale");
225 bool reseeded
= false;
226 if(pparse
.count("id") && pparse
.count("challenge") && (!ready
|| pparse
["id"] != id
|| stale
)) {
228 std::string challenge
= pparse
["challenge"];
229 if(challenge
.length() != 64) goto no_reseed
;
230 uint8_t _challenge
[32];
231 unhex(_challenge
, challenge
);
232 curve25519(ssecret
, privkey
, _challenge
);
238 if(pparse
.count("error")) {
239 if(pparse
["error"] == "ok")
241 else if(pparse
["error"] == "stale") {
242 //This is only an error if not reseeded this round.
245 throw std::runtime_error("Authentication is stale");
247 } else if(pparse
["error"] == "badkey") {
249 throw std::runtime_error("Client key not registed with target site");
250 } else if(pparse
["error"] == "badmac")
251 throw std::runtime_error("Request failed MAC check");
252 else if(pparse
["error"] == "replay")
253 throw std::runtime_error("Request was replayed");
254 else if(pparse
["error"] == "syntaxerr")
255 throw std::runtime_error("Request syntax error");
257 throw std::runtime_error("Unknown error '" + pparse
["error"] + "'");
261 void dh25519_http_auth::get_pubkey(uint8_t* _pubkey
)
263 memcpy(_pubkey
, pubkey
, 32);