Appending the CRC to the data and then encrypting the whole block before sending will severely compromise the error detection capabilities of the CRC. In fact, a single bit error during transmission can cause you to decrypt a completely garbage message and still have a valid CRC check with a non-negligible probability.
A CRC is designed to detect a small set of bit errors due to line noise. Every transmission line (ie: serial cable, RF link, etc.) suffers form the effects of noise. It's impossible to eliminate it. Instead, a designer is interested in knowing how often this noise will affect data bits being sent over the line. This is measured as a bit error rate (BER). Conceptually, you send a large number of bits over a link and count how many were correctly received vs how many were not. The ratio of the bad bits to the total is an estimate of the BER. For a practical link, this has to be pretty low, otherwise there will be a large number of message retries. Generally, the lower the BER, the more effective speed you have as there will be fewer errors to recover from.
A CRC is designed with a small BER in mind. Choosing a good primitive polynomial will gaurantee certain classes of bit errors will be 100% detected (ex: 1-bit, bursts up to size of polynomial, odd errors, etc.). There is a small upper limit of how many bit errors can be reliably detected. If you exceed this number, the CRC may fail and report a false positive.
Now a properly designed block cipher (ex: AES) has the property of good diffusion, meaning that a single bit change in the plaintext will cause the ciphertext to have about 50% of its bits changed. With respect to the impact of encrytion on data going through a link, encryption acts as a massive amplifier of the BER. A single bit error in the ciphertext, when decrypted, will lead to about 50% of the plaintext bits being wrong. This will drastically exceed the limits of the CRC to detect and the CRC will most likly fail to detect this situation.
Example :
- You format your data into 12 bytes.
- You calculate and append the CRC for a total of 16 bytes.
- You encrypt with AES and transmit the packet.
- Ambient noise causes a single bit flip to occur anywhere in the packet.
- Receiver decrypts packet
- Approximately 50% of data & crc bits are now wrong
- Receiver calculates CRC and it passes (because there are now too many bit errors to detect)
- You have garbage data, but a valid CRC!
The CRC must always be appended to the actual data being sent, otherwise you violate the entire assumption of how the CRC works.
Suggested solutions:
- Pad the packet to 16 bytes with some random data;
- Encrypt the packet
- Calculate the CRC and append to the packet
Note that this will now be a 20 byte packet (assuming 32-bit CRC)
If packet size is a design constraint, then the following solution can be used:
- Using a balanced Feistel Network on a 12 byte block and using AES as the mixing function, create a 12-byte block cipher.
- Encrypt the 12 byte data
- Compute the CRC of the encrypted data and append to the encrypted data for a 16 byte packet
- Send with confidence that transmission errors wll be detected :)
If you are going to implement the second suggestion (Feistel Network), I strongly suggest you verify your design carefully for correctness as rolling your own crypto is iherently insecure.
See here for some info on how to do this : Using AES for smaller blocks in a Feistel network