// 
// Decompiled by Procyon v0.6.0
// 

package com.google.crypto.tink;

import com.google.crypto.tink.internal.Util;
import java.util.Iterator;
import com.google.crypto.tink.proto.OutputPrefixType;
import com.google.crypto.tink.proto.KeyData;
import com.google.errorprone.annotations.InlineMe;
import com.google.crypto.tink.proto.KeyStatusType;
import com.google.crypto.tink.tinkkey.KeyAccess;
import com.google.crypto.tink.internal.KeyStatusTypeProtoConverter;
import com.google.crypto.tink.tinkkey.SecretKeyAccess;
import com.google.crypto.tink.tinkkey.internal.ProtoKey;
import com.google.crypto.tink.tinkkey.KeyHandle;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.crypto.tink.proto.KeyTemplate;
import java.security.GeneralSecurityException;
import javax.annotation.concurrent.GuardedBy;
import com.google.crypto.tink.proto.Keyset;

public final class KeysetManager
{
    @GuardedBy("this")
    private final Keyset.Builder keysetBuilder;
    
    private KeysetManager(final Keyset.Builder val) {
        this.keysetBuilder = val;
    }
    
    public static KeysetManager withKeysetHandle(final KeysetHandle val) {
        return new KeysetManager(val.getKeyset().toBuilder());
    }
    
    public static KeysetManager withEmptyKeyset() {
        return new KeysetManager(Keyset.newBuilder());
    }
    
    public synchronized KeysetHandle getKeysetHandle() throws GeneralSecurityException {
        return KeysetHandle.fromKeyset(this.keysetBuilder.build());
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager rotate(final KeyTemplate keyTemplate) throws GeneralSecurityException {
        this.addNewKey(keyTemplate, true);
        return this;
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager add(final KeyTemplate keyTemplate) throws GeneralSecurityException {
        this.addNewKey(keyTemplate, false);
        return this;
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager add(final com.google.crypto.tink.KeyTemplate keyTemplate) throws GeneralSecurityException {
        this.addNewKey(keyTemplate.getProtoMaybeThrow(), false);
        return this;
    }
    
    @Deprecated
    @CanIgnoreReturnValue
    public synchronized KeysetManager add(final KeyHandle keyHandle) throws GeneralSecurityException {
        ProtoKey pkey;
        try {
            pkey = (ProtoKey)keyHandle.getKey(SecretKeyAccess.insecureSecretAccess());
        }
        catch (final ClassCastException e) {
            throw new UnsupportedOperationException("KeyHandles which contain TinkKeys that are not ProtoKeys are not yet supported.", e);
        }
        if (this.keyIdExists(keyHandle.getId())) {
            throw new GeneralSecurityException("Trying to add a key with an ID already contained in the keyset.");
        }
        this.keysetBuilder.addKey(Keyset.Key.newBuilder().setKeyData(pkey.getProtoKey()).setKeyId(keyHandle.getId()).setStatus(KeyStatusTypeProtoConverter.toProto(keyHandle.getStatus())).setOutputPrefixType(com.google.crypto.tink.KeyTemplate.toProto(pkey.getOutputPrefixType())).build());
        return this;
    }
    
    @Deprecated
    @CanIgnoreReturnValue
    public synchronized KeysetManager add(final KeyHandle keyHandle, final KeyAccess access) throws GeneralSecurityException {
        return this.add(keyHandle);
    }
    
    @CanIgnoreReturnValue
    public synchronized int addNewKey(final KeyTemplate keyTemplate, final boolean asPrimary) throws GeneralSecurityException {
        final Keyset.Key key = this.newKey(keyTemplate);
        this.keysetBuilder.addKey(key);
        if (asPrimary) {
            this.keysetBuilder.setPrimaryKeyId(key.getKeyId());
        }
        return key.getKeyId();
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager setPrimary(final int keyId) throws GeneralSecurityException {
        int i = 0;
        while (i < this.keysetBuilder.getKeyCount()) {
            final Keyset.Key key = this.keysetBuilder.getKey(i);
            if (key.getKeyId() == keyId) {
                if (!key.getStatus().equals(KeyStatusType.ENABLED)) {
                    throw new GeneralSecurityException("cannot set key as primary because it's not enabled: " + keyId);
                }
                this.keysetBuilder.setPrimaryKeyId(keyId);
                return this;
            }
            else {
                ++i;
            }
        }
        throw new GeneralSecurityException("key not found: " + keyId);
    }
    
    @InlineMe(replacement = "this.setPrimary(keyId)")
    @CanIgnoreReturnValue
    public synchronized KeysetManager promote(final int keyId) throws GeneralSecurityException {
        return this.setPrimary(keyId);
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager enable(final int keyId) throws GeneralSecurityException {
        int i = 0;
        while (i < this.keysetBuilder.getKeyCount()) {
            final Keyset.Key key = this.keysetBuilder.getKey(i);
            if (key.getKeyId() == keyId) {
                if (key.getStatus() != KeyStatusType.ENABLED && key.getStatus() != KeyStatusType.DISABLED) {
                    throw new GeneralSecurityException("cannot enable key with id " + keyId);
                }
                this.keysetBuilder.setKey(i, key.toBuilder().setStatus(KeyStatusType.ENABLED).build());
                return this;
            }
            else {
                ++i;
            }
        }
        throw new GeneralSecurityException("key not found: " + keyId);
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager disable(final int keyId) throws GeneralSecurityException {
        if (keyId == this.keysetBuilder.getPrimaryKeyId()) {
            throw new GeneralSecurityException("cannot disable the primary key");
        }
        int i = 0;
        while (i < this.keysetBuilder.getKeyCount()) {
            final Keyset.Key key = this.keysetBuilder.getKey(i);
            if (key.getKeyId() == keyId) {
                if (key.getStatus() != KeyStatusType.ENABLED && key.getStatus() != KeyStatusType.DISABLED) {
                    throw new GeneralSecurityException("cannot disable key with id " + keyId);
                }
                this.keysetBuilder.setKey(i, key.toBuilder().setStatus(KeyStatusType.DISABLED).build());
                return this;
            }
            else {
                ++i;
            }
        }
        throw new GeneralSecurityException("key not found: " + keyId);
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager delete(final int keyId) throws GeneralSecurityException {
        if (keyId == this.keysetBuilder.getPrimaryKeyId()) {
            throw new GeneralSecurityException("cannot delete the primary key");
        }
        for (int i = 0; i < this.keysetBuilder.getKeyCount(); ++i) {
            final Keyset.Key key = this.keysetBuilder.getKey(i);
            if (key.getKeyId() == keyId) {
                this.keysetBuilder.removeKey(i);
                return this;
            }
        }
        throw new GeneralSecurityException("key not found: " + keyId);
    }
    
    @CanIgnoreReturnValue
    public synchronized KeysetManager destroy(final int keyId) throws GeneralSecurityException {
        if (keyId == this.keysetBuilder.getPrimaryKeyId()) {
            throw new GeneralSecurityException("cannot destroy the primary key");
        }
        int i = 0;
        while (i < this.keysetBuilder.getKeyCount()) {
            final Keyset.Key key = this.keysetBuilder.getKey(i);
            if (key.getKeyId() == keyId) {
                if (key.getStatus() != KeyStatusType.ENABLED && key.getStatus() != KeyStatusType.DISABLED && key.getStatus() != KeyStatusType.DESTROYED) {
                    throw new GeneralSecurityException("cannot destroy key with id " + keyId);
                }
                this.keysetBuilder.setKey(i, key.toBuilder().setStatus(KeyStatusType.DESTROYED).clearKeyData().build());
                return this;
            }
            else {
                ++i;
            }
        }
        throw new GeneralSecurityException("key not found: " + keyId);
    }
    
    private synchronized Keyset.Key newKey(final KeyTemplate keyTemplate) throws GeneralSecurityException {
        return this.createKeysetKey(Registry.newKeyData(keyTemplate), keyTemplate.getOutputPrefixType());
    }
    
    private synchronized Keyset.Key createKeysetKey(final KeyData keyData, final OutputPrefixType outputPrefixType) throws GeneralSecurityException {
        final int keyId = this.newKeyId();
        if (outputPrefixType == OutputPrefixType.UNKNOWN_PREFIX) {
            throw new GeneralSecurityException("unknown output prefix type");
        }
        return Keyset.Key.newBuilder().setKeyData(keyData).setKeyId(keyId).setStatus(KeyStatusType.ENABLED).setOutputPrefixType(outputPrefixType).build();
    }
    
    private synchronized boolean keyIdExists(final int keyId) {
        for (final Keyset.Key key : this.keysetBuilder.getKeyList()) {
            if (key.getKeyId() == keyId) {
                return true;
            }
        }
        return false;
    }
    
    private synchronized int newKeyId() {
        int keyId;
        for (keyId = Util.randKeyId(); this.keyIdExists(keyId); keyId = Util.randKeyId()) {}
        return keyId;
    }
}
