I think you want to unpack
with C
(8-bit unsigned) rather than B
(bit string) since the xor operator ^
operates on numbers rather than strings:
'AA'.unpack('C*').zip('BB'.unpack('C*')).map { |a,b| a^b }.pack('C*')
# => "\x03\x03"
3 is what one would expect from xoring 65 ('A'
) with 66 ('B'
).