// 
// 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.Pack;
import org.bouncycastle.util.Encodable;

public final class XMSSPrivateKeyParameters extends XMSSKeyParameters implements XMSSStoreableObjectInterface, Encodable
{
    private final XMSSParameters params;
    private final byte[] secretKeySeed;
    private final byte[] secretKeyPRF;
    private final byte[] publicSeed;
    private final byte[] root;
    private volatile BDS bdsState;
    
    private XMSSPrivateKeyParameters(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) {
            final int height = this.params.getHeight();
            final int n = 4;
            final int n2 = treeDigestSize;
            final int n3 = treeDigestSize;
            final int n4 = treeDigestSize;
            final int n5 = treeDigestSize;
            final int n6 = 0;
            final int bigEndianToInt = Pack.bigEndianToInt(access$100, n6);
            if (!XMSSUtil.isIndexValid(height, bigEndianToInt)) {
                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 {
                final BDS bds = (BDS)XMSSUtil.deserialize(bytesAtOffset, BDS.class);
                if (bds.getIndex() != bigEndianToInt) {
                    throw new IllegalStateException("serialized BDS has wrong index");
                }
                this.bdsState = bds.withWOTSDigest(builder.params.getTreeDigestOID());
            }
            catch (final IOException cause) {
                throw new IllegalArgumentException(cause.getMessage(), cause);
            }
            catch (final ClassNotFoundException cause2) {
                throw new IllegalArgumentException(cause2.getMessage(), cause2);
            }
        }
        else {
            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 BDS access$105 = builder.bdsState;
            if (access$105 != null) {
                this.bdsState = access$105;
            }
            else if (builder.index < (1 << this.params.getHeight()) - 2 && access$103 != null && access$101 != null) {
                this.bdsState = new BDS(this.params, access$103, access$101, (OTSHashAddress)new OTSHashAddress.Builder().build(), builder.index);
            }
            else {
                this.bdsState = new BDS(this.params, (1 << this.params.getHeight()) - 1, builder.index);
            }
            if (builder.maxIndex >= 0 && builder.maxIndex != this.bdsState.getMaxIndex()) {
                throw new IllegalArgumentException("maxIndex set but not reflected in state");
            }
        }
    }
    
    public long getUsagesRemaining() {
        synchronized (this) {
            return this.bdsState.getMaxIndex() - this.getIndex() + 1;
        }
    }
    
    @Override
    public byte[] getEncoded() throws IOException {
        synchronized (this) {
            return this.toByteArray();
        }
    }
    
    XMSSPrivateKeyParameters rollKey() {
        synchronized (this) {
            if (this.bdsState.getIndex() < this.bdsState.getMaxIndex()) {
                this.bdsState = this.bdsState.getNextState(this.publicSeed, this.secretKeySeed, (OTSHashAddress)new OTSHashAddress.Builder().build());
            }
            else {
                this.bdsState = new BDS(this.params, this.bdsState.getMaxIndex(), this.bdsState.getMaxIndex() + 1);
            }
            return this;
        }
    }
    
    public XMSSPrivateKeyParameters getNextKey() {
        synchronized (this) {
            return this.extractKeyShard(1);
        }
    }
    
    public XMSSPrivateKeyParameters 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 XMSSPrivateKeyParameters build = new Builder(this.params).withSecretKeySeed(this.secretKeySeed).withSecretKeyPRF(this.secretKeyPRF).withPublicSeed(this.publicSeed).withRoot(this.root).withIndex(this.getIndex()).withBDSState(this.bdsState.withMaxIndex(this.bdsState.getIndex() + n - 1, this.params.getTreeDigestOID())).build();
                if (n == this.getUsagesRemaining()) {
                    this.bdsState = new BDS(this.params, this.bdsState.getMaxIndex(), this.getIndex() + n);
                }
                else {
                    final OTSHashAddress otsHashAddress = (OTSHashAddress)new OTSHashAddress.Builder().build();
                    for (int i = 0; i != n; ++i) {
                        this.bdsState = this.bdsState.getNextState(this.publicSeed, this.secretKeySeed, otsHashAddress);
                    }
                }
                return build;
            }
            throw new IllegalArgumentException("usageCount exceeds usages remaining");
        }
    }
    
    @Deprecated
    @Override
    public byte[] toByteArray() {
        synchronized (this) {
            final int treeDigestSize = this.params.getTreeDigestSize();
            final int n = 4;
            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;
            Pack.intToBigEndian(this.bdsState.getIndex(), array, 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);
            byte[] serialize;
            try {
                serialize = XMSSUtil.serialize(this.bdsState);
            }
            catch (final IOException ex) {
                throw new RuntimeException("error serializing bds state: " + ex.getMessage());
            }
            return Arrays.concatenate(array, serialize);
        }
    }
    
    public int getIndex() {
        return this.bdsState.getIndex();
    }
    
    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);
    }
    
    BDS getBDSState() {
        return this.bdsState;
    }
    
    public XMSSParameters getParameters() {
        return this.params;
    }
    
    public static class Builder
    {
        private final XMSSParameters params;
        private int index;
        private int maxIndex;
        private byte[] secretKeySeed;
        private byte[] secretKeyPRF;
        private byte[] publicSeed;
        private byte[] root;
        private BDS bdsState;
        private byte[] privateKey;
        
        public Builder(final XMSSParameters params) {
            this.index = 0;
            this.maxIndex = -1;
            this.secretKeySeed = null;
            this.secretKeyPRF = null;
            this.publicSeed = null;
            this.root = null;
            this.bdsState = null;
            this.privateKey = null;
            this.params = params;
        }
        
        public Builder withIndex(final int index) {
            this.index = index;
            return this;
        }
        
        public Builder withMaxIndex(final int 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 BDS bdsState) {
            this.bdsState = bdsState;
            return this;
        }
        
        public Builder withPrivateKey(final byte[] array) {
            this.privateKey = XMSSUtil.cloneArray(array);
            return this;
        }
        
        public XMSSPrivateKeyParameters build() {
            return new XMSSPrivateKeyParameters(this, null);
        }
    }
}
