Module: Kindle::DRM

Defined in:
lib/kindle/drm.rb

Constant Summary collapse

LETTERS =
"ABCDEFGHIJKLMNPQRSTUVWXYZ123456789".split(//)

Instance Method Summary collapse

Instance Method Details

#checksumPid(s) ⇒ Object

Generates the actual Personal ID (PID) needed to tie/untie content (such as .mobi/.azw files) to a specific device.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/kindle/drm.rb', line 16

def checksumPid(s)
  tmp = crc32(s)
  crc = tmp ^ (tmp >> 16)
  res = s
  l = LETTERS.length
  for i in 0..1 do
    b = crc & 0xFF
    t = b.divmod(l)[0]
    pos = t ^ (b % l)
    res = "#{res}#{LETTERS[pos % l]}"
    crc >>= 8
  end
  res
end

#crc32(s) ⇒ Object

Special cyclical redundancy check (checksumming) function.



10
11
12
# File 'lib/kindle/drm.rb', line 10

def crc32(s)
  (~Zlib.crc32(s, -1)) & 0xFFFFFFFF
end

#serialToDeviceTypeAndPidSize(serial) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/kindle/drm.rb', line 58

def serialToDeviceTypeAndPidSize(serial)
  type = nil
  size = nil
  pad = nil
  
  l = serial.length
  if l == 16
    case serial[0, 4]
    when /^B001/
      type = "Kindle 1"
    when /^B101/
      type = "Kindle 1"
    when /^B002/
      type = "Kindle 2"
    when /^B003/
      type = "Kindle 2 International"
    when /^B004/
      type = "Kindle DX"
    when /^B005/
      type = "Kindle DX 2"
    when /^B009/
      type = "Kindle DX International 2 (Graphite)"
    when /^B/
      type = "Unknown Kindle model. Please report!"
    end
    size = 7
    pad = '*'
  elsif l == 40
    type = "iPhone"
    size = 8
  end

  return nil if type.nil? || size.nil?
  return [type, size, pad]
end

#serialToIntermediaryPid(s, l) ⇒ Object

Figures out the intermediary Personal ID (PID) of a device based on its serial number and expected length of the output. Apparently this varies by the type of device.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/kindle/drm.rb', line 34

def serialToIntermediaryPid(s, l)
  crc = crc32(s)  
  arr1 = Array.new(l, 0)
  for i in 0..(s.length - 1) do
    code = s[i]
    code = code.ord if code.class == String # Fix for Ruby 1.9+
    arr1[i%l] ^= code
  end

  # Grab each CRC byte and OR with a portion of the 
  crc_bytes = [crc >> 24 & 0xff, crc >> 16 & 0xff, crc >> 8 & 0xff, crc & 0xff]
  for i in 0..(l - 1) do
    arr1[i] ^= crc_bytes[i&3]
  end

  pid = ""
  for i in 0..(l-1) do
    b = arr1[i] & 0xff
    pid += LETTERS[(b >> 7) + ((b >> 5 & 3) ^ (b & 0x1f))]
  end
  pid
end

#serialToPid(serial) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/kindle/drm.rb', line 95

def serialToPid(serial)
  pid = nil
  type, size, pad = serialToDeviceTypeAndPidSize(serial)
  if type.nil?
    # No dice.. :(
  else
    ipid = serialToIntermediaryPid(serial, size)
    ipid += pad unless pad.nil?
    pid = checksumPid(ipid)
  end
  pid
end