1 package org.bouncycastle.crypto.engines;
2 
3 import java.util.ArrayList;
4 
5 import org.bouncycastle.crypto.CipherParameters;
6 import org.bouncycastle.crypto.DataLengthException;
7 import org.bouncycastle.crypto.InvalidCipherTextException;
8 import org.bouncycastle.crypto.Wrapper;
9 import org.bouncycastle.crypto.params.KeyParameter;
10 import org.bouncycastle.crypto.params.ParametersWithRandom;
11 import org.bouncycastle.util.Arrays;
12 
13 /**
14  * Implementation of DSTU7624 KEY WRAP mode
15  */
16 public class DSTU7624WrapEngine
17     implements Wrapper
18 {
19 
20     private static final int BYTES_IN_INTEGER = 4;
21 
22     private boolean forWrapping;
23     private DSTU7624Engine engine;
24 
25     private byte[] B, intArray;
26     private byte[] checkSumArray, zeroArray;
27     private ArrayList<byte[]> Btemp;
28 
29 
DSTU7624WrapEngine(int blockBitLength)30     public DSTU7624WrapEngine(int blockBitLength)
31     {
32 
33         this.engine = new DSTU7624Engine(blockBitLength);
34         this.B = new byte[engine.getBlockSize() / 2];
35         this.checkSumArray = new byte[engine.getBlockSize()];
36         this.zeroArray = new byte[engine.getBlockSize()];
37         this.Btemp = new ArrayList<byte[]>();
38         this.intArray = new byte[BYTES_IN_INTEGER];
39 
40     }
41 
init(boolean forWrapping, CipherParameters param)42     public void init(boolean forWrapping, CipherParameters param)
43     {
44         if (param instanceof ParametersWithRandom)
45         {
46             param = ((ParametersWithRandom)param).getParameters();
47         }
48 
49         this.forWrapping = forWrapping;
50         if (param instanceof KeyParameter)
51         {
52             engine.init(forWrapping, param);
53         }
54         else
55         {
56             throw new IllegalArgumentException("invalid parameters passed to DSTU7624WrapEngine");
57         }
58 
59     }
60 
getAlgorithmName()61     public String getAlgorithmName()
62     {
63         return "DSTU7624WrapEngine";
64     }
65 
wrap(byte[] in, int inOff, int inLen)66     public byte[] wrap(byte[] in, int inOff, int inLen)
67     {
68         if (!forWrapping)
69         {
70             throw new IllegalStateException("not set for wrapping");
71         }
72 
73         if ((inLen % engine.getBlockSize()) != 0)
74         {
75             //Partial blocks not supported
76             throw new DataLengthException("wrap data must be a multiple of " + engine.getBlockSize() + " bytes");
77         }
78 
79         if (inOff + inLen > in.length)
80         {
81             throw new DataLengthException("input buffer too short");
82         }
83 
84         int n = 2 * (1 + inLen / engine.getBlockSize()); /* Defined in DSTU7624 standard */
85         int V = (n - 1) * 6; /* Defined in DSTU7624 standard */
86 
87 
88         byte[] wrappedBuffer = new byte[inLen + engine.getBlockSize()];
89         System.arraycopy(in, inOff, wrappedBuffer, 0, inLen);
90 
91         System.arraycopy(wrappedBuffer, 0, B, 0, engine.getBlockSize() / 2);
92 
93         Btemp.clear();
94 
95         int bHalfBlocksLen = wrappedBuffer.length - engine.getBlockSize() / 2;
96         int bufOff = engine.getBlockSize() / 2;
97         while (bHalfBlocksLen != 0)
98         {
99             byte[] temp = new byte[engine.getBlockSize() / 2];
100             System.arraycopy(wrappedBuffer, bufOff, temp, 0, engine.getBlockSize() / 2);
101 
102             Btemp.add(temp);
103 
104             bHalfBlocksLen -= engine.getBlockSize() / 2;
105             bufOff += engine.getBlockSize() / 2;
106         }
107 
108         for (int j = 0; j < V; j++)
109         {
110             System.arraycopy(B, 0, wrappedBuffer, 0, engine.getBlockSize() / 2);
111             System.arraycopy(Btemp.get(0), 0, wrappedBuffer, engine.getBlockSize() / 2, engine.getBlockSize() / 2);
112 
113             engine.processBlock(wrappedBuffer, 0, wrappedBuffer, 0);
114 
115             intToBytes(j + 1, intArray, 0);
116             for (int byteNum = 0; byteNum < BYTES_IN_INTEGER; byteNum++)
117             {
118                 wrappedBuffer[byteNum + engine.getBlockSize() / 2] ^= intArray[byteNum];
119             }
120 
121             System.arraycopy(wrappedBuffer, engine.getBlockSize() / 2, B, 0, engine.getBlockSize() / 2);
122 
123             for (int i = 2; i < n; i++)
124             {
125                 System.arraycopy(Btemp.get(i - 1), 0, Btemp.get(i - 2), 0, engine.getBlockSize() / 2);
126             }
127 
128             System.arraycopy(wrappedBuffer, 0, Btemp.get(n - 2), 0, engine.getBlockSize() / 2);
129         }
130 
131 
132         System.arraycopy(B, 0, wrappedBuffer, 0, engine.getBlockSize() / 2);
133         bufOff = engine.getBlockSize() / 2;
134 
135         for (int i = 0; i < n - 1; i++)
136         {
137             System.arraycopy(Btemp.get(i), 0, wrappedBuffer, bufOff, engine.getBlockSize() / 2);
138             bufOff += engine.getBlockSize() / 2;
139         }
140 
141         return wrappedBuffer;
142 
143     }
144 
unwrap(byte[] in, int inOff, int inLen)145     public byte[] unwrap(byte[] in, int inOff, int inLen)
146         throws InvalidCipherTextException
147     {
148         if (forWrapping)
149         {
150             throw new IllegalStateException("not set for unwrapping");
151         }
152 
153         if ((inLen % engine.getBlockSize()) != 0)
154         {
155             //Partial blocks not supported
156             throw new DataLengthException("unwrap data must be a multiple of " + engine.getBlockSize() + " bytes");
157         }
158 
159         int n = 2 * inLen / engine.getBlockSize();
160 
161         int V = (n - 1) * 6;
162 
163         byte[] buffer = new byte[inLen];
164         System.arraycopy(in, inOff, buffer, 0, inLen);
165 
166         byte[] B = new byte[engine.getBlockSize() / 2];
167         System.arraycopy(buffer, 0, B, 0, engine.getBlockSize() / 2);
168 
169         Btemp.clear();
170 
171         int bHalfBlocksLen = buffer.length - engine.getBlockSize() / 2;
172         int bufOff = engine.getBlockSize() / 2;
173         while (bHalfBlocksLen != 0)
174         {
175             byte[] temp = new byte[engine.getBlockSize() / 2];
176             System.arraycopy(buffer, bufOff, temp, 0, engine.getBlockSize() / 2);
177 
178             Btemp.add(temp);
179 
180             bHalfBlocksLen -= engine.getBlockSize() / 2;
181             bufOff += engine.getBlockSize() / 2;
182         }
183 
184         for (int j = 0; j < V; j++)
185         {
186             System.arraycopy(Btemp.get(n - 2), 0, buffer, 0, engine.getBlockSize() / 2);
187             System.arraycopy(B, 0, buffer, engine.getBlockSize() / 2, engine.getBlockSize() / 2);
188             intToBytes(V - j, intArray, 0);
189             for (int byteNum = 0; byteNum < BYTES_IN_INTEGER; byteNum++)
190             {
191                 buffer[byteNum + engine.getBlockSize() / 2] ^= intArray[byteNum];
192             }
193 
194             engine.processBlock(buffer, 0, buffer, 0);
195 
196             System.arraycopy(buffer, 0, B, 0, engine.getBlockSize() / 2);
197 
198             for (int i = 2; i < n; i++)
199             {
200                 System.arraycopy(Btemp.get(n - i - 1), 0, Btemp.get(n - i), 0, engine.getBlockSize() / 2);
201             }
202 
203             System.arraycopy(buffer, engine.getBlockSize() / 2, Btemp.get(0), 0, engine.getBlockSize() / 2);
204         }
205 
206         System.arraycopy(B, 0, buffer, 0, engine.getBlockSize() / 2);
207         bufOff = engine.getBlockSize() / 2;
208 
209         for (int i = 0; i < n - 1; i++)
210         {
211             System.arraycopy(Btemp.get(i), 0, buffer, bufOff, engine.getBlockSize() / 2);
212             bufOff += engine.getBlockSize() / 2;
213         }
214 
215         System.arraycopy(buffer, buffer.length - engine.getBlockSize(), checkSumArray, 0, engine.getBlockSize());
216 
217         byte[] wrappedBuffer = new byte[buffer.length - engine.getBlockSize()];
218         if (!Arrays.areEqual(checkSumArray, zeroArray))
219         {
220             throw new InvalidCipherTextException("checksum failed");
221         }
222         else
223         {
224             System.arraycopy(buffer, 0, wrappedBuffer, 0, buffer.length - engine.getBlockSize());
225         }
226 
227 
228         return wrappedBuffer;
229     }
230 
231 
intToBytes(int number, byte[] outBytes, int outOff)232     private void intToBytes(int number, byte[] outBytes, int outOff)
233     {
234         outBytes[outOff + 3] = (byte)(number >> 24);
235         outBytes[outOff + 2] = (byte)(number >> 16);
236         outBytes[outOff + 1] = (byte)(number >> 8);
237         outBytes[outOff] = (byte)number;
238     }
239 }
240