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