1 /* EMSA_PSS.java --
2    Copyright (C) 2001, 2002, 2003, 2006, 2010 Free Software Foundation, Inc.
3 
4 This file is a part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version.  */
37 
38 
39 package gnu.java.security.sig.rsa;
40 
41 import gnu.java.security.Configuration;
42 import gnu.java.security.hash.HashFactory;
43 import gnu.java.security.hash.IMessageDigest;
44 import gnu.java.security.util.Util;
45 
46 import java.util.Arrays;
47 import java.util.logging.Logger;
48 
49 /**
50  * An implementation of the EMSA-PSS encoding/decoding scheme.
51  * <p>
52  * EMSA-PSS coincides with EMSA4 in IEEE P1363a D5 except that EMSA-PSS acts on
53  * octet strings and not on bit strings. In particular, the bit lengths of the
54  * hash and the salt must be multiples of 8 in EMSA-PSS. Moreover, EMSA4 outputs
55  * an integer of a desired bit length rather than an octet string.
56  * <p>
57  * EMSA-PSS is parameterized by the choice of hash function Hash and mask
58  * generation function MGF. In this submission, MGF is based on a Hash
59  * definition that coincides with the corresponding definitions in IEEE Std
60  * 1363-2000, PKCS #1 v2.0, and the draft ANSI X9.44. In PKCS #1 v2.0 and the
61  * draft ANSI X9.44, the recommended hash function is SHA-1, while IEEE Std
62  * 1363-2000 recommends SHA-1 and RIPEMD-160.
63  * <p>
64  * References:
65  * <ol>
66  * <li><a
67  * href="http://www.cosic.esat.kuleuven.ac.be/nessie/workshop/submissions/rsa-pss.zip">
68  * RSA-PSS Signature Scheme with Appendix, part B.</a><br>
69  * Primitive specification and supporting documentation.<br>
70  * Jakob Jonsson and Burt Kaliski.</li>
71  * </ol>
72  */
73 public class EMSA_PSS
74     implements Cloneable
75 {
76   private static final Logger log = Configuration.DEBUG ?
77                         Logger.getLogger(EMSA_PSS.class.getName()) : null;
78 
79   /** The underlying hash function to use with this instance. */
80   private IMessageDigest hash;
81 
82   /** The output size of the hash function in octets. */
83   private int hLen;
84 
85   /**
86    * Trivial private constructor to enforce use through Factory method.
87    *
88    * @param hash the message digest instance to use with this scheme instance.
89    */
EMSA_PSS(IMessageDigest hash)90   private EMSA_PSS(IMessageDigest hash)
91   {
92     super();
93 
94     this.hash = hash;
95     hLen = hash.hashSize();
96   }
97 
98   /**
99    * Returns an instance of this object given a designated name of a hash
100    * function.
101    *
102    * @param mdName the canonical name of a hash function.
103    * @return an instance of this object configured for use with the designated
104    *         options.
105    */
getInstance(String mdName)106   public static EMSA_PSS getInstance(String mdName)
107   {
108     IMessageDigest hash = HashFactory.getInstance(mdName);
109     return new EMSA_PSS(hash);
110   }
111 
clone()112   public Object clone()
113   {
114     return getInstance(hash.name());
115   }
116 
117   /**
118    * The encoding operation EMSA-PSS-Encode computes the hash of a message
119    * <code>M</code> using a hash function and maps the result to an encoded
120    * message <code>EM</code> of a specified length using a mask generation
121    * function.
122    *
123    * @param mHash the byte sequence resulting from applying the message digest
124    *          algorithm Hash to the message <i>M</i>.
125    * @param emBits the maximal bit length of the integer OS2IP(EM), at least
126    *          <code>8.hLen + 8.sLen + 9</code>.
127    * @param salt the salt to use when encoding the output.
128    * @return the encoded message <code>EM</code>, an octet string of length
129    *         <code>emLen = CEILING(emBits / 8)</code>.
130    * @exception IllegalArgumentException if an exception occurs.
131    */
encode(byte[] mHash, int emBits, byte[] salt)132   public byte[] encode(byte[] mHash, int emBits, byte[] salt)
133   {
134     int sLen = salt.length;
135     // 1. If the length of M is greater than the input limitation for the hash
136     // function (2**61 - 1 octets for SHA-1) then output "message too long"
137     // and stop.
138     // 2. Let mHash = Hash(M), an octet string of length hLen.
139     if (hLen != mHash.length)
140       throw new IllegalArgumentException("wrong hash");
141     // 3. If emBits < 8.hLen + 8.sLen + 9, output 'encoding error' and stop.
142     if (emBits < (8 * hLen + 8 * sLen + 9))
143       throw new IllegalArgumentException("encoding error");
144     int emLen = (emBits + 7) / 8;
145     // 4. Generate a random octet string salt of length sLen; if sLen = 0,
146     // then salt is the empty string.
147     // ...passed as argument to accomodate JCE
148     // 5. Let M0 = 00 00 00 00 00 00 00 00 || mHash || salt;
149     // M0 is an octet string of length 8 + hLen + sLen with eight initial zero
150     // octets.
151     // 6. Let H = Hash(M0), an octet string of length hLen.
152     byte[] H;
153     int i;
154     synchronized (hash)
155       {
156         for (i = 0; i < 8; i++)
157           hash.update((byte) 0x00);
158 
159         hash.update(mHash, 0, hLen);
160         hash.update(salt, 0, sLen);
161         H = hash.digest();
162       }
163     // 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
164     // zero octets. The length of PS may be 0.
165     // 8. Let DB = PS || 01 || salt.
166     byte[] DB = new byte[emLen - sLen - hLen - 2 + 1 + sLen];
167     DB[emLen - sLen - hLen - 2] = 0x01;
168     System.arraycopy(salt, 0, DB, emLen - sLen - hLen - 1, sLen);
169     // 9. Let dbMask = MGF(H, emLen - hLen - 1).
170     byte[] dbMask = MGF(H, emLen - hLen - 1);
171     if (Configuration.DEBUG)
172       {
173         log.fine("dbMask (encode): " + Util.toString(dbMask));
174         log.fine("DB (encode): " + Util.toString(DB));
175       }
176     // 10. Let maskedDB = DB XOR dbMask.
177     for (i = 0; i < DB.length; i++)
178       DB[i] = (byte)(DB[i] ^ dbMask[i]);
179     // 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
180     // maskedDB to zero.
181     DB[0] &= (0xFF >>> (8 * emLen - emBits));
182     // 12. Let EM = maskedDB || H || bc, where bc is the single octet with
183     // hexadecimal value 0xBC.
184     byte[] result = new byte[emLen];
185     System.arraycopy(DB, 0, result, 0, emLen - hLen - 1);
186     System.arraycopy(H, 0, result, emLen - hLen - 1, hLen);
187     result[emLen - 1] = (byte) 0xBC;
188     // 13. Output EM.
189     return result;
190   }
191 
192   /**
193    * The decoding operation EMSA-PSS-Decode recovers the message hash from an
194    * encoded message <code>EM</code> and compares it to the hash of
195    * <code>M</code>.
196    *
197    * @param mHash the byte sequence resulting from applying the message digest
198    *          algorithm Hash to the message <i>M</i>.
199    * @param EM the <i>encoded message</i>, an octet string of length
200    *          <code>emLen = CEILING(emBits/8).
201    * @param emBits the maximal bit length of the integer OS2IP(EM), at least
202    * <code>8.hLen + 8.sLen + 9</code>.
203    * @param sLen the length, in octets, of the expected salt.
204    * @return <code>true</code> if the result of the verification was
205    * <i>consistent</i> with the expected reseult; and <code>false</code> if the
206    * result was <i>inconsistent</i>.
207    * @exception IllegalArgumentException if an exception occurs.
208    */
decode(byte[] mHash, byte[] EM, int emBits, int sLen)209   public boolean decode(byte[] mHash, byte[] EM, int emBits, int sLen)
210   {
211     if (Configuration.DEBUG)
212       {
213         log.fine("mHash: " + Util.toString(mHash));
214         log.fine("EM: " + Util.toString(EM));
215         log.fine("emBits: " + String.valueOf(emBits));
216         log.fine("sLen: " + String.valueOf(sLen));
217       }
218     if (sLen < 0)
219       throw new IllegalArgumentException("sLen");
220     // 1. If the length of M is greater than the input limitation for the hash
221     // function (2**61 ? 1 octets for SHA-1) then output 'inconsistent' and
222     // stop.
223     // 2. Let mHash = Hash(M), an octet string of length hLen.
224     if (hLen != mHash.length)
225       {
226         if (Configuration.DEBUG)
227           log.fine("hLen != mHash.length; hLen: " + String.valueOf(hLen));
228         throw new IllegalArgumentException("wrong hash");
229       }
230     // 3. If emBits < 8.hLen + 8.sLen + 9, output 'decoding error' and stop.
231     if (emBits < (8 * hLen + 8 * sLen + 9))
232       {
233         if (Configuration.DEBUG)
234           log.fine("emBits < (8hLen + 8sLen + 9); sLen: "
235                    + String.valueOf(sLen));
236         throw new IllegalArgumentException("decoding error");
237       }
238     int emLen = (emBits + 7) / 8;
239     // 4. If the rightmost octet of EM does not have hexadecimal value bc,
240     // output 'inconsistent' and stop.
241     if ((EM[EM.length - 1] & 0xFF) != 0xBC)
242       {
243         if (Configuration.DEBUG)
244           log.fine("EM does not end with 0xBC");
245         return false;
246       }
247     // 5. Let maskedDB be the leftmost emLen ? hLen ? 1 octets of EM, and let
248     // H be the next hLen octets.
249     // 6. If the leftmost 8.emLen ? emBits bits of the leftmost octet in
250     // maskedDB are not all equal to zero, output 'inconsistent' and stop.
251     if ((EM[0] & (0xFF << (8 - (8 * emLen - emBits)))) != 0)
252       {
253         if (Configuration.DEBUG)
254           log.fine("Leftmost 8emLen - emBits bits of EM are not 0s");
255         return false;
256       }
257     byte[] DB = new byte[emLen - hLen - 1];
258     byte[] H = new byte[hLen];
259     System.arraycopy(EM, 0, DB, 0, emLen - hLen - 1);
260     System.arraycopy(EM, emLen - hLen - 1, H, 0, hLen);
261     // 7. Let dbMask = MGF(H, emLen ? hLen ? 1).
262     byte[] dbMask = MGF(H, emLen - hLen - 1);
263     // 8. Let DB = maskedDB XOR dbMask.
264     int i;
265     for (i = 0; i < DB.length; i++)
266       DB[i] = (byte)(DB[i] ^ dbMask[i]);
267     // 9. Set the leftmost 8.emLen ? emBits bits of DB to zero.
268     DB[0] &= (0xFF >>> (8 * emLen - emBits));
269     if (Configuration.DEBUG)
270       {
271         log.fine("dbMask (decode): " + Util.toString(dbMask));
272         log.fine("DB (decode): " + Util.toString(DB));
273       }
274     // 10. If the emLen -hLen -sLen -2 leftmost octets of DB are not zero or
275     // if the octet at position emLen -hLen -sLen -1 is not equal to 0x01,
276     // output 'inconsistent' and stop.
277     // IMPORTANT (rsn): this is an error in the specs, the index of the 0x01
278     // byte should be emLen -hLen -sLen -2 and not -1! authors have been advised
279     for (i = 0; i < (emLen - hLen - sLen - 2); i++)
280       {
281         if (DB[i] != 0)
282           {
283             if (Configuration.DEBUG)
284               log.fine("DB[" + String.valueOf(i) + "] != 0x00");
285             return false;
286           }
287       }
288     if (DB[i] != 0x01)
289       { // i == emLen -hLen -sLen -2
290         if (Configuration.DEBUG)
291           log.fine("DB's byte at position (emLen -hLen -sLen -2); i.e. "
292                    + String.valueOf(i) + " is not 0x01");
293         return false;
294       }
295     // 11. Let salt be the last sLen octets of DB.
296     byte[] salt = new byte[sLen];
297     System.arraycopy(DB, DB.length - sLen, salt, 0, sLen);
298     // 12. Let M0 = 00 00 00 00 00 00 00 00 || mHash || salt;
299     // M0 is an octet string of length 8 + hLen + sLen with eight initial
300     // zero octets.
301     // 13. Let H0 = Hash(M0), an octet string of length hLen.
302     byte[] H0;
303     synchronized (hash)
304       {
305         for (i = 0; i < 8; i++)
306           hash.update((byte) 0x00);
307 
308         hash.update(mHash, 0, hLen);
309         hash.update(salt, 0, sLen);
310         H0 = hash.digest();
311       }
312     // 14. If H = H0, output 'consistent.' Otherwise, output 'inconsistent.'
313     return Arrays.equals(H, H0);
314   }
315 
316   /**
317    * A mask generation function takes an octet string of variable length and a
318    * desired output length as input, and outputs an octet string of the desired
319    * length. There may be restrictions on the length of the input and output
320    * octet strings, but such bounds are generally very large. Mask generation
321    * functions are deterministic; the octet string output is completely
322    * determined by the input octet string. The output of a mask generation
323    * function should be pseudorandom, that is, it should be infeasible to
324    * predict, given one part of the output but not the input, another part of
325    * the output. The provable security of RSA-PSS relies on the random nature of
326    * the output of the mask generation function, which in turn relies on the
327    * random nature of the underlying hash function.
328    *
329    * @param Z a seed.
330    * @param l the desired output length in octets.
331    * @return the mask.
332    * @exception IllegalArgumentException if the desired output length is too
333    *              long.
334    */
MGF(byte[] Z, int l)335   private byte[] MGF(byte[] Z, int l)
336   {
337     // 1. If l > (2**32).hLen, output 'mask too long' and stop.
338     if (l < 1 || (l & 0xFFFFFFFFL) > ((hLen & 0xFFFFFFFFL) << 32L))
339       throw new IllegalArgumentException("mask too long");
340     // 2. Let T be the empty octet string.
341     byte[] result = new byte[l];
342     // 3. For i = 0 to CEILING(l/hLen) ? 1, do
343     int limit = ((l + hLen - 1) / hLen) - 1;
344     IMessageDigest hashZ = null;
345     hashZ = (IMessageDigest) hash.clone();
346     hashZ.digest();
347     hashZ.update(Z, 0, Z.length);
348     IMessageDigest hashZC = null;
349     byte[] t;
350     int sofar = 0;
351     int length;
352     for (int i = 0; i < limit; i++)
353       {
354         // 3.1 Convert i to an octet string C of length 4 with the primitive
355         // I2OSP: C = I2OSP(i, 4).
356         // 3.2 Concatenate the hash of the seed Z and C to the octet string T:
357         // T = T || Hash(Z || C)
358         hashZC = (IMessageDigest) hashZ.clone();
359         hashZC.update((byte)(i >>> 24));
360         hashZC.update((byte)(i >>> 16));
361         hashZC.update((byte)(i >>> 8));
362         hashZC.update((byte) i);
363         t = hashZC.digest();
364         length = l - sofar;
365         length = (length > hLen ? hLen : length);
366         System.arraycopy(t, 0, result, sofar, length);
367         sofar += length;
368       }
369     // 4. Output the leading l octets of T as the octet string mask.
370     return result;
371   }
372 }
373