def make_encrypted(self, compress=False): data = np.arange(2**16, dtype=np.uint16).tobytes() fh = io.BytesIO(F1) fw = datafile.DataFileWriter(fh) signing_key_prv = bytes(range(32)) encryption_key = bytes(range(1, 33)) nonce = bytes(range(16, 16 + 24 * 2, 2)) associated_data = bytes(range(2, 50, 2)) fw.append_encrypted(b'RAW', data, signing_key_prv, encryption_key, nonce, associated_data, compress=compress) fw.finalize() fh.seek(0) fr = datafile.DataFileReader(fh) return fr, { 'data': data, 'signing_key_prv': signing_key_prv, 'signing_key_pub': monocypher.compute_signing_public_key(signing_key_prv), 'encryption_key': encryption_key, 'nonce': nonce, 'associated_data': associated_data, 'fh': fh, }
def test_read_known_value(self): fh = io.BytesIO(F1) f = datafile.DataFileReader(fh) tag, value = next(f) self.assertEqual(b'TAG', tag) self.assertEqual(bytes(range(10)), value) with self.assertRaises(StopIteration): next(f)
def test_seek(self): fh = io.BytesIO(F1) f = datafile.DataFileReader(fh) pos = f.tell() self.assertEqual(b'TAG', f.skip()) f.seek(pos) self.assertEqual(b'TAG', f.skip()) with self.assertRaises(StopIteration): next(f)
def test_peek_tag_length(self): fh = io.BytesIO(F1) f = datafile.DataFileReader(fh) tag, length = f.peek_tag_length() self.assertEqual(b'TAG', tag) self.assertEqual(10, length) tag, value = next(f) self.assertEqual(b'TAG', tag) self.assertEqual(bytes(range(10)), value) with self.assertRaises(StopIteration): next(f)
def test_compressed_write_read(self): data = np.arange(2**16, dtype=np.uint16).tobytes() fh = io.BytesIO(F1) fw = datafile.DataFileWriter(fh) fw.append(b'RAW', data, compress=True) fw.finalize() fh.seek(0) fr = datafile.DataFileReader(fh) tag, value = next(fr) self.assertEqual(b'RAW', tag) self.assertEqual(data, value) with self.assertRaises(StopIteration): next(fr)
def load(self, data, keys=None): """Load calibration from bytes. :param data: The bytes containing the calibration. :param keys: The list of allowed public keys. None (default) uses the official signing key. :return: This instance. :raise ValueError: on invalid data. """ self.data = data fh = io.BytesIO(data) r = datafile.DataFileReader(fh) self.signed = True tag, value = next(r) public_key = value[8:] if keys is None: keys = [public_keys.CALIBRATION_SIGNING] if datafile.TAG_SIGNATURE_START != tag: self.signed = False log.warning('Invalid format: missing signature start tag') else: if public_key not in keys: self.signed = False log.warning('Invalid signing key') tag, header = next(r) if datafile.TAG_HEADER != tag: raise ValueError('Invalid format: missing header tag') tag, cal = next(r) if tag not in [datafile.TAG_DATA_JSON, datafile.TAG_CALIBRATION_JSON]: raise ValueError('Invalid format: missing calibration tag') if self.signed: tag, _ = next(r) if datafile.TAG_SIGNATURE_END != tag: self.signed = False log.warning('Invalid format: missing signature end tag') cal = json.loads(cal.decode('utf-8')) if cal['product'] != PRODUCT: raise ValueError('Invalid calibration') self.version = cal['version'] self.time = dateutil.parser.parse(cal['time']) self.serial_number = cal['serial_number'] self.current_offset = _stuffed(cal['current']['offset']) self.current_gain = _stuffed(cal['current']['gain']) self.voltage_offset = np.array(cal['voltage']['offset'], dtype=np.float32) self.voltage_gain = np.array(cal['voltage']['gain'], dtype=np.float32) return self
def test_write_read(self): tags = [[42, bytes(range(1, 17))], (43, b'1234')] fh = io.BytesIO(F1) fw = datafile.DataFileWriter(fh) for tag, value in tags: fw.append(tag, value) fw.finalize() fh.seek(0) fr = datafile.DataFileReader(fh) for tag, value in tags: tag_rd, value_rd = yield fr tag_rd, = struct.unpack('<I', tag_rd) self.assertEqual(tag, tag_rd) self.assertEqual(value, value_rd) with self.assertRaises(StopIteration): next(fr)
def test_valid_signature(self): key = b';\xfa\xe7\xa7(\xa5\xc8M\xcb\xb8\xe1H\x84\x95rB\x99\xafW\x91T\x10\nE\x80\xb2]AT\xfd\xf3\xcb' fh = io.BytesIO() fw = datafile.DataFileWriter(fh) fw.signature_start(key) fw.append(datafile.TAG_DATA_BINARY, bytes(range(256))) fw.signature_end() fw.finalize() fh.seek(0) fr = datafile.DataFileReader(fh) tag, value = next(fr) self.assertEqual(datafile.TAG_SIGNATURE_START, tag) next(fr) tag, value = next(fr) self.assertEqual(datafile.TAG_SIGNATURE_END, tag) self.assertTrue(value)
def test_collection_with_data(self): data, fh = self._construct_collection(collection_data=b'hello world') fr = datafile.DataFileReader(fh) tag, collection_bytes = next(fr) c = datafile.Collection.decode(collection_bytes) self.assertEqual(datafile.TAG_COLLECTION_START, tag) self.assertEqual(c.data, b'hello world') for d in data: tag, value = next(fr) self.assertEqual(b'TAG', tag) self.assertEqual(d, value) self.assertEqual(c.end_position, fh.tell()) tag, value = next(fr) self.assertEqual(datafile.TAG_COLLECTION_END, tag) with self.assertRaises(StopIteration): next(fr)
def test_invalid_signature(self): key = b';\xfa\xe7\xa7(\xa5\xc8M\xcb\xb8\xe1H\x84\x95rB\x99\xafW\x91T\x10\nE\x80\xb2]AT\xfd\xf3\xcb' fh = io.BytesIO() fw = datafile.DataFileWriter(fh) fw.signature_start(key) fw.append(datafile.TAG_DATA_BINARY, bytes(range(256))) # add data to signature computation, but mess with underlying file pos = fh.tell() fw.append(datafile.TAG_DATA_BINARY, bytes(range(256))) fh.seek(pos) fw.signature_end() fw.finalize() fh.seek(0) fr = datafile.DataFileReader(fh) tag, value = next(fr) self.assertEqual(datafile.TAG_SIGNATURE_START, tag) next(fr) with self.assertRaises(ValueError): next(fr)
def firmware_program(self, filename, progress_cbk=None): """Program or update the application firmware. :param filename: The filename containing the firmware, which must be in Joulescope firmware format and correctly signed. :param progress_cbk: The optional Callable[[float], None] which is called with the progress fraction from 0.0 to 1.0 :return: 0 on success or error code. """ log.info('%s: firmware_program', self) data = datafile.filename_or_bytes(filename) if len(data): fh = io.BytesIO(data) dr = datafile.DataFileReader(fh) # todo: check distribution signature tag, hdr_value = next(dr) if tag != datafile.TAG_HEADER: raise ValueError( 'incorrect format: expected header, received %r' % tag) tag, data = next(dr) if tag != datafile.TAG_DATA_BINARY: raise ValueError( 'incorrect format: expected data, received %r' % tag) tag, enc = next(dr) if tag != datafile.TAG_ENCRYPTION: raise ValueError( 'incorrect format: expected encryption, received %r' % tag) metadata = { 'encryption': 1, 'header': hdr_value[:24], 'mac': enc[:16], 'signature': enc[16:], } log.info('header = %r', binascii.hexlify(metadata['header'])) log.info('mac = %r', binascii.hexlify(metadata['mac'])) log.info('signature = %r', binascii.hexlify(metadata['signature'])) else: metadata = None return self.program(Segment.FIRMWARE, data, metadata, progress_cbk)
def test_skip_collection(self): data, fh = self._construct_collection() fr = datafile.DataFileReader(fh) fr.skip() with self.assertRaises(StopIteration): next(fr)