File: 001.md

package info (click to toggle)
glome 0.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 716 kB
  • sloc: ansic: 2,468; python: 508; sh: 149; makefile: 20
file content (154 lines) | stat: -rw-r--r-- 7,390 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
---
authors: Markus Rudy (@burgerdev)
state: committed
---

# RFD 001: GLOME Login v2

## Objective

Make the GLOME Login Protocol unambiguous.

## Background

See also [google/glome#62](https://github.com/google/glome/issues/62).

- The ambiguous interpretation of `prefix7` may lead to a change of server authorization behaviour that cannot be controlled by the client (e.g. a new key is added to the server whose index conflicts with a public key prefix of an existing one).
- It's currently legal to have colon (`:`) and slash (`/`) characters in all message fields, which may cause ambiguity in parsing and, ultimately, lead to authorization of unintended messages.
- The protocol gives advice to "maximize the human readability of the URL", which conflicts with an unambiguous presentation of said characters in percent-encoded form.

## Requirements

- There must be a well-defined interpretation of the GLOME Login handshake
  that does not depend on the public keys a server holds.
- There must be a well-defined, bijective conversion from the message embedded
  in a GLOME Login URL to the message being authorized.
- Subject to the preceding requirements, the URL layout should be optimized for
  human readability (e.g. don't encode
  [unreserved characters](https://www.rfc-editor.org/rfc/rfc3986#section-2.3))
  and brevity.
  - Assuming humans will have to read the message to be authorized much more often than parse the involved keys.

## Design ideas

- The `prefix-type` bit determines interpretation of `prefix7`:
  * 0: `prefix7` is matched with the high byte of the server's public key
  * 1: `prefix7` is an index into the server's public keys.
- The GLOME Login challenge is a URI path.
- Completely specify the encoding and decoding of the message part.
- Include detailed instructions for server and client into the protocol.
- Publish the result as GLOME Login v2, as it is incompatible with v1 URLs.

### `prefix7`

The most significant bit of a 256bit X25519 public key should not be interpreted by the Diffie-Hellman key exchange [RFC7748]. We use this fact to define a `prefix7` config that is somewhat self-configuring: `prefix-type` is 0, `prefix7` is the high byte of the server's public key, and thus the 8bit prefix is, too. Alternatively, if indices are to be used, `prefix-type` is 1 and `prefix7` is the index between 0 and 127, inclusive. Note that this is a large amount of public keys, even in case of automatic rotation - if this is a concern, `prefixN` can be used to verify (or pick) the public key on the server side.

Note that this is incompatible with _all_ subsets of v1: indices need to be taken `mod 128`, and public key prefixes are now taken from the MSB, not the LSB.

[RFC7748]: https://www.rfc-editor.org/rfc/rfc7748#section-5

### URI

Prior versions of GLOME assumed that the challenge would always be rendered as a URL. This is not true in many cases: for example, a URL challenge does not make too much sense for a response generated with the `glome` cli. On the other hand, presenting the challenge as a URL works reasonably well in practice, so we don't want to change the challenge format in an incompatible way. Thus, a challenge in v2 is what used to be the URL path in v1.

```abnf
challenge = "v2/" handshake-segment "/" message "/"
```

A URI path can still be prefixed with scheme and host to build a URL. Subsequent sections describe how a challenge is encoded to a valid URI path and how to compute the tag over that encoding.

### Message

New restrictions to make message encoding unambiguous:

- `hostid-type` and `hostid` must not contain the `:` character.
- `hostid-type`, `hostid` and `action` should not contain any characters that would be escaped in a URI path segment (as detailed below). Differing from previous protocol versions, `/` is discouraged.

#### URI Path Segments

The URI specification [RFC 3986](https://www.ietf.org/rfc/rfc3986.html#section-3.3) defines a path segment as

```abnf
segment = *pchar
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
sub-delims  = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
pct-encoded = "%" HEXDIG HEXDIG
```

where HEXDIG should refer to a digit or an uppercase letter A-F. This matches the definition in <https://url.spec.whatwg.org/#url-path-segment-string>, which supposedly supersedes the RFC.

Thus, define `EscapePathSegment` as a function that escapes all characters that are not unreserved, sub delimiters, `:` or `@`. See the Appendix for how this function can be implemented in some of the major programming languages.

#### Message Encoding

Constructing a message takes three parameters: `hostid`, `action` and (optionally) `hostid-type`. These are encoded into a `message` - a URI (sub)path - using `EscapePathSegment` as follows.

```abnf
message = host-segment "/" action-segment
host-segment = EscapePathSegment( [hostid-type ":"] hostid )
action-segment = EscapePathSegment(action)
```

The `hostid-type` prefix is added if and only if the `hostid-type` of the message is not empty.

Note that this voids some of the existing recommendations for 'good' actions: `shell/root`, for example, would have to be escaped and thus be less readable. Instead, using URI sub-delimiters as in `shell=root` should be recommended. This format would interact nicely with a host-identity-based authorization scheme working with key-value pairs.

#### Message Decoding

Given a URI path, strip the path prefix up to including the `/` after the handshake message. Split the remaining string on the character `/` and keep only the first and second element, denoted `host-segment` and `action-segment`; or fail if there are less than two elements. Replace all percent-encoded octets in the `host-segment` with their raw, unencoded form. Split the result at the character `:`. If there is one element, assign that element to `hostid` and assign the literal string `hostname` to `hostid-type`; if there are two elements assign the first one to `hostid-type` and the second to `hostid`; if there are more than two elements, fail. Replace all percent-encoded octets in the `action-segment` with their raw, unencoded form, and assign the result to `action`.

#### Message Tagging

The tag for a message is produced by passing the **encoded message** string into `glome_tag`.

## Alternatives considered

### Allow unescaped slashes in action

- Allow an action to span more than one path segment.
  - This prevents us from having an unambiguous encoding: `xxx/yyy%2Fzzz` vs. `xxx/yyy/zzz`.

### Calculate the tag on the unescaped message

- Tag the message before URL escaping.
  - This would have the benefit of decoupling the tagging from the transport (here, URL segments).
  - However, we need to encode the message into a byte array before we can tag it. This encoding must be unambiguous as well, simply concatenating the triple won't cut it.

## Appendix

### URI Path Escaping APIs

#### Python

```python
urllib.parse.quote(segment, safe=":@!$&'()*+,;=")
```

#### Golang

:'-( <https://github.com/golang/go/issues/27559>

#### C

GLib:

```c
g_uri_escape_string(segment, ":@!$&'()*+,;=", /*allow_utf8=*/false);
```

#### Java

Guava:

```java
com.google.common.net.UrlEscapers.urlPathSegmentEscaper().escape(segment)
```

#### OCaml

Uri:

```ocaml
Uri.pct_encode segment
```