2003/02/12 06:21:04
[org.ibex.core.git] / src / org / bouncycastle / crypto / encodings / PKCS1Encoding.java
1 package org.bouncycastle.crypto.encodings;
2
3 import java.math.BigInteger;
4 import java.security.SecureRandom;
5
6 import org.bouncycastle.crypto.CipherParameters;
7 import org.bouncycastle.crypto.AsymmetricBlockCipher;
8 import org.bouncycastle.crypto.InvalidCipherTextException;
9 import org.bouncycastle.crypto.params.ParametersWithRandom;
10 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
11
12 /**
13  * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
14  * depends on your application - see PKCS1 Version 2 for details.
15  */
16 public class PKCS1Encoding
17     implements AsymmetricBlockCipher
18 {
19     private static int      HEADER_LENGTH = 10;
20
21     private SecureRandom            random;
22     private AsymmetricBlockCipher   engine;
23     private boolean                 forEncryption;
24     private boolean                 forPrivateKey;
25
26     public PKCS1Encoding(
27         AsymmetricBlockCipher   cipher)
28     {
29         this.engine = cipher;
30     }   
31
32     public AsymmetricBlockCipher getUnderlyingCipher()
33     {
34         return engine;
35     }
36
37     public void init(
38         boolean             forEncryption,
39         CipherParameters    param)
40     {
41         AsymmetricKeyParameter  kParam;
42
43         if (param instanceof ParametersWithRandom)
44         {
45             ParametersWithRandom    rParam = (ParametersWithRandom)param;
46
47             this.random = rParam.getRandom();
48             kParam = (AsymmetricKeyParameter)rParam.getParameters();
49         }
50         else
51         {
52             this.random = new SecureRandom();
53             kParam = (AsymmetricKeyParameter)param;
54         }
55
56         engine.init(forEncryption, kParam);
57
58         this.forPrivateKey = kParam.isPrivate();
59         this.forEncryption = forEncryption;
60     }
61
62     public int getInputBlockSize()
63     {
64         int     baseBlockSize = engine.getInputBlockSize();
65
66         if (forEncryption)
67         {
68             return baseBlockSize - HEADER_LENGTH;
69         }
70         else
71         {
72             return baseBlockSize;
73         }
74     }
75
76     public int getOutputBlockSize()
77     {
78         int     baseBlockSize = engine.getOutputBlockSize();
79
80         if (forEncryption)
81         {
82             return baseBlockSize;
83         }
84         else
85         {
86             return baseBlockSize - HEADER_LENGTH;
87         }
88     }
89
90     public byte[] processBlock(
91         byte[]  in,
92         int     inOff,
93         int     inLen)
94         throws InvalidCipherTextException
95     {
96         if (forEncryption)
97         {
98             return encodeBlock(in, inOff, inLen);
99         }
100         else
101         {
102             return decodeBlock(in, inOff, inLen);
103         }
104     }
105
106     private byte[] encodeBlock(
107         byte[]  in,
108         int     inOff,
109         int     inLen)
110         throws InvalidCipherTextException
111     {
112         byte[]  block = new byte[engine.getInputBlockSize()];
113
114         if (forPrivateKey)
115         {
116             block[0] = 0x01;                        // type code 1
117
118             for (int i = 1; i != block.length - inLen - 1; i++)
119             {
120                 block[i] = (byte)0xFF;
121             }
122         }
123         else
124         {
125             random.nextBytes(block);                // random fill
126
127             block[0] = 0x02;                        // type code 2
128
129             //
130             // a zero byte marks the end of the padding, so all
131             // the pad bytes must be non-zero.
132             //
133             for (int i = 1; i != block.length - inLen - 1; i++)
134             {
135                 while (block[i] == 0)
136                 {
137                     block[i] = (byte)random.nextInt();
138                 }
139             }
140         }
141
142         block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
143         System.arraycopy(in, inOff, block, block.length - inLen, inLen);
144
145         return engine.processBlock(block, 0, block.length);
146     }
147
148     /**
149      * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
150      */
151     private byte[] decodeBlock(
152         byte[]  in,
153         int     inOff,
154         int     inLen)
155         throws InvalidCipherTextException
156     {
157         byte[]  block = engine.processBlock(in, inOff, inLen);
158
159         if (block.length < getOutputBlockSize())
160         {
161             throw new InvalidCipherTextException("block truncated");
162         }
163
164         if (block[0] != 1 && block[0] != 2)
165         {
166             throw new InvalidCipherTextException("unknown block type");
167         }
168
169         //
170         // find and extract the message block.
171         //
172         int start;
173
174         for (start = 1; start != block.length; start++)
175         {
176             if (block[start] == 0)
177             {
178                 break;
179             }
180         }
181
182         start++;           // data should start at the next byte
183
184         if (start >= block.length || start < HEADER_LENGTH)
185         {
186             throw new InvalidCipherTextException("no data in block");
187         }
188
189         byte[]  result = new byte[block.length - start];
190
191         System.arraycopy(block, start, result, 0, result.length);
192
193         return result;
194     }
195 }