From: Lady Date: Mon, 22 May 2023 02:50:19 +0000 (-0700) Subject: Make base32 handling less forgiving X-Git-Tag: 0.3.1 X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/6e6d4e3261c1c943fe44fa9e381bcf8bf1441fd6?ds=inline;hp=f0c3b510a532ded70c1a72ad753204b21d0f53bb Make base32 handling less forgiving Ordinarily, one only encounters base32 strings of length 0, 2, 4, 5, or 7 characters (mod 8). If it is of length 3 or 6, the final character is in the middle of an incomplete byte. As with length 1, this should probably throw an error, as the other alternative is to entirely ignore the character. (It already did throw an error before this change, but unintentionally.) --- diff --git a/binary.js b/binary.js index 5c78d39..5ee2bda 100644 --- a/binary.js +++ b/binary.js @@ -135,10 +135,13 @@ const decodeBase16 = (source) => { ); const { length } = u4s; if (length % 2 == 1) { + // The length is such that an entire letter would be dropped during + // a forgiving decode. throw new RangeError( `Piscēs: Base16 string has invalid length: ${source}.`, ); } else { + // Every letter contributes at least some bits to the result. const dataView = new View(new Buffer(floor(length / 2))); for (let index = 0; index < length - 1;) { call(viewSetUint8, dataView, [ @@ -224,11 +227,15 @@ const decodeBase32 = (source, wrmg) => { }, ); const { length } = u5s; - if (length % 8 == 1) { + const lengthMod8 = length % 8; + if (lengthMod8 == 1 || lengthMod8 == 3 || lengthMod8 == 6) { + // The length is such that an entire letter would be dropped during + // a forgiving decode. throw new RangeError( `Piscēs: Base32 string has invalid length: ${source}.`, ); } else { + // Every letter contributes at least some bits to the result. const dataView = new View(new Buffer(floor(length * 5 / 8))); for (let index = 0; index < length - 1;) { // The final index is not handled; if the string is not divisible @@ -302,10 +309,13 @@ const decodeBase64 = (source, safe = false) => { ); const { length } = u6s; if (length % 4 == 1) { + // The length is such that an entire letter would be dropped during + // a forgiving decode. throw new RangeError( `Piscēs: Base64 string has invalid length: ${source}.`, ); } else { + // Every letter contributes at least some bits to the result. const dataView = new View(new Buffer(floor(length * 3 / 4))); for (let index = 0; index < length - 1;) { // The final index is not handled; if the string is not divisible diff --git a/binary.test.js b/binary.test.js index f3c8317..82fa4e0 100755 --- a/binary.test.js +++ b/binary.test.js @@ -356,9 +356,11 @@ describe("base32Binary", () => { assertThrows(() => base32Binary("CT3ZYAY==")); }); - it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => { + it("[[Call]] throws when provided with a length with a remainder of 1, 3 ∣ 6 when divided by 8", () => { assertThrows(() => base32Binary("234BCDEAA")); assertThrows(() => base32Binary("234BCDEAA=======")); + assertThrows(() => base32Binary("UHI")); + assertThrows(() => base32Binary("UHIUJD")); }); }); @@ -863,6 +865,13 @@ describe("wrmgBase32Binary", () => { assertThrows(() => wrmgBase32Binary("ABC=")); }); + it("[[Call]] throws when provided with a length with a remainder of 1, 3 ∣ 6 when divided by 8", () => { + assertThrows(() => base32Binary("TVW123400")); + assertThrows(() => base32Binary("TVW123400=======")); + assertThrows(() => base32Binary("M78")); + assertThrows(() => base32Binary("M78M93")); + }); + it("[[Call]] throws when provided with a length with a remainder of 1 when divided by 4", () => { assertThrows(() => wrmgBase32Binary("234BCDEAA")); assertThrows(() => wrmgBase32Binary("2-34-B--CD-EA-A-"));