Skip to content

Commit aac56b0

Browse files
committed
Release 0.1.0
2 parents afcf8cc + 721c8ed commit aac56b0

36 files changed

+4218
-15
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*~
22
__pycache__
33
*.pyc
4-
docs/_build
4+
docs/_build
5+
bof.log

README.md

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,135 @@
1-
Boiboite Opener Framework
2-
=========================
1+
BOF
2+
===
33

4-
Industrial network protocols communication and packet crafting framework.
4+
BOF (Boiboite Opener Framework) is a testing framework for field protocols
5+
implementations and devices. It is a Python 3.6+ library that provides means to
6+
send, receive, create, parse and manipulate frames from supported protocols.
57

6-
Documentation
7-
-------------
8+
The library currently supports **KNXnet/IP**, which is our focus, but it can be
9+
extended to other types of BMS or industrial network protocols.
810

9-
* Build documentation as HTML : `cd docs && make html`
10-
* [Access complete documentation](./docs/_build/html/index.html)
11+
There are three ways to use BOF:
12+
13+
* Automated: Use of higher-level interaction functions to discover devices and
14+
start basic exchanges, without requiring to know anything about the protocol.
15+
16+
* Standard: Perform more advanced (legitimate) operations. This requires the end
17+
user to know how the protocol works (how to establish connections, what kind
18+
of messages to send).
19+
20+
* Playful: Modify every single part of exchanged frames and misuse the protocol
21+
instead of using it (we fuzz devices with it). The end user should have
22+
started digging into the protocol's specifications.
23+
24+
**Please note that targeting BMS systems can have a severe impact on buildings and
25+
people and that BOF must be used carefully.**
26+
27+
[![GitHub license](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://github.com/Orange-Cyberdefense/bof/blob/master/LICENSE)
28+
[![GitHub release](https://img.shields.io/github/release/Orange-Cyberdefense/bof.svg)](https://gitHub.com/Orange-Cyberdefense/bof/releases/)
29+
30+
Getting started
31+
---------------
32+
33+
```
34+
git clone https://github.com/Orange-Cyberdefense/bof.git
35+
```
36+
37+
BOF is a Python 3.6+ library that should be imported in scripts. It has no
38+
installer yet so you need to refer to the `bof` subdirectory which contains the
39+
library (inside the repository) in your project or to copy the folder to your
40+
project's folder. Then, inside your code (or interactively), you can import the
41+
library:
42+
43+
```python
44+
import bof
45+
```
46+
47+
Now you can start using BOF!
48+
49+
> The following code samples interact using the building management system
50+
protocol KNXnet/IP (the framework supports only this one for now).
51+
52+
### Discover devices on a network
53+
54+
```python
55+
from bof import knx
56+
57+
devices = knx.discover("192.168.1.0/24")
58+
for device in devices:
59+
print(device)
60+
```
61+
62+
### Send and receive packets
63+
64+
```python
65+
from bof import knx, BOFNetworkError
66+
67+
knxnet = knx.KnxNet()
68+
try:
69+
knxnet.connect("192.168.1.1", 3671)
70+
frame = knx.KnxFrame(type="DESCRIPTION REQUEST")
71+
print(frame)
72+
knxnet.send(frame)
73+
response = knxnet.receive()
74+
print(response)
75+
except BOFNetworkError as bne:
76+
print(str(bne))
77+
finally:
78+
knxnet.disconnect()
79+
```
80+
81+
### Craft your own packets!
82+
83+
```python
84+
from bof import knx
85+
86+
frame = knx.KnxFrame()
87+
frame.header.service_identifier.value = b"\x02\x03"
88+
hpai = knx.KnxBlock(type="HPAI")
89+
frame.body.append(hpai)
90+
print(frame)
91+
```
92+
93+
Complete documentation
94+
----------------------
95+
96+
[![made-with-sphinx-doc](https://img.shields.io/badge/Made%20with-Sphinx-1f425f.svg)](https://www.sphinx-doc.org/)
97+
98+
Link to the documentation: https://bof.readthedocs.io
99+
100+
The HTML user manual and source code documentation can be built from the
101+
repository:
102+
103+
1. `$> cd docs && make html`
104+
2. Navigate to `[path to repository]/docs/_build/html/index.html)`
105+
106+
Example scripts are in folder `examples`.
107+
108+
Contributing
109+
------------
110+
111+
Contributors are welcome! BOF is still an ongoing project and so far, we focused
112+
on the detailed implementation of the KNX specification. One can already do the
113+
main basic operations, but there are many types of frames and features in the
114+
KNX standard (without even mentioning the extensions) and not all of them have
115+
been implemented yet. Furthermore, there will still be room for higher-level
116+
functions that will make tests easier. We also wrote BOF so that it can be
117+
extended to other field protocols (industrial, building management, etc.), so
118+
why not implement another one?
119+
120+
Here a few things to know beforehand:
121+
122+
* We like clean code and expect contributions to be PEP-8 compliant as much as
123+
possible (even though we don't test for it). New code should be readable
124+
easily and maintainable. And remember: if you need to use "and" while
125+
explaining what your function does, then you can probably split it.
126+
127+
* Please write Unit tests! They are in `tests/`. You can run all unit tests
128+
with: `python -m unittest discover -s tests`
129+
130+
Reporting issues
131+
----------------
132+
133+
Report bugs, ask questions or request for missing documentation and new features
134+
by submitting an issue with GitHub. For bugs, please describe your problem as
135+
clearly as you can.

bof/__init__.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,34 @@
1-
"""BOF module"""
1+
"""
2+
Boiboite Opener Framework / Ouvre-Boiboite Framework contains a set of features
3+
to write scripts using industrial network protocols for test and attack
4+
purposes.
5+
6+
The following submodules are available:
7+
8+
:base:
9+
Basic helpers for correct module usage (error handling, logging, some
10+
parsing features. Available from direct bof import (``import bof``).
11+
12+
:network:
13+
Global network classes, used by protocol implementations in submodules.
14+
The content of this class should not be used directly, unless writing a
15+
new protocol submodule. Available from direct bof import (``import bof``)
16+
17+
:byte:
18+
Set of functions for byte conversion and handling. Accessed via import of
19+
the byte submodule (``from bof import byte``).
20+
21+
:knx:
22+
Implementation of the BMS protocol KNX, relying on ``bof.UDP``. Provides
23+
classes and methods for sending and receiving KNX datagrams on the network
24+
over KNXnet/IP, and for reading and writing valid or invalid KNX
25+
frames.
26+
"""
27+
28+
###############################################################################
29+
# Include content to be imported by module users #
30+
###############################################################################
31+
32+
from .base import *
33+
from .network import *
34+
from .byte import *

bof/base.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""Set of global and useful classes and functions used within the module.
2+
3+
:Exceptions: BOF-specific exceptions raised by the module.
4+
:Logging: Functions to enable or disable logging for the module.
5+
:JSON: JSON files handling (JSON is used for protocols specification).
6+
"""
7+
8+
import logging
9+
import json
10+
from datetime import datetime
11+
from re import sub
12+
13+
###############################################################################
14+
# BOF EXCEPTIONS #
15+
###############################################################################
16+
17+
class BOFError(Exception):
18+
"""Base class for all BOF exceptions.
19+
20+
.. warning:: Should not be used directly, please raise or catch subclasses
21+
instead.
22+
"""
23+
24+
class BOFLibraryError(BOFError):
25+
"""Library, files and import-related exceptions.
26+
27+
Usually raised when the library cannot find what it needs to work correctly
28+
(such as an external module or a file).
29+
"""
30+
pass
31+
32+
class BOFNetworkError(BOFError):
33+
"""Network-related exceptions.
34+
35+
Occurs when the network connection fails or is interrupted.
36+
"""
37+
pass
38+
39+
class BOFProgrammingError(BOFError):
40+
"""Script and module programming-related errors.
41+
42+
This occurs when a function or an argument is not used as expected.
43+
44+
.. note:: As a module user, this exception is the one that you may
45+
encounter the most.
46+
"""
47+
pass
48+
49+
###############################################################################
50+
# BOF LOGGING #
51+
###############################################################################
52+
53+
__DEFAULT_FILENAME = "bof"
54+
__LOG_SUFFIX = "log"
55+
__LOG_FORMAT = "%(asctime)s:%(levelname)s:%(message)s:%(filename)s:%(lineno)s"
56+
57+
_LOGGING_ENABLED = False
58+
59+
def enable_logging(filename:str="", error_only:bool=False) -> None:
60+
"""Turn on logging features to store BOF-autogenerated events and user-
61+
generated events (call to ``bof.log()`` function). Relies on Python's
62+
``logging`` module.
63+
64+
:param filename: Optional name of the file in which events will be saved.
65+
Default is ``bof.log``.
66+
:param error_only: All types of events are logged (info, warning, error)
67+
are saved unless this parameter is set to ``True``.
68+
"""
69+
level = logging.WARNING if error_only else logging.INFO
70+
filename = "{0}.{1}".format(filename if filename else __DEFAULT_FILENAME,
71+
__LOG_SUFFIX)
72+
logging.basicConfig(filename=filename, level=level,
73+
format=__LOG_FORMAT)
74+
now = datetime.now().strftime("%y%m%d-%H%M%S")
75+
logging.info("Starting BOF session {0}".format(now))
76+
global _LOGGING_ENABLED
77+
_LOGGING_ENABLED = True
78+
79+
def disable_logging() -> None:
80+
"""Turn off logging features,"""
81+
global _LOGGING_ENABLED
82+
_LOGGING_ENABLED = False
83+
84+
def log(message:str, level:str="INFO") -> bool:
85+
"""Logs an event (``message``) to a file, if BOF logging is enabled
86+
(requires previous call to `bof.`enable_logging()``). A ``message`` is
87+
recorded along with event-related information:
88+
89+
- date and time
90+
- level (can be changed with parameter ``level``)
91+
- event location in the code (file name, line number)
92+
93+
:param message: Event definition.
94+
:param level: Type of event to record: ``ERROR``, ``WARNING``, ``DEBUG``.
95+
`INFO`` (default). Levels from Python's ``logging`` are used.
96+
:returns: Current state of logging (enabled/``True``, disabled/``False``).
97+
"""
98+
if _LOGGING_ENABLED:
99+
level = getattr(logging, level.upper())
100+
if not isinstance(level, int):
101+
raise BOFProgrammingError("Invalid logging level")
102+
logging.log(level, message)
103+
return _LOGGING_ENABLED
104+
105+
###############################################################################
106+
# BOF JSON FILE HANDLING #
107+
###############################################################################
108+
109+
def load_json(filename:str) -> dict:
110+
"""Loads a JSON file and returns the associated dictionary.
111+
112+
:raises BOFLibraryError: if the file cannot be opened.
113+
"""
114+
try:
115+
with open(filename, 'r') as jsonfile:
116+
return json.load(jsonfile)
117+
except Exception as e:
118+
raise BOFLibraryError("JSON File {0} cannot be used.".format(filename)) from None
119+
120+
###############################################################################
121+
# STRING MANIPULATION #
122+
###############################################################################
123+
124+
def to_property(value:str) -> str:
125+
"""Replace all non alphanumeric characters in a string with ``_``"""
126+
return sub('[^0-9a-zA-Z]+', '_', value)

0 commit comments

Comments
 (0)