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

package org.bouncycastle.pqc.crypto.xmss;

import org.bouncycastle.util.Arrays;
import java.io.IOException;
import org.bouncycastle.util.Encodable;

public final class XMSSMTPrivateKeyParameters extends XMSSMTKeyParameters implements XMSSStoreableObjectInterface, Encodable
{
    private final XMSSMTParameters params;
    private final byte[] secretKeySeed;
    private final byte[] secretKeyPRF;
    private final byte[] publicSeed;
    private final byte[] root;
    private volatile long index;
    private volatile BDSStateMap bdsState;
    private volatile boolean used;
    
    private XMSSMTPrivateKeyParameters(final Builder builder) {
        super(true, builder.params.getTreeDigest());
        this.params = builder.params;
        if (this.params == null) {
            throw new NullPointerException("params == null");
        }
        final int treeDigestSize = this.params.getTreeDigestSize();
        final byte[] access$100 = builder.privateKey;
        if (access$100 != null) {
            if (builder.xmss == null) {
                throw new NullPointerException("xmss == null");
            }
            final int height = this.params.getHeight();
            final int n = (height + 7) / 8;
            final int n2 = treeDigestSize;
            final int n3 = treeDigestSize;
            final int n4 = treeDigestSize;
            final int n5 = treeDigestSize;
            final int n6 = 0;
            this.index = XMSSUtil.bytesToXBigEndian(access$100, n6, n);
            if (!XMSSUtil.isIndexValid(height, this.index)) {
                throw new IllegalArgumentException("index out of bounds");
            }
            final int n7 = n6 + n;
            this.secretKeySeed = XMSSUtil.extractBytesAtOffset(access$100, n7, n2);
            final int n8 = n7 + n2;
            this.secretKeyPRF = XMSSUtil.extractBytesAtOffset(access$100, n8, n3);
            final int n9 = n8 + n3;
            this.publicSeed = XMSSUtil.extractBytesAtOffset(access$100, n9, n4);
            final int n10 = n9 + n4;
            this.root = XMSSUtil.extractBytesAtOffset(access$100, n10, n5);
            final int n11 = n10 + n5;
            final byte[] bytesAtOffset = XMSSUtil.extractBytesAtOffset(access$100, n11, access$100.length - n11);
            try {
                this.bdsState = ((BDSStateMap)XMSSUtil.deserialize(bytesAtOffset, BDSStateMap.class)).withWOTSDigest(builder.xmss.getTreeDigestOID());
            }
            catch (final IOException cause) {
                throw new IllegalArgumentException(cause.getMessage(), cause);
            }
            catch (final ClassNotFoundException cause2) {
                throw new IllegalArgumentException(cause2.getMessage(), cause2);
            }
        }
        else {
            this.index = builder.index;
            final byte[] access$101 = builder.secretKeySeed;
            if (access$101 != null) {
                if (access$101.length != treeDigestSize) {
                    throw new IllegalArgumentException("size of secretKeySeed needs to be equal size of digest");
                }
                this.secretKeySeed = access$101;
            }
            else {
                this.secretKeySeed = new byte[treeDigestSize];
            }
            final byte[] access$102 = builder.secretKeyPRF;
            if (access$102 != null) {
                if (access$102.length != treeDigestSize) {
                    throw new IllegalArgumentException("size of secretKeyPRF needs to be equal size of digest");
                }
                this.secretKeyPRF = access$102;
            }
            else {
                this.secretKeyPRF = new byte[treeDigestSize];
            }
            final byte[] access$103 = builder.publicSeed;
            if (access$103 != null) {
                if (access$103.length != treeDigestSize) {
                    throw new IllegalArgumentException("size of publicSeed needs to be equal size of digest");
                }
                this.publicSeed = access$103;
            }
            else {
                this.publicSeed = new byte[treeDigestSize];
            }
            final byte[] access$104 = builder.root;
            if (access$104 != null) {
                if (access$104.length != treeDigestSize) {
                    throw new IllegalArgumentException("size of root needs to be equal size of digest");
                }
                this.root = access$104;
            }
            else {
                this.root = new byte[treeDigestSize];
            }
            final BDSStateMap access$105 = builder.bdsState;
            if (access$105 != null) {
                this.bdsState = access$105;
            }
            else if (XMSSUtil.isIndexValid(this.params.getHeight(), builder.index) && access$103 != null && access$101 != null) {
                this.bdsState = new BDSStateMap(this.params, builder.index, access$103, access$101);
            }
            else {
                this.bdsState = new BDSStateMap(builder.maxIndex + 1L);
            }
            if (builder.maxIndex >= 0L && builder.maxIndex != this.bdsState.getMaxIndex()) {
                throw new IllegalArgumentException("maxIndex set but not reflected in state");
            }
        }
    }
    
    @Override
    public byte[] getEncoded() throws IOException {
        synchronized (this) {
            return this.toByteArray();
        }
    }
    
    @Deprecated
    @Override
    public byte[] toByteArray() {
        synchronized (this) {
            final int treeDigestSize = this.params.getTreeDigestSize();
            final int n = (this.params.getHeight() + 7) / 8;
            final int n2 = treeDigestSize;
            final int n3 = treeDigestSize;
            final int n4 = treeDigestSize;
            final byte[] array = new byte[n + n2 + n3 + n4 + treeDigestSize];
            final int n5 = 0;
            XMSSUtil.copyBytesAtOffset(array, XMSSUtil.toBytesBigEndian(this.index, n), n5);
            final int n6 = n5 + n;
            XMSSUtil.copyBytesAtOffset(array, this.secretKeySeed, n6);
            final int n7 = n6 + n2;
            XMSSUtil.copyBytesAtOffset(array, this.secretKeyPRF, n7);
            final int n8 = n7 + n3;
            XMSSUtil.copyBytesAtOffset(array, this.publicSeed, n8);
            XMSSUtil.copyBytesAtOffset(array, this.root, n8 + n4);
            try {
                return Arrays.concatenate(array, XMSSUtil.serialize(this.bdsState));
            }
            catch (final IOException cause) {
                throw new IllegalStateException("error serializing bds state: " + cause.getMessage(), cause);
            }
        }
    }
    
    public long getIndex() {
        return this.index;
    }
    
    public long getUsagesRemaining() {
        synchronized (this) {
            return this.bdsState.getMaxIndex() - this.getIndex() + 1L;
        }
    }
    
    public byte[] getSecretKeySeed() {
        return XMSSUtil.cloneArray(this.secretKeySeed);
    }
    
    public byte[] getSecretKeyPRF() {
        return XMSSUtil.cloneArray(this.secretKeyPRF);
    }
    
    public byte[] getPublicSeed() {
        return XMSSUtil.cloneArray(this.publicSeed);
    }
    
    public byte[] getRoot() {
        return XMSSUtil.cloneArray(this.root);
    }
    
    BDSStateMap getBDSState() {
        return this.bdsState;
    }
    
    public XMSSMTParameters getParameters() {
        return this.params;
    }
    
    public XMSSMTPrivateKeyParameters getNextKey() {
        synchronized (this) {
            return this.extractKeyShard(1);
        }
    }
    
    XMSSMTPrivateKeyParameters rollKey() {
        synchronized (this) {
            if (this.getIndex() < this.bdsState.getMaxIndex()) {
                this.bdsState.updateState(this.params, this.index, this.publicSeed, this.secretKeySeed);
                ++this.index;
                this.used = false;
            }
            else {
                this.index = this.bdsState.getMaxIndex() + 1L;
                this.bdsState = new BDSStateMap(this.bdsState.getMaxIndex());
                this.used = false;
            }
            return this;
        }
    }
    
    public XMSSMTPrivateKeyParameters extractKeyShard(final int n) {
        if (n < 1) {
            throw new IllegalArgumentException("cannot ask for a shard with 0 keys");
        }
        synchronized (this) {
            if (n <= this.getUsagesRemaining()) {
                final XMSSMTPrivateKeyParameters build = new Builder(this.params).withSecretKeySeed(this.secretKeySeed).withSecretKeyPRF(this.secretKeyPRF).withPublicSeed(this.publicSeed).withRoot(this.root).withIndex(this.getIndex()).withBDSState(new BDSStateMap(this.bdsState, this.getIndex() + n - 1L)).build();
                for (int i = 0; i != n; ++i) {
                    this.rollKey();
                }
                return build;
            }
            throw new IllegalArgumentException("usageCount exceeds usages remaining");
        }
    }
    
    public static class Builder
    {
        private final XMSSMTParameters params;
        private long index;
        private long maxIndex;
        private byte[] secretKeySeed;
        private byte[] secretKeyPRF;
        private byte[] publicSeed;
        private byte[] root;
        private BDSStateMap bdsState;
        private byte[] privateKey;
        private XMSSParameters xmss;
        
        public Builder(final XMSSMTParameters params) {
            this.index = 0L;
            this.maxIndex = -1L;
            this.secretKeySeed = null;
            this.secretKeyPRF = null;
            this.publicSeed = null;
            this.root = null;
            this.bdsState = null;
            this.privateKey = null;
            this.xmss = null;
            this.params = params;
        }
        
        public Builder withIndex(final long index) {
            this.index = index;
            return this;
        }
        
        public Builder withMaxIndex(final long maxIndex) {
            this.maxIndex = maxIndex;
            return this;
        }
        
        public Builder withSecretKeySeed(final byte[] array) {
            this.secretKeySeed = XMSSUtil.cloneArray(array);
            return this;
        }
        
        public Builder withSecretKeyPRF(final byte[] array) {
            this.secretKeyPRF = XMSSUtil.cloneArray(array);
            return this;
        }
        
        public Builder withPublicSeed(final byte[] array) {
            this.publicSeed = XMSSUtil.cloneArray(array);
            return this;
        }
        
        public Builder withRoot(final byte[] array) {
            this.root = XMSSUtil.cloneArray(array);
            return this;
        }
        
        public Builder withBDSState(final BDSStateMap bdsState) {
            if (bdsState.getMaxIndex() == 0L) {
                this.bdsState = new BDSStateMap(bdsState, (1L << this.params.getHeight()) - 1L);
            }
            else {
                this.bdsState = bdsState;
            }
            return this;
        }
        
        public Builder withPrivateKey(final byte[] array) {
            this.privateKey = XMSSUtil.cloneArray(array);
            this.xmss = this.params.getXMSSParameters();
            return this;
        }
        
        public XMSSMTPrivateKeyParameters build() {
            return new XMSSMTPrivateKeyParameters(this, null);
        }
    }
}
