attiny 412 serial number

Your strategy is fine, but your code has a bug, and is more complicated than it needs to be.
The usual problem with using the built-in serial number is that it isn't the same size (bytes) as the "unique IDs" that you might need (ie ethernet addresses, which are 6 bytes.)

The bug:

  msg[21]=0x00;     //zero terminate string for tx library

arrays are 0-based, so the 21st element of msg is msg[20]; Right now, you're overwriting data one past the end of the array. Who knows what that'll do...
Simpler:
The chip serial number is is normal memory, and is sequential, so you can eliminate the intermediate array and do something like:

#define SERNUM_SIZE 10  // Number of bytes in the on-chip serial number
byte msg[SERNUM_SIZE*2 + 1];   //array for ASCII HEX representation of serial number
const byte *serno = &SIGROW.SERNUM0;  // pointer to the sernums
  for (byte i=0;i<SERNUM_SIZE;++i) //convert 10 byte serial number to 20 byte ASCII HEX serial number
  {
    msg[2*i]=table[(serno[i]>>4) &0x0F];   //convert high nibl. Shift right 4 & mask & lookup
    msg[2*i+1]=table[serno[i] &0x0F];       //low nibl. Mask & lookup
  }
  msg[2*SERNUM_SIZE] = 0;