Class: PWN::SDR::Decoder::P25::PacketData

Inherits:
Object
  • Object
show all
Defined in:
lib/pwn/sdr/decoder/p25.rb

Overview

Unconfirmed rate-1/2 packet data units, separately bounded by BTF.

Class Method Summary collapse

Class Method Details

.parse(opts = {}) ⇒ Object



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/pwn/sdr/decoder/p25.rb', line 268

public_class_method def self.parse(opts = {})
  data = opts[:data]
  info = opts[:info]
  decoder = opts[:decoder]
  return :incomplete if data.length < 154

  decoded = decoder.call(data[56, 98])
  return nil unless decoded

  header, errors = decoded
  return nil unless DSP.crc16(bytes: header.first(10), init: 0, xorout: 0xFFFF) == ((header[10] << 8) | header[11])
  return nil unless (header[0] & 31) == 0x15 && header[0].nobits?(0xC0) && header[1].allbits?(0xC0) && header[6].anybits?(0x80)

  blocks = header[6] & 127
  pad = header[7] & 31
  offset = header[9] & 63
  return nil unless blocks.positive? && pad <= 11 && header[7].nobits?(0xE0) && header[8].zero? && header[9].nobits?(0xC0)

  count = 56 + ((blocks + 1) * 98)
  return :incomplete if data.length < count

  bytes = []
  blocks.times do |index|
    part = decoder.call(data[154 + (index * 98), 98])
    return nil unless part

    bytes.concat(part[0])
    errors += part[1]
  end
  crc = 0
  bytes[0...-4].each do |byte|
    7.downto(0) do |bit|
      feedback = ((crc >> 31) ^ (byte >> bit)) & 1
      crc = (crc << 1) & 0xFFFFFFFF
      crc ^= 0x04C11DB7 if feedback == 1
    end
  end
  received = bytes.last(4).reduce(0) { |value, byte| (value << 8) | byte }
  return nil unless (crc ^ 0xFFFFFFFF) == received

  length = bytes.length - pad - 4
  return nil if length.negative? || offset > length

  payload = bytes.first(length).pack('C*').unpack1('H*').upcase
  sap = header[1] & 63
  # Only SAP 0/4 are interpreted as clear user/packet data. Unknown
  # and encrypted services remain opaque; no key guessing.
  encrypted = if sap == 9
                true
              else
                ([0, 4].include?(sap) ? false : nil)
              end
  [{ protocol: 'P25', event: 'pdu', capability: 'unconfirmed-pdu', decoded: true,
     nac: format('%03X', info >> 4), duid: 12, duid_name: 'PDU', format: 0x15,
     sap: sap, manufacturer_id: header[2], outbound: header[0].anybits?(0x20),
     llid: (header[3] << 16) | (header[4] << 8) | header[5], blocks: blocks,
     pad_octets: pad, data_header_offset: offset, header_crc_ok: true, packet_crc_ok: true,
     checksum_verified: true, fec_corrected_bits: errors, encrypted: encrypted,
     payload_hex: encrypted == false ? payload : nil, ciphertext_hex: encrypted == true ? payload : nil,
     opaque_payload_hex: encrypted.nil? ? payload : nil }, count]
end