/******************************************************************************* * Copyright (c) 2013, 2014 Lectorius, Inc. * Authors: * Vijay Pandurangan (vijayp@mitro.co) * Evan Jones (ej@mitro.co) * Adam Hilss (ahilss@mitro.co) * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * You can contact the authors at inbound@mitro.co. *******************************************************************************/ package co.mitro.core.servlets; import java.io.IOException; import java.sql.SQLException; import java.util.List; import javax.servlet.annotation.WebServlet; import org.keyczar.DefaultKeyType; import org.keyczar.GenericKeyczar; import org.keyczar.enums.KeyPurpose; import org.keyczar.exceptions.KeyczarException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import co.mitro.core.exceptions.MitroServletException; import co.mitro.core.server.Manager; import co.mitro.core.server.data.DBDeviceSpecificInfo; import co.mitro.core.server.data.DBIdentity; import co.mitro.core.server.data.RPC; import co.mitro.core.server.data.RPC.MitroRPC; import co.mitro.keyczar.JsonWriter; import co.mitro.keyczar.Util; import com.google.common.collect.ImmutableMap; /** * Servlet used by the client to retrieve an AES key which * it should use to encrypt stuff on local disk. */ @WebServlet("/api/GetMyDeviceKey") public class GetMyDeviceKey extends MitroServlet { private static final Logger logger = LoggerFactory.getLogger(GetMyDeviceKey.class); private static final long serialVersionUID = 1L; @Override protected MitroRPC processCommand(MitroRequestContext context) throws IOException, SQLException, MitroServletException { RPC.GetMyDeviceKeyRequest in = gson.fromJson(context.jsonRequest, RPC.GetMyDeviceKeyRequest.class); RPC.GetMyDeviceKeyResponse out = new RPC.GetMyDeviceKeyResponse(); out.deviceKeyString = maybeGetOrCreateDeviceKey( context.manager, context.requestor, in.deviceId, false, context.platform); return out; } protected static String maybeGetOrCreateDeviceKey( Manager manager, DBIdentity requestor, String deviceId, boolean forLogin, String platform) throws SQLException, MitroServletException { // give the client a key! // search DB if (deviceId == null) { return null; } List<DBDeviceSpecificInfo> devInfos = null; try { // Note: queryForFieldValuesArgs escapes using SelectArg. devInfos = manager.deviceSpecificDao.queryForFieldValuesArgs( ImmutableMap.of( DBDeviceSpecificInfo.DEVICE_ID_NAME, deviceId, DBDeviceSpecificInfo.IDENTITY_FIELD_NAME, requestor )); } catch (Exception e) { logger.error("ignoring weird error probably utf-8", e); } DBDeviceSpecificInfo devInfo; boolean keyInDb = !(null == devInfos || devInfos.isEmpty()); if (keyInDb) { assert (devInfos.size() == 1); devInfo = devInfos.get(0); devInfo.setLastUseSec(System.currentTimeMillis() / 1000.0); if (forLogin) { // TODO: check expiration } // TODO: this will break operations on the replica. // TODO: we should have a way of easily detecting if we are on the replica or not. // enable this when we check expiration, above. // manager.deviceSpecificDao.update(devInfo); // } else if (!keyInDb && !forLogin) { devInfo = new DBDeviceSpecificInfo(); devInfo.setLastUseSec(System.currentTimeMillis()/1000.0); devInfo.setUser(requestor); devInfo.setPlatform(platform); // generate an AES key GenericKeyczar key; try { key = Util.createKey(DefaultKeyType.AES, KeyPurpose.DECRYPT_AND_ENCRYPT); } catch (KeyczarException e) { throw new MitroServletException(e); } devInfo.setClientLocalStorageKey(JsonWriter.toString(key)); try { devInfo.setDeviceId(deviceId); manager.deviceSpecificDao.create(devInfo); } catch (Exception e) { logger.error("ignoring weird error probably utf-8", e); } } else { // no key in db and they want to log in. Sadly, we can't provide the key! devInfo = null; } return (null == devInfo) ? null : devInfo.getClientLocalStorageKey(); } public static String maybeGetClientKeyForLogin( Manager manager, DBIdentity identity, String deviceId, String platform) throws SQLException, MitroServletException { return maybeGetOrCreateDeviceKey(manager, identity, deviceId, true, platform); } }