2011-08-15 2 views
4

이상한 루비 인코딩 만남을 갖는 괴롭게 :루비 base64로 인코딩/디코딩/압축 해제 ('m')는

ruby-1.9.2-p180 :618 > s = "a8dnsjg8aiw8jq".ljust(16,'=') 
=> "a8dnsjg8aiw8jq==" 
ruby-1.9.2-p180 :619 > s.size 
=> 16 

ruby-1.9.2-p180 :620 > s.unpack('m0') 
ArgumentError: invalid base64 
    from (irb):631:in `unpack' 

ruby-1.9.2-p180 :621 > s.unpack('m') 
=> ["k\xC7g\xB28<j,<\x8E"] 
ruby-1.9.2-p180 :622 > s.unpack('m').first.size 
=> 10 

ruby-1.9.2-p180 :623 > s.unpack('m').pack('m') 
=> "a8dnsjg8aiw8jg==\n" 
ruby-1.9.2-p180 :624 > s.unpack('m').pack('m') == s 
=> false 

이 대칭되지 않는 이유 어떤 생각!? 그리고 'm0'(decode64_strict)이 전혀 작동하지 않는 이유는 무엇입니까? 입력 문자열은 base64 알파벳의 4 자 배수로 채워집니다. 여기서 14 × 6 비트 = 84 비트, 즉 10 1/2 8 비트 바이트, 즉 11 바이트입니다. 하지만 디코딩 된 문자열은 마지막 니블을 버리는 것처럼 보입니까?

나는 명백한 것이 없거나 버그인가? 해결 방법은 무엇입니까? cf. http://www.ietf.org/rfc/rfc4648.txt

답변

3

없습니다 Base64로의 일대일 매핑이 아니므을 패딩 된 끈. 실제 디코딩 된 콘텐츠부터 시작해 보겠습니다. 당신은 예를 들어 s.unpack('H*')를 사용하여 (16 진수에서 디코딩 문자열을 볼 경우이 될 것입니다 :

6B C7 67 | B2 38 3C | 6A 2C 3C | 8E 

나는 Base64로 알고리즘 각 입력 블록에 대한 경계를 추가 :이 입력의 3 개 옥텟을 받아 출력의 4 개 문자를 반환합니다. 따라서 마지막 블록에는 하나의 입력 옥텟 만 포함되므로 결과는 표준에 따라 "=="로 끝나는 4 자입니다.

이 마지막 블록의 정규 인코딩이 무엇인지 보겠습니다. 이진 표현 8E10001110입니다. RFC는 필요한 24 비트가 될 때까지 누락 된 비트를 0으로 채우라고 알려줍니다.

100011 100000 000000 000000 

6 비트의 그룹을 만들었습니다. 그 이유는 Base64 알파벳에서 해당 문자를 가져와야하기 때문입니다. 첫 번째 그룹 (100011)은 십진수 35 자로 변환되므로 Base64 알파벳의 j입니다. 두 번째 (100000)는 십진수 32이므로 'g'입니다. 나머지 두 문자는 규칙에 따라 "=="로 채워집니다. 당신이 JQ 보면 그래서 정규 인코딩은 바이너리이

100011 101010 000000 000000 

그래서 차이가 두 번째 그룹에있을 것입니다, 지금 ==

jg== 

입니다. 그러나 우리는 이미 처음 8 비트 만이 우리에게 관심이 있다는 것을 알고 있기 때문에 ("=="는 우리에게 이렇게 알려줍니다 -이 4 개의 문자로부터 하나의 디코딩 된 옥텟을 검색 할 것입니다) 실제로 우리는 왜냐하면 그룹 1의 6 비트와 그룹 2의 첫 번째 2 비트가 디코딩 된 옥텟을 형성하기 때문입니다. 100011 10 함께 우리의 초기 8E 바이트 값을 다시 형성합니다. 나머지 16 비트는 우리와 관련이 없으므로 버려 질 수 있습니다.

"엄격한"Base64 인코딩의 개념이 이해되는 이유를 의미합니다. 엄격하지 않은 디코딩은 마지막에 모든 쓰레기를 버리는 반면, 엄격한 디코딩은 나머지 6 비트의 최종 그룹을 0으로 확인합니다.이것이 엄격한 디코딩 규칙에 의해 비표준 인코딩이 거부되는 이유입니다.

2

RFC의 링크를 사용하면 양식 xx==의 마지막 쿼드가 입력 시퀀스의 한 옥텟에 해당한다고 분명히 말합니다. 12 개 중에서 16 비트의 정보 (2 개의 임의의 옥텟)를 만들 수 없으므로 여기서 반올림은 유효하지 않습니다.

올바른 Base64 인코딩 프로세스의 결과로 jq==이 나타나지 않으므로 문자열이 엄격 모드에서 거부되었습니다. 길이가 3의 배수가 아닌 입력 순서는 제로 패딩, 그리고 문자열은 나타나지 않을 수 있습니다 비 - 제로 비트가 있습니다 더 대칭

j  q  =  = 
|100011|101010|000000|000000| 
|10001110|10100000|00000000| 
      ^^^ 
2

RFC4648section 3.5 Canonical Encoding에서 : 입력베이스 64 인코딩을위한 하나의 옥텟 경우

예를 들어, 첫 번째 심볼의 모든 6 비트가 사용되며, 오직 제 두 비트 다음 기호의 이러한 패드 비트 ... 순응 인코더로 제로 일부 환경

설정되어야 개찬이 중요하므로 디코더 패드 비트하지 않은 경우 인코딩을 거부하도록 선택할 수도 이 0으로 설정되었습니다.

중 마지막 4 바이트 (jq==)이 이진 값으로 디코딩 : 밑줄 비트 인코딩 마지막 바이트 (육각 8E)을 형성하는데 사용된다

100011 101010 
------ --**** 

. 나머지 비트 (그 아래에 별표가 있음)는 0으로 간주됩니다 (이 아닌 jg==으로 인코딩 됨).

m 언 패킹은 0이어야하지만 그렇지 않아야하는 패딩 비트에 대해 용서 중입니다. m0 압축 풀기는 허용되지 않으므로 용서가되지 않습니다 (RFC에서 인용 된 "MAY"참조). 인코딩 된 값이 비표준이므로 코드화되지 않은 결과를 패킹하는 것은 대칭이 아니지만 pack 메서드는 정식 인코딩 (패드 비트가 0 임)을 생성합니다.

0

b64에 대한 좋은 설명에 감사드립니다. 나는 당신에게 모든 것을 upvoted했고 @ 양각의 반응을 받아 들였다.

그러나 이것은 내가 찾던 답변이 아닙니다. 더 나은 질문을 상태로, 그것은 것,

어떻게 패드 B64 문자열을이 압축을 풀고 ('M0')로 제로 패딩 8 비트 바이트로 디코딩 할 수 있도록하는? 당신의 설명에서

는 지금이 우리의 목적을 위해 일할 것을 볼 :

ruby-1.9.2-p180 :858 > s = "a8dnsjg8aiw8jq".ljust(16,'A') 
=> "a8dnsjg8aiw8jqAA" 
ruby-1.9.2-p180 :859 > s.unpack('m0') 
=> ["k\xC7g\xB28<j,<\x8E\xA0\x00"] 
ruby-1.9.2-p180 :861 > s.unpack('m0').pack('m0') == s 
=> true 

유일한 문제 다음 디코딩 된 문자열 길이가 보존되지 않는다는 것을, 그러나 우리가 해결할 수 있습니다.