diff --git a/CHANGELOG.md b/CHANGELOG.md index d41b789bd..2ff2425fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## master + +Changes: + +- Only allow padding characters in base64 strings + + ## 12.2.2 Jun 4, 2023 Changes: diff --git a/packages/util-crypto/src/base32/helpers.ts b/packages/util-crypto/src/base32/helpers.ts index c73d1b3f9..88ccfc521 100644 --- a/packages/util-crypto/src/base32/helpers.ts +++ b/packages/util-crypto/src/base32/helpers.ts @@ -19,6 +19,7 @@ interface Config { ipfs?: string; regex?: RegExp; type: string; + withPadding?: boolean; } type DecodeFn = (value: string, ipfsCompat?: boolean) => Uint8Array; @@ -63,7 +64,7 @@ export function createIs (validate: ValidateFn): ValidateFn { } /** @internal */ -export function createValidate ({ chars, ipfs, type }: Config): ValidateFn { +export function createValidate ({ chars, ipfs, type, withPadding }: Config): ValidateFn { return (value?: unknown, ipfsCompat?: boolean): value is string => { if (typeof value !== 'string') { throw new Error(`Expected ${type} string input`); @@ -72,12 +73,17 @@ export function createValidate ({ chars, ipfs, type }: Config): ValidateFn { } for (let i = (ipfsCompat ? 1 : 0), count = value.length; i < count; i++) { - if (!(chars.includes(value[i]) || ( - value[i] === '=' && ( - (i === value.length - 1) || - !chars.includes(value[i + 1]) - ) - ))) { + if (chars.includes(value[i])) { + // all ok, character found + } else if (withPadding && value[i] === '=') { + if (i === count - 1) { + // last character, everything ok + } else if (value[i + 1] === '=') { + // next one is also padding, sequence ok + } else { + throw new Error(`Invalid ${type} padding sequence "${value[i]}${value[i + 1]}" at index ${i}`); + } + } else { throw new Error(`Invalid ${type} character "${value[i]}" (0x${value.charCodeAt(i).toString(16)}) at index ${i}`); } } diff --git a/packages/util-crypto/src/base58/validate.spec.ts b/packages/util-crypto/src/base58/validate.spec.ts new file mode 100644 index 000000000..0fab236b2 --- /dev/null +++ b/packages/util-crypto/src/base58/validate.spec.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2023 @polkadot/util-crypto authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/// + +import { base58Validate } from './index.js'; + +describe('base58Validate', (): void => { + it('validates encoded', (): void => { + expect( + base58Validate('a1UbyspTdnyZXLUQaQbciCxrCWWxz24kgSwGXSQnkbs', false) + ).toEqual(true); + }); + + it('fails on string with extra padding', (): void => { + expect( + () => base58Validate('a1UbyspTdnyZXLUQaQbciCxrCWWxz24kgSwGXSQnkbs=', false) + ).toThrow(/Invalid base58 character "="/); + }); +}); diff --git a/packages/util-crypto/src/base64/bs64.ts b/packages/util-crypto/src/base64/bs64.ts index 1d0099306..5d055fbbf 100644 --- a/packages/util-crypto/src/base64/bs64.ts +++ b/packages/util-crypto/src/base64/bs64.ts @@ -8,7 +8,8 @@ import { createDecode, createEncode, createIs, createValidate } from '../base32/ const config = { chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', coder: base64, - type: 'base64' + type: 'base64', + withPadding: true }; /** diff --git a/packages/util-crypto/src/base64/validate.spec.ts b/packages/util-crypto/src/base64/validate.spec.ts index ebf5adfe6..fb0ff972f 100644 --- a/packages/util-crypto/src/base64/validate.spec.ts +++ b/packages/util-crypto/src/base64/validate.spec.ts @@ -11,4 +11,22 @@ describe('base64Validate', (): void => { () => base64Validate('aGVsbG8gd29ybGQg0J/RgNC40LLQtd^GC0YHRgtCy0YPRjiDQvNC4IOS9oOWlvQ==') ).toThrow(/Invalid base64 character "\^" \(0x5e\) at index 30/); }); + + it('validates with one extra padding character', (): void => { + expect( + base64Validate('bGlnaHQgd28=') + ).toEqual(true); + }); + + it('validates with two extra padding characters', (): void => { + expect( + base64Validate('bGlnaHQgdw==') + ).toEqual(true); + }); + + it('validates misplaced padding characters', (): void => { + expect( + () => base64Validate('bGlnaHQgdw=g=') + ).toThrow(/Invalid base64 padding sequence "=g"/); + }); });