Base64 规范编码

kir*_*rov 2 base64 encoding haskell decoding

我正在尝试测试encodedecode函数(在 中定义Data.ByteString.Base64.Lazy)是相反的:

import qualified Data.ByteString.Lazy as BL

encoded :: Gen BL.ByteString
encoded = do
  body <- concat <$> listOf (group 0)
  end <- group =<< choose (0, 2)
  return . BL.pack $ body <> end
 where
  group :: Int -> Gen [Word8]
  group pad = do
    letters <- vectorOf (4 - pad)
      . elements . map (fromIntegral . ord)
      $ ['A'..'Z'] <> ['a'..'z'] <> ['0'..'9'] <> ['+','/','=']
    return $ letters <> replicate pad 61  -- 61 is ascii for =

prop_encDec = forAll encoded $ \b ->
  [b] == (encode <$> rights [decode b])
Run Code Online (Sandbox Code Playgroud)

但是 QuickCheck 发现了一个问题:

=== prop_encDec ===
*** Failed! Falsifiable (after 1 test):
"1yx="
Run Code Online (Sandbox Code Playgroud)

我已经调查过这个问题一定与非规范编码有关,但我很难理解它是什么以及如何处理它。你能解释一下这个例子为什么解码“1yx=”和重新编码会产生“1yw=”。

Ack*_*ari 5

问题是x确实包含与编码无关的位置。

让我详细说明:base64 字符串1yx=将被解码为以下二进制模式

base64: 1      y      x      =
binary: 000001 110010 110001 000000
Run Code Online (Sandbox Code Playgroud)

然而,=字符串的末尾告诉编码器最后 8 位不相关,所以

000001 110010 110001 000000
^^^^^^ ^^^^^^ ^^^^
Run Code Online (Sandbox Code Playgroud)

只有标记的位将被解码为

binary: 000001 110010 1100
Run Code Online (Sandbox Code Playgroud)

如您所见,01由 编码的最后两位 ( )x被忽略

然后,如果我们对解码后的数据进行编码,编码器将用零位填充位流,从而导致

binary: 000001 110010 110000 000000
                          ^^ ^^^^^^
base64: 1      y      w      =
Run Code Online (Sandbox Code Playgroud)

所以总结一下:编码器无法“正确”重新编码解码,1yx=因为它包含因解码而丢失的信息。

为了您的测试目的,我建议交换操作顺序。因此,生成一些随机字符串作为输入,对其进行编码,然后对其进行解码并将其与原始输入进行比较。

您可能还想查看Wikipedia 文章中 Base64 编码的示例部分。它包含有关数据填充的商品示例。


关于规范和非规范编码:

当且仅当所有填充位都为零时,base64 字符串是规范的,如果填充位之一不为零,则该字符串是非规范的。因此,如果 base64 字符串末尾有 1 =,则最后 8 位必须为零才能使其成为规范;如果字符串末尾有 2 =,则最后 16 位必须为零。

所以该字符串1yx=是非规范的,因为正如我们在上面看到的,填充位之一是 1。另一方面,该字符串1yw=是规范的,因为所有 8 个填充位都为零。