source: trunk/src/allmydata/introducer/common.py

Last change on this file was 1cfe843d, checked in by Alexandre Detiste <alexandre.detiste@…>, at 2024-02-22T23:40:25Z

more python2 removal

  • Property mode set to 100644
File size: 4.7 KB
Line 
1"""
2Ported to Python 3.
3"""
4
5import re
6
7from foolscap.furl import decode_furl
8from allmydata.crypto.util import remove_prefix
9from allmydata.crypto import ed25519
10from allmydata.util import base32, jsonbytes as json
11
12
13def get_tubid_string_from_ann(ann):
14    furl = ann.get("anonymous-storage-FURL") or ann.get("FURL")
15    return get_tubid_string(furl)
16
17def get_tubid_string(furl):
18    m = re.match(r'pb://(\w+)@', furl)
19    assert m
20    return m.group(1).lower().encode("ascii")
21
22
23def sign_to_foolscap(announcement, signing_key):
24    """
25    :param signing_key: a (private) signing key, as returned from
26        e.g. :func:`allmydata.crypto.ed25519.signing_keypair_from_string`
27
28    :returns: 3-tuple of (msg, sig, vk) where msg is a UTF8 JSON
29        serialization of the `announcement` (bytes), sig is bytes (a
30        signature of msg) and vk is the verifying key bytes
31    """
32    # return (bytes, sig-str, pubkey-str). A future HTTP-based serialization
33    # will use JSON({msg:b64(JSON(msg).utf8), sig:v0-b64(sig),
34    # pubkey:v0-b64(pubkey)}) .
35    msg = json.dumps(announcement).encode("utf-8")
36    sig = b"v0-" + base32.b2a(
37        ed25519.sign_data(signing_key, msg)
38    )
39    verifying_key_string = ed25519.string_from_verifying_key(
40        ed25519.verifying_key_from_signing_key(signing_key)
41    )
42    ann_t = (msg, sig, remove_prefix(verifying_key_string, b"pub-"))
43    return ann_t
44
45
46class UnknownKeyError(Exception):
47    pass
48
49
50def unsign_from_foolscap(ann_t):
51    (msg, sig_vs, claimed_key_vs) = ann_t
52    if not sig_vs or not claimed_key_vs:
53        raise UnknownKeyError("only signed announcements recognized")
54    if not sig_vs.startswith(b"v0-"):
55        raise UnknownKeyError("only v0- signatures recognized")
56    if not claimed_key_vs.startswith(b"v0-"):
57        raise UnknownKeyError("only v0- keys recognized")
58
59    claimed_key = ed25519.verifying_key_from_string(b"pub-" + claimed_key_vs)
60    sig_bytes = base32.a2b(remove_prefix(sig_vs, b"v0-"))
61    ed25519.verify_signature(claimed_key, sig_bytes, msg)
62    key_vs = claimed_key_vs
63    ann = json.loads(msg.decode("utf-8"))
64    return (ann, key_vs)
65
66
67class SubscriberDescriptor(object):
68    """This describes a subscriber, for status display purposes. It contains
69    the following attributes:
70
71    .service_name: what they subscribed to (string)
72    .when: time when they subscribed (seconds since epoch)
73    .nickname: their self-provided nickname, or "?" (unicode)
74    .version: their self-provided version (string)
75    .app_versions: versions of each library they use (dict str->str)
76    .remote_address: the external address from which they connected (string)
77    .tubid: for subscribers connecting with Foolscap, their tubid (string)
78    """
79
80    def __init__(self, service_name, when,
81                 nickname, version, app_versions,
82                 remote_address, tubid):
83        self.service_name = service_name
84        self.when = when
85        self.nickname = nickname
86        self.version = version
87        self.app_versions = app_versions
88        self.remote_address = remote_address
89        self.tubid = tubid
90
91class AnnouncementDescriptor(object):
92    """This describes an announcement, for status display purposes. It
93    contains the following attributes, which will be empty ("" for
94    strings) if the client did not provide them:
95
96     .when: time the announcement was first received (seconds since epoch)
97     .index: the announcements 'index', a tuple of (string-or-None).
98             The server remembers one announcement per index.
99     .canary: a Referenceable on the announcer, so the server can learn
100              when they disconnect (for the status display)
101     .announcement: raw dictionary of announcement data
102     .service_name: which service they are announcing (string)
103     .version: 'my-version' portion of announcement (string)
104     .nickname: their self-provided nickname, or "" (unicode)
105     .serverid: the server identifier. This is a pubkey (for V2 clients),
106                or a tubid (for V1 clients).
107     .connection_hints: where they listen (list of strings) if the
108                        announcement included a key for
109                        'anonymous-storage-FURL', else an empty list.
110    """
111
112    def __init__(self, when, index, canary, ann_d):
113        self.when = when
114        self.index = index
115        self.canary = canary
116        self.announcement = ann_d
117        self.service_name = ann_d["service-name"]
118        self.version = ann_d.get("my-version", "")
119        self.nickname = ann_d.get("nickname", u"")
120        (_, key_s) = index
121        self.serverid = key_s
122        furl = ann_d.get("anonymous-storage-FURL")
123        if furl:
124            _, self.connection_hints, _ = decode_furl(furl)
125        else:
126            self.connection_hints = []
Note: See TracBrowser for help on using the repository browser.