Skip to content

Commit df0a4ed

Browse files
author
Lauris Kaplinski
committed
Preliminary working MobileId authentication
1 parent adc94f4 commit df0a4ed

9 files changed

Lines changed: 310 additions & 182 deletions

File tree

cdoc/CDoc2Reader.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,31 @@ CDoc2Reader::getFMK(std::vector<uint8_t>& fmk, unsigned int lock_idx)
269269
return libcdoc::CONFIGURATION_ERROR;
270270
}
271271
// auth_url = "https://cdoc2-auth.dev.riaint.ee";
272+
// fixme:
273+
std::string signer = conf->getValue(Configuration::SHARE_SIGNER);
274+
LOG_DBG("Signer: {}", signer);
275+
bool mid = false;
276+
if (signer == Configuration::SHARE_SIGNER_SID) {
277+
//
278+
} else if (signer == Configuration::SHARE_SIGNER_MID) {
279+
mid = true;
280+
} else {
281+
setLastError(t_("Unknown or missing signer type"));
282+
LOG_ERROR("Unknown or missing signer type");
283+
return libcdoc::CONFIGURATION_ERROR;
284+
}
285+
std::string phone;
286+
if (mid) {
287+
phone = conf->getValue({}, Configuration::PHONE_NUMBER);
288+
if (phone.empty()) {
289+
setLastError(t_("Missing phone number"));
290+
LOG_ERROR("Missing phone number");
291+
return libcdoc::CONFIGURATION_ERROR;
292+
}
293+
}
294+
272295
SessionData session;
273-
if (auto rv = network->authenticateForShares(auth_url, rcpt_id, session.token, session.cert); rv != OK) {
296+
if (auto rv = network->authenticateForShares(auth_url, rcpt_id, phone, session.token, session.cert); rv != OK) {
274297
setLastError(network->getLastErrorStr(rv));
275298
LOG_ERROR("{}", last_error);
276299
return rv;
@@ -298,7 +321,7 @@ CDoc2Reader::getFMK(std::vector<uint8_t>& fmk, unsigned int lock_idx)
298321
// must not be expired. Also learns the schemeName/rpName claims needed
299322
// to verify the signed ticket later.
300323
std::string scheme_name, rp_name, v_err;
301-
if (auto rv = validateSessionData(crypto, rcpt_id, session.token, session.cert, scheme_name, rp_name, v_err); rv != OK) {
324+
if (auto rv = validateSessionData(crypto, rcpt_id, mid, session.token, session.cert, scheme_name, rp_name, v_err); rv != OK) {
302325
setLastError(v_err);
303326
LOG_ERROR("{}", last_error);
304327
return rv;
@@ -328,10 +351,8 @@ CDoc2Reader::getFMK(std::vector<uint8_t>& fmk, unsigned int lock_idx)
328351
std::vector<std::string> auth_tokens;
329352
AuthenticationData auth;
330353
result_t result = NOT_IMPLEMENTED;
331-
// fixme:
332-
std::string signer = "SMART_ID";// conf->getValue(Configuration::SHARE_SIGNER);
333-
LOG_DBG("Signer: {}", signer);
334-
if (signer == "SMART_ID") {
354+
355+
if (!mid) {
335356
SIDSigner signer(rp_url, session, rcpt_id, network);
336357
result = signer.generateTickets(auth_tokens, shares);
337358
if (result != OK) {
@@ -340,22 +361,15 @@ CDoc2Reader::getFMK(std::vector<uint8_t>& fmk, unsigned int lock_idx)
340361
auth.cert = std::move(signer.cert);
341362
auth.params = std::move(signer.params);
342363
}
343-
} else if (signer == "MOBILE_ID") {
344-
// "https://sid.demo.sk.ee/smart-id-rp/v2"
345-
std::string url = conf->getValue(Configuration::MID_DOMAIN, Configuration::BASE_URL);
346-
// "37200000566"
347-
std::string phone = conf->getValue(Configuration::MID_DOMAIN, Configuration::PHONE_NUMBER);
348-
MIDSigner signer(url, {}, {}, phone, rcpt_id, network);
364+
} else {
365+
MIDSigner signer(rp_url, phone, session, rcpt_id, network);
349366
result = signer.generateTickets(auth_tokens, shares);
350367
if (result != OK) {
351368
setLastError(signer.error);
352369
} else {
353370
auth.cert = std::move(signer.cert);
371+
auth.params = std::move(signer.params);
354372
}
355-
} else {
356-
setLastError(t_("Unknown or missing signer type"));
357-
LOG_ERROR("Unknown or missing signer type");
358-
return result;
359373
}
360374
if (result != libcdoc::OK) {
361375
LOG_ERROR("Cannot generate share tickets");
@@ -366,8 +380,9 @@ CDoc2Reader::getFMK(std::vector<uint8_t>& fmk, unsigned int lock_idx)
366380
// signature must verify (binds identity, the consent text shown to
367381
// the user, and freshness). All tickets share the same signed JWT,
368382
// so validating the first one covers them all.
369-
if (!auth_tokens.empty()) {
370-
if (auto rv = validateAuthTicket(crypto, rcpt_id, auth_tokens[0], auth.cert, auth.params, scheme_name, rp_name, v_err); rv != OK) {
383+
if (!auth_tokens.empty() && !mid) {
384+
std::vector params = fromBase64URL(auth.params[network->X_CDOC2_SID_RPV3_SIGNATURE_PARAMETERS]);
385+
if (auto rv = validateAuthTicket(crypto, rcpt_id, auth_tokens[0], auth.cert, std::string(params.cbegin(), params.cend()), scheme_name, rp_name, v_err); rv != OK) {
371386
setLastError(v_err);
372387
LOG_ERROR("{}", last_error);
373388
return rv;

cdoc/Configuration.h

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,10 @@ struct CDOC_EXPORT Configuration {
5959
* @brief Method for signing keyshare tickets (SMART_ID or MOBILE_ID)
6060
*/
6161
static constexpr char const *SHARE_SIGNER = "SHARE_SIGNER";
62+
static constexpr char const *SHARE_SIGNER_SID = "SMART_ID";
63+
static constexpr char const *SHARE_SIGNER_MID = "MOBILE_ID";
6264
/**
63-
* @brief Domain of SmartID settings
64-
*/
65-
static constexpr char const *SID_DOMAIN = "SMART_ID";
66-
/**
67-
* @brief Domain of Mobile ID settings
68-
*/
69-
static constexpr char const *MID_DOMAIN = "MOBILE_ID";
70-
/**
71-
* @brief MID/SID base url (domain is SMART_ID or MOBILE_ID)
72-
*/
73-
static constexpr char const *BASE_URL = "BASE_URL";
74-
/**
75-
* @brief Mobile ID phone number (domain is MOBILE_ID)
65+
* @brief Mobile ID phone number
7666
*/
7767
static constexpr char const *PHONE_NUMBER = "PHONE_NUMBER";
7868
#endif

cdoc/KeyShares.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ MIDSigner::signDigest(std::vector<uint8_t>& dst, const std::vector<uint8_t>& dig
219219

220220
LOG_TRACE_KEY("MID signing: {}", digest);
221221

222-
result_t result = network->signMID(dst, cert, url, rp_uuid, rp_name, phone, rcpt_id, digest, libcdoc::CryptoBackend::SHA_256);
222+
result_t result = network->signMID(dst, cert, params, url, phone, session.token, session.cert, rcpt_id, digest, libcdoc::CryptoBackend::SHA_256);
223223
if (result != OK) {
224224
error = network->getLastErrorStr(result);
225225
}
@@ -345,7 +345,7 @@ decodeTicket(const std::string& ticket)
345345

346346

347347
std::string
348-
libcdoc::buildAcspV2Payload(const std::string& scheme_name, const std::string& server_random,
348+
buildAcspV2Payload(const std::string& scheme_name, const std::string& server_random,
349349
const std::string& rp_challenge, const std::string& user_challenge,
350350
const std::string& rp_name, const std::string& interactions_digest,
351351
const std::string& interaction_type_used, const std::string& flow_type)
@@ -359,7 +359,7 @@ libcdoc::buildAcspV2Payload(const std::string& scheme_name, const std::string& s
359359
}
360360

361361
libcdoc::result_t
362-
libcdoc::validateSessionData(CryptoBackend *crypto, const std::string& rcpt_id,
362+
validateSessionData(CryptoBackend *crypto, const std::string& rcpt_id, bool is_mid,
363363
const std::string& session_token, const std::string& session_cert_b64,
364364
std::string& scheme_name, std::string& rp_name, std::string& error)
365365
{
@@ -393,15 +393,15 @@ libcdoc::validateSessionData(CryptoBackend *crypto, const std::string& rcpt_id,
393393
}
394394
scheme_name = json.get("schemeName").is<std::string>() ? json.get("schemeName").get<std::string>() : std::string();
395395
rp_name = json.get("rpName").is<std::string>() ? json.get("rpName").get<std::string>() : std::string();
396-
if (scheme_name.empty() || rp_name.empty()) {
396+
if (!is_mid && (scheme_name.empty() || rp_name.empty())) {
397397
error = "Session token misses schemeName/rpName claims";
398398
return DATA_FORMAT_ERROR;
399399
}
400400
return OK;
401401
}
402402

403403
libcdoc::result_t
404-
libcdoc::validateAuthTicket(CryptoBackend *crypto, const std::string& rcpt_id,
404+
validateAuthTicket(CryptoBackend *crypto, const std::string& rcpt_id,
405405
const std::string& ticket, const std::vector<uint8_t>& cert_der,
406406
const std::string& signature_params_json,
407407
const std::string& scheme_name, const std::string& rp_name,

cdoc/KeyShares.h

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ struct SessionData {
7171
*/
7272
struct AuthenticationData {
7373
std::vector<uint8_t> cert;
74-
std::string params;
74+
std::map<std::string, std::string> params;
7575
};
7676

7777
/**
@@ -100,6 +100,11 @@ struct Signer {
100100
* @return result_t error code or ok
101101
*/
102102
virtual result_t signDigest(std::vector<uint8_t>& dst, const std::vector<uint8_t>& digest) = 0;
103+
/**
104+
* @brief Full session token
105+
*
106+
*/
107+
const SessionData& session;
103108
/**
104109
* @brief Signing algorithm name (RS256/ES256)
105110
*
@@ -115,7 +120,7 @@ struct Signer {
115120
*
116121
*/
117122
std::vector<uint8_t> cert;
118-
std::string params;
123+
std::map<std::string, std::string> params;
119124
/**
120125
* @brief The text of last error
121126
*
@@ -126,10 +131,11 @@ struct Signer {
126131
/**
127132
* @brief Construct a new Signer object
128133
*
134+
* @param _session Full session data (token and certificate)
129135
* @param _rcpt_id Recipient full id in etsi format (ets/PNOEE-XYZXYZXYZXY)
130136
* @param _algo_name Signing algorithm name (RS256/ES256)
131137
*/
132-
Signer(const std::string& _rcpt_id, const std::string _algo_name, NetworkBackend *_network) : rcpt_id(_rcpt_id), algo_name(_algo_name), network(_network) {}
138+
Signer(const SessionData& _session, const std::string& _rcpt_id, const std::string& _algo_name, NetworkBackend *_network) : session(_session), rcpt_id(_rcpt_id), algo_name(_algo_name), network(_network) {}
133139
};
134140

135141
/**
@@ -142,11 +148,6 @@ struct SIDSigner : public Signer {
142148
*
143149
*/
144150
const std::string url;
145-
/**
146-
* @brief Full session token
147-
*
148-
*/
149-
const SessionData& session;
150151

151152
/**
152153
* @brief Construct a new SIDSigner object
@@ -156,7 +157,7 @@ struct SIDSigner : public Signer {
156157
* @param _rcpt_id Recipient full id in etsi format (ets/PNOEE-XYZXYZXYZXY)
157158
*/
158159
SIDSigner(const std::string& _url, const SessionData& _session, const std::string& _rcpt_id, NetworkBackend *network)
159-
: Signer(_rcpt_id, "RSASSA-PSS+ACSP_V2", network), url(_url), session(_session) {}
160+
: Signer(_session, _rcpt_id, "RSASSA-PSS+ACSP_V2", network), url(_url) {}
160161

161162
result_t signDigest(std::vector<uint8_t>& dst, const std::vector<uint8_t>& digest) final;
162163
};
@@ -171,16 +172,6 @@ struct MIDSigner : public Signer {
171172
*
172173
*/
173174
const std::string url;
174-
/**
175-
* @brief Relying party UUID
176-
*
177-
*/
178-
const std::string rp_uuid;
179-
/**
180-
* @brief Relying party name
181-
*
182-
*/
183-
const std::string rp_name;
184175
/**
185176
* @brief Recipient phone number (with country code)
186177
*
@@ -190,12 +181,10 @@ struct MIDSigner : public Signer {
190181
* @brief Construct a new MIDSigner object
191182
*
192183
* @param _url Mobile ID gateway url
193-
* @param _rp_uuid Relying party UUID
194-
* @param _rp_name Relying party name
195184
* @param _rcpt_id Recipient full id in etsi format (ets/PNOEE-XYZXYZXYZXY)
196185
*/
197-
MIDSigner(const std::string& _url, const std::string& _rp_uuid, const std::string& _rp_name, const std::string& _phone, const std::string& _rcpt_id, NetworkBackend *network)
198-
: Signer(_rcpt_id, "ES256", network), url(_url), rp_uuid(_rp_uuid), rp_name(_rp_name), phone(_phone) {}
186+
MIDSigner(const std::string& _url, const std::string& _phone, const SessionData& _session, const std::string& _rcpt_id, NetworkBackend *network)
187+
: Signer(_session, _rcpt_id, "ES256", network), url(_url), phone(_phone) {}
199188

200189
result_t signDigest(std::vector<uint8_t>& dst, const std::vector<uint8_t>& digest) final;
201190
};
@@ -241,7 +230,7 @@ std::string buildAcspV2Payload(const std::string& scheme_name, const std::string
241230
*
242231
* Checks that the session signing certificate belongs to rcpt_id (via
243232
* CryptoBackend::validateCertificate), that the session token is not expired,
244-
* and extracts the schemeName/rpName claims needed for ticket validation.
233+
* and extracts the schemeName/rpName claims needed for ticket validation for SmartId.
245234
*
246235
* @param crypto crypto backend
247236
* @param rcpt_id recipient id from the lock (etsi/PNOEE-...)
@@ -252,7 +241,7 @@ std::string buildAcspV2Payload(const std::string& scheme_name, const std::string
252241
* @param error output: error description on failure
253242
* @return error code or OK
254243
*/
255-
result_t validateSessionData(CryptoBackend *crypto, const std::string& rcpt_id,
244+
result_t validateSessionData(CryptoBackend *crypto, const std::string& rcpt_id, bool is_mid,
256245
const std::string& session_token, const std::string& session_cert_b64,
257246
std::string& scheme_name, std::string& rp_name, std::string& error);
258247

0 commit comments

Comments
 (0)