Base64 规范编码
Base64 canonical encodings
我正在尝试测试 encode
和 decode
函数(在 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])
但是 QuickCheck 在那里发现了一个问题:
=== prop_encDec ===
*** Failed! Falsifiable (after 1 test):
"1yx="
我已经调查过这个问题一定与非规范编码有关,但我很难理解它是什么以及如何处理它。你能解释一下这个例子为什么解码“1yx=”并重新编码会产生“1yw=”。
问题是 x
确实包含与编码无关的位置。
让我详细说明:
base64 字符串 1yx=
将被解码为以下二进制模式
base64: 1 y x =
binary: 000001 110010 110001 000000
但是字符串末尾的 =
告诉编码器最后 8 位不相关,所以
000001 110010 110001 000000
^^^^^^ ^^^^^^ ^^^^
只有标记的位会被解码为
binary: 000001 110010 1100
如您所见,x
编码的最后两位 (01
) 被忽略
然后如果我们对解码数据进行编码,编码器将用零位填充位流,从而导致
binary: 000001 110010 110000 000000
^^ ^^^^^^
base64: 1 y w =
因此得出结论:编码器无法“正确”重新编码已解码的1yx=
,因为它包含因解码而丢失的信息。
出于测试目的,我建议交换操作顺序。
因此生成一些随机字符串作为输入,对其进行编码然后解码并将其与原始输入进行比较。
您可能还想查看有关 Base64 编码的维基百科文章的 example 部分。它包含有关数据填充的商品示例。
关于规范和非规范编码:
当且仅当所有填充位都为零时,base64 字符串才是规范的,如果其中一个填充位不为零,则该字符串是非规范的。
因此,如果 base64 字符串末尾有一个 =
,那么最后 8 位必须为零才能成为规范字符串,如果字符串末尾有两个 =
,则最后 16 位必须为零。
所以字符串 1yx=
是非规范的,因为正如我们在上面看到的,其中一个填充位是 1。
另一方面,字符串 1yw=
是规范的,因为所有 8 个填充位都是零。
我正在尝试测试 encode
和 decode
函数(在 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])
但是 QuickCheck 在那里发现了一个问题:
=== prop_encDec ===
*** Failed! Falsifiable (after 1 test):
"1yx="
我已经调查过这个问题一定与非规范编码有关,但我很难理解它是什么以及如何处理它。你能解释一下这个例子为什么解码“1yx=”并重新编码会产生“1yw=”。
问题是 x
确实包含与编码无关的位置。
让我详细说明:
base64 字符串 1yx=
将被解码为以下二进制模式
base64: 1 y x =
binary: 000001 110010 110001 000000
但是字符串末尾的 =
告诉编码器最后 8 位不相关,所以
000001 110010 110001 000000
^^^^^^ ^^^^^^ ^^^^
只有标记的位会被解码为
binary: 000001 110010 1100
如您所见,x
编码的最后两位 (01
) 被忽略
然后如果我们对解码数据进行编码,编码器将用零位填充位流,从而导致
binary: 000001 110010 110000 000000
^^ ^^^^^^
base64: 1 y w =
因此得出结论:编码器无法“正确”重新编码已解码的1yx=
,因为它包含因解码而丢失的信息。
出于测试目的,我建议交换操作顺序。 因此生成一些随机字符串作为输入,对其进行编码然后解码并将其与原始输入进行比较。
您可能还想查看有关 Base64 编码的维基百科文章的 example 部分。它包含有关数据填充的商品示例。
关于规范和非规范编码:
当且仅当所有填充位都为零时,base64 字符串才是规范的,如果其中一个填充位不为零,则该字符串是非规范的。
因此,如果 base64 字符串末尾有一个 =
,那么最后 8 位必须为零才能成为规范字符串,如果字符串末尾有两个 =
,则最后 16 位必须为零。
所以字符串 1yx=
是非规范的,因为正如我们在上面看到的,其中一个填充位是 1。
另一方面,字符串 1yw=
是规范的,因为所有 8 个填充位都是零。