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