diff options
author | clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2011-01-07 09:47:39 +0000 |
---|---|---|
committer | clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2011-01-07 09:47:39 +0000 |
commit | 3aae8daf74fdc71abc4352000efb5f6c6a96246a (patch) | |
tree | d9c92da34c8a5c669ee56edc511e5fd5363455bd /smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java | |
parent | 097733261f61b27b5eaa4aed3fe023c5b3ac5f11 (diff) | |
download | mocca-3aae8daf74fdc71abc4352000efb5f6c6a96246a.tar.gz mocca-3aae8daf74fdc71abc4352000efb5f6c6a96246a.tar.bz2 mocca-3aae8daf74fdc71abc4352000efb5f6c6a96246a.zip |
Retail CBC MAC
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@875 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java')
-rw-r--r-- | smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java new file mode 100644 index 00000000..79f7be62 --- /dev/null +++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java @@ -0,0 +1,186 @@ +package at.gv.egiz.smcc.activation; + +import iaik.utils.CryptoUtils; + +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class RetailCBCMac { + + /** + * Calculates a Retail CBC Mac from the given message. + * <p> + * The retail CBC Mac is calculated according to the following + * algorithm: + * <ul> + * <li> + * Pad the message to a multiple n of the CBC cipher block size with + * a leading one bit followed by as many zero bits as necessary. + * </li> + * <li> + * Create a CBC cipher key from the first <code>cbcCipherKeyLen</code> + * bytes of the <code>csk</code> key and use it to calculate a + * CBC Mac value from the first n-1 blocks of the padded message. + * For CBC Mac calculation initialize the CBC Cipher with an + * IV of all zero bytes. + * </li> + * <li> + * XOR the last block of the padded message with the CBC mac value + * and calculate the final retail MAC by encrypting the XOR result + * with the given Cipher algorithm in ECB mode using no padding. + * </li> + * </ul> + * + * @param msg the message + * @param cbcCipherAlg the name of the CBC Cipher algorithm to be used + * @param cipherAlg the name of the final Cipher algorithm to be used + * @param csk the secret key to be used + * @param cbcCipherKeyLen the length of the CBC cipher key to be used + * @param blockSize the block size of the CBC Cipher + * + * @return the retail CBC Mac value + * + * @throws NoSuchAlgorithmException if any of the requested Cipher algorithms + * is not available + * @throws NoSuchProviderException if the IAIK provider is not installed + * @throws InvalidKeyException if the key cannot be used with the Ciphers + * @throws GeneralSecurityException if the Cipher operation(s) fails + */ + static byte[] retailMac(byte[] msg, + String cbcCipherAlg, + String cipherAlg, + SecretKey csk, + int cbcCipherKeyLen, + int blockSize) + throws NoSuchAlgorithmException, + NoSuchProviderException, + InvalidKeyException, + GeneralSecurityException { + + if (msg == null) { + throw new NullPointerException("Message m must not be null!"); + } + if (csk == null) { + throw new NullPointerException("Key csk must not be null!"); + } + + // calculate key for CBC cipher + byte[] rawCsk = csk.getEncoded(); + int cskLen = rawCsk.length; + SecretKey cbcCipherKey; + if (cskLen == cbcCipherKeyLen) { + cbcCipherKey = csk; + } else if (cskLen < cbcCipherKeyLen) { + throw new InvalidKeyException("Key too short!"); + } else { + byte[] rawCbcCipherKey = new byte[blockSize]; + System.arraycopy(rawCsk, 0, rawCbcCipherKey, 0, blockSize); + cbcCipherKey = new SecretKeySpec(rawCbcCipherKey, cbcCipherAlg); + } + // if necessary pad message with zeros + byte[] paddedMsg = pad(msg, blockSize); + + // calculate CBC Mac for the first n-1 blocks + int n = paddedMsg.length; + int n_1 = n - blockSize; + byte[] cbcMac = cbcMac(paddedMsg, 0, n_1, cbcCipherKey, cbcCipherAlg, blockSize); + + // calculate retail mac + byte[] xor = new byte[blockSize]; + CryptoUtils.xorBlock(paddedMsg, n_1, cbcMac, 0, xor, 0, blockSize); + Cipher cipher = Cipher.getInstance(cipherAlg+"/ECB/NoPadding", "IAIK"); + cipher.init(Cipher.ENCRYPT_MODE, csk); + byte[] retailMac = cipher.doFinal(xor); + return retailMac; + } + + /** + * Calculates a simple CBC Mac from the given (already) padded message. + * + * @param paddedMsg the (zero) padded message + * @param off the start offset in the paddedMsg array + * @param len the number of bytes to be processed, starting at <code>off</code> + * @param key the Cipher key + * @param cipherAlg the name of the CBC Cipher algorithm to be used + * @param blockSize the block size of the CBC Cipher + * + * @return the CBC Mac value + * + * @throws NoSuchAlgorithmException if the requested Cipher algorithm + * is not available + * @throws NoSuchProviderException if the IAIK provider is not installed + * @throws InvalidKeyException if the key cannot be used with the Ciphers + * @throws GeneralSecurityException if the Cipher operation fails + */ + static byte[] cbcMac(byte[] paddedMsg, + int off, + int len, + SecretKey key, + String cipherAlg, + int blockSize) + throws NoSuchAlgorithmException, + NoSuchProviderException, + InvalidKeyException, + GeneralSecurityException { + + if (paddedMsg == null) { + throw new NullPointerException("Message must not be null!"); + } + if (key == null) { + throw new NullPointerException("Key csk must not be null!"); + } + + + Cipher cbcCipher = Cipher.getInstance(cipherAlg+"/CBC/NoPadding", "IAIK"); + byte[] iv = new byte[blockSize]; + cbcCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); + int finOff = off; + if (len > blockSize) { + finOff = len - blockSize; + cbcCipher.update(paddedMsg, 0, finOff); + } + byte[] mac = cbcCipher.doFinal(paddedMsg, finOff, blockSize); + return mac; + } + + /** + * Pads the given message to a multiple of the given blocksize with + * a leading one bit followed by as many zero bits as necessary + * + * @param msg the message to be padded + * @param blockSize the block size + * + * @return the padded message + */ + static byte[] pad(byte[] msg, int blockSize) { + int paddingLen; + byte[] paddedMsg; + + int msgLen = msg.length; + if (msgLen == 0) { + paddingLen = blockSize; + } else { + paddingLen = blockSize - msgLen % blockSize; + } + if (paddingLen > 0) { + paddedMsg = new byte[msgLen + paddingLen]; + System.arraycopy(msg, 0, paddedMsg, 0, msgLen); + paddedMsg[msgLen] = (byte)0x80; + } else { + paddedMsg = msg; + } + return paddedMsg; + } + + + + + +} |