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

package com.google.crypto.tink.mac;

import java.util.Iterator;
import com.google.crypto.tink.mac.internal.LegacyFullMac;
import com.google.crypto.tink.internal.PrimitiveRegistry;
import com.google.crypto.tink.internal.MutablePrimitiveRegistry;
import com.google.crypto.tink.internal.MonitoringClient;
import com.google.crypto.tink.internal.MonitoringUtil;
import com.google.crypto.tink.internal.MutableMonitoringRegistry;
import com.google.crypto.tink.KeyStatus;
import com.google.crypto.tink.internal.PrefixMap;
import com.google.crypto.tink.internal.MonitoringAnnotations;
import com.google.crypto.tink.internal.KeysetHandleInterface;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.util.Bytes;
import com.google.crypto.tink.Key;
import com.google.crypto.tink.internal.LegacyProtoKey;
import com.google.crypto.tink.internal.PrimitiveConstructor;
import com.google.crypto.tink.Mac;
import com.google.crypto.tink.internal.PrimitiveWrapper;

public class MacWrapper implements PrimitiveWrapper<Mac, Mac>
{
    private static final MacWrapper WRAPPER;
    private static final PrimitiveConstructor<LegacyProtoKey, Mac> LEGACY_FULL_MAC_PRIMITIVE_CONSTRUCTOR;
    
    private static Bytes getOutputPrefix(final Key key) throws GeneralSecurityException {
        if (key instanceof MacKey) {
            return ((MacKey)key).getOutputPrefix();
        }
        if (key instanceof LegacyProtoKey) {
            return ((LegacyProtoKey)key).getOutputPrefix();
        }
        throw new GeneralSecurityException("Cannot get output prefix for key of class " + key.getClass().getName() + " with parameters " + key.getParameters());
    }
    
    MacWrapper() {
    }
    
    @Override
    public Mac wrap(final KeysetHandleInterface keysetHandle, final MonitoringAnnotations annotations, final PrimitiveFactory<Mac> factory) throws GeneralSecurityException {
        final PrefixMap.Builder<MacWithId> builder = new PrefixMap.Builder<MacWithId>();
        for (int i = 0; i < keysetHandle.size(); ++i) {
            final KeysetHandleInterface.Entry entry = keysetHandle.getAt(i);
            if (entry.getStatus().equals(KeyStatus.ENABLED)) {
                final Mac mac = factory.create(entry);
                builder.put(getOutputPrefix(entry.getKey()), new MacWithId(mac, entry.getId()));
            }
        }
        MonitoringClient.Logger computeLogger;
        MonitoringClient.Logger verifyLogger;
        if (!annotations.isEmpty()) {
            final MonitoringClient client = MutableMonitoringRegistry.globalInstance().getMonitoringClient();
            computeLogger = client.createLogger(keysetHandle, annotations, "mac", "compute");
            verifyLogger = client.createLogger(keysetHandle, annotations, "mac", "verify");
        }
        else {
            computeLogger = MonitoringUtil.DO_NOTHING_LOGGER;
            verifyLogger = MonitoringUtil.DO_NOTHING_LOGGER;
        }
        final Mac primaryMac = factory.create(keysetHandle.getPrimary());
        return new WrappedMac(new MacWithId(primaryMac, keysetHandle.getPrimary().getId()), (PrefixMap)builder.build(), computeLogger, verifyLogger);
    }
    
    @Override
    public Class<Mac> getPrimitiveClass() {
        return Mac.class;
    }
    
    @Override
    public Class<Mac> getInputPrimitiveClass() {
        return Mac.class;
    }
    
    static void register() throws GeneralSecurityException {
        MutablePrimitiveRegistry.globalInstance().registerPrimitiveWrapper((PrimitiveWrapper<Object, Object>)MacWrapper.WRAPPER);
        MutablePrimitiveRegistry.globalInstance().registerPrimitiveConstructor(MacWrapper.LEGACY_FULL_MAC_PRIMITIVE_CONSTRUCTOR);
    }
    
    public static void registerToInternalPrimitiveRegistry(final PrimitiveRegistry.Builder primitiveRegistryBuilder) throws GeneralSecurityException {
        primitiveRegistryBuilder.registerPrimitiveWrapper((PrimitiveWrapper<Object, Object>)MacWrapper.WRAPPER);
    }
    
    static {
        WRAPPER = new MacWrapper();
        LEGACY_FULL_MAC_PRIMITIVE_CONSTRUCTOR = PrimitiveConstructor.create(LegacyFullMac::create, LegacyProtoKey.class, Mac.class);
    }
    
    private static class MacWithId
    {
        public final Mac mac;
        public final int id;
        
        public MacWithId(final Mac mac, final int id) {
            this.mac = mac;
            this.id = id;
        }
    }
    
    private static class WrappedMac implements Mac
    {
        private final MacWithId primary;
        private final PrefixMap<MacWithId> allMacs;
        private final MonitoringClient.Logger computeLogger;
        private final MonitoringClient.Logger verifyLogger;
        
        private WrappedMac(final MacWithId primary, final PrefixMap<MacWithId> allMacs, final MonitoringClient.Logger computeLogger, final MonitoringClient.Logger verifyLogger) {
            this.primary = primary;
            this.allMacs = allMacs;
            this.computeLogger = computeLogger;
            this.verifyLogger = verifyLogger;
        }
        
        @Override
        public byte[] computeMac(final byte[] data) throws GeneralSecurityException {
            try {
                final byte[] output = this.primary.mac.computeMac(data);
                this.computeLogger.log(this.primary.id, data.length);
                return output;
            }
            catch (final GeneralSecurityException e) {
                this.computeLogger.logFailure();
                throw e;
            }
        }
        
        @Override
        public void verifyMac(final byte[] mac, final byte[] data) throws GeneralSecurityException {
            for (final MacWithId macWithId : this.allMacs.getAllWithMatchingPrefix(mac)) {
                try {
                    macWithId.mac.verifyMac(mac, data);
                    this.verifyLogger.log(macWithId.id, data.length);
                    return;
                }
                catch (final GeneralSecurityException ex) {
                    continue;
                }
                break;
            }
            this.verifyLogger.logFailure();
            throw new GeneralSecurityException("invalid MAC");
        }
    }
}
