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