mirror of
https://github.com/adulau/abstract-key-server.git
synced 2024-12-22 08:46:00 +00:00
Alexandre Dulaunoy
95e460655d
Only supported query is: curl http://127.0.0.1:5000/pks/lookup?op=get\&search=0x3b12dcc282fa29312f5b709a09e2cd4944e6cbcd
590 lines
No EOL
24 KiB
Text
590 lines
No EOL
24 KiB
Text
|
||
Internet-Draft D. Shaw
|
||
Expires September 2003 Jabberwocky Tech
|
||
March 2003
|
||
|
||
The OpenPGP HTTP Keyserver Protocol (HKP)
|
||
draft-shaw-openpgp-hkp-00.txt
|
||
|
||
Status of this Memo
|
||
|
||
This document is an Internet-Draft and is in full conformance with
|
||
all provisions of Section 10 of RFC2026.
|
||
|
||
Internet-Drafts are working documents of the Internet Engineering
|
||
Task Force (IETF), its areas, and its working groups. Note that
|
||
other groups may also distribute working documents as
|
||
Internet-Drafts.
|
||
|
||
Internet-Drafts are draft documents valid for a maximum of six months
|
||
and may be updated, replaced, or obsoleted by other documents at any
|
||
time. It is inappropriate to use Internet-Drafts as reference
|
||
material or to cite them other than as "work in progress."
|
||
|
||
The list of current Internet-Drafts can be accessed at
|
||
http://www.ietf.org/ietf/1id-abstracts.txt
|
||
|
||
The list of Internet-Draft Shadow Directories can be accessed at
|
||
http://www.ietf.org/shadow.html.
|
||
|
||
Abstract
|
||
|
||
This document specifies a series of conventions to implement an
|
||
OpenPGP keyserver using the Hypertext Transfer Protocol (HTTP). As
|
||
this document is a codification and extension of a protocol that is
|
||
already in wide use, strict attention is paid to backward
|
||
compatibility with these existing implementations.
|
||
|
||
Table of Contents
|
||
|
||
Status of this Memo ...................................... 1
|
||
Abstract ................................................. 1
|
||
Table of Contents ........................................ 1
|
||
1. Introduction ............................................. 2
|
||
1.1 Terms ................................................ 2
|
||
2. HKP and HTTP ............................................. 2
|
||
3. Requesting Data From A Keyserver ......................... 3
|
||
3.1 Basic Variables ...................................... 3
|
||
3.1.1 The "search" variable .......................... 3
|
||
3.1.1.1 Key ID and V4 Fingerprint Searches ..... 3
|
||
3.1.1.2 V3 Fingerprint Searches ................ 3
|
||
3.1.1.3 Text Searches .......................... 4
|
||
3.1.2 The "op" (operation) Variable .................. 4
|
||
3.1.2.1 The "get" operation .................... 4
|
||
3.1.2.2 The "index" operation .................. 4
|
||
3.1.2.3 The "vindex" (verbose index) operation.. 4
|
||
3.1.2.4 Other operations ....................... 5
|
||
|
||
Shaw [Page 1]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
3.2 Modifier Variables ................................... 5
|
||
3.2.1 The "options" variable ......................... 5
|
||
3.2.2 The "fingerprint" variable ..................... 5
|
||
3.2.3 The "exact" variable ........................... 6
|
||
3.2.3 Other variables ................................ 6
|
||
3.4 Request Examples ..................................... 6
|
||
4. Submitting Keys To A Keyserver ........................... 6
|
||
5. Output Formats ........................................... 6
|
||
5.1 Machine Readable Output .............................. 7
|
||
5.2 Machine Readable Indexes ............................. 7
|
||
6. Extended Status Codes .................................... 8
|
||
7. Locating a HKP Keyserver ................................. 9
|
||
8. Security Considerations .................................. 9
|
||
9. IANA Considerations ...................................... 9
|
||
10. Normative References ..................................... 10
|
||
11. Author's Address ......................................... 10
|
||
|
||
1. Introduction
|
||
|
||
For ease of use, public key cryptography requires a key distribution
|
||
system. For many years, the most commonly used system has been a key
|
||
server - a server that stores public keys and can be searched for a
|
||
given key. The HTTP Keyserver Protocol is a OpenPGP keyserver
|
||
implemented using HTTP.
|
||
|
||
1.1. Terms
|
||
|
||
This document uses the terms "MUST", "SHOULD" and "MAY" as defined in
|
||
RFC-2119 [1], along with the negated forms of those terms.
|
||
|
||
2. HKP and HTTP
|
||
|
||
As HKP is implemented over HTTP, everything in RFC-1945 [2] applies
|
||
to HKP as well, and HKP error codes are the same as the ones used in
|
||
HTTP. In order to give as much information to the client about error
|
||
conditions, is good practice to return the most specific error code
|
||
possible: for example, returning 404 ("Not Found") rather than 400
|
||
("Bad Request") when a key is not found.
|
||
|
||
This document gives suggested HTTP error codes for several common
|
||
situations. Note that these are only suggestions, and
|
||
implementations may have good reasons (such as not revealing the
|
||
reason why a request failed) for using other error codes.
|
||
|
||
Due the very large deployment of HKP clients based on HTTP version
|
||
1.0, HKP keyservers MUST support HTTP 1.0. HKP keyservers MAY
|
||
additionally support other HTTP versions.
|
||
|
||
[ dshaw : I expect this to be controversial, but we've got tons of
|
||
deployed code that only works with 1.0. I'd be willing to discuss
|
||
removing this MUST or make it a SHOULD and add a "implementation
|
||
notes" section pointing out the problem instead. ]
|
||
|
||
By convention and history, HKP uses HTTP on TCP port 11371. It has
|
||
|
||
Shaw [Page 2]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
been suggested by some that for reasons of maximum compatibility
|
||
with firewalls and filtering HTTP proxies, it is better to use the
|
||
standard HTTP port (TCP port 80). See section 7, Locating a HKP
|
||
Keyserver for an automated way for clients to discover the correct
|
||
port.
|
||
|
||
3. Requesting Data From A Keyserver
|
||
|
||
Keyserver requests are done via a HTTP GET URL that encodes the
|
||
request within it. Specifically, the abs_path (see [2], section
|
||
3.2) is built up of the base request "/pks/lookup", followed by any
|
||
variables. Arguments are passed through the usual means as
|
||
specified in [3], section 8.2.2. The variables may be given in any
|
||
order. Keyservers MUST ignore any unknown variables.
|
||
|
||
3.1. Basic Variables
|
||
|
||
All HKP requests contain the "op" (operation) and "search"
|
||
variables. The "op" variable determines what operation the
|
||
keyserver will take, and the "search" variable determines that keys
|
||
are operated on.
|
||
|
||
3.1.1. The "search" Variable
|
||
|
||
The search variable contains arbitrary text encoded as usual for a
|
||
HTTP URL. This text may represent the key ID of the key being
|
||
sought or some text from a user ID on the key being sought.
|
||
|
||
If any particular type of searching is not supported, the keyserver
|
||
should return an appropriate HTTP error code such as 501 ("Not
|
||
Implemented"). The server MUST NOT return an error code (such as
|
||
404 ("Not Found")) that could be mistaken by the client for a valid
|
||
response.
|
||
|
||
3.1.1.1. Key ID and V4 Fingerprint Searches
|
||
|
||
If a key is being sought by its key ID, the key ID string is
|
||
prefixed with an "0x" to indicate a hexadecimal number. Key ID
|
||
strings may be 8 digits (32-bit key ID), 16 digits (64-bit key ID),
|
||
32 digits (version 3 fingerprint), or 40 digits (version 4
|
||
fingerprint). The hexadecimal digits are not case sensitive.
|
||
|
||
A keyserver that allows searching by keyid MUST accept the 160-bit
|
||
version 4 fingerprint, 64-bit key IDs, and 32-bit key IDs in the
|
||
"search" variable. Note this does not mean that the keyserver will
|
||
actually use the full length of the request in the search, as it
|
||
may internally create a 32-bit or 64-bit key ID from a version 4
|
||
fingerprint (by taking the low-order 32 or 64 bits respectively),
|
||
or a 32-bit key ID from a 64-bit key ID (by taking low-order 32
|
||
bits). That said, a keyserver SHOULD use at least 64 bits of the
|
||
key ID if available.
|
||
|
||
3.1.1.2. V3 Fingerprint Searches
|
||
|
||
|
||
Shaw [Page 3]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
The 128-bit version 3 fingerprint is represented by a leading "0x",
|
||
followed by 32 case-insensitive hexadecimal digits. Note that v3
|
||
fingerprints are treated differently and not grouped with keyid or
|
||
v4 fingerprint searches as it is not possible to calculate a 64-bit
|
||
or 32-bit keyid from a v3 fingerprint.
|
||
|
||
3.1.1.3. Text Searches
|
||
|
||
How a keyserver handles a textual search is implementation defined.
|
||
See also the definition of the "exact" variable for a method to
|
||
give additional instructions to the server on how the search is to
|
||
be executed.
|
||
|
||
3.1.2. The "op" (operation) Variable
|
||
|
||
The op variable specifies the operation to be performed on the
|
||
keyserver. The op variable is generally used with the "search"
|
||
variable to specify the keys that should be operated on.
|
||
|
||
3.1.2.1. The "get" operation
|
||
|
||
The "get" operation requests keys from the keyserver. A string that
|
||
specifies which key(s) to return is provided in the "search"
|
||
variable.
|
||
|
||
The response to a successful "get" request is a HTTP document
|
||
containing a keyring as specified in RFC-2440 [4], section 11.1, and
|
||
ASCII armored as specified in section 6.2.
|
||
|
||
The response may be wrapped in any HTML or other text desired, except
|
||
that the actual key data consisting of an initial line break, the
|
||
"-----BEGIN PGP PUBLIC KEY BLOCK-----" header, the armored key data
|
||
itself, the "-----END PGP PUBLIC KEY BLOCK-----" header, and a final
|
||
line break MUST NOT be modified from the form specified in [4].
|
||
|
||
If no keys match the request, the keyserver should return an
|
||
appropriate HTTP error code such as 404 ("Not Found").
|
||
|
||
3.1.2.2. The "index" Operation
|
||
|
||
The "index" operation requests a list of keys on the keyserver that
|
||
match the text or key ID in the "search" variable. Historically, the
|
||
"index" operation returned a human readable HTML document containing
|
||
links for each found key, but this is not required.
|
||
|
||
If the "index" operation is not supported, the keyserver should
|
||
return an appropriate HTTP error code such as 501 ("Not
|
||
Implemented").
|
||
|
||
3.1.2.3. The "vindex" (verbose index) Operation
|
||
|
||
The "vindex" operation is similar to "index" in that it provides a
|
||
list of keys on the keyserver that match the text of key ID in the
|
||
"search" variable. Historically, a "vindex" response was the same as
|
||
|
||
Shaw [Page 4]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
"index" with the addition of showing the signatures on each key, but
|
||
this is not required.
|
||
|
||
If the "vindex" operation is not supported, the keyserver should
|
||
return an appropriate HTTP error code such as 501 ("Not
|
||
Implemented").
|
||
|
||
3.1.2.4. Other Operations
|
||
|
||
Other site-specific or nonstandard operations can be indicated by
|
||
prefixing the operation name with the string "x-".
|
||
|
||
3.2. Modifier Variables
|
||
|
||
These variables are used to modify basic requests.
|
||
|
||
3.2.1. The "options" Variable
|
||
|
||
This variable takes one or more arguments, separated by commas.
|
||
These are used to modify the behavior of the keyserver on a
|
||
per-request basis.
|
||
|
||
3.2.1.1. The "mr" (Machine Readable) Option
|
||
|
||
The machine readable option instructs the server that a program
|
||
(rather than a person) is making the request, so the output may be
|
||
customized for that use. See Section 5, Output Formats for the
|
||
specific details of machine readable output.
|
||
|
||
3.2.1.2. The "nm" (No Modification) Option
|
||
|
||
As keyservers may modify submitted keys to suit a particular
|
||
policy, this option is used to inform the keyserver that the
|
||
submitter would rather have the submission fail completely then
|
||
have the submitted key(s) modified. An example of this would be a
|
||
keyserver that does not accept user IDs with an email address
|
||
outside of the local domain. If such a key was submitted, the
|
||
keyserver could trim any noncompliant user IDs before accepting the
|
||
key. If this option was set, then the key submission would fail.
|
||
|
||
3.2.1.3. Other Options
|
||
|
||
Other site-specific or nonstandard options can be indicated by
|
||
prefixing the option name with the string "x-".
|
||
|
||
3.2.2. The "fingerprint" Variable
|
||
|
||
This variable takes one argument: "on" or "off". If present and
|
||
on, it instructs the server to provide the key fingerprint for each
|
||
key in an "index" or "vindex" operation. This variable has no
|
||
effect on any other operation. The exact format of the displayed
|
||
fingerprint, like the "index" and "vindex" operations themselves,
|
||
is implementation defined.
|
||
|
||
|
||
Shaw [Page 5]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
3.2.3. The "exact" Variable
|
||
|
||
This variable takes one argument: "on" or "off". If present and
|
||
on, it instructs the server to search for an exact match for the
|
||
contents of the "search" variable. The exact meaning of "exact
|
||
match" is implementation defined.
|
||
|
||
3.2.3. Other Variables
|
||
|
||
Other site-specific or nonstandard variables can be indicated by
|
||
prefixing the variable name with the string "x-".
|
||
|
||
3.4. Request Examples
|
||
|
||
Search for all keys containing the string "dshaw":
|
||
http://keys.example.com:11371/pks/lookup?search=dshaw&op=index
|
||
|
||
Get key 0x99242560 (32-bit key ID):
|
||
http://keys.example.com:11371/pks/lookup?op=get&search=0x99242560
|
||
|
||
4. Submitting Keys To A Keyserver
|
||
|
||
Keyserver submissions are done via a HTTP POST URL. Specifically,
|
||
the abs_path (see [2], section 3.2) is set to "/pks/add", and the key
|
||
data is provided via HTTP POST as specified in [2], section 8.3, and
|
||
[3], section 8.2.3.
|
||
|
||
The body of the POST message contains a "keytext" variable which is
|
||
set to an ASCII armored keyring as specified in [4], sections 6.2
|
||
and 11.1. The ASCII armored keyring should also be urlencoded as
|
||
specified in [3], section 8.2.1. Note that more than one key may
|
||
be submitted in a single transaction.
|
||
|
||
There may also be an "options" variable, as specified in section
|
||
3.2.1 above.
|
||
|
||
If a keyserver does not support adding keys via HTTP, then requests
|
||
to do so should return an appropriate HTTP error code, such as 403
|
||
("Forbidden") if key submission has been disallowed, or 501 ("Not
|
||
Implemented") if the server does not support HTTP key submission.
|
||
The keyserver MUST NOT return an error code (such as 404 ("Not
|
||
Found")) that could be mistaken by the client for a valid response.
|
||
|
||
5. Output Formats
|
||
|
||
HKP is intended for both human and programmatic use. The "machine
|
||
readable" option is used to tailor the output for a given use. In
|
||
general, the "human readable" output is implementation specific.
|
||
For interoperability, the "machine readable" output MUST carefully
|
||
follow the guidelines given here.
|
||
|
||
Note that there is an installed base of programs that do in fact
|
||
attempt to parse the human readable "index" format used in the pksd
|
||
keyserver. Care should be taken with the choice of an "index" format
|
||
|
||
Shaw [Page 6]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
if compatibility with these programs is desired.
|
||
|
||
5.1. Machine Readable Output
|
||
|
||
When machine readable output is requested, several changes are made
|
||
to output:
|
||
|
||
- Key retrievals (op=get) do not contain any text aside from the
|
||
ASCII armored keyring. The document is also sent to the user
|
||
using Content-Type: application/pgp-keys as specified in RFC-3156
|
||
[6].
|
||
|
||
- Key indexes (op=index) use the format specified in Section 5.2,
|
||
Machine Readable Indexes.
|
||
|
||
5.2. Machine Readable Indexes
|
||
|
||
The machine readable index format is a list of records that can be
|
||
easily parsed by a machine. The document is 7-bit clean, and as such
|
||
is sent with no encoding and Content-Type: text/plain.
|
||
|
||
The machine readable response begins with an optional information
|
||
line:
|
||
|
||
info:<version>:<count>
|
||
|
||
<version> = this is the version of this output format.
|
||
Currently, this is the number 1.
|
||
|
||
<count> = the number of keys returned in this response. Note
|
||
this is the number of keys, and not the number of
|
||
lines returned. That is, it should match the number
|
||
of "pub:" lines returned.
|
||
|
||
If this optional line is not included, or the version information is
|
||
not supplied, the version number is assumed to be 1.
|
||
|
||
The key listings themselves are made up of several lines per key.
|
||
The first line specifies the primary key:
|
||
|
||
pub:<keyid>:<algo>:<keylen>:<creationdate>:<expirationdate>:<flags>
|
||
|
||
<keyid> = this is either the fingerprint or the key ID of the
|
||
key. Either the 16-digit or 8-digit key IDs are
|
||
acceptable, but obviously the fingerprint is best. A
|
||
keyserver should use the most specific of the key IDs
|
||
that it has available. Since it is not possible to
|
||
calculate the key ID from a V3 key fingerprint, for V3
|
||
keys this should be either the 16-digit or 8-digit
|
||
key ID only.
|
||
|
||
<algo> = the algorithm number from [4]. (i.e. 1==RSA, 17==DSA,
|
||
etc).
|
||
|
||
|
||
Shaw [Page 7]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
<keylen> = the key length (i.e. 1024, 2048, 4096, etc.)
|
||
|
||
<creationdate> = creation date of the key in standard RFC-2440
|
||
[4] form (i.e. number of seconds since 1/1/1970
|
||
UTC time)
|
||
|
||
<expirationdate> = expiration date of the key in standard
|
||
RFC-2440 [4] form (i.e. number of seconds
|
||
since 1/1/1970 UTC time)
|
||
|
||
<flags> = letter codes to indicate details of the key, if any.
|
||
Flags may be in any order. The meaning of "disabled"
|
||
is implementation-specific. Note that individual
|
||
flags may be unimplemented, so the absence of a given
|
||
flag does not necessarily mean the absence of the
|
||
detail.
|
||
|
||
r == revoked
|
||
d == disabled
|
||
e == expired
|
||
|
||
Following the "pub" line are one or more "uid" lines to indicate user
|
||
IDs on the key:
|
||
|
||
uid:<escaped uid string>:<creationdate>:<expirationdate>:<flags>
|
||
|
||
<escaped uid string> = the user ID string, with HTTP %-escaping
|
||
for anything that isn't 7-bit safe as
|
||
well as for the ":" character. Any other
|
||
characters may be escaped, as desired.
|
||
|
||
creationdate, expirationdate, and flags mean the same here as in the
|
||
"pub" line. The information is taken from the self-signature, if
|
||
any, and applies to the user ID in question, and not to the key as a
|
||
whole.
|
||
|
||
Note that empty fields are allowed. For example, a key with no
|
||
expiration date would have the <expirationdate> field empty. Also,
|
||
a keyserver that does not track a particular piece of information
|
||
may leave that field empty as well. Colons for empty fields on the
|
||
end of each line may be left off, if desired.
|
||
|
||
6. Extended Status Codes
|
||
|
||
As HKP is implemented over HTTP, when a status or error code needs
|
||
to be returned, the most appropriate HTTP code should be used.
|
||
|
||
Occasionally there is a need to express a condition that cannot be
|
||
expressed via the HTTP 1.0 status code list. In these cases, an
|
||
additional HTTP header may be added to the response. This
|
||
additional header is of the form "X-HKP-Status:" and is followed by
|
||
one of the following status codes:
|
||
|
||
xxx - Submitted key was altered to match keyserver policy.
|
||
|
||
Shaw [Page 8]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
xxx - Submitted key was rejected as per keyserver policy.
|
||
xxx - The search resulted in more responses then the keyserver
|
||
was willing to return.
|
||
|
||
7. Locating a HKP Keyserver
|
||
|
||
Clients are usually manually configured with the address of a HKP
|
||
keyserver. Client implementors should be aware that it is
|
||
reasonably common practice to use a single name in DNS that
|
||
resolves to multiple address records. When receiving a DNS
|
||
response with multiple addresses, clients SHOULD try each address
|
||
until a server is reached. The order to try these addresses in is
|
||
implementation defined.
|
||
|
||
A far more flexible scheme for listing multiple HKP keyservers in
|
||
DNS is the use of DNS SRV records as specified in RFC-2782 [5].
|
||
DNS SRV allows for different priorities and weights to be applied
|
||
to each HKP keyserver in the list, which allows an administrator
|
||
much more control over how clients will contact the servers. The
|
||
SRV symbolic service name for HKP keyservers is "hkp". For
|
||
example, the SRV record for HKP keyservers in domain "example.com"
|
||
would be "_hkp._tcp.example.com".
|
||
|
||
SRV records contain the port that the target server runs on, so SRV
|
||
can also be used to automatically discover the proper port for
|
||
contacting a HKP keyserver.
|
||
|
||
An additional use of SRV records is when a client needs to locate a
|
||
specified key by email address. For example, a client trying to
|
||
locate a key for isabella@silvie.example.com could consult
|
||
"_hkp._tcp.silvie.example.com".
|
||
|
||
HKP clients SHOULD support SRV records.
|
||
|
||
8. Security Considerations
|
||
|
||
As described here, a keyserver is a searchable database of public
|
||
keys accessed over the network. While there may be security
|
||
considerations arising from distributing keys in this manner, this
|
||
does not impact the security of OpenPGP itself.
|
||
|
||
Without some sort of trust relationship between the client and
|
||
server, information returned from a keyserver in search results
|
||
cannot be trusted by the client until the OpenPGP client actually
|
||
retrieves and checks the key for itself. This is important and
|
||
must be stressed: without a specific reason to treat information
|
||
otherwise, all search results must be regarded as untrustworthy and
|
||
informational only.
|
||
|
||
9. IANA Considerations
|
||
|
||
This document assigns the DNS SRV symbolic name "hkp".
|
||
|
||
10. Normative References
|
||
|
||
Shaw [Page 9]
|
||
|
||
Internet-Draft OpenPGP HTTP Keyserver Protocol March 2003
|
||
|
||
|
||
[1] S. Bradner, "Key words for use in RFCs to Indicate
|
||
Requirement Level", BCP 14, RFC 2119, March 1997.
|
||
|
||
[2] T. Berners-Lee, "Hypertext Transfer Protocol 1.0",
|
||
RFC 1945, May 1996.
|
||
|
||
[3] T. Berners-Lee, "Hypertext Markup Language - 2.0",
|
||
RFC 1866, November 1995.
|
||
|
||
[4] J. Callas, L. Donnerhacke, H. Finney and R. Thayer,
|
||
"OpenPGP Message Format", RFC 2440, November 1998.
|
||
|
||
[5] A. Gulbrandsen, P. Vixie, and L. Esibov, "A DNS RR for
|
||
specifying the location of services (DNS SRV)", RFC
|
||
2782, February 2000.
|
||
|
||
[6] M. Elkins, D. Del Torto, R. Levien, and T. Roessler,
|
||
"MIME Security with OpenPGP", RFC 3156, August 2001.
|
||
|
||
11. Author's Address
|
||
|
||
David Shaw
|
||
16 Farina Road
|
||
Newton, MA 02459
|
||
|
||
Email: dshaw@jabberwocky.com
|
||
Tel: +1 (617) 332-8443
|
||
|
||
This document is a formalization and extension of the HKP
|
||
originally implemented in the PKS keyserver by Marc Horowitz, which
|
||
in turn was based on earlier work by Brian LaMacchia and Michael
|
||
Graff. Without their grounding, this document would not exist.
|
||
|
||
The author would also like to thank Peter Gutmann for his work on
|
||
the Certstore protocol, some of which was applicable here, and the
|
||
members of the pgp-keyserver-folk mailing list who contributed
|
||
valuable comments and suggestions.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Shaw [Page 10]
|
||
|