With doubling in hand you can multiply any two field elements the way you multiply by hand in binary: for each set bit of one operand, add in a doubled copy of the other. Today you build full GF(256) multiplication from the doubling step.
Multiply two field elements by adding shifted copies, reducing with the doubling step.
To multiply a by b, think of b in binary: b = b0*1 + b1*2 + b2*4 + .... Then a*b is a times each of those powers of two, all added together. You already have doubling, so you can produce a*1, a*2, a*4, ... by repeatedly calling xtime, and “added together” means XOR. Walk the bits of b from the bottom: whenever a bit is set, XOR the current doubled a into the accumulator, then double a and move to the next bit. This is exactly binary long multiplication, with carries replaced by the field’s reduce step.
gmul(3, 3) shows the flavour: 3 is x + 1, and (x+1)(x+1) is x^2 + 2x + 1, but 2x is x + x which XORs to 0, leaving x^2 + 1, which is 0b101 = 5. This routine is correct but does a loop per multiply. Over the next three lessons you will replace it with a table lookup that is both faster and, once you trust it, easier to reason about - but keep this version, because it is the ground truth you will check the fast one against.
// Long multiplication in GF(2): walk the bits of b. Whenever a// bit is set, XOR the running-doubled a into the result.func gmul(a, b byte) byte {var r byte = 0for b > 0 {if b&1 != 0 {r ^= a}a = xtime(a) // double a for the next bit positionb >>= 1}return r}