From a8755d3f04e86920be32b6fa01b4364472c2926c Mon Sep 17 00:00:00 2001 From: Sindre Myren Date: Thu, 10 Mar 2022 19:39:00 +0100 Subject: [PATCH] fix: let decode look for additional base32 padding Update FromString and XID.TextUnmarshal so that it looks for discarded bits in the final source character. This fixes an issue where XIDs that have been manually tampered with in a way that's ignored by base32 decode, will not pass as valid. --- id.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/id.go b/id.go index 17a4721..1f536b4 100644 --- a/id.go +++ b/id.go @@ -238,7 +238,9 @@ func (id *ID) UnmarshalText(text []byte) error { return ErrInvalidID } } - decode(id, text) + if !decode(id, text) { + return ErrInvalidID + } return nil } @@ -256,8 +258,8 @@ func (id *ID) UnmarshalJSON(b []byte) error { return id.UnmarshalText(b[1 : len(b)-1]) } -// decode by unrolling the stdlib base32 algorithm + removing all safe checks -func decode(id *ID, src []byte) { +// decode by unrolling the stdlib base32 algorithm + customized safe check. +func decode(id *ID, src []byte) bool { _ = src[19] _ = id[11] @@ -273,6 +275,16 @@ func decode(id *ID, src []byte) { id[2] = dec[src[3]]<<4 | dec[src[4]]>>1 id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4 id[0] = dec[src[0]]<<3 | dec[src[1]]>>2 + + // Validate that there are no discarer bits (padding) in src that would + // cause the string-encoded id not to equal src. + var check [4]byte + + check[3] = encoding[(id[11]<<4)&0x1F] + check[2] = encoding[(id[11]>>1)&0x1F] + check[1] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F] + check[0] = encoding[id[10]>>3] + return bytes.Equal([]byte(src[16:20]), check[:]) } // Time returns the timestamp part of the id.