| package org.bouncycastle.crypto.engines; |
| |
| import java.security.SecureRandom; |
| |
| import org.bouncycastle.crypto.BlockCipher; |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.CryptoServicesRegistrar; |
| import org.bouncycastle.crypto.InvalidCipherTextException; |
| import org.bouncycastle.crypto.Wrapper; |
| import org.bouncycastle.crypto.modes.CBCBlockCipher; |
| import org.bouncycastle.crypto.params.ParametersWithIV; |
| import org.bouncycastle.crypto.params.ParametersWithRandom; |
| import org.bouncycastle.util.Arrays; |
| |
| /** |
| * an implementation of the RFC 3211 Key Wrap |
| * Specification. |
| */ |
| public class RFC3211WrapEngine |
| implements Wrapper |
| { |
| private CBCBlockCipher engine; |
| private ParametersWithIV param; |
| private boolean forWrapping; |
| private SecureRandom rand; |
| |
| public RFC3211WrapEngine(BlockCipher engine) |
| { |
| this.engine = new CBCBlockCipher(engine); |
| } |
| |
| public void init( |
| boolean forWrapping, |
| CipherParameters param) |
| { |
| this.forWrapping = forWrapping; |
| |
| if (param instanceof ParametersWithRandom) |
| { |
| ParametersWithRandom p = (ParametersWithRandom)param; |
| |
| rand = p.getRandom(); |
| |
| if (!(p.getParameters() instanceof ParametersWithIV)) |
| { |
| throw new IllegalArgumentException("RFC3211Wrap requires an IV"); |
| } |
| |
| this.param = (ParametersWithIV)p.getParameters(); |
| } |
| else |
| { |
| if (forWrapping) |
| { |
| rand = CryptoServicesRegistrar.getSecureRandom(); |
| } |
| |
| if (!(param instanceof ParametersWithIV)) |
| { |
| throw new IllegalArgumentException("RFC3211Wrap requires an IV"); |
| } |
| |
| this.param = (ParametersWithIV)param; |
| } |
| } |
| |
| public String getAlgorithmName() |
| { |
| return engine.getUnderlyingCipher().getAlgorithmName() + "/RFC3211Wrap"; |
| } |
| |
| public byte[] wrap( |
| byte[] in, |
| int inOff, |
| int inLen) |
| { |
| if (!forWrapping) |
| { |
| throw new IllegalStateException("not set for wrapping"); |
| } |
| |
| if (inLen > 255 || inLen < 0) |
| { |
| throw new IllegalArgumentException("input must be from 0 to 255 bytes"); |
| } |
| |
| engine.init(true, param); |
| |
| int blockSize = engine.getBlockSize(); |
| byte[] cekBlock; |
| |
| if (inLen + 4 < blockSize * 2) |
| { |
| cekBlock = new byte[blockSize * 2]; |
| } |
| else |
| { |
| cekBlock = new byte[(inLen + 4) % blockSize == 0 ? inLen + 4 : ((inLen + 4) / blockSize + 1) * blockSize]; |
| } |
| |
| cekBlock[0] = (byte)inLen; |
| |
| System.arraycopy(in, inOff, cekBlock, 4, inLen); |
| |
| byte[] pad = new byte[cekBlock.length - (inLen + 4)]; |
| |
| rand.nextBytes(pad); |
| System.arraycopy(pad, 0, cekBlock, inLen + 4, pad.length); |
| |
| cekBlock[1] = (byte)~cekBlock[4]; |
| cekBlock[2] = (byte)~cekBlock[4 + 1]; |
| cekBlock[3] = (byte)~cekBlock[4 + 2]; |
| |
| for (int i = 0; i < cekBlock.length; i += blockSize) |
| { |
| engine.processBlock(cekBlock, i, cekBlock, i); |
| } |
| |
| for (int i = 0; i < cekBlock.length; i += blockSize) |
| { |
| engine.processBlock(cekBlock, i, cekBlock, i); |
| } |
| |
| return cekBlock; |
| } |
| |
| public byte[] unwrap( |
| byte[] in, |
| int inOff, |
| int inLen) |
| throws InvalidCipherTextException |
| { |
| if (forWrapping) |
| { |
| throw new IllegalStateException("not set for unwrapping"); |
| } |
| |
| int blockSize = engine.getBlockSize(); |
| |
| if (inLen < 2 * blockSize) |
| { |
| throw new InvalidCipherTextException("input too short"); |
| } |
| |
| byte[] cekBlock = new byte[inLen]; |
| byte[] iv = new byte[blockSize]; |
| |
| System.arraycopy(in, inOff, cekBlock, 0, inLen); |
| System.arraycopy(in, inOff, iv, 0, iv.length); |
| |
| engine.init(false, new ParametersWithIV(param.getParameters(), iv)); |
| |
| for (int i = blockSize; i < cekBlock.length; i += blockSize) |
| { |
| engine.processBlock(cekBlock, i, cekBlock, i); |
| } |
| |
| System.arraycopy(cekBlock, cekBlock.length - iv.length, iv, 0, iv.length); |
| |
| engine.init(false, new ParametersWithIV(param.getParameters(), iv)); |
| |
| engine.processBlock(cekBlock, 0, cekBlock, 0); |
| |
| engine.init(false, param); |
| |
| for (int i = 0; i < cekBlock.length; i += blockSize) |
| { |
| engine.processBlock(cekBlock, i, cekBlock, i); |
| } |
| |
| boolean invalidLength = ((cekBlock[0] & 0xff) > cekBlock.length - 4); |
| |
| byte[] key; |
| if (invalidLength) |
| { |
| key = new byte[cekBlock.length - 4]; |
| } |
| else |
| { |
| key = new byte[cekBlock[0] & 0xff]; |
| } |
| |
| System.arraycopy(cekBlock, 4, key, 0, key.length); |
| |
| // Note: Using constant time comparison |
| int nonEqual = 0; |
| for (int i = 0; i != 3; i++) |
| { |
| byte check = (byte)~cekBlock[1 + i]; |
| nonEqual |= (check ^ cekBlock[4 + i]); |
| } |
| |
| Arrays.clear(cekBlock); |
| |
| if (nonEqual != 0 | invalidLength) |
| { |
| throw new InvalidCipherTextException("wrapped key corrupted"); |
| } |
| |
| return key; |
| } |
| } |