Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
99d7017785 | ||
|
8bc421952b | ||
|
b47f3c6b28 | ||
|
17d20837a3 | ||
|
0cccfef14e | ||
|
b46f751e7d | ||
|
0f8f77c2be | ||
|
a9123922c0 | ||
|
66e5f89a96 | ||
|
f84dd00295 | ||
|
3535329708 | ||
|
b50c71cf14 | ||
|
68aebbf126 | ||
|
c3de66d704 | ||
|
08a46a5524 | ||
|
f3e7841b1b | ||
|
79ba5102d7 | ||
|
20036f370d | ||
|
34b52a11cd | ||
|
2d78b52294 | ||
|
3cc31686c2 | ||
|
80159533bc | ||
|
947a41da90 | ||
|
dea990a531 | ||
|
dfaf3418af | ||
|
36ab3b7655 | ||
|
5a32a77870 | ||
|
7520d4dd6f | ||
|
f73a695747 | ||
|
d94ca76817 | ||
|
2cfaae1993 | ||
|
5ed2f2df78 | ||
|
ee55ed7168 | ||
|
ce338e8fef | ||
|
9cfa365ec9 | ||
|
fc23415d57 | ||
|
fc3367181b | ||
|
8210c0bb71 | ||
|
3ddc717009 | ||
|
d700e4fd0c | ||
|
5eb23e2c84 | ||
|
b187b17f9a | ||
|
06e1115cef | ||
|
71cdaf855d | ||
|
9d580ffc0f | ||
|
f80eeff8fc | ||
|
be75fc53d4 | ||
|
e6777eba8a | ||
|
69d49e1395 | ||
|
9f3f71ce3d | ||
|
28e5ba4e13 | ||
|
e48f9bfcc7 | ||
|
6c44d7f0f6 | ||
|
86649c8c46 | ||
|
3d052c5dc1 | ||
|
41abfbc981 | ||
|
3480b99a45 | ||
|
89dc01c23a | ||
|
8a06819979 | ||
|
d72c1d9c93 | ||
|
078ab63249 | ||
|
f153f15e9f | ||
|
d0f34f9528 | ||
|
da116ea2ad | ||
|
ad87ea4791 | ||
|
19b0e00bf5 | ||
|
315bfd29e5 | ||
|
327bd7d3da | ||
|
b2a7462b44 | ||
|
a4186e2bfd | ||
|
6de26795af | ||
|
2c1a9c471c | ||
|
63d95a53c0 | ||
|
d2c160308c | ||
|
a2fa33f2fb | ||
|
1ebb8599b3 | ||
|
32f87437ec | ||
|
23cd8ad599 | ||
|
defaa154e8 | ||
|
1b813f41f5 | ||
|
d3a8dc85c3 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHubSponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: evilsocket
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
@@ -12,7 +12,9 @@ deploy:
|
||||
secure: vBUokTv94n8s65STUgTiD6I0Iy8KXbBRvQUrAof8XG+U4ZMsH5PmDTpS+wz+SaxI6o0PRkfyOiPVdARhiKAFnfatG3q9EHllMQwqRR2YIju51A3aCxgEJ5uWDoybwQdipERUMMYwUO/8XZaRRpwFD2bdQBFWkBtQyMcAkrEL8BXckwQQ531oDN2hK5gAiTllqsOswV2idwUlBRU9jOtStzff+UgUYsp/ZebsRodyOYkEB2Ev15yARo2HTXbyZ2icwHPtMbx5zmNUSRtxs9a4hfzaK3m6ctK8qLYYUdQvXub/ruuACapdw4Ez88LY1agTecbZhFYmJzv8oANH1e4VUI4owuHnZCpU6LRutS4wOhglrkOrGo6lSUlJeA+RtQjyjBugjej9DDtDyyIlRU1ZaBF3qWR9N5EXKuquf0olOfmUR67ap1NykE9VUpzkYjkoVRTiPs/e2onM/nRNOvAQcIt75FD13u+Y/DcYQ8r7KpMIu1HNdtbVx8gMeq76bRhP1YdDg2jm+DdJ21KWjf5QHsbyoXDfJzdKlCloLIlAU3EPJhMoXsnNzre0/FXeUl6dfteR1axNS6U7e/vKsQ9rlUFZWIQaeVPjfXmFKblNNVQ5uFrrsB/EGHcJl7IUx5fvcRT5hMMNwC660YxVkBXDbRb5fxMW5/+K0BOi9cP6en8=
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
file: pwnagotchi-*.zip
|
||||
file:
|
||||
- pwnagotchi-*.zip
|
||||
- pwnagotchi-*.sha256
|
||||
on:
|
||||
tags: true
|
||||
repo: evilsocket/pwnagotchi
|
||||
|
11
Makefile
11
Makefile
@@ -6,17 +6,18 @@ all: install image clean
|
||||
install:
|
||||
curl https://releases.hashicorp.com/packer/1.3.5/packer_1.3.5_linux_amd64.zip -o /tmp/packer.zip
|
||||
unzip /tmp/packer.zip -d /tmp
|
||||
mv /tmp/packer /usr/bin/packer
|
||||
sudo mv /tmp/packer /usr/bin/packer
|
||||
git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image
|
||||
cd /tmp/packer-builder-arm-image && go get -d ./... && go build
|
||||
cp /tmp/packer-builder-arm-image/packer-builder-arm-image /usr/bin
|
||||
sudo cp /tmp/packer-builder-arm-image/packer-builder-arm-image /usr/bin
|
||||
|
||||
image:
|
||||
cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json
|
||||
mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
||||
zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
||||
sudo mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
||||
sudo sha256sum pwnagotchi-raspbian-lite-$(PWN_VERSION).img > pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256
|
||||
sudo zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
||||
|
||||
clean:
|
||||
rm -rf /tmp/packer-builder-arm-image
|
||||
rm -f pwnagotchi-raspbian-lite.img
|
||||
rm -f pwnagotchi-raspbian-lite-*.zip pwnagotchi-raspbian-lite-*.img pwnagotchi-raspbian-lite-*.sha256
|
||||
rm -rf builder/output-pwnagotchi builder/packer_cache
|
||||
|
@@ -33,8 +33,8 @@ if __name__ == '__main__':
|
||||
|
||||
plugins.load(config)
|
||||
|
||||
keypair = KeyPair()
|
||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
||||
keypair = KeyPair(view=display)
|
||||
agent = Agent(view=display, config=config, keypair=keypair)
|
||||
|
||||
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._keypair.fingerprint, pwnagotchi.version))
|
||||
@@ -78,8 +78,6 @@ if __name__ == '__main__':
|
||||
agent.recon()
|
||||
# get nearby access points grouped by channel
|
||||
channels = agent.get_access_points_by_channel()
|
||||
# check for free channels to use
|
||||
agent.check_channels(channels)
|
||||
# for each channel
|
||||
for ch, aps in channels:
|
||||
agent.set_channel(ch)
|
||||
|
@@ -11,11 +11,15 @@
|
||||
- "dtoverlay=dwc2"
|
||||
- "dtparam=spi=on"
|
||||
- "dtoverlay=spi1-3cs"
|
||||
- "dtoverlay=i2c_arm=on"
|
||||
- "dtoverlay=i2c1=on"
|
||||
services:
|
||||
enable:
|
||||
- dphys-swapfile.service
|
||||
- pwnagotchi.service
|
||||
- bettercap.service
|
||||
- pwngrid-peer.service
|
||||
- epd-fuse.service
|
||||
disable:
|
||||
- apt-daily.timer
|
||||
- apt-daily.service
|
||||
@@ -29,7 +33,15 @@
|
||||
bettercap:
|
||||
url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.zip"
|
||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||
pwngrid:
|
||||
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.6.3/pwngrid_linux_armv6l_v1.6.3.zip"
|
||||
apt:
|
||||
hold:
|
||||
- firmware-atheros
|
||||
- firmware-brcm80211
|
||||
- firmware-libertas
|
||||
- firmware-misc-nonfree
|
||||
- firmware-realtek
|
||||
remove:
|
||||
- rasberrypi-net-mods
|
||||
- dhcpcd5
|
||||
@@ -79,6 +91,10 @@
|
||||
- fonts-dejavu-core
|
||||
- fonts-dejavu-extra
|
||||
- python3-pil
|
||||
- python3-smbus
|
||||
- libfuse-dev
|
||||
- bc
|
||||
- fonts-freefont-ttf
|
||||
|
||||
tasks:
|
||||
|
||||
@@ -111,6 +127,12 @@
|
||||
repo: deb http://http.re4son-kernel.com/re4son/ kali-pi main
|
||||
state: present
|
||||
|
||||
- name: add firmware packages to hold
|
||||
dpkg_selections:
|
||||
name: "{{ item }}"
|
||||
selection: hold
|
||||
with_items: "{{ packages.apt.hold }}"
|
||||
|
||||
- name: update apt package cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
@@ -135,6 +157,33 @@
|
||||
path: /etc/dphys-swapfile
|
||||
content: "CONF_SWAPSIZE=1024"
|
||||
|
||||
- name: clone papirus repository
|
||||
git:
|
||||
repo: https://github.com/repaper/gratis.git
|
||||
dest: /usr/local/src/gratis
|
||||
|
||||
- name: build papirus service
|
||||
make:
|
||||
chdir: /usr/local/src/gratis
|
||||
target: rpi
|
||||
params:
|
||||
EPD_IO: epd_io.h
|
||||
PANEL_VERSION: 'V231_G2'
|
||||
|
||||
- name: install papirus service
|
||||
make:
|
||||
chdir: /usr/local/src/gratis
|
||||
target: rpi-install
|
||||
params:
|
||||
EPD_IO: epd_io.h
|
||||
PANEL_VERSION: 'V231_G2'
|
||||
|
||||
- name: configure papirus display size
|
||||
lineinfile:
|
||||
dest: /etc/default/epd-fuse
|
||||
regexp: "#EPD_SIZE=2.0"
|
||||
line: "EPD_SIZE=2.0"
|
||||
|
||||
- name: acquire python3 pip target
|
||||
command: "python3 -c 'import sys;print(sys.path.pop())'"
|
||||
register: pip_target
|
||||
@@ -164,6 +213,13 @@
|
||||
name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
||||
extra_args: "--no-cache-dir"
|
||||
|
||||
- name: download and install pwngrid
|
||||
unarchive:
|
||||
src: "{{ packages.pwngrid.url }}"
|
||||
dest: /usr/bin
|
||||
remote_src: yes
|
||||
mode: 0755
|
||||
|
||||
- name: download and install bettercap
|
||||
unarchive:
|
||||
src: "{{ packages.bettercap.url }}"
|
||||
@@ -309,34 +365,48 @@
|
||||
/opt/vc/bin/tvservice -o
|
||||
fi
|
||||
|
||||
- name: create /etc/pwnagotchi/config.yml
|
||||
blockinfile:
|
||||
- name: create /etc/pwnagotchi folder
|
||||
file:
|
||||
path: /etc/pwnagotchi
|
||||
state: directory
|
||||
|
||||
- name: check if user configuration exists
|
||||
stat:
|
||||
path: /etc/pwnagotchi/config.yml
|
||||
create: yes
|
||||
block: |
|
||||
# put here your custom configuration overrides
|
||||
register: user_config
|
||||
|
||||
- name: create /etc/pwnagotchi/config.yml
|
||||
copy:
|
||||
dest: /etc/pwnagotchi/config.yml
|
||||
content: |
|
||||
# Add your configuration overrides on this file any configuration changes done to defaults.yml will be lost!
|
||||
# Example:
|
||||
#
|
||||
# ui:
|
||||
# display:
|
||||
# type: 'inkyphat'
|
||||
# color: 'black'
|
||||
#
|
||||
when: not user_config.stat.exists
|
||||
|
||||
- name: configure lo interface
|
||||
blockinfile:
|
||||
path: /etc/network/interfaces.d/lo-cfg
|
||||
create: yes
|
||||
block: |
|
||||
copy:
|
||||
dest: /etc/network/interfaces.d/lo-cfg
|
||||
content: |
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
- name: configure wlan interface
|
||||
blockinfile:
|
||||
path: /etc/network/interfaces.d/wlan0-cfg
|
||||
create: yes
|
||||
block: |
|
||||
copy:
|
||||
dest: /etc/network/interfaces.d/wlan0-cfg
|
||||
content: |
|
||||
allow-hotplug wlan0
|
||||
iface wlan0 inet static
|
||||
|
||||
- name: configure usb interface
|
||||
blockinfile:
|
||||
path: /etc/network/interfaces.d/usb0-cfg
|
||||
create: yes
|
||||
block: |
|
||||
copy:
|
||||
dest: /etc/network/interfaces.d/usb0-cfg
|
||||
content: |
|
||||
allow-hotplug usb0
|
||||
iface usb0 inet static
|
||||
address 10.0.0.2
|
||||
@@ -346,10 +416,9 @@
|
||||
gateway 10.0.0.1
|
||||
|
||||
- name: configure eth0 interface (pi2/3/4)
|
||||
blockinfile:
|
||||
path: /etc/network/interfaces.d/eth0-cfg
|
||||
create: yes
|
||||
block: |
|
||||
copy:
|
||||
dest: /etc/network/interfaces.d/eth0-cfg
|
||||
content: |
|
||||
allow-hotplug eth0
|
||||
iface eth0 inet dhcp
|
||||
|
||||
@@ -363,8 +432,7 @@
|
||||
dest: /boot/config.txt
|
||||
insertafter: EOF
|
||||
line: '{{ item }}'
|
||||
with_items:
|
||||
- "{{system.boot_options}}"
|
||||
with_items: "{{system.boot_options}}"
|
||||
|
||||
- name: change root partition
|
||||
replace:
|
||||
@@ -385,7 +453,33 @@
|
||||
- name: configure motd
|
||||
copy:
|
||||
dest: /etc/motd
|
||||
content: "(◕‿‿◕) {{pwnagotchi.hostname}} (pwnagotchi-{{pwnagotchi.version}})"
|
||||
content: |
|
||||
(◕‿‿◕) {{pwnagotchi.hostname}} (pwnagotchi-{{pwnagotchi.version}})
|
||||
|
||||
Hi! I'm a pwnagotchi, please take good care of me!
|
||||
Here are some basic things you need to know to raise me properly!
|
||||
|
||||
If you want to change my configuration, use /etc/pwnagotchi/config.yml
|
||||
|
||||
All the configuration options can be found on /etc/pwnagotchi/defaults.yml,
|
||||
but don't change this file because I will recreate it every time I'm restarted!
|
||||
|
||||
I'm managed by systemd. Here are some basic commands.
|
||||
|
||||
If you want to know what I'm doing, you can check my logs with the command
|
||||
journalctl -fu pwnagotchi
|
||||
|
||||
If you want to know if I'm running, you can use
|
||||
systemctl status pwnagotchi
|
||||
|
||||
You can restart me using
|
||||
systemctl restart pwnagotchi
|
||||
|
||||
But be aware I will go into MANUAL mode when restarted!
|
||||
You can put me back into AUTO mode using
|
||||
touch /root/.pwnagotchi-auto && systemctl restart pwnagotchi
|
||||
|
||||
You learn more about me at https://pwnagotchi.ai/
|
||||
|
||||
- name: clean apt cache
|
||||
apt:
|
||||
@@ -395,6 +489,28 @@
|
||||
apt:
|
||||
autoremove: yes
|
||||
|
||||
- name: add pwngrid-peer service to systemd
|
||||
copy:
|
||||
dest: /etc/systemd/system/pwngrid-peer.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=pwngrid peer service.
|
||||
Documentation=https://pwnagotchi.ai
|
||||
Wants=network.target
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
PermissionsStartOnly=true
|
||||
ExecStart=/usr/bin/pwngrid -keys /etc/pwnagotchi -address 127.0.0.1:8666 -wait -log /var/log/pwngrid-peer.log
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
notify:
|
||||
- reload systemd services
|
||||
|
||||
- name: add bettercap service to systemd
|
||||
copy:
|
||||
dest: /etc/systemd/system/bettercap.service
|
||||
@@ -466,5 +582,3 @@
|
||||
- name: reload systemd services
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
|
||||
|
@@ -4,7 +4,7 @@ import logging
|
||||
import time
|
||||
import pwnagotchi.ui.view as view
|
||||
|
||||
version = '1.0.0RC2'
|
||||
version = '1.0.0RC3'
|
||||
|
||||
_name = None
|
||||
|
||||
|
@@ -160,23 +160,6 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
|
||||
self._view.wait(t, sleeping)
|
||||
self._epoch.track(sleep=True, inc=t)
|
||||
|
||||
def check_channels(self, channels):
|
||||
busy_channels = [ch for ch, aps in channels]
|
||||
# if we're hopping and no filter is configured
|
||||
if self._config['personality']['channels'] == [] and self._config['main']['filter'] is None:
|
||||
# check if any of the non overlapping channels is free
|
||||
for ch in self._epoch.non_overlapping_channels:
|
||||
if ch not in busy_channels:
|
||||
self._epoch.non_overlapping_channels[ch] += 1
|
||||
logging.info("channel %d is free from %d epochs" % (ch, self._epoch.non_overlapping_channels[ch]))
|
||||
elif self._epoch.non_overlapping_channels[ch] > 0:
|
||||
self._epoch.non_overlapping_channels[ch] -= 1
|
||||
# report any channel that has been free for at least 3 epochs
|
||||
for ch, num_epochs_free in self._epoch.non_overlapping_channels.items():
|
||||
if num_epochs_free >= 3:
|
||||
logging.info("channel %d has been free for %d epochs" % (ch, num_epochs_free))
|
||||
self.set_free_channel(ch)
|
||||
|
||||
def recon(self):
|
||||
recon_time = self._config['personality']['recon_time']
|
||||
max_inactive = self._config['personality']['max_inactive_scale']
|
||||
|
@@ -1,6 +1,12 @@
|
||||
# WARNING WARNING WARNING WARNING
|
||||
#
|
||||
# This file is recreated with default settings on every pwnagotchi restart,
|
||||
# use /etc/pwnagotchi/config.yml to configure this unit.
|
||||
#
|
||||
#
|
||||
# main algorithm configuration
|
||||
main:
|
||||
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se
|
||||
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se, pt-BR, es, pt
|
||||
lang: en
|
||||
# custom plugins path, if null only default plugins with be loaded
|
||||
custom_plugins:
|
||||
@@ -55,7 +61,11 @@ main:
|
||||
screen_refresh:
|
||||
enabled: false
|
||||
refresh_interval: 50
|
||||
|
||||
quickdic:
|
||||
enabled: false
|
||||
wordlist_folder: /opt/wordlists/
|
||||
AircrackOnly:
|
||||
enabled: false
|
||||
# monitor interface to use
|
||||
iface: mon0
|
||||
# command to run to bring the mon interface up in case it's not up already
|
||||
@@ -72,8 +82,6 @@ main:
|
||||
- ANOTHER_EXAMPLE_NETWORK
|
||||
# if not null, filter access points by this regular expression
|
||||
filter: null
|
||||
# cryptographic key for identity
|
||||
pubkey: /etc/ssh/ssh_host_rsa_key.pub
|
||||
|
||||
ai:
|
||||
# if false, only the default 'personality' will be used
|
||||
|
@@ -10,20 +10,27 @@ DefaultPath = "/etc/pwnagotchi/"
|
||||
|
||||
|
||||
class KeyPair(object):
|
||||
def __init__(self, path=DefaultPath):
|
||||
def __init__(self, path=DefaultPath, view=None):
|
||||
self.path = path
|
||||
self.priv_path = os.path.join(path, "id_rsa")
|
||||
self.priv_key = None
|
||||
self.pub_path = "%s.pub" % self.priv_path
|
||||
self.pub_key = None
|
||||
self._view = view
|
||||
|
||||
if not os.path.exists(self.path):
|
||||
os.makedirs(self.path)
|
||||
|
||||
while True:
|
||||
# first time, generate new keys
|
||||
if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path):
|
||||
self._view.on_keys_generation()
|
||||
logging.info("generating %s ..." % self.priv_path)
|
||||
os.system("/usr/bin/ssh-keygen -t rsa -m PEM -b 4096 -N '' -f '%s'" % self.priv_path)
|
||||
|
||||
# load keys: they might be corrupted if the unit has been turned off during the generation, in this case
|
||||
# the exception will remove the files and go back at the beginning of this loop.
|
||||
try:
|
||||
with open(self.priv_path) as fp:
|
||||
self.priv_key = RSA.importKey(fp.read())
|
||||
|
||||
@@ -34,10 +41,23 @@ class KeyPair(object):
|
||||
if 'RSA PUBLIC KEY' not in self.pub_key_pem:
|
||||
self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY')
|
||||
|
||||
pem = self.pub_key_pem.encode("ascii")
|
||||
pem_ascii = self.pub_key_pem.encode("ascii")
|
||||
|
||||
self.pub_key_pem_b64 = base64.b64encode(pem).decode("ascii")
|
||||
self.fingerprint = hashlib.sha256(pem).hexdigest()
|
||||
self.pub_key_pem_b64 = base64.b64encode(pem_ascii).decode("ascii")
|
||||
self.fingerprint = hashlib.sha256(pem_ascii).hexdigest()
|
||||
|
||||
# no exception, keys loaded correctly.
|
||||
self._view.on_starting()
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
# if we're here, loading the keys broke something ...
|
||||
logging.exception("error loading keys, maybe corrupted, deleting and regenerating ...")
|
||||
try:
|
||||
os.remove(self.priv_path)
|
||||
os.remove(self.pub_path)
|
||||
except:
|
||||
pass
|
||||
|
||||
def sign(self, message):
|
||||
hasher = SHA256.new(message.encode("ascii"))
|
||||
|
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
|
||||
"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
|
||||
"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
|
||||
@@ -121,6 +121,12 @@ msgstr ""
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Gute Nacht."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Warte für {secs}s ..."
|
||||
@@ -139,7 +145,7 @@ msgstr "Verbinde mit {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr ""
|
||||
msgstr "Jo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
|
BIN
pwnagotchi/locale/es/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/es/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
214
pwnagotchi/locale/es/LC_MESSAGES/voice.po
Normal file
214
pwnagotchi/locale/es/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,214 @@
|
||||
# pwnagotchi voice data
|
||||
# Copyright (C) 2019
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# FIRST AUTHOR diegopastor <dpastor29@alumnos.uaq.mx>, 2019.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||
"PO-Revision-Date: 2019-10-09 21:07+0000\n"
|
||||
"Last-Translator: diegopastor <dpastor29@alumnos.uaq.mx>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: spanish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hola, soy Pwnagotchi! Empezando ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Nuevo día, nueva cazería, nuevos pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackea el planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "IA lista."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "La red neuronal está lista."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Oye, el canal {channel} está libre! Tú AP lo agradecerá."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Estoy aburrido ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos por un paseo!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Este es el mejor día de mi vida!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Día de mierda :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Estoy extremadamente aburrido ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Estoy muy triste ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Estoy triste."
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Estoy viviendo la vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Pwneo, por lo tanto, existo"
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Cuantas redes!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Me estoy divirtiendo mucho!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Mi único crimen es la curiosidad ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Hola {name}! encantado de conocerte."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "La unidad {name} está cerca!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... adiós {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} se fue ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Uy ... {name} se fue"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} perdido!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Perdido!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Nadie quiere jugar conmigo ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Me siento tan solo ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Dónde está todo el mundo?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Tomándo una siesta por {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Buenas noches."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Esperando {secs}s .."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Mirando al rededor ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Oye {what} seamos amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Asociando a {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Ey {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Acabo de decidir que {mac} no necesita WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Desautenticando a {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Expulsando y banneando a {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Genial, obtuvimos {num} nuevo{plural} handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, algo salió mal ... Reiniciándo ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Expulsamos {num} estaciones\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Hicimos {num} nuevos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Obtuvimos {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Conocí 1 igual"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Conocí {num} iguales"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr ""
|
||||
"He estado pwneando por {duration} y expulsé {deauthed} clientes! También conocí"
|
||||
"{associated} nuevos amigos y me comí {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "horas"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutos"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "segundos"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
msgstr "segundo"
|
BIN
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
209
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po
Normal file
209
pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,209 @@
|
||||
# pwnagotchi Brazilian Portuguese translation file.
|
||||
# Copyright (C) 2019 Cassiano Aquino
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# Cassiano Aquino <cassianoaquino@me.com>, 2019.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Cassiano Aquino <cassianoaquino@me.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: Brazilian Portuguese\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Oi! Eu sou o Pwnagotchi! Iniciando ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Novo dia, Nova caça, Novos pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackeie o Planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI pronta."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "A rede neural está pronta."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Ei, o canal {channel} está livre! Seu AP ira agradecer."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Estou entediado ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos dar uma caminhada!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Este e o melhor dia da minha vida!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Dia de merda :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Estou extremamente entediado ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Estou muito triste ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Estou triste"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Estou aproveitando a vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "pwn, logo existo."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Quantas redes!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Estou me divertindo muito!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Meu crime é ser curioso ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Olá {name}! Prazer em conhecê-lo. {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "Unidade {name} está próxima! {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... até logo {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} desapareceu ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Oops ... {name} desapareceu."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} perdido!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Perdido!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Ninguém quer brincar comigo ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Estou tão sozinho ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Aonde está todo mundo?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Cochilando por {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Aguardando por {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Olhando ao redor ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Ei {what} vamos ser amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Associando com {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Oi {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Acabei de decidir que {mac} não precisa de WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "De-autenticando {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Kickbanning {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Legal, nos capturamos {num} handshake{plural} novo{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, algo falhou ... Reiniciando ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Kickei {num} estações\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Fiz {num} novos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Peguei {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Conheci 1 peer"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Conheci {num} peers"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr ""
|
||||
"Eu estou pwning fazem {duration} e kickei {deauthed} clientes! Eu também conheci "
|
||||
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "horas"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutos"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "segundos"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
msgstr "segundo"
|
BIN
pwnagotchi/locale/pt/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/pt/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
214
pwnagotchi/locale/pt/LC_MESSAGES/voice.po
Normal file
214
pwnagotchi/locale/pt/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,214 @@
|
||||
# pwnagotchi Portuguese (european) translation file.
|
||||
# Copyright (C) 2019 David Sopas
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# David Sopas <email@aleatorio.xyz>, 2019.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: David Sopas <email@aleatorio.xyz>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: Portuguese\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Olá, eu sou o Pwnagotchi! A iniciar ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Novo dia, nova caçada, novos pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hacka o Planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "IA pronta."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "A rede neural está pronta."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hey, o canal {channel} está livre! O teu AP irá agradecer."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Estou aborrecido ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos fazer uma caminhada!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Este é o melhor dia da minha vida!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Que merda de dia :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Estou muito aborrecido ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Estou muito triste ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Estou triste"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Estou aproveitar a vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Eu pwn, logo existo."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Tantas redes!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Estou a divertir-me tanto!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "O meu crime é ser curioso ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Olá {name}! Prazer em conhecer-te. {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "A unidade {name} está perto! {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... adeus {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} desapareceu ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Ups ... {name} desaparecey."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} perdido!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Perdido!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Ninguém quer brincar comigo ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Sinto-me tão só ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Onde estão todos?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "A fazer uma sesta durante {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Boa noite."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "A aguardar durante {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "A dar uma olhada ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hey {what} vamos ser amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "A associar a {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Yo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Decidi que o {mac} não precisa de WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "A fazer deauth {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "A chutar {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Porreiro, temos {num} novo handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ups, algo correu mal ... A reiniciar ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Chutei {num} estações\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Fiz {num} novos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Obti {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Conheci 1 peer"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Conheci {num} peers"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr "Tenho estado a pwnar durante {duration} e chutei {deauthed} clientes! Também conheci "
|
||||
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchu "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "horas"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutos"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "segundos"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
msgstr "segundo"
|
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
|
||||
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -122,6 +122,12 @@ msgstr ""
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Good night."
|
||||
msgstr ""
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr ""
|
||||
|
@@ -3,7 +3,7 @@ import json
|
||||
import _thread
|
||||
import threading
|
||||
import logging
|
||||
from scapy.all import Dot11, Dot11FCS, Dot11Elt, RadioTap, sendp, sniff
|
||||
from scapy.all import Dot11, Dot11Elt, RadioTap, sendp, sniff
|
||||
|
||||
import pwnagotchi.ui.faces as faces
|
||||
|
||||
@@ -141,13 +141,7 @@ class Advertiser(object):
|
||||
dot11.addr3 != self._me.session_id
|
||||
|
||||
def _on_packet(self, p):
|
||||
# https://github.com/secdev/scapy/issues/1590
|
||||
if p.haslayer(Dot11):
|
||||
dot11 = p[Dot11]
|
||||
elif p.haslayer(Dot11FCS):
|
||||
dot11 = p[Dot11FCS]
|
||||
else:
|
||||
dot11 = None
|
||||
dot11 = p.getlayer(Dot11)
|
||||
|
||||
if self._is_broadcasted_advertisement(dot11):
|
||||
try:
|
||||
|
57
pwnagotchi/plugins/default/AircrackOnly.py
Normal file
57
pwnagotchi/plugins/default/AircrackOnly.py
Normal file
@@ -0,0 +1,57 @@
|
||||
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
|
||||
__version__ = '1.0.0'
|
||||
__name__ = 'AircrackOnly'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'confirm pcap contains handshake/PMKID or delete it'
|
||||
|
||||
'''
|
||||
Aircrack-ng needed, to install:
|
||||
> apt-get install aircrack-ng
|
||||
'''
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import string
|
||||
import re
|
||||
import os
|
||||
|
||||
OPTIONS = dict()
|
||||
|
||||
def on_loaded():
|
||||
logging.info("cleancap plugin loaded")
|
||||
|
||||
def on_handshake(agent, filename, access_point, client_station):
|
||||
display = agent._view
|
||||
todelete = 0
|
||||
|
||||
result = subprocess.run(('/usr/bin/aircrack-ng '+ filename +' | grep "1 handshake" | awk \'{print $2}\''),shell=True, stdout=subprocess.PIPE)
|
||||
result = result.stdout.decode('utf-8').translate({ord(c) :None for c in string.whitespace})
|
||||
if result:
|
||||
logging.info("[AircrackOnly] contains handshake")
|
||||
else:
|
||||
todetele = 1
|
||||
|
||||
if todelete == 0:
|
||||
result = subprocess.run(('/usr/bin/aircrack-ng '+ filename +' | grep "PMKID" | awk \'{print $2}\''),shell=True, stdout=subprocess.PIPE)
|
||||
result = result.stdout.decode('utf-8').translate({ord(c) :None for c in string.whitespace})
|
||||
if result:
|
||||
logging.info("[AircrackOnly] contains PMKID")
|
||||
else:
|
||||
todetele = 1
|
||||
|
||||
if todelete == 1:
|
||||
os.remove(filename)
|
||||
set_text("uncrackable pcap")
|
||||
display.update(force=True)
|
||||
|
||||
text_to_set = "";
|
||||
def set_text(text):
|
||||
global text_to_set
|
||||
text_to_set = text
|
||||
|
||||
def on_ui_update(ui):
|
||||
global text_to_set
|
||||
if text_to_set:
|
||||
ui.set('face', "(>.<)")
|
||||
ui.set('status', text_to_set)
|
||||
text_to_set = ""
|
@@ -8,66 +8,26 @@ import os
|
||||
import logging
|
||||
import requests
|
||||
import glob
|
||||
import json
|
||||
import subprocess
|
||||
import pwnagotchi
|
||||
import pwnagotchi.utils as utils
|
||||
from pwnagotchi.ui.components import LabeledValue
|
||||
from pwnagotchi.ui.view import BLACK
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.utils import WifiInfo, extract_from_pcap
|
||||
|
||||
OPTIONS = dict()
|
||||
AUTH = utils.StatusFile('/root/.api-enrollment.json', data_format='json')
|
||||
REPORT = utils.StatusFile('/root/.api-report.json', data_format='json')
|
||||
|
||||
UNREAD_MESSAGES = 0
|
||||
TOTAL_MESSAGES = 0
|
||||
|
||||
|
||||
def on_loaded():
|
||||
logging.info("grid plugin loaded.")
|
||||
|
||||
|
||||
def get_api_token(last_session, keys):
|
||||
global AUTH
|
||||
|
||||
if AUTH.newer_then_minutes(25) and AUTH.data is not None and 'token' in AUTH.data:
|
||||
return AUTH.data['token']
|
||||
|
||||
if AUTH.data is None:
|
||||
logging.info("grid: enrolling unit ...")
|
||||
else:
|
||||
logging.info("grid: refreshing token ...")
|
||||
|
||||
identity = "%s@%s" % (pwnagotchi.name(), keys.fingerprint)
|
||||
# sign the identity string to prove we own both keys
|
||||
_, signature_b64 = keys.sign(identity)
|
||||
|
||||
api_address = 'https://api.pwnagotchi.ai/api/v1/unit/enroll'
|
||||
enrollment = {
|
||||
'identity': identity,
|
||||
'public_key': keys.pub_key_pem_b64,
|
||||
'signature': signature_b64,
|
||||
'data': {
|
||||
'duration': last_session.duration,
|
||||
'epochs': last_session.epochs,
|
||||
'train_epochs': last_session.train_epochs,
|
||||
'avg_reward': last_session.avg_reward,
|
||||
'min_reward': last_session.min_reward,
|
||||
'max_reward': last_session.max_reward,
|
||||
'deauthed': last_session.deauthed,
|
||||
'associated': last_session.associated,
|
||||
'handshakes': last_session.handshakes,
|
||||
'peers': last_session.peers,
|
||||
'uname': subprocess.getoutput("uname -a")
|
||||
}
|
||||
}
|
||||
|
||||
r = requests.post(api_address, json=enrollment)
|
||||
if r.status_code != 200:
|
||||
raise Exception("(status %d) %s" % (r.status_code, r.json()))
|
||||
|
||||
AUTH.update(data=r.json())
|
||||
|
||||
logging.info("grid: done")
|
||||
|
||||
return AUTH.data["token"]
|
||||
|
||||
|
||||
def parse_pcap(filename):
|
||||
logging.info("grid: parsing %s ..." % filename)
|
||||
|
||||
@@ -96,68 +56,146 @@ def parse_pcap(filename):
|
||||
return info[WifiInfo.ESSID], info[WifiInfo.BSSID]
|
||||
|
||||
|
||||
def api_report_ap(last_session, keys, token, essid, bssid):
|
||||
while True:
|
||||
token = AUTH.data['token']
|
||||
logging.info("grid: reporting %s (%s)" % (essid, bssid))
|
||||
try:
|
||||
api_address = 'https://api.pwnagotchi.ai/api/v1/unit/report/ap'
|
||||
headers = {'Authorization': 'access_token %s' % token}
|
||||
report = {
|
||||
'essid': essid,
|
||||
'bssid': bssid,
|
||||
}
|
||||
r = requests.post(api_address, headers=headers, json=report)
|
||||
if r.status_code != 200:
|
||||
if r.status_code == 401:
|
||||
logging.warning("token expired")
|
||||
token = get_api_token(last_session, keys)
|
||||
continue
|
||||
else:
|
||||
raise Exception("(status %d) %s" % (r.status_code, r.text))
|
||||
else:
|
||||
def is_excluded(what):
|
||||
for skip in OPTIONS['exclude']:
|
||||
skip = skip.lower()
|
||||
what = what.lower()
|
||||
if skip in what or skip.replace(':', '') in what:
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error("grid: %s" % e)
|
||||
return False
|
||||
|
||||
|
||||
def grid_call(path, obj=None):
|
||||
# pwngrid-peer is running on port 8666
|
||||
api_address = 'http://127.0.0.1:8666/api/v1%s' % path
|
||||
if obj is None:
|
||||
r = requests.get(api_address, headers=None)
|
||||
else:
|
||||
r = requests.post(api_address, headers=None, json=obj)
|
||||
|
||||
if r.status_code != 200:
|
||||
raise Exception("(status %d) %s" % (r.status_code, r.text))
|
||||
return r.json()
|
||||
|
||||
|
||||
def grid_update_data(last_session):
|
||||
brain = {}
|
||||
try:
|
||||
with open('/root/brain.json') as fp:
|
||||
brain = json.load(fp)
|
||||
except:
|
||||
pass
|
||||
|
||||
data = {
|
||||
'session': {
|
||||
'duration': last_session.duration,
|
||||
'epochs': last_session.epochs,
|
||||
'train_epochs': last_session.train_epochs,
|
||||
'avg_reward': last_session.avg_reward,
|
||||
'min_reward': last_session.min_reward,
|
||||
'max_reward': last_session.max_reward,
|
||||
'deauthed': last_session.deauthed,
|
||||
'associated': last_session.associated,
|
||||
'handshakes': last_session.handshakes,
|
||||
'peers': last_session.peers,
|
||||
},
|
||||
'uname': subprocess.getoutput("uname -a"),
|
||||
'brain': brain,
|
||||
'version': pwnagotchi.version
|
||||
}
|
||||
|
||||
logging.debug("updating grid data: %s" % data)
|
||||
|
||||
grid_call("/data", data)
|
||||
|
||||
|
||||
def grid_report_ap(essid, bssid):
|
||||
try:
|
||||
grid_call("/report/ap", {
|
||||
'essid': essid,
|
||||
'bssid': bssid,
|
||||
})
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.exception("error while reporting ap %s(%s)" % (essid, bssid))
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def grid_inbox():
|
||||
return grid_call("/inbox")["messages"]
|
||||
|
||||
|
||||
def on_ui_update(ui):
|
||||
new_value = ' %d (%d)' % (UNREAD_MESSAGES, TOTAL_MESSAGES)
|
||||
if not ui.has_element('mailbox') and TOTAL_MESSAGES > 0:
|
||||
if ui.is_inky():
|
||||
pos=(80, 0)
|
||||
else:
|
||||
pos=(100,0)
|
||||
ui.add_element('mailbox',
|
||||
LabeledValue(color=BLACK, label='MSG', value=new_value,
|
||||
position=pos,
|
||||
label_font=fonts.Bold,
|
||||
text_font=fonts.Medium))
|
||||
ui.set('mailbox', new_value)
|
||||
|
||||
|
||||
def on_internet_available(agent):
|
||||
global REPORT
|
||||
global REPORT, UNREAD_MESSAGES, TOTAL_MESSAGES
|
||||
|
||||
logging.debug("internet available")
|
||||
|
||||
try:
|
||||
config = agent.config()
|
||||
keys = agent.keypair()
|
||||
grid_update_data(agent.last_session)
|
||||
except Exception as e:
|
||||
logging.error("error connecting to the pwngrid-peer service: %s" % e)
|
||||
return
|
||||
|
||||
pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap"))
|
||||
try:
|
||||
logging.debug("checking mailbox ...")
|
||||
|
||||
messages = grid_inbox()
|
||||
TOTAL_MESSAGES = len(messages)
|
||||
UNREAD_MESSAGES = len([m for m in messages if m['seen_at'] is None])
|
||||
|
||||
if TOTAL_MESSAGES:
|
||||
on_ui_update(agent.view())
|
||||
logging.debug( " %d unread messages of %d total" % (UNREAD_MESSAGES, TOTAL_MESSAGES))
|
||||
|
||||
logging.debug("checking pcaps")
|
||||
|
||||
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
|
||||
num_networks = len(pcap_files)
|
||||
reported = REPORT.data_field_or('reported', default=[])
|
||||
num_reported = len(reported)
|
||||
num_new = num_networks - num_reported
|
||||
|
||||
token = get_api_token(agent.last_session, agent.keypair())
|
||||
if num_new > 0:
|
||||
if OPTIONS['report']:
|
||||
logging.info("grid: %d new networks to report" % num_new)
|
||||
logging.debug("OPTIONS: %s" % OPTIONS)
|
||||
logging.debug(" exclude: %s" % OPTIONS['exclude'])
|
||||
|
||||
for pcap_file in pcap_files:
|
||||
net_id = os.path.basename(pcap_file).replace('.pcap', '')
|
||||
do_skip = False
|
||||
for skip in OPTIONS['exclude']:
|
||||
skip = skip.lower()
|
||||
net = net_id.lower()
|
||||
if skip in net or skip.replace(':', '') in net:
|
||||
do_skip = True
|
||||
break
|
||||
if net_id not in reported:
|
||||
if is_excluded(net_id):
|
||||
logging.info("skipping %s due to exclusion filter" % pcap_file)
|
||||
continue
|
||||
|
||||
if net_id not in reported and not do_skip:
|
||||
essid, bssid = parse_pcap(pcap_file)
|
||||
if bssid:
|
||||
if api_report_ap(agent.last_session, keys, token, essid, bssid):
|
||||
if is_excluded(essid) or is_excluded(bssid):
|
||||
logging.debug("not reporting %s due to exclusion filter" % pcap_file)
|
||||
|
||||
elif grid_report_ap(essid, bssid):
|
||||
reported.append(net_id)
|
||||
REPORT.update(data={'reported': reported})
|
||||
else:
|
||||
logging.warning("no bssid found?!")
|
||||
else:
|
||||
logging.debug("grid: reporting disabled")
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("error while enrolling the unit")
|
||||
logging.exception("grid api error")
|
||||
|
52
pwnagotchi/plugins/default/quickdic.py
Normal file
52
pwnagotchi/plugins/default/quickdic.py
Normal file
@@ -0,0 +1,52 @@
|
||||
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
|
||||
__version__ = '1.0.0'
|
||||
__name__ = 'quickdic'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'Run a quick dictionary scan against captured handshakes'
|
||||
|
||||
'''
|
||||
Aircrack-ng needed, to install:
|
||||
> apt-get install aircrack-ng
|
||||
Upload wordlist files in .txt format to folder in config file (Default: /opt/wordlists/)
|
||||
Cracked handshakes stored in handshake folder as [essid].pcap.cracked
|
||||
'''
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import string
|
||||
import re
|
||||
|
||||
OPTIONS = dict()
|
||||
|
||||
def on_loaded():
|
||||
logging.info("Quick dictionary check plugin loaded")
|
||||
|
||||
def on_handshake(agent, filename, access_point, client_station):
|
||||
display = agent._view
|
||||
|
||||
result = subprocess.run(('/usr/bin/aircrack-ng '+ filename +' | grep "1 handshake" | awk \'{print $2}\''),shell=True, stdout=subprocess.PIPE)
|
||||
result = result.stdout.decode('utf-8').translate({ord(c) :None for c in string.whitespace})
|
||||
if not result:
|
||||
logging.info("[quickdic] No handshake")
|
||||
else:
|
||||
logging.info("[quickdic] Handshake confirmed")
|
||||
result2 = subprocess.run(('aircrack-ng -w `echo '+OPTIONS['wordlist_folder']+'*.txt | sed \'s/\ /,/g\'` -l '+filename+'.cracked -q -b '+result+' '+filename+' | grep KEY'),shell=True,stdout=subprocess.PIPE)
|
||||
result2 = result2.stdout.decode('utf-8').strip()
|
||||
logging.info("[quickdic] "+result2)
|
||||
if result2 != "KEY NOT FOUND":
|
||||
key = re.search('\[(.*)\]', result2)
|
||||
pwd = str(key.group(1))
|
||||
set_text("Cracked password: "+pwd)
|
||||
display.update(force=True)
|
||||
|
||||
text_to_set = "";
|
||||
def set_text(text):
|
||||
global text_to_set
|
||||
text_to_set = text
|
||||
|
||||
def on_ui_update(ui):
|
||||
global text_to_set
|
||||
if text_to_set:
|
||||
ui.set('face', "(·ω·)")
|
||||
ui.set('status', text_to_set)
|
||||
text_to_set = ""
|
@@ -1,5 +1,6 @@
|
||||
import _thread
|
||||
from threading import Lock
|
||||
from PIL import Image
|
||||
|
||||
import shutil
|
||||
import logging
|
||||
@@ -117,30 +118,30 @@ class Display(View):
|
||||
else:
|
||||
logging.info("could not get ip of usb0, video server not starting")
|
||||
|
||||
def _is_inky(self):
|
||||
def is_inky(self):
|
||||
return self._display_type in ('inkyphat', 'inky')
|
||||
|
||||
def _is_papirus(self):
|
||||
def is_papirus(self):
|
||||
return self._display_type in ('papirus', 'papi')
|
||||
|
||||
def _is_waveshare_v1(self):
|
||||
def is_waveshare_v1(self):
|
||||
return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1')
|
||||
|
||||
def _is_waveshare_v2(self):
|
||||
def is_waveshare_v2(self):
|
||||
return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2')
|
||||
|
||||
def _is_waveshare(self):
|
||||
return self._is_waveshare_v1() or self._is_waveshare_v2()
|
||||
def is_waveshare_any(self):
|
||||
return self.is_waveshare_v1() or self.is_waveshare_v2()
|
||||
|
||||
def _init_display(self):
|
||||
if self._is_inky():
|
||||
if self.is_inky():
|
||||
logging.info("initializing inky display")
|
||||
from inky import InkyPHAT
|
||||
self._display = InkyPHAT(self._display_color)
|
||||
self._display.set_border(InkyPHAT.BLACK)
|
||||
self._render_cb = self._inky_render
|
||||
|
||||
elif self._is_papirus():
|
||||
elif self.is_papirus():
|
||||
logging.info("initializing papirus display")
|
||||
from pwnagotchi.ui.papirus.epd import EPD
|
||||
os.environ['EPD_SIZE'] = '2.0'
|
||||
@@ -148,8 +149,9 @@ class Display(View):
|
||||
self._display.clear()
|
||||
self._render_cb = self._papirus_render
|
||||
|
||||
elif self._is_waveshare_v1():
|
||||
logging.info("initializing waveshare v1 display")
|
||||
elif self.is_waveshare_v1():
|
||||
if self._display_color == 'black':
|
||||
logging.info("initializing waveshare v1 display in monochromatic mode")
|
||||
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
|
||||
self._display = EPD()
|
||||
self._display.init(self._display.lut_full_update)
|
||||
@@ -157,7 +159,15 @@ class Display(View):
|
||||
self._display.init(self._display.lut_partial_update)
|
||||
self._render_cb = self._waveshare_render
|
||||
|
||||
elif self._is_waveshare_v2():
|
||||
else:
|
||||
logging.info("initializing waveshare v1 display 3-color mode")
|
||||
from pwnagotchi.ui.waveshare.v1.epd2in13bc import EPD
|
||||
self._display = EPD()
|
||||
self._display.init()
|
||||
self._display.Clear()
|
||||
self._render_cb = self._waveshare_bc_render
|
||||
|
||||
elif self.is_waveshare_v2():
|
||||
logging.info("initializing waveshare v2 display")
|
||||
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
|
||||
self._display = EPD()
|
||||
@@ -176,11 +186,11 @@ class Display(View):
|
||||
def clear(self):
|
||||
if self._display is None:
|
||||
logging.error("no display object created")
|
||||
elif self._is_inky():
|
||||
elif self.is_inky():
|
||||
self._display.Clear()
|
||||
elif self._is_papirus():
|
||||
elif self.is_papirus():
|
||||
self._display.clear()
|
||||
elif self._is_waveshare():
|
||||
elif self.is_waveshare_any():
|
||||
self._display.Clear(WHITE)
|
||||
else:
|
||||
logging.critical("unknown display type %s" % self._display_type)
|
||||
@@ -214,7 +224,7 @@ class Display(View):
|
||||
try:
|
||||
self._display.show()
|
||||
except:
|
||||
print("")
|
||||
logging.exception("error while rendering on inky")
|
||||
|
||||
def _papirus_render(self):
|
||||
self._display.display(self._canvas)
|
||||
@@ -222,11 +232,21 @@ class Display(View):
|
||||
|
||||
def _waveshare_render(self):
|
||||
buf = self._display.getbuffer(self._canvas)
|
||||
if self._is_waveshare_v1():
|
||||
if self.is_waveshare_v1():
|
||||
self._display.display(buf)
|
||||
elif self._is_waveshare_v2():
|
||||
elif self.is_waveshare_v2():
|
||||
self._display.displayPartial(buf)
|
||||
|
||||
def _waveshare_bc_render(self):
|
||||
buf_black = self._display.getbuffer(self._canvas)
|
||||
# emptyImage = Image.new('1', (self._display.height, self._display.width), 255)
|
||||
# buf_color = self._display.getbuffer(emptyImage)
|
||||
# self._display.display(buf_black,buf_color)
|
||||
|
||||
# Custom display function that only handles black
|
||||
# Was included in epd2in13bc.py
|
||||
self._display.displayBlack(buf_black)
|
||||
|
||||
def image(self):
|
||||
img = None
|
||||
if self._canvas is not None:
|
||||
|
@@ -4,7 +4,9 @@ PATH = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono'
|
||||
|
||||
Bold = ImageFont.truetype("%s-Bold.ttf" % PATH, 10)
|
||||
BoldSmall = ImageFont.truetype("%s-Bold.ttf" % PATH, 8)
|
||||
BoldBig = ImageFont.truetype("%s-Bold.ttf" % PATH, 25)
|
||||
Medium = ImageFont.truetype("%s.ttf" % PATH, 10)
|
||||
Small = ImageFont.truetype("%s.ttf" % PATH, 9)
|
||||
Huge = ImageFont.truetype("%s-Bold.ttf" % PATH, 25)
|
||||
|
||||
|
||||
|
@@ -12,6 +12,13 @@ class State(object):
|
||||
self._state[key] = elem
|
||||
self._changes[key] = True
|
||||
|
||||
def has_element(self, key):
|
||||
return key in self._state
|
||||
|
||||
def remove_element(self, key):
|
||||
del self._state[key]
|
||||
self._changes[key] = True
|
||||
|
||||
def add_listener(self, key, cb):
|
||||
with self._lock:
|
||||
self._listeners[key] = cb
|
||||
|
@@ -2,7 +2,7 @@ import _thread
|
||||
from threading import Lock
|
||||
import time
|
||||
import logging
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import ImageDraw
|
||||
|
||||
import pwnagotchi.utils as utils
|
||||
import pwnagotchi.plugins as plugins
|
||||
@@ -17,21 +17,26 @@ WHITE = 0xff
|
||||
BLACK = 0x00
|
||||
ROOT = None
|
||||
|
||||
|
||||
def setup_display_specifics(config):
|
||||
width = 0
|
||||
height = 0
|
||||
face_pos = (0, 0)
|
||||
name_pos = (0, 0)
|
||||
status_pos = (0, 0)
|
||||
status_font = fonts.Medium
|
||||
status_max_length = None
|
||||
|
||||
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
|
||||
fonts.setup(10, 8, 10, 25)
|
||||
fonts.setup(10, 8, 10, 28)
|
||||
|
||||
width = 212
|
||||
height = 104
|
||||
face_pos = (0, int(height / 4))
|
||||
name_pos = (5, int(height * .15))
|
||||
status_pos = (int(width / 2) - 15, int(height * .15))
|
||||
face_pos = (0, 37)
|
||||
name_pos = (5, 18)
|
||||
status_pos = (102, 18)
|
||||
status_font = fonts.Small
|
||||
status_max_length = 20
|
||||
|
||||
elif config['ui']['display']['type'] in ('papirus', 'papi'):
|
||||
fonts.setup(10, 8, 10, 23)
|
||||
@@ -41,9 +46,12 @@ def setup_display_specifics(config):
|
||||
face_pos = (0, int(height / 4))
|
||||
name_pos = (5, int(height * .15))
|
||||
status_pos = (int(width / 2) - 15, int(height * .15))
|
||||
status_font = fonts.Medium
|
||||
status_max_length = (width - status_pos[0]) // 6
|
||||
|
||||
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1',
|
||||
'ws_2', 'ws2', 'waveshare_2', 'waveshare2'):
|
||||
if config['ui']['display']['color'] == 'black':
|
||||
fonts.setup(10, 9, 10, 35)
|
||||
|
||||
width = 250
|
||||
@@ -51,8 +59,19 @@ def setup_display_specifics(config):
|
||||
face_pos = (0, 40)
|
||||
name_pos = (5, 20)
|
||||
status_pos = (125, 20)
|
||||
status_font = fonts.Medium
|
||||
else:
|
||||
fonts.setup(10, 8, 10, 25)
|
||||
|
||||
return width, height, face_pos, name_pos, status_pos
|
||||
width = 212
|
||||
height = 104
|
||||
face_pos = (0, int(height / 4))
|
||||
name_pos = (5, int(height * .15))
|
||||
status_pos = (int(width / 2) - 15, int(height * .15))
|
||||
status_font = fonts.Medium
|
||||
status_max_length = (width - status_pos[0]) // 6
|
||||
|
||||
return width, height, face_pos, name_pos, status_pos, status_font, status_max_length
|
||||
|
||||
|
||||
class View(object):
|
||||
@@ -67,7 +86,7 @@ class View(object):
|
||||
self._voice = Voice(lang=config['main']['lang'])
|
||||
|
||||
self._width, self._height, \
|
||||
face_pos, name_pos, status_pos = setup_display_specifics(config)
|
||||
face_pos, name_pos, status_pos, status_font, status_max_length = setup_display_specifics(config)
|
||||
|
||||
self._state = State(state={
|
||||
'channel': LabeledValue(color=BLACK, label='CH', value='00', position=(0, 0), label_font=fonts.Bold,
|
||||
@@ -98,10 +117,10 @@ class View(object):
|
||||
'status': Text(value=self._voice.default(),
|
||||
position=status_pos,
|
||||
color=BLACK,
|
||||
font=fonts.Medium,
|
||||
font=status_font,
|
||||
wrap=True,
|
||||
# the current maximum number of characters per line, assuming each character is 6 pixels wide
|
||||
max_length=(self._width - status_pos[0]) // 6),
|
||||
max_length=status_max_length),
|
||||
|
||||
'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK,
|
||||
position=(0, self._height - int(self._height * .12) + 1), label_font=fonts.Bold,
|
||||
@@ -124,9 +143,15 @@ class View(object):
|
||||
|
||||
ROOT = self
|
||||
|
||||
def has_element(self, key):
|
||||
self._state.has_element(key)
|
||||
|
||||
def add_element(self, key, elem):
|
||||
self._state.add_element(key, elem)
|
||||
|
||||
def remove_element(self, key):
|
||||
self._state.remove_element(key)
|
||||
|
||||
def width(self):
|
||||
return self._width
|
||||
|
||||
@@ -188,6 +213,11 @@ class View(object):
|
||||
faces.SAD,
|
||||
faces.LONELY)
|
||||
|
||||
def on_keys_generation(self):
|
||||
self.set('face', faces.AWAKE)
|
||||
self.set('status', self._voice.on_keys_generation())
|
||||
self.update()
|
||||
|
||||
def on_normal(self):
|
||||
self.set('face', faces.AWAKE)
|
||||
self.set('status', self._voice.on_normal())
|
||||
|
165
pwnagotchi/ui/waveshare/v1/epd2in13bc.py
Normal file
165
pwnagotchi/ui/waveshare/v1/epd2in13bc.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# *****************************************************************************
|
||||
# * | File : epd2in13bc.py
|
||||
# * | Author : Waveshare team
|
||||
# * | Function : Electronic paper driver
|
||||
# * | Info :
|
||||
# *----------------
|
||||
# * | This version: V4.0
|
||||
# * | Date : 2019-06-20
|
||||
# # | Info : python demo
|
||||
# -----------------------------------------------------------------------------
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documnetation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
import logging
|
||||
from . import epdconfig
|
||||
from PIL import Image
|
||||
import RPi.GPIO as GPIO
|
||||
# import numpy as np
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = 104
|
||||
EPD_HEIGHT = 212
|
||||
|
||||
class EPD:
|
||||
def __init__(self):
|
||||
self.reset_pin = epdconfig.RST_PIN
|
||||
self.dc_pin = epdconfig.DC_PIN
|
||||
self.busy_pin = epdconfig.BUSY_PIN
|
||||
self.cs_pin = epdconfig.CS_PIN
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
# Hardware reset
|
||||
def reset(self):
|
||||
epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
|
||||
epdconfig.delay_ms(200)
|
||||
epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset
|
||||
epdconfig.delay_ms(200)
|
||||
epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
|
||||
epdconfig.delay_ms(200)
|
||||
|
||||
def send_command(self, command):
|
||||
epdconfig.digital_write(self.dc_pin, GPIO.LOW)
|
||||
epdconfig.digital_write(self.cs_pin, GPIO.LOW)
|
||||
epdconfig.spi_writebyte([command])
|
||||
epdconfig.digital_write(self.cs_pin, GPIO.HIGH)
|
||||
|
||||
def send_data(self, data):
|
||||
epdconfig.digital_write(self.dc_pin, GPIO.HIGH)
|
||||
epdconfig.digital_write(self.cs_pin, GPIO.LOW)
|
||||
epdconfig.spi_writebyte([data])
|
||||
epdconfig.digital_write(self.cs_pin, GPIO.HIGH)
|
||||
|
||||
def ReadBusy(self):
|
||||
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
|
||||
epdconfig.delay_ms(100)
|
||||
|
||||
def init(self):
|
||||
if (epdconfig.module_init() != 0):
|
||||
return -1
|
||||
# EPD hardware init start
|
||||
self.reset()
|
||||
|
||||
self.send_command(0x06) # BOOSTER_SOFT_START
|
||||
self.send_data(0x17)
|
||||
self.send_data(0x17)
|
||||
self.send_data(0x17)
|
||||
|
||||
self.send_command(0x04) # POWER_ON
|
||||
self.ReadBusy()
|
||||
|
||||
self.send_command(0x00) # PANEL_SETTING
|
||||
self.send_data(0x8F)
|
||||
|
||||
self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING
|
||||
self.send_data(0xF0)
|
||||
|
||||
self.send_command(0x61) # RESOLUTION_SETTING
|
||||
self.send_data(self.width & 0xff)
|
||||
self.send_data(self.height >> 8)
|
||||
self.send_data(self.height & 0xff)
|
||||
return 0
|
||||
|
||||
def getbuffer(self, image):
|
||||
buf = [0xFF] * (int(self.width/8) * self.height)
|
||||
image_monocolor = image.convert('1')
|
||||
imwidth, imheight = image_monocolor.size
|
||||
pixels = image_monocolor.load()
|
||||
if(imwidth == self.width and imheight == self.height):
|
||||
for y in range(imheight):
|
||||
for x in range(imwidth):
|
||||
# Set the bits for the column of pixels at the current position.
|
||||
if pixels[x, y] == 0:
|
||||
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
|
||||
elif(imwidth == self.height and imheight == self.width):
|
||||
for y in range(imheight):
|
||||
for x in range(imwidth):
|
||||
newx = y
|
||||
newy = self.height - x - 1
|
||||
if pixels[x, y] == 0:
|
||||
buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8))
|
||||
return buf
|
||||
|
||||
def displayBlack(self, imageblack):
|
||||
self.send_command(0x10)
|
||||
for i in range(0, int(self.width * self.height / 8)):
|
||||
self.send_data(imageblack[i])
|
||||
self.send_command(0x92)
|
||||
|
||||
self.send_command(0x12) # REFRESH
|
||||
self.ReadBusy()
|
||||
|
||||
def display(self, imageblack, imagecolor):
|
||||
self.send_command(0x10)
|
||||
for i in range(0, int(self.width * self.height / 8)):
|
||||
self.send_data(imageblack[i])
|
||||
self.send_command(0x92)
|
||||
|
||||
self.send_command(0x13)
|
||||
for i in range(0, int(self.width * self.height / 8)):
|
||||
self.send_data(imagecolor[i])
|
||||
self.send_command(0x92)
|
||||
|
||||
self.send_command(0x12) # REFRESH
|
||||
self.ReadBusy()
|
||||
|
||||
def Clear(self):
|
||||
self.send_command(0x10)
|
||||
for i in range(0, int(self.width * self.height / 8)):
|
||||
self.send_data(0xFF)
|
||||
self.send_command(0x92)
|
||||
|
||||
self.send_command(0x13)
|
||||
for i in range(0, int(self.width * self.height / 8)):
|
||||
self.send_data(0xFF)
|
||||
self.send_command(0x92)
|
||||
|
||||
self.send_command(0x12) # REFRESH
|
||||
self.ReadBusy()
|
||||
|
||||
def sleep(self):
|
||||
self.send_command(0x02) # POWER_OFF
|
||||
self.ReadBusy()
|
||||
self.send_command(0x07) # DEEP_SLEEP
|
||||
self.send_data(0xA5) # check code
|
||||
|
||||
# epdconfig.module_exit()
|
||||
### END OF FILE ###
|
||||
|
@@ -28,6 +28,10 @@ class Voice:
|
||||
self._('AI ready.'),
|
||||
self._('The neural network is ready.')])
|
||||
|
||||
def on_keys_generation(self):
|
||||
return random.choice([
|
||||
self._('Generating keys, do not turn off ...')])
|
||||
|
||||
def on_normal(self):
|
||||
return random.choice([
|
||||
'',
|
||||
|
Reference in New Issue
Block a user