1package org.bouncycastle.asn1.pkcs;
2
3import java.math.BigInteger;
4import java.util.Enumeration;
5
6import org.bouncycastle.asn1.ASN1EncodableVector;
7import org.bouncycastle.asn1.ASN1Integer;
8import org.bouncycastle.asn1.ASN1Object;
9import org.bouncycastle.asn1.ASN1OctetString;
10import org.bouncycastle.asn1.ASN1Primitive;
11import org.bouncycastle.asn1.ASN1Sequence;
12import org.bouncycastle.asn1.DERNull;
13import org.bouncycastle.asn1.DEROctetString;
14import org.bouncycastle.asn1.DERSequence;
15import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
16
17/**
18 * <pre>
19 *     PBKDF2-params ::= SEQUENCE {
20 *               salt CHOICE {
21 *                      specified OCTET STRING,
22 *                      otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
23 *               },
24 *              iterationCount INTEGER (1..MAX),
25 *              keyLength INTEGER (1..MAX) OPTIONAL,
26 *              prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 }
27 * </pre>
28 */
29public class PBKDF2Params
30    extends ASN1Object
31{
32    private static final AlgorithmIdentifier algid_hmacWithSHA1 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
33
34    private ASN1OctetString octStr;
35    private ASN1Integer      iterationCount;
36    private ASN1Integer      keyLength;
37    private AlgorithmIdentifier prf;
38
39    /**
40     * Create PBKDF2Params from the passed in object,
41     *
42     * @param obj either PBKDF2Params or an ASN2Sequence.
43     * @return a PBKDF2Params instance.
44     */
45    public static PBKDF2Params getInstance(
46        Object  obj)
47    {
48        if (obj instanceof PBKDF2Params)
49        {
50            return (PBKDF2Params)obj;
51        }
52
53        if (obj != null)
54        {
55            return new PBKDF2Params(ASN1Sequence.getInstance(obj));
56        }
57
58        return null;
59    }
60
61    /**
62     * Create a PBKDF2Params with the specified salt, iteration count, and algid-hmacWithSHA1 for the prf.
63     *
64     * @param salt  input salt.
65     * @param iterationCount input iteration count.
66     */
67    public PBKDF2Params(
68        byte[]  salt,
69        int     iterationCount)
70    {
71        this.octStr = new DEROctetString(salt);
72        this.iterationCount = new ASN1Integer(iterationCount);
73    }
74
75    /**
76     * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and algid-hmacWithSHA1 for the prf.
77     *
78     * @param salt  input salt.
79     * @param iterationCount input iteration count.
80     * @param keyLength intended key length to be produced.
81     */
82    public PBKDF2Params(
83        byte[]  salt,
84        int     iterationCount,
85        int     keyLength)
86    {
87        this(salt, iterationCount);
88
89        this.keyLength = new ASN1Integer(keyLength);
90    }
91
92    /**
93     * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and a defined prf.
94     *
95     * @param salt  input salt.
96     * @param iterationCount input iteration count.
97     * @param keyLength intended key length to be produced.
98     * @param prf the pseudo-random function to use.
99     */
100    public PBKDF2Params(
101        byte[]  salt,
102        int     iterationCount,
103        int     keyLength,
104        AlgorithmIdentifier prf)
105    {
106        this(salt, iterationCount);
107
108        this.keyLength = new ASN1Integer(keyLength);
109        this.prf = prf;
110    }
111
112    /**
113     * Create a PBKDF2Params with the specified salt, iteration count, and a defined prf.
114     *
115     * @param salt  input salt.
116     * @param iterationCount input iteration count.
117     * @param prf the pseudo-random function to use.
118     */
119    public PBKDF2Params(
120        byte[]  salt,
121        int     iterationCount,
122        AlgorithmIdentifier prf)
123    {
124        this(salt, iterationCount);
125        this.prf = prf;
126    }
127
128    private PBKDF2Params(
129        ASN1Sequence  seq)
130    {
131        Enumeration e = seq.getObjects();
132
133        octStr = (ASN1OctetString)e.nextElement();
134        iterationCount = (ASN1Integer)e.nextElement();
135
136        if (e.hasMoreElements())
137        {
138            Object o = e.nextElement();
139
140            if (o instanceof ASN1Integer)
141            {
142                keyLength = ASN1Integer.getInstance(o);
143                if (e.hasMoreElements())
144                {
145                    o = e.nextElement();
146                }
147                else
148                {
149                    o = null;
150                }
151            }
152            else
153            {
154                keyLength = null;
155            }
156
157            if (o != null)
158            {
159                prf = AlgorithmIdentifier.getInstance(o);
160            }
161        }
162    }
163
164    /**
165     * Return the salt to use.
166     *
167     * @return the input salt.
168     */
169    public byte[] getSalt()
170    {
171        return octStr.getOctets();
172    }
173
174    /**
175     * Return the iteration count to use.
176     *
177     * @return the input iteration count.
178     */
179    public BigInteger getIterationCount()
180    {
181        return iterationCount.getValue();
182    }
183
184    /**
185     * Return the intended length in octets of the derived key.
186     *
187     * @return length in octets for derived key, if specified.
188     */
189    public BigInteger getKeyLength()
190    {
191        if (keyLength != null)
192        {
193            return keyLength.getValue();
194        }
195
196        return null;
197    }
198
199    /**
200     * Return true if the PRF is the default (hmacWithSHA1)
201     *
202     * @return true if PRF is default, false otherwise.
203     */
204    public boolean isDefaultPrf()
205    {
206        return prf == null || prf.equals(algid_hmacWithSHA1);
207    }
208
209    /**
210     * Return the algId of the underlying pseudo random function to use.
211     *
212     * @return the prf algorithm identifier.
213     */
214    public AlgorithmIdentifier getPrf()
215    {
216        if (prf != null)
217        {
218            return prf;
219        }
220
221        return algid_hmacWithSHA1;
222    }
223
224    /**
225     * Return an ASN.1 structure suitable for encoding.
226     *
227     * @return the object as an ASN.1 encodable structure.
228     */
229    public ASN1Primitive toASN1Primitive()
230    {
231        ASN1EncodableVector  v = new ASN1EncodableVector();
232
233        v.add(octStr);
234        v.add(iterationCount);
235
236        if (keyLength != null)
237        {
238            v.add(keyLength);
239        }
240
241        if (prf != null && !prf.equals(algid_hmacWithSHA1))
242        {
243            v.add(prf);
244        }
245
246        return new DERSequence(v);
247    }
248}
249