Source code: cryptix/openpgp/util/PGPMPI.java
1 /* $Id: PGPMPI.java,v 1.2 2005/03/13 17:47:08 woudt Exp $
2 *
3 * Copyright (C) 1999-2005 The Cryptix Foundation Limited.
4 * All rights reserved.
5 *
6 * Use, modification, copying and distribution of this software is subject
7 * the terms and conditions of the Cryptix General Licence. You should have
8 * received a copy of the Cryptix General License along with this library;
9 * if not, you can download a copy from http://www.cryptix.org/ .
10 */
11
12 package cryptix.openpgp.util;
13
14
15 import cryptix.openpgp.PGPDataFormatException;
16 import cryptix.openpgp.PGPFatalDataFormatException;
17
18 import java.io.DataInput;
19 import java.io.DataOutput;
20 import java.io.IOException;
21
22 import java.math.BigInteger;
23
24
25 /**
26 * Read and write BigIntegers in a PGP compatible format.
27 *
28 * <p>From <A HREF="http://www.ietf.org/rfc/rfc2440.txt">RFC 2440</A>:</p>
29 * <pre> 3.2. Multi-Precision Integers
30 *
31 * Multi-Precision Integers (also called MPIs) are unsigned integers
32 * used to hold large integers such as the ones used in cryptographic
33 * calculations.
34 *
35 * An MPI consists of two pieces: a two-octet scalar that is the length
36 * of the MPI in bits followed by a string of octets that contain the
37 * actual integer.
38 *
39 * These octets form a big-endian number; a big-endian number can be
40 * made into an MPI by prefixing it with the appropriate length.
41 *
42 * Examples:
43 *
44 * (all numbers are in hexadecimal)
45 *
46 * The string of octets [00 01 01] forms an MPI with the value 1. The
47 * string [00 09 01 FF] forms an MPI with the value of 511.
48 *
49 * Additional rules:
50 *
51 * The size of an MPI is ((MPI.length + 7) / 8) + 2 octets.
52 *
53 * The length field of an MPI describes the length starting from its
54 * most significant non-zero bit. Thus, the MPI [00 02 01] is not formed
55 * correctly. It should be [00 01 01].</pre>
56 *
57 * @author Edwin Woudt (edwin@cryptix.org)
58 * @author Jeroen C. van Gelderen (gelderen@cryptix.org)
59 * @version $Revision: 1.2 $
60 */
61
62 public class PGPMPI {
63
64 private PGPMPI() {} //static methods only
65
66
67 /**
68 * Read a BigInteger from a stream
69 *
70 * @param in the DataInput that contains a BigInteger in PGP format
71 * @return the BigInteger read
72 * @throws IOException if an I/O error occurs
73 * @throws EOFException if the end of the stream is reached before a
74 * complete BigInteger could be read
75 */
76 public static BigInteger decode(DataInput in) throws IOException {
77
78 int len = in.readUnsignedShort();
79 byte[] buf = new byte[(len + 7) / 8];
80 in.readFully(buf);
81 return new BigInteger(1,buf);
82
83 }
84
85
86 /**
87 * Write a BigInteger to a stream
88 *
89 * @param out the DataOutput to write the BigInteger to
90 * @param x the BigInteger to be written
91 * @throws IOException if an I/O error occurs
92 */
93 public static void encode(DataOutput out, BigInteger x)
94 throws IOException
95 {
96
97 out.writeShort(x.bitLength());
98 byte[] bytes = x.toByteArray();
99 if (bytes[0] == 0) {
100 out.write(bytes,1,bytes.length-1); // remove sign byte
101 } else {
102 out.write(bytes);
103 }
104
105 }
106
107
108 /**
109 * Fit (stretch or shrink) the given positive BigInteger into a
110 * byte[] of resultByteLen bytes without losing precision.
111 *
112 * @trhows IllegalArgumentException
113 * If x negative, or won't fit in requested number of bytes.
114 */
115 public static byte[] toFixedLenByteArray(BigInteger x, int resultByteLen) {
116
117 if (x.signum() != 1)
118 throw new IllegalArgumentException("BigInteger not positive.");
119
120 byte[] x_bytes = x.toByteArray();
121 int x_len = x_bytes.length;
122
123 if (x_len <= 0)
124 throw new IllegalArgumentException("BigInteger too small.");
125
126 /*
127 * The BigInteger contract specifies that we now have at most one
128 * superfluous leading zero byte:
129 */
130 int x_off = (x_bytes[0] == 0) ? 1 : 0;
131 x_len -= x_off;
132
133 /*
134 * Check whether the BigInteger will fit in the requested byte length.
135 */
136 if ( x_len > resultByteLen)
137 throw new IllegalArgumentException("BigInteger too large.");
138
139 /*
140 * Now stretch or shrink the encoding to fit in resByteLen bytes.
141 */
142 byte[] res_bytes = new byte[resultByteLen];
143 int res_off = resultByteLen-x_len;
144 System.arraycopy(x_bytes, x_off, res_bytes, res_off, x_len);
145 return res_bytes;
146 }
147 }