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

package com.google.crypto.tink.mac;

import java.util.ArrayList;
import com.google.errorprone.annotations.Immutable;
import java.util.Iterator;
import java.nio.ByteBuffer;
import java.util.List;
import com.google.crypto.tink.internal.PrimitiveRegistry;
import com.google.crypto.tink.internal.MutablePrimitiveRegistry;
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.internal.LegacyProtoKey;
import com.google.crypto.tink.util.Bytes;
import com.google.crypto.tink.Key;
import com.google.crypto.tink.internal.PrimitiveWrapper;

public class ChunkedMacWrapper implements PrimitiveWrapper<ChunkedMac, ChunkedMac>
{
    private static final ChunkedMacWrapper WRAPPER;
    
    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());
    }
    
    private ChunkedMacWrapper() {
    }
    
    @Override
    public ChunkedMac wrap(final KeysetHandleInterface keysetHandle, final MonitoringAnnotations annotations, final PrimitiveFactory<ChunkedMac> factory) throws GeneralSecurityException {
        final KeysetHandleInterface.Entry primaryEntry = keysetHandle.getPrimary();
        if (primaryEntry == null) {
            throw new GeneralSecurityException("no primary in primitive set");
        }
        final PrefixMap.Builder<ChunkedMac> allChunkedMacsBuilder = new PrefixMap.Builder<ChunkedMac>();
        for (int i = 0; i < keysetHandle.size(); ++i) {
            final KeysetHandleInterface.Entry entry = keysetHandle.getAt(i);
            if (entry.getStatus().equals(KeyStatus.ENABLED)) {
                final ChunkedMac chunkedMac = factory.create(entry);
                allChunkedMacsBuilder.put(getOutputPrefix(entry.getKey()), chunkedMac);
            }
        }
        final ChunkedMac primaryChunkedMac = factory.create(primaryEntry);
        return new WrappedChunkedMac((PrefixMap)allChunkedMacsBuilder.build(), primaryChunkedMac);
    }
    
    @Override
    public Class<ChunkedMac> getPrimitiveClass() {
        return ChunkedMac.class;
    }
    
    @Override
    public Class<ChunkedMac> getInputPrimitiveClass() {
        return ChunkedMac.class;
    }
    
    static void register() throws GeneralSecurityException {
        MutablePrimitiveRegistry.globalInstance().registerPrimitiveWrapper((PrimitiveWrapper<Object, Object>)ChunkedMacWrapper.WRAPPER);
    }
    
    public static void registerToInternalPrimitiveRegistry(final PrimitiveRegistry.Builder primitiveRegistryBuilder) throws GeneralSecurityException {
        primitiveRegistryBuilder.registerPrimitiveWrapper((PrimitiveWrapper<Object, Object>)ChunkedMacWrapper.WRAPPER);
    }
    
    static {
        WRAPPER = new ChunkedMacWrapper();
    }
    
    private static class WrappedChunkedMacVerification implements ChunkedMacVerification
    {
        private final List<ChunkedMacVerification> verifications;
        
        private WrappedChunkedMacVerification(final List<ChunkedMacVerification> verificationEntries) {
            this.verifications = verificationEntries;
        }
        
        @Override
        public void update(final ByteBuffer data) throws GeneralSecurityException {
            final ByteBuffer clonedData = data.duplicate();
            clonedData.mark();
            for (final ChunkedMacVerification entry : this.verifications) {
                clonedData.reset();
                entry.update(clonedData);
            }
            data.position(data.limit());
        }
        
        @Override
        public void verifyMac() throws GeneralSecurityException {
            final GeneralSecurityException errorSink = new GeneralSecurityException("MAC verification failed for all suitable keys in keyset");
            for (final ChunkedMacVerification entry : this.verifications) {
                try {
                    entry.verifyMac();
                    return;
                }
                catch (final GeneralSecurityException e) {
                    errorSink.addSuppressed(e);
                    continue;
                }
                break;
            }
            throw errorSink;
        }
    }
    
    @Immutable
    private static class WrappedChunkedMac implements ChunkedMac
    {
        private final PrefixMap<ChunkedMac> allChunkedMacs;
        private final ChunkedMac primaryChunkedMac;
        
        private WrappedChunkedMac(final PrefixMap<ChunkedMac> allChunkedMacs, final ChunkedMac primaryChunkedMac) {
            this.allChunkedMacs = allChunkedMacs;
            this.primaryChunkedMac = primaryChunkedMac;
        }
        
        @Override
        public ChunkedMacComputation createComputation() throws GeneralSecurityException {
            return this.primaryChunkedMac.createComputation();
        }
        
        @Override
        public ChunkedMacVerification createVerification(final byte[] tag) throws GeneralSecurityException {
            final List<ChunkedMacVerification> allVerifications = new ArrayList<ChunkedMacVerification>();
            for (final ChunkedMac mac : this.allChunkedMacs.getAllWithMatchingPrefix(tag)) {
                allVerifications.add(mac.createVerification(tag));
            }
            return new WrappedChunkedMacVerification((List)allVerifications);
        }
    }
}
