/* PIP - Platform Independent Primitives PIP Authentication API Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piauth.h" #define PIAUTH_NOISE_MAX_SIZE 256 PIAuth::PIAuth(const PIByteArray & sign) : PIObject() { setName("Client"); role = Client; state = NotConnected; sign_sk = sign; sign_pk = crypt.extractSignPublicKey(sign); } void PIAuth::setServerPassword(const PIString & ps) { pass_hash = crypt.passwordHash(ps, PIByteArray::fromHex("PIAuth")); } void PIAuth::stop() { role = Client; state = NotConnected; auth_sign.clear(); box_sk.clear(); box_pk.clear(); my_pk.clear(); } void PIAuth::startClient() { role = Client; state = AuthProbe; } PIByteArray PIAuth::startServer() { setName("Server"); role = Server; state = AuthProbe; PIByteArray ba; crypt.generateKeypair(my_pk, box_sk); PIByteArray noise = crypt.generateRandomBuff(randomi()%PIAUTH_NOISE_MAX_SIZE+128); ba << (int)state << custom_info << sign_pk << my_pk << noise; PIByteArray sign = crypt.signMessage(ba, sign_sk); ba << sign; return ba; } PIAuth::State PIAuth::receive(PIByteArray & ba) { if (ba.size() < sizeof(int)) return disconnect(ba, "invalid data size"); State rstate; int s; ba >> s; rstate = (State)s; // if (state != rstate) return disconect(ba); //client side if (role == Client) { if (state == AuthProbe && rstate == AuthProbe) { if (ba.size() < sizeof(int)*5) return disconnect(ba, "invalid data size"); PIByteArray rinfo; PIByteArray rsign; PIByteArray rsign_pk; PIByteArray noise; ba >> rinfo >> rsign_pk >> box_pk >> noise >> rsign; if (rsign_pk.isEmpty() || box_pk.isEmpty() || rsign.isEmpty()) return disconnect(ba, "invalid key size"); PIByteArray tba; tba << (int)rstate << rinfo << rsign_pk << box_pk << noise; if (!crypt.verifySign(tba, rsign, rsign_pk)) return disconnect(ba, "Incorrect sign"); bool auth = false; if (isAuthorizedKey(rsign_pk)) { auth = true; } else { authorize(rinfo, &auth); if (auth) auth_pkeys << rsign_pk; } if (!auth) return disconnect(ba, "Unauthorised"); ba.clear(); auth_sign = rsign_pk; crypt.generateKeypair(my_pk, box_sk); tba.clear(); tba << sign_pk << my_pk << box_pk; PIByteArray sign = crypt.signMessage(tba, sign_sk); tba << sign; tba = crypt.crypt(tba, box_pk, box_sk); state = AuthReply; noise = crypt.generateRandomBuff(randomi()%PIAUTH_NOISE_MAX_SIZE); ba << (int)state << tba << my_pk << noise; sign = crypt.signMessage(ba, sign_sk); ba << sign; return state; } if (state == AuthReply && rstate == PassRequest) { PIByteArray ctba, tba; PIByteArray noise; PIByteArray rsign, rsign_pk; ba >> ctba >> rsign; bool ok = false; tba = crypt.decrypt(ctba, box_pk, box_sk, &ok); if (tba.isEmpty() || !ok) return disconnect(ba, "Message corrupted"); ba.clear(); ba << (int)rstate << ctba; if (!crypt.verifySign(ba, rsign, auth_sign)) return disconnect(ba, "Incorrect sign"); ctba.clear(); tba >> rsign_pk >> noise >> ctba; if (rsign_pk != auth_sign || ctba != my_pk) return disconnect(ba, "Invalid public key"); PIString ps; PIByteArray ph; passwordRequest(&ps); if (ps.isEmpty()) return disconnect(ba, "Canceled by user"); ph = crypt.passwordHash(ps, PIByteArray::fromHex("PIAuth")); ps.fill(0); tba.clear(); tba << ph << auth_sign << sign_pk; tba = crypt.crypt(tba, box_pk, box_sk); ba.clear(); state = PassRequest; ba << (int)state << tba; rsign = crypt.signMessage(ba, sign_sk); ba << rsign; return state; } if ((state == AuthReply && rstate == KeyExchange) || (state == PassRequest && rstate == KeyExchange)) { PIByteArray tba, ctba; PIByteArray rsign; ba >> ctba >> rsign; bool ok = false; tba = crypt.decrypt(ctba, box_pk, box_sk, &ok); if (tba.isEmpty() || !ok) return disconnect(ba, "Message corrupted"); ba.clear(); ba << (int)rstate << ctba; if (!crypt.verifySign(ba, rsign, auth_sign)) return disconnect(ba, "Incorrect sign"); tba >> secret_key; if (secret_key.size() != crypt.sizeKey()) return disconnect(ba, "Invalid key"); ba.clear(); state = Connected; connected(PIString()); ba << (int)state << crypt.crypt(custom_info, secret_key) << crypt.generateRandomBuff(randomi()%PIAUTH_NOISE_MAX_SIZE); return state; } if (state == Connected && rstate == Connected) { ba.clear(); state = Connected; connected(PIString()); ba << (int)state << crypt.crypt(custom_info, secret_key) << crypt.generateRandomBuff(randomi()%PIAUTH_NOISE_MAX_SIZE); return state; } } // server side if (role == Server) { if (state == AuthProbe && rstate == AuthReply) { if (ba.size() < sizeof(int)*4) return disconnect(ba, "invalid data size"); PIByteArray ctba, tba; PIByteArray noise; PIByteArray rsign1, rsign2; PIByteArray rsign_pk; PIByteArray pk, mpk; ba >> ctba >> pk >> noise >> rsign1; bool ok = false; tba = crypt.decrypt(ctba, pk, box_sk, &ok); if (tba.isEmpty() || !ok) return disconnect(ba, "Message corrupted"); if (tba.size() < sizeof(int)*3) return disconnect(tba, "invalid data size"); tba >> rsign_pk >> box_pk >> mpk >> rsign2; if (pk != box_pk || mpk != my_pk) return disconnect(ba, "Invalid public key"); ba.clear(); ba << (int)rstate << ctba << box_pk << noise; if (!crypt.verifySign(ba, rsign1, rsign_pk)) return disconnect(ba, "Incorrect sign"); ba.clear(); ba << rsign_pk << box_pk << my_pk; if (!crypt.verifySign(ba, rsign2, rsign_pk)) return disconnect(ba, "Incorrect sign"); auth_sign = rsign_pk; if (isAuthorizedKey(rsign_pk)) { state = KeyExchange; ba = createSKMessage(); return state; } else { ba.clear(); tba.clear(); state = PassRequest; noise = crypt.generateRandomBuff(randomi()%PIAUTH_NOISE_MAX_SIZE); tba << sign_pk << noise << box_pk; tba = crypt.crypt(tba, box_pk, box_sk); ba << (int)state << tba; rsign1 = crypt.signMessage(ba, sign_sk); ba << rsign1; return state; } } if (state == PassRequest && rstate == PassRequest) { PIByteArray tba, ctba; PIByteArray rsign_pk, rsign, mpk; ba >> ctba >> rsign; bool ok = false; tba = crypt.decrypt(ctba, box_pk, box_sk, &ok); if (tba.isEmpty() || !ok) return disconnect(ba, "Message corrupted"); ba.clear(); ba << (int)rstate << ctba; if (!crypt.verifySign(ba, rsign, auth_sign)) return disconnect(ba, "Incorrect sign"); ctba.clear(); tba >> ctba >> mpk >> rsign_pk; if (rsign_pk != auth_sign || mpk != sign_pk) return disconnect(ba, "Invalid public key"); bool auth = (ctba == pass_hash); if (ctba.isEmpty() || pass_hash.isEmpty()) auth = false; passwordCheck(auth); if (!auth) { // piSleep(1); return disconnect(ba, "Invalid password"); } state = KeyExchange; ba = createSKMessage(); return state; } if ((state == KeyExchange && rstate == Connected) || (state == Connected && rstate == Connected)) { ba.clear(); PIByteArray rinfo; ba >> rinfo; bool ok = false; rinfo = crypt.decrypt(rinfo, secret_key, &ok); if (!ok) return disconnect(ba, "Error while exchange keys"); state = Connected; connected(rinfo); ba << (int)state << crypt.generateRandomBuff(randomi()%PIAUTH_NOISE_MAX_SIZE); return state; } } return disconnect(ba, "invalid state " + PIString::fromNumber((int)state)); } PIByteArray PIAuth::getSecretKey() { if (state == Connected) return secret_key; return PIByteArray(); } PIByteArray PIAuth::generateSign(const PIByteArray & seed) { PIByteArray pk, sk; PICrypt::generateSignKeys(pk, sk, seed); return sk; } PIAuth::State PIAuth::disconnect(PIByteArray & ba, const PIString & error) { if (!error.isEmpty()) piCoutObj << error; auth_sign.clear(); box_sk.clear(); box_pk.clear(); my_pk.clear(); secret_key.clear(); ba.clear(); state = NotConnected; disconnected(error); return state; } bool PIAuth::isAuthorizedKey(const PIByteArray & pkey) { for (int i=0; i