Package io.fathom.cloud.identity.secrets

Source Code of io.fathom.cloud.identity.secrets.Secrets

package io.fathom.cloud.identity.secrets;

import io.fathom.cloud.CloudException;
import io.fathom.cloud.identity.Users;
import io.fathom.cloud.identity.model.AuthenticatedProject;
import io.fathom.cloud.identity.model.AuthenticatedUser;
import io.fathom.cloud.identity.secrets.SecretToken.SecretTokenType;
import io.fathom.cloud.identity.state.AuthRepository;
import io.fathom.cloud.openstack.client.identity.ChallengeResponses;
import io.fathom.cloud.protobuf.CloudCommons.EncryptedWith;
import io.fathom.cloud.protobuf.CloudCommons.SecretData;
import io.fathom.cloud.protobuf.CloudCommons.TokenInfo;
import io.fathom.cloud.protobuf.IdentityModel.ClientAppSecretData;
import io.fathom.cloud.protobuf.IdentityModel.CredentialData;
import io.fathom.cloud.protobuf.IdentityModel.KeyData;
import io.fathom.cloud.protobuf.IdentityModel.ProjectData;
import io.fathom.cloud.protobuf.IdentityModel.ProjectRoles;
import io.fathom.cloud.protobuf.IdentityModel.ProjectRolesSecretData;
import io.fathom.cloud.protobuf.IdentityModel.SecretKeyData;
import io.fathom.cloud.protobuf.IdentityModel.SecretKeyType;
import io.fathom.cloud.protobuf.IdentityModel.SecretStoreData;
import io.fathom.cloud.protobuf.IdentityModel.UserData;
import io.fathom.cloud.protobuf.IdentityModel.UserSecretData;
import io.fathom.cloud.server.auth.SharedKeystore;
import io.fathom.cloud.server.resources.ClientCertificate;

import java.io.IOException;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.keyczar.AesKey;
import org.keyczar.Crypter;
import org.keyczar.Encrypter;
import org.keyczar.HmacKey;
import org.keyczar.KeyczarReaderWrapper;
import org.keyczar.KeyczarUtils;
import org.keyczar.RsaPrivateKey;
import org.keyczar.RsaPublicKey;
import org.keyczar.exceptions.KeyczarException;
import org.keyczar.interfaces.KeyczarReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fathomdb.crypto.AesCbcCryptoKey;
import com.fathomdb.crypto.CryptoKey;
import com.fathomdb.crypto.FathomdbCrypto;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.protobuf.ByteString;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.InvalidProtocolBufferException;

@Singleton
public class Secrets {
    private static final Logger log = LoggerFactory.getLogger(Secrets.class);

    public static final String KEY_TOKEN_ENCRYPT = "token_encrypt";
    public static final String KEY_FORGOT_PASSWORD_PUBLIC = "forgot_password_public";

    @Inject
    AuthRepository authRepository;

    @Inject
    SharedKeystore keystore;

    @Inject
    Migrations migrations;

    public ByteString buildTokenSecret(AuthenticatedUser user) {
        AesKey userKey = user.getKeys().getSecretToken().cryptoKey;

        AesKey tokenKey = userKey;
        byte[] plaintext = KeyczarUtils.pack(tokenKey);

        // TODO: Key rotation
        byte[] tokenKeySerialized;
        try {
            tokenKeySerialized = getCrypter().encrypt(plaintext);
        } catch (KeyczarException e) {
            throw new IllegalStateException();
        }

        return ByteString.copyFrom(tokenKeySerialized);
    }

    Crypter crypter;

    private synchronized Crypter getCrypter() {
        if (crypter == null) {
            crypter = keystore.buildCrypter(KEY_TOKEN_ENCRYPT);
        }
        return crypter;
    }

    public UserWithSecret getFromToken(UserData user, TokenInfo token) throws KeyczarException {
        byte[] tokenKeySerialized;
        try {
            ByteString tokenSecret = token.getTokenSecret();
            tokenKeySerialized = getCrypter().decrypt(tokenSecret.toByteArray());
        } catch (KeyczarException e) {
            // This should have been validated
            log.warn("Error decrypting user key");
            return null;
        }

        AesKey tokenKey = KeyczarUtils.unpack(tokenKeySerialized);

        // We could have extra layers here, but I don't think they achieve
        // anything
        AesKey userKey = tokenKey;

        SecretToken secretToken = new SecretToken(SecretTokenType.USER_SECRET, userKey, null);
        return checkSecret(user, secretToken);
    }

    public UserWithSecret checkPassword(UserData user, CredentialData credential, String password) {
        // if (user.getName().equals("admin")) {
        // try {
        // user = hotfixDomainAdmin(user);
        // } catch (Exception e) {
        // throw new IllegalStateException("Error migrating user", e);
        // }
        // }

        if (credential.hasDeprecatedPasswordHash()) {
            // TODO: We need to remove these
            log.warn("Credential still has deprecated password hash");
            try {
                user = migrations.migrateUser(credential, user, password);
            } catch (Exception e) {
                throw new IllegalStateException("Error migrating user", e);
            }
        }

        SecretToken secretToken = null;

        try {
            secretToken = getSecretFromPassword(user.getSecretStore(), password);
        } catch (KeyczarException e) {
            log.info("Error while checking password", e);
        }

        if (secretToken == null) {
            return null;
        }

        UserWithSecret ret = checkSecret(user, secretToken);

        if (ret != null) {
            try {
                ret = migrations.migrateUser(ret, password, secretToken);
            } catch (Exception e) {
                throw new IllegalStateException("Error migrating user", e);
            }
        }

        if (ret != null) {
            try {
                ret = migrations.migrateUserAddPasswordRecovery(ret, secretToken);
            } catch (Exception e) {
                throw new IllegalStateException("Error migrating user", e);
            }
        }

        return ret;
    }

    public UserWithSecret checkPasswordRecovery(UserData user, CredentialData credential, KeyczarReader recoveryKey) {
        SecretToken secretToken = null;

        try {
            secretToken = getSecretWithRecovery(user.getSecretStore(), recoveryKey);
        } catch (KeyczarException e) {
            log.info("Error while checking recovery key", e);
        }

        if (secretToken == null) {
            return null;
        }

        UserWithSecret ret = checkSecret(user, secretToken);
        return ret;
    }

    // private UserData hotfixDomainAdmin(UserData user) throws CloudException {
    // if (user.getDomainRolesCount() != 0) {
    // return user;
    // }
    //
    // log.warn("Hotfixing admin user with domain admin role");
    //
    // UserData.Builder b = UserData.newBuilder(user);
    //
    // DomainRoles.Builder rb = b.addDomainRolesBuilder();
    // rb.setDomain(user.getDomainId());
    // rb.addRole(WellKnownRoles.ROLE_ID_ADMIN);
    //
    // user = authRepository.getUsers().update(b);
    //
    // return user;
    // }

    private UserWithSecret checkSecret(UserData user, SecretToken secretToken) {
        SecretData secretData = user.getSecretData();

        if (secretData.hasEncryptedWith() && secretData.getEncryptedWith() != EncryptedWith.SECRET_KEY) {
            throw new IllegalStateException();
        }

        UserSecretData userSecretData;
        try {
            userSecretData = unlock(secretData, secretToken, UserSecretData.newBuilder());
        } catch (IOException e) {
            log.debug("Error decoding secret data (likely because of wrong password)");
            return null;
        }

        // This stuff is deprecated because we sign the secret data
        if (userSecretData.hasDeprecatedVerifyPublicKey()) {
            // throw new IllegalStateException();

            log.warn("UserSecretData has deprecated verify public key");

            //
            // long hash =
            // Hashing.md5().hashBytes(user.getPublicKey().getEncoded().toByteArray()).asLong();
            // if (userSecretData.getDeprecatedVerifyPublicKey() != hash) {
            // // This is unexpected
            // log.warn("Verify public key did not match (but decode did not fail)");
            // return null;
            // }
        } else if (userSecretData.hasDeprecatedVerifyUserId()) {
            log.warn("UserSecretData has deprecated verify user id");

            // // This is deprecated because it makes creation painful. A
            // // pretty crappy reason, really.
            // log.warn("Falling back to deprecated user id validation for user: {}",
            // user.getName());
            //
            // if (userSecretData.getDeprecatedVerifyUserId() != user.getId()) {
            // // This is unexpected
            // log.warn("VerifyUserId did not match (but decode did not fail)");
            // return null;
            // }
        }

        return new UserWithSecret(user, userSecretData, secretToken);
    }

    public AuthenticatedProject authenticate(ProjectData project, AuthenticatedUser user) {
        ProjectRoles projectRoles = Users.findProjectRoles(user.getUserData(), project.getId());
        if (projectRoles == null) {
            // TODO: We probably need another path for domain admins
            return null;
        }

        if (!projectRoles.hasSecretData()) {
            throw new IllegalStateException("Project role has no secret data");
        }

        ProjectRolesSecretData projectRolesSecretData;
        try {
            projectRolesSecretData = unlock(projectRoles.getSecretData(), user, ProjectRolesSecretData.newBuilder());
        } catch (IOException e) {
            throw new IllegalStateException("Error unlocking project", e);
        }

        int version = 0;
        if (projectRolesSecretData.hasProjectKeyVersion()) {
            version = projectRolesSecretData.getProjectKeyVersion();
        }

        ByteString projectKeyBytes = projectRolesSecretData.getProjectKey();
        if (projectKeyBytes == null) {
            throw new IllegalStateException();
        }

        if (version == 1) {
            AesKey projectKey;
            try {
                projectKey = KeyczarUtils.unpack(projectKeyBytes.toByteArray());
            } catch (KeyczarException e) {
                throw new IllegalStateException("Error reading project key", e);
            }
            SecretToken token = new SecretToken(SecretTokenType.PROJECT_SECRET, projectKey, null);
            return new AuthenticatedProject(project, token);
        } else if (version == 0) {
            // We had a project key in version 0, but we didn't use it!
            return new AuthenticatedProject(project, null);
        } else {
            throw new IllegalStateException();
        }
    }

    public static SecretData buildProjectRolesSecret(UserData user, AuthenticatedProject authenticatedProject)
            throws KeyczarException {
        byte[] plaintext;
        {
            ProjectRolesSecretData.Builder b = ProjectRolesSecretData.newBuilder();
            byte[] serialized = KeyczarUtils.pack(authenticatedProject.getKeys().getSecretToken().cryptoKey);
            b.setProjectKey(ByteString.copyFrom(serialized));
            b.setProjectKeyVersion(1);

            plaintext = b.build().toByteArray();
        }

        Encrypter publicKeyEncrypter = getPublicKeyEncrypter(user.getPublicKey());
        byte[] ciphertext = publicKeyEncrypter.encrypt(plaintext);

        SecretData.Builder s = SecretData.newBuilder();
        s.setCiphertext(ByteString.copyFrom(ciphertext));
        s.setEncryptedWith(EncryptedWith.PUBLIC_KEY);
        s.setVersion(1);
        return s.build();
    }

    public static SecretData buildUserSecret(SecretToken userSecret, UserSecretData data) {
        byte[] plaintext = data.toByteArray();
        // CryptoKey userKey = userSecret.cryptoKey;

        byte[] ciphertext = userSecret.encrypt(plaintext);

        SecretData.Builder s = SecretData.newBuilder();
        s.setCiphertext(ByteString.copyFrom(ciphertext));
        s.setEncryptedWith(EncryptedWith.SECRET_KEY);
        s.setVersion(1);
        return s.build();
    }

    public static SecretData buildClientAppSecret(SecretToken secretToken, ClientAppSecretData data) {
        byte[] plaintext = data.toByteArray();

        byte[] ciphertext = secretToken.encrypt(plaintext);

        SecretData.Builder s = SecretData.newBuilder();
        s.setCiphertext(ByteString.copyFrom(ciphertext));
        s.setEncryptedWith(EncryptedWith.SECRET_KEY);
        s.setVersion(1);
        return s.build();
    }

    public static <T extends GeneratedMessage> T unlockWithSecretKey(ByteString secured, CryptoKey key,
            GeneratedMessage.Builder newBuilder) throws InvalidProtocolBufferException {
        byte[] plaintext = key.decrypt(secured.toByteArray());
        newBuilder.mergeFrom(plaintext);
        return (T) newBuilder.build();
    }

    // static <T extends GeneratedMessage> T unlockWithPrivateKey(ByteString
    // secured, PrivateKey key,
    // GeneratedMessage.Builder newBuilder) throws
    // InvalidProtocolBufferException {
    // byte[] plaintext = KeyPairs.decrypt(key, secured.toByteArray());
    // newBuilder.mergeFrom(plaintext);
    // return (T) newBuilder.build();
    // }

    static <T extends GeneratedMessage> T unlock(ByteString secured, Crypter crypter,
            GeneratedMessage.Builder newBuilder) throws IOException {
        byte[] plaintext;
        try {
            plaintext = crypter.decrypt(secured.toByteArray());
        } catch (KeyczarException e) {
            throw new IOException("Error decrypting data", e);
        }
        newBuilder.mergeFrom(plaintext);
        return (T) newBuilder.build();
    }

    public static <T extends GeneratedMessage> T unlock(ByteString secured, AesKey key,
            GeneratedMessage.Builder newBuilder) throws IOException {
        Crypter crypter = KeyczarUtils.buildCrypter(key);
        return unlock(secured, crypter, newBuilder);
    }

    static <T extends GeneratedMessage> T unlock(SecretData secured, AuthenticatedUser user,
            GeneratedMessage.Builder newBuilder) throws IOException {
        EncryptedWith encryptedWith = EncryptedWith.PUBLIC_KEY;

        if (secured.hasEncryptedWith()) {
            encryptedWith = secured.getEncryptedWith();
        }

        int version = 0;
        if (secured.hasVersion()) {
            version = secured.getVersion();
        }

        if (version == 0) {
            switch (encryptedWith) {
            case PUBLIC_KEY:
                // We could actually support this fairly easily...
                throw new IllegalStateException("Unwrapping with deprecated private key not supported");

                // log.warn("Using deprecated private key for user: {}", user);
                // return unlockWithPrivateKey(secured.getCiphertext(),
                // user.getKeys().getDeprecatedPrivateKey(),
                // newBuilder);

            case SECRET_KEY:
                return unlockWithSecretKey(secured.getCiphertext(), user.getKeys().getSecretToken().getDeprecatedKey(),
                        newBuilder);
            default:
                throw new IllegalArgumentException();
            }
        } else if (version == 1) {
            Crypter crypter;

            switch (encryptedWith) {
            case PUBLIC_KEY:
                crypter = user.getKeys().getAsymetricCrypter();
                break;
            case SECRET_KEY:
                crypter = user.getKeys().getSecretToken().getCrypter();
                break;
            default:
                throw new IllegalArgumentException();
            }

            return unlock(secured.getCiphertext(), crypter, newBuilder);

        } else {
            throw new IllegalArgumentException();
        }
    }

    public static <T extends GeneratedMessage> T unlock(SecretData secured, SecretToken secretToken,
            GeneratedMessage.Builder newBuilder) throws IOException {
        EncryptedWith encryptedWith = EncryptedWith.PUBLIC_KEY;

        if (secured.hasEncryptedWith()) {
            encryptedWith = secured.getEncryptedWith();
        }

        int version = 0;
        if (secured.hasVersion()) {
            version = secured.getVersion();
        }

        if (version == 0) {
            try {
                switch (encryptedWith) {
                case PUBLIC_KEY:
                    throw new IllegalArgumentException();
                case SECRET_KEY:
                    log.warn("Trying deprecated secret key");
                    return unlockWithSecretKey(secured.getCiphertext(), secretToken.getDeprecatedKey(), newBuilder);
                default:
                    throw new IllegalArgumentException();
                }
            } catch (Exception e) {
                log.warn("Error decrypting with v0, trying v1: {}", e.getMessage());
                version = 1;
            }
        }

        if (version == 1) {
            Crypter crypter;

            switch (encryptedWith) {
            case PUBLIC_KEY:
                throw new IllegalArgumentException();
            case SECRET_KEY:
                crypter = secretToken.getCrypter();
                break;
            default:
                throw new IllegalArgumentException();
            }

            return unlock(secured.getCiphertext(), crypter, newBuilder);
        } else {
            throw new IllegalArgumentException();
        }
    }

    public static Encrypter getPublicKeyEncrypter(KeyData keyData) throws KeyczarException {
        if (keyData.hasKeyczar()) {
            RsaPublicKey key;
            try {
                key = KeyczarUtils.readRsaPublicKey(keyData.getKeyczar());
            } catch (KeyczarException e) {
                throw new IllegalStateException("Error reading public key", e);
            }
            return new Encrypter(new KeyczarReaderWrapper(key));
        } else {
            throw new IllegalStateException("Only deprecated public key available");
        }
        // ByteString encoded = keyData.getEncoded();
        // if (encoded == null) {
        // throw new IllegalArgumentException();
        // }
        //
        // PublicKey publicKey =
        // KeyPairs.deserializePublicKey(encoded.toByteArray());
        //
        //
        // return publicKey;
    }

    public static KeyczarReaderWrapper getPrivateKey(KeyData keyData) {
        if (keyData.hasKeyczar()) {
            RsaPrivateKey key;
            try {
                key = KeyczarUtils.readRsaPrivateKey(keyData.getKeyczar());
            } catch (KeyczarException e) {
                throw new IllegalStateException("Error reading private key", e);
            }
            return new KeyczarReaderWrapper(key);
        } else {
            throw new IllegalStateException("Only deprecated private key available");
        }

        // if keyczar
        //
        //
        // ByteString encoded = keyData.getEncoded();
        // if (encoded == null) {
        // throw new IllegalArgumentException();
        // }
        //
        // PrivateKey privateKey =
        // KeyPairs.deserializePrivateKey(encoded.toByteArray());
        // return privateKey;
    }

    public void addPasswordAuth(UserData.Builder user, SecretToken userSecret, String password) {
        SecretStoreData.Builder secretStore = user.getSecretStoreBuilder();
        if (Strings.isNullOrEmpty(password)) {
            throw new IllegalArgumentException();
        }

        setPassword(secretStore, password, userSecret);
    }

    public void addPublicKeyAuth(UserData.Builder user, String credentialKey, ByteString challenge) {
        if (Strings.isNullOrEmpty(credentialKey)) {
            throw new IllegalArgumentException();
        }

        if (challenge == null) {
            throw new IllegalArgumentException();
        }

        SecretStoreData.Builder secretStore = user.getSecretStoreBuilder();
        SecretKeyData.Builder b = getSecretKeyData(secretStore, SecretKeyType.ENCRYPTED_WITH_CREDENTIAL);

        b.setCredentialKey(credentialKey);

        b.setCiphertext(challenge);
    }

    public void addTokenRecovery(UserData.Builder user, SecretToken secret) {
        SecretStoreData.Builder secretStore = user.getSecretStoreBuilder();

        SecretKeyData.Builder b = getSecretKeyData(secretStore, SecretKeyType.ENCRYPTED_WITH_FORGOTPASSWORD_PUBKEY);

        Encrypter encrypter = keystore.buildEncrypter(KEY_FORGOT_PASSWORD_PUBLIC);

        b.setVersion(1);

        byte[] serialized = KeyczarUtils.pack(secret.cryptoKey);
        byte[] ciphertext;
        try {
            ciphertext = encrypter.encrypt(serialized);
        } catch (KeyczarException e) {
            throw new IllegalStateException("Error encrypting key", e);
        }

        b.setCiphertext(ByteString.copyFrom(ciphertext));
    }

    public static void storeLockedByProject(SecretStoreData.Builder data, AuthenticatedProject project,
            SecretToken secret) {
        SecretKeyData.Builder b = getSecretKeyData(data, SecretKeyType.ENCRYPTED_WITH_PROJECT_KEY);

        SecretToken secretToken = project.getKeys().getSecretToken();

        if (secretToken.type != SecretTokenType.PROJECT_SECRET) {
            throw new IllegalArgumentException();
        }

        Encrypter encrypter = secretToken.getCrypter();

        b.setVersion(1);

        byte[] serialized = KeyczarUtils.pack(secret.cryptoKey);
        byte[] ciphertext;
        try {
            ciphertext = encrypter.encrypt(serialized);
        } catch (KeyczarException e) {
            throw new IllegalStateException("Error encrypting key", e);
        }

        b.setCiphertext(ByteString.copyFrom(ciphertext));
    }

    private static SecretKeyData.Builder getSecretKeyData(SecretStoreData.Builder data, SecretKeyType secretKeyType) {
        for (SecretKeyData.Builder entry : data.getSecretKeyBuilderList()) {
            if (entry.getType() != secretKeyType) {
                continue;
            }

            return entry;
        }

        SecretKeyData.Builder b = data.addSecretKeyBuilder();
        b.setType(secretKeyType);
        return b;
    }

    public static void setPassword(SecretStoreData.Builder data, String password, AuthenticatedUserKeys secret) {
        setPassword(data, password, secret.getSecretToken());
    }

    public static void setPassword(SecretStoreData.Builder data, String password, SecretToken secret) {
        SecretKeyData.Builder b = getSecretKeyData(data, SecretKeyType.ENCRYPTED_WITH_USER_PASSWORD);

        // b.setUserId(value);
        b.setIterations(2000);

        {
            byte[] seed = KeyczarUtils.generateSecureRandom(16);
            b.setSeed(ByteString.copyFrom(seed));
        }

        AesKey passwordKey = KeyczarUtils.deriveKey(b.getIterations(), b.getSeed().toByteArray(), password);

        b.setVersion(1);
        byte[] ciphertext = encryptSymetricKey(passwordKey, secret.cryptoKey);
        b.setCiphertext(ByteString.copyFrom(ciphertext));
    }

    public static SecretToken getSecretWithRecovery(SecretStoreData data, KeyczarReader recoveryKey)
            throws KeyczarException {
        for (SecretKeyData entry : data.getSecretKeyList()) {
            if (entry.getType() != SecretKeyType.ENCRYPTED_WITH_FORGOTPASSWORD_PUBKEY) {
                continue;
            }

            int version = 0;
            if (entry.hasVersion()) {
                version = entry.getVersion();
            }

            AesKey aesKey;

            if (version == 1) {
                Crypter crypter = new Crypter(recoveryKey);

                byte[] plaintext;
                try {
                    plaintext = crypter.decrypt(entry.getCiphertext().toByteArray());
                } catch (KeyczarException e) {
                    throw new IllegalStateException("Error decrypting token", e);
                }

                aesKey = KeyczarUtils.unpack(plaintext);

            } else {
                throw new IllegalStateException();
            }

            return new SecretToken(SecretTokenType.USER_SECRET, aesKey, null);
        }

        return null;
    }

    public static SecretToken getSecretFromPassword(SecretStoreData data, final String password)
            throws KeyczarException {
        for (SecretKeyData entry : data.getSecretKeyList()) {
            if (entry.getType() != SecretKeyType.ENCRYPTED_WITH_USER_PASSWORD) {
                continue;
            }

            // if (entry.getUserId() != userId) {
            // continue;
            // }

            int version = 0;
            if (entry.hasVersion()) {
                version = entry.getVersion();
            }

            CryptoKey v0;
            AesKey v1;

            if (version == 0) {
                AesCbcCryptoKey passwordKey = AesCbcCryptoKey.deriveKey(entry.getIterations(), entry.getSeed()
                        .toByteArray(), password);
                byte[] plaintext = FathomdbCrypto.decrypt(passwordKey, entry.getCiphertext().toByteArray());
                v0 = FathomdbCrypto.deserializeKey(plaintext);

                HmacKey hmacKey = KeyczarUtils.deriveHmac(plaintext, entry.getSeed().toByteArray(), password);
                v1 = new AesKey(((AesCbcCryptoKey) v0).getJce().getEncoded(), hmacKey);
            } else if (version == 1) {
                AesKey passwordKey = KeyczarUtils.deriveKey(entry.getIterations(), entry.getSeed().toByteArray(),
                        password);

                byte[] plaintext = KeyczarUtils.decrypt(passwordKey, entry.getCiphertext().toByteArray());

                v1 = KeyczarUtils.unpack(plaintext);
                v0 = null;
            } else {
                throw new IllegalStateException();
            }

            return new SecretToken(SecretTokenType.USER_SECRET, v1, v0);
        }

        return null;
    }

    // private static CryptoKey decryptSymetricKey(CryptoKey key, ByteString
    // ciphertext) {
    // return toSecretKey(FathomdbCrypto.decrypt(key,
    // ciphertext.toByteArray()));
    // }

    //
    // private static CryptoKey toSecretKey(byte[] keyData) {
    // return FathomdbCrypto.deserializeKey(keyData);
    // }

    private static byte[] encryptSymetricKey(AesKey passwordKey, AesKey secret) {
        byte[] serialized = KeyczarUtils.pack(secret);

        return KeyczarUtils.encrypt(passwordKey, serialized);
    }

    public UserWithSecret checkPublicKey(UserData user, CredentialData credential, ClientCertificate clientCertificate,
            ByteString challenge, ByteString responseData) {
        AesKey secretKey;

        if (!ChallengeResponses.hasPrefix(responseData.toByteArray())) {
            log.warn("Challenge response was not valid");
            return null;
        }

        byte[] payload = ChallengeResponses.getPayload(responseData.toByteArray());
        try {
            secretKey = KeyczarUtils.unpack(payload);
        } catch (KeyczarException e) {
            log.warn("Error unpacking key", e);
            return null;
        }

        SecretToken secretToken = new SecretToken(SecretTokenType.USER_SECRET, secretKey, null);
        // if (secretToken == null) {
        // return null;
        // }

        UserWithSecret ret = checkSecret(user, secretToken);
        return ret;
    }

    public ByteString buildAuthChallenge(UserData user, CredentialData credential, ClientCertificate clientCertificate) {
        for (SecretKeyData secretKey : user.getSecretStore().getSecretKeyList()) {
            SecretKeyType type = secretKey.getType();
            if (type != SecretKeyType.ENCRYPTED_WITH_CREDENTIAL) {
                continue;
            }

            if (!Objects.equal(secretKey.getCredentialKey(), credential.getKey())) {
                continue;
            }

            ByteString ciphertext = secretKey.getCiphertext();

            // TODO: Encrypt?? I think this would need a commutative encryption
            // algorithm.
            // We could also return a pair of challenges, one repeated, one not.

            return ByteString.copyFrom(ChallengeResponses.addHeader(ciphertext.toByteArray()));
        }

        log.warn("Unable to build auth challenge for credential: {}", credential);

        return null;
    }

    public void changePassword(UserData user, CredentialData credential, String password, KeyczarReader recoveryKey)
            throws CloudException {
        if (Strings.isNullOrEmpty(password)) {
            throw new IllegalArgumentException();
        }

        UserWithSecret userWithSecret = checkPasswordRecovery(user, credential, recoveryKey);
        if (userWithSecret == null) {
            throw new IllegalArgumentException();
        }

        UserData.Builder b = UserData.newBuilder(user);

        SecretStoreData.Builder secretStore = b.getSecretStoreBuilder();

        Secrets.setPassword(secretStore, password, userWithSecret.getSecretToken());

        UserSecretData.Builder s = UserSecretData.newBuilder(userWithSecret.userSecretData);
        b.setSecretData(Secrets.buildUserSecret(userWithSecret.getSecretToken(), s.build()));

        user = authRepository.getUsers().update(b);
    }

}
TOP

Related Classes of io.fathom.cloud.identity.secrets.Secrets

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.