Issuer-key discovery
Beyond DIDs, an issuer key can be discovered from an https URL or an X.509 chain.
/.well-known/jwt-vc-issuer
openvc.jwt_vc_issuer
openvc.jwt_vc_issuer — SD-JWT VC / OID4VC issuer-key discovery.
When a JOSE credential's iss is an https URL rather than a DID, the issuer
publishes its signing keys at a well-known endpoint (draft-ietf-oauth-sd-jwt-vc,
"JWT VC Issuer Metadata"). For iss = https://host/path the metadata lives at
https://host/.well-known/jwt-vc-issuer/path
(the well-known segment is inserted between the host and the issuer path). The
document is JSON with a REQUIRED issuer — which must equal the iss
(anti-substitution) — and either an inline jwks JWK Set or a jwks_uri to
fetch one. The issuer JWT's kid header selects the key.
Fetching is delegated to an injected fetch (pass :func:openvc.fetch.https_json_fetch
so the SSRF guards apply), keeping this module transport-agnostic. This is the
opt-in HTTPS-issuer counterpart to DID-based key resolution in the pipeline.
JwtVcIssuerError
Bases: OpenvcError
The issuer metadata could not be resolved, did not match, or has no key.
Source code in src/openvc/jwt_vc_issuer.py
34 35 | |
jwt_vc_issuer_metadata_url(iss)
The JWT VC Issuer Metadata URL for an https iss — the well-known segment inserted between the host and the issuer's path.
Source code in src/openvc/jwt_vc_issuer.py
38 39 40 41 42 43 44 45 46 47 | |
resolve_jwt_vc_issuer_key(iss, kid, *, fetch)
Resolve the public JWK for an https iss + kid via its JWT VC Issuer
Metadata. Verifies the metadata's issuer equals iss before trusting any
key. fetch performs the (SSRF-guarded) https GETs.
Source code in src/openvc/jwt_vc_issuer.py
90 91 92 93 94 95 96 97 98 | |
resolve_jwt_vc_issuer_key_async(iss, kid, *, fetch)
async
Async :func:resolve_jwt_vc_issuer_key — awaits the (up to two) https GETs;
identical metadata validation and key selection (the same pure code).
Source code in src/openvc/jwt_vc_issuer.py
101 102 103 104 105 106 107 108 109 110 | |
X.509 x5c
openvc.x5c
openvc.x5c — validate a JOSE x5c certificate chain and bind it to the issuer.
Some issuers (notably eIDAS / EUDI document signers) anchor trust in X.509
rather than a DID: the JOSE header carries x5c, a chain of base64 (not
base64url) DER certificates, leaf first. This validates that chain to a
caller-provided set of trust anchors and returns the leaf's public key as a
JWK for the signature check.
Two things make this safe:
- Path validation (signatures, validity window, name chaining, and
basicConstraintson CA certs) is done bycryptography's X.509 verifier — which refuses to skipbasicConstraints, so a non-CA cert cannot be smuggled in as an intermediate. Only the TLS-specific EKU requirement is relaxed (a VC issuer cert is not a TLS server/client cert). Requirescryptography >= 45. - Issuer binding — the token's
issmust appear in the leaf certificate's Subject Alternative Name (a matching URI, or a DNS name equal to theisshost). Without it, a holder of any certificate under a trusted anchor could forge a credential naming an arbitrary issuer.
Only an EC P-256 leaf is usable (the JOSE allow-list is {ES256, ES384, EdDSA, Ed25519}; an
RSA leaf is rejected by the algorithm allow-list anyway). openvc ships no root
store — the trust anchors are the caller's.
X5cError
Bases: OpenvcError
The x5c chain is malformed, does not validate, is not bound to the issuer, or has an unusable key.
Source code in src/openvc/x5c.py
36 37 38 | |
load_x5c_chain(x5c)
Load a JOSE x5c header value — base64 (not base64url) DER certificates, leaf
first — into x509.Certificate objects. Raises :class:X5cError on a missing,
empty, or malformed chain. Shared with the EUDI registration-certificate lane
(:mod:openvc.rp_registration), which carries its signer chain the same way.
Source code in src/openvc/x5c.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | |
validate_cert_chain(leaf, intermediates, *, trust_anchors, now=None)
Path-validate leaf + intermediates (x509.Certificate objects, leaf first)
to one of trust_anchors at now — the shared X.509 core behind both the JOSE
x5c path and the mdoc IssuerAuth adapter (ADR-0005 D5). Checks chain signatures,
validity windows, name chaining and basicConstraints (cryptography refuses to
skip the latter, so a non-CA cert cannot be smuggled in as an intermediate); only the
TLS-specific EKU is relaxed (a VC/mdoc signer cert is not a TLS server cert). Raises
:class:X5cError on any failure; returns None on success.
Source code in src/openvc/x5c.py
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 | |
resolve_x5c_key(x5c, iss, *, trust_anchors, now=None)
Validate the x5c chain (leaf first) against trust_anchors (trusted root
x509.Certificate objects), confirm the leaf is bound to iss via its SAN,
and return the leaf's public key as an EC P-256 JWK.
Raises :class:X5cError on a malformed chain, a path-validation failure, an
unbound issuer, or a non-P-256 leaf key.
Source code in src/openvc/x5c.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
load_der_chain(x5chain)
Load a COSE x5chain (RFC 9360 label 33) — raw DER certificates, leaf first —
into x509.Certificate objects. Raises :class:X5cError on a missing, empty, or
malformed chain. Shared by the mdoc IssuerAuth and EUDI registration-certificate
(CWT) lanes.
Source code in src/openvc/x5c.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
leaf_public_jwk(leaf)
A certificate's public key as a JWK, restricted to the curves
:func:openvc.keys.verify_signature accepts (EC P-256 / P-384, Ed25519). Any other
key type — notably RSA — raises :class:X5cError rather than producing a JWK no
allow-listed algorithm could consume.
Source code in src/openvc/x5c.py
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
check_mdoc_signed_within_ds_validity(x5chain, signed)
ISO 18013-5 §9.3.1: the MSO signed time must fall within the document-signer
certificate's own validity window. (The chain path is validated at verification time
— the conservative policy — so a currently-expired DS is still rejected; this additionally
catches a signed inconsistent with the cert that produced it.) Raises :class:X5cError.
Source code in src/openvc/x5c.py
223 224 225 226 227 228 229 230 231 232 | |
resolve_mdoc_signer_key(x5chain, *, trust_anchors, now=None)
Validate an mdoc IssuerAuth x5chain (COSE label 33: DER certificates,
leaf first) to a caller-provided IACA anchor set and return the document-signer
leaf's public key as a JWK (P-256 / P-384 / Ed25519).
Unlike :func:resolve_x5c_key there is no iss→SAN binding: mdoc trust is
"the DS cert chains to a trusted IACA root" (ISO 18013-5 §9.1.2). The docType and
validityInfo are bound against the MSO by the mdoc verifier, not the certificate.
Raises :class:X5cError on a malformed chain, a path-validation failure, or an
unusable leaf key.
Source code in src/openvc/x5c.py
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | |
EUDI relying-party certificates (WRPAC)
Parse an EUDI relying-party access certificate (ETSI TS 119 411-8) to read who is asking — entity identifier, service identifier, trade name — verify-side only.
openvc.rp_cert
openvc.rp_cert — parse and validate an EUDI Wallet-Relying-Party Access Certificate (WRPAC).
Under CIR (EU) 2025/848 every wallet relying party carries an access certificate
(WRPAC, mandatory): an X.509 certificate profiled by ETSI TS 119 411-8 that
authenticates who is asking — the relying party's EU-wide entity identifier, its
service identifier and trade name — rooted in the Access Certificate Authority (ACA)
trust anchors a Member State notifies. This reads that certificate over the existing
cryptography X.509 machinery and exposes its attributes as a typed object the
caller can gate on, with the same fail-closed posture as :mod:openvc.x5c.
Two entry points, mirroring the library's trusted/untrusted split:
- :func:
parse_rp_access_certificate— read the attributes WITHOUT establishing trust (UNTRUSTED, likepeek_*); for inspection only. - :func:
verify_rp_access_certificate— validate the chain to caller-provided ACA anchors first, then parse; the result is safe to act on.
The registration certificate (WRPRC — the entitlements artifact) is a signed JWT or
CWT (ETSI TS 119 475), not an X.509 certificate, so it lives in its own module:
:mod:openvc.rp_registration, which also carries the cross-check binding a WRPRC back
to the WRPAC parsed here. This module is WRPAC-only. Scope: parse + validate. NOT
registrar workflows or certificate issuance.
RpCertError
Bases: OpenvcError
A relying-party access certificate is malformed, does not validate to the provided anchors, or lacks a required attribute.
Source code in src/openvc/rp_cert.py
37 38 39 | |
RelyingPartyAccessCertificate
dataclass
The parsed attributes of a WRPAC — the answer to "who is asking?".
entity_identifier is the EU-wide unique identifier (subject
organizationIdentifier, OID 2.5.4.97); trade_name is the human-readable
name (subject commonName). extended_key_usages, certificate_policies
and registration_records (the Subject Information Access locations pointing at
the RP's registration record) are the caller-gateable attribute set — this module
does not hardcode which EKU/policy the EUDI profile mandates (that OID is still
settling); it surfaces them for the caller to check. public_jwk is the leaf's
EC public key as a JWK when it is P-256/P-384, else None.
Source code in src/openvc/rp_cert.py
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 | |
parse_rp_access_certificate(cert)
Parse a WRPAC's attributes WITHOUT establishing trust.
UNTRUSTED — it does not validate the chain or the signature. Use it only to
inspect a certificate (e.g. to read its registration-record URL); call
:func:verify_rp_access_certificate to root it in ACA anchors before making any
trust decision on the identity it names.
Source code in src/openvc/rp_cert.py
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
verify_rp_access_certificate(cert, *, trust_anchors, intermediates=(), required_eku=None, now=None)
Validate a WRPAC and return its parsed attributes.
The chain (leaf cert + any intermediates) is path-validated to trust_anchors
(the ACA roots — the trusted-list anchors that root them; each an
x509.Certificate, DER/PEM bytes, or a base64 string) by cryptography's
verifier: signatures, the validity window, basicConstraints and path length are
enforced; only the TLS-specific EKU requirement is relaxed (a WRPAC is an
e-seal/signature certificate, not a TLS server cert). If required_eku (an EKU OID
dotted string) is given, the leaf must carry it.
This proves the certificate chains to your anchors (and carries required_eku if
given) — it does NOT, by itself, prove the certificate is a WRPAC. If your
trust_anchors certify end-entities beyond relying-party access certificates (e.g. a
broad national/eIDAS root rather than a dedicated ACA), pass required_eku — or gate
on the returned certificate_policies — to distinguish a WRPAC. With no such gate
this accepts any end-entity under the anchor.
Raises :class:RpCertError on a malformed certificate, a bad/empty anchor set, a
path-validation failure, or a missing required EKU. Same fail-closed posture as
:func:openvc.x5c.resolve_x5c_key.
Source code in src/openvc/rp_cert.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
EUDI relying-party certificates (WRPRC)
Parse and verify an EUDI relying-party registration certificate (ETSI TS 119 475) — the signed JWT/CWT carrying the registered entitlements and requestable attributes — and cross-check it against a WRPAC and a presentation request.
openvc.rp_registration
openvc.rp_registration — parse and verify an EUDI Wallet-Relying-Party Registration Certificate (WRPRC).
Under CIR (EU) 2025/848 a wallet relying party carries two artifacts. The access
certificate (WRPAC, Art. 7 — mandatory) authenticates who is asking: it is X.509 and
lives in :mod:openvc.rp_cert. The registration certificate (WRPRC, Art. 8 —
optional per Member State) is the other half, and answers a different question:
"were they registered to ask for this?" It carries the relying party's registered
entitlements and the credentials/attributes it may request.
A WRPRC is not an X.509 certificate. ETSI TS 119 475 V1.2.1 clause 5.2 profiles it as
a signed JWT (typ: rc-wrp+jwt, clause 5.2.2) or CWT (typ: rc-wrp+cwt,
clause 5.2.3), so this module reads it over machinery openvc already has: the JOSE lane
for the JWT form (with the {ES256, ES384, EdDSA, Ed25519} allow-list applied
before any crypto, as everywhere else), the dependency-free CBOR/COSE codec for the
CWT form (:mod:openvc.cbor / :mod:openvc.cose, ADR-0005), and
:func:openvc.x5c.validate_cert_chain for the signer's chain against
caller-provided registrar anchors — openvc ships no root store.
Three things about the profile are worth knowing before you read the code, because each one is a place the specification is thinner than it looks:
- One WRPRC carries exactly one intended use. TS5's data model nests an
intendedUse[0..*]array, but clause 5.2.4 flattens it away:credentials,purposeandintended_use_idare top-level payload claims. A relying party with several intended uses holds several WRPRCs. expis optional (Table 10). An absent expiry is conformant — revocation runs through thestatusclaim (an IETF Token Status List, which :func:openvc.status.check_token_statusresolves). The clause-5.2.4 GEN-5.2.4-08 twelve-month ceiling therefore binds only whenexpis present.- The CWT form has no claim-key mapping. TS 119 475 presents its claim tables once, format-agnostically, with text field names; it never allocates CBOR integer labels for them, and TS 119 152-1 (CBOR AdES) is a forward reference. The envelope is fully specified (RFC 9052 + RFC 9360) and is implemented here; the claims map is read accepting both the RFC 8392 registered integer keys and text keys, which is the only reading available to an issuer today. Treat the CWT lane as provisional until a real artifact exists to pin.
JAdES scope. GEN-5.2.1-04 requires the JWT form to be signed as a JAdES baseline
B-B signature (ETSI TS 119 182-1). This implements a verify subset of B-B — the
signed-header profile (typ, allow-listed alg, the x5c chain of clause 5.1.7
/ 5.1.8, a fail-closed crit) and the chain validation — not a full JAdES library:
no signature-policy processing, no timestamps, no augmentation to higher levels. Note
that JAdES clause 5.1.11 mandates a header iat for signatures made after
2025-07-15 while TS 119 475 Table 5 omits it; since the two normative texts disagree,
the header iat is surfaced but not required — the security-bearing timestamps
are the payload's.
Two entry points, mirroring the library's trusted/untrusted split (and
:mod:openvc.rp_cert):
- :func:
parse_rp_registration_certificate— read the claims WITHOUT establishing trust (UNTRUSTED, likepeek_*); for inspection only. - :func:
verify_rp_registration_certificate— validate the signature to caller-provided registrar anchors first, then parse; the result is safe to act on.
Two cross-checks turn the parsed object into an authorization decision:
:func:check_matches_access_certificate (this WRPRC describes the party that WRPAC
authenticates) and :func:check_request_within_registration (a DCQL request asks only
for what was registered).
Scope: parse + verify + cross-check. NOT registrar workflows or certificate issuance — openvc is a consumer.
RpRegistrationError
Bases: OpenvcError
A relying-party registration certificate is malformed, does not validate to the provided anchors, or fails a registered-scope cross-check.
Source code in src/openvc/rp_registration.py
89 90 91 | |
RequestableCredential
dataclass
One entry of the registered credentials (clause 5.2.4 Table 9) or
provides_attestations (Table 8) arrays — the credential format the relying
party may ask for and, within it, the claim paths it registered.
claim_paths holds each registered path as a tuple; None inside a path is the
DCQL array wildcard. An entry that registers no paths grants no attributes —
see :func:check_request_within_registration for that fail-closed reading.
meta is {} when the entry carries no constraint (which matches an equally
unconstrained request) and None when it carried one that is not an object.
The two are deliberately distinct: coercing a malformed constraint to {} would
turn it into no constraint, i.e. widen the entry to every credential of that
format. A None here matches nothing. (The distinction is not hypothetical — the
specification's own data model, clause B.2.9, types meta as a string while
clause 5.2.4 and the Annex C example use an object.)
Source code in src/openvc/rp_registration.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
RelyingPartyRegistrationCertificate
dataclass
The parsed content of a WRPRC.
subject_identifier is the sub claim: the ETSI EN 319 412-1 semantic
identifier of the relying party (VATES-B12345678, LEIXG-…, NTRDE-…).
Table 7 NOTE 2 is worth repeating — sub always identifies the relying party,
never the intermediary, even when an intermediary presents the certificate. It is
what :func:check_matches_access_certificate binds against the WRPAC.
header and claims are the raw, verbatim protected header and claim set: the
typed fields are a view, and a caller needing a claim this release does not model
reads it from claims rather than going without. form is "jwt" or
"cwt".
Source code in src/openvc/rp_registration.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | |
intermediary_identifier
property
The intermediary's semantic identifier, if the WRPRC is intermediated.
The specification names this three ways — intermediary.sub (Table 10),
and act.sub (GEN-5.2.4-09, which appears nowhere else). Both are read.
intermediary_name
property
The intermediary's common name. Table 10 spells the field sname while the
Annex C example uses name; since Annex C is what implementers copy but only
the table is normative, both spellings are accepted.
parse_rp_registration_certificate(token)
Parse a WRPRC's claims WITHOUT establishing trust.
token is the JWT form (a compact-JWS str) or the CWT form (bytes: a
COSE_Sign1). The signed-header profile is still enforced — typ, the
algorithm allow-list, a fail-closed crit — because a token that is not shaped
like a WRPRC should not be reported as one; but the signature is not checked and
no chain is validated.
UNTRUSTED — the returned entitlements are whatever the bytes claimed. Use it only to
inspect a token (e.g. to read its sub before choosing anchors); call
:func:verify_rp_registration_certificate before making any authorization decision
on the registered scope it names.
Source code in src/openvc/rp_registration.py
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | |
verify_rp_registration_certificate(token, *, trust_anchors, intermediates=(), now=None, leeway_s=60, required_eku=None, max_validity=_MAX_VALIDITY, require_expiry=False, require_entitlement=True)
Verify a WRPRC and return its parsed content.
In order: the signed-header profile (typ, the {ES256, ES384, EdDSA, Ed25519}
allow-list applied before any crypto, a fail-closed crit); the signer's chain
— x5c for the JWT form, x5chain for the CWT form, plus any caller-supplied
intermediates — path-validated to trust_anchors (the registrar roots) by
cryptography's verifier; then the signature against that chain's leaf key;
then the temporal claims and the entitlement floor.
Policy knobs, all defaulting to the specification's own reading:
- required_eku — additionally require this EKU OID on the signing leaf.
- max_validity — the GEN-5.2.4-08 twelve-month ceiling. It is measured from the
payload
iat, so it applies only whenexpandiatare both present; a token carrying neither has an unbounded lifetime that onlystatusretires.Nonedisables the ceiling. - require_expiry —
expis optional in TS 119 475 (Table 10), so this defaults toFalse. Set it if your policy refuses a certificate that can only be retired through revocation. - require_entitlement — GEN-5.2.4-03 requires at least one entitlement from clause
A.2, checked as a URI under :data:
ENTITLEMENT_URI_PREFIX; a WRPRC without one authorizes nothing anyway. Turn it off for a registrar issuing outside that namespace.
This proves the token was signed by a certificate that chains to your anchors — it
does NOT, by itself, prove the signer was entitled to register this relying
party. As with :func:openvc.rp_cert.verify_rp_access_certificate, if your anchors
certify end-entities beyond registrars, pass required_eku (or gate on the returned
header) to distinguish one. Trust is anchored through the chain, not through
iss — TS 119 475 defines no iss claim at all. openvc ships no root store.
It also does not check the status claim: a WRPRC is revoked through the IETF
Token Status List, which :func:openvc.status.check_token_status resolves. That
needs network access, so it stays an explicit, separate call.
Raises :class:RpRegistrationError on a malformed token, a rejected header, a
path-validation failure, a bad signature, or a failed policy check.
Source code in src/openvc/rp_registration.py
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 | |
check_matches_access_certificate(registration, access, *, match_trade_name=False)
Bind a verified WRPRC to the verified WRPAC that authenticated the caller.
Both artifacts must name the same relying party: the WRPRC's sub — its ETSI
EN 319 412-1 semantic identifier — must equal the WRPAC's entity_identifier
(subject organizationIdentifier, the same namespace). That is GEN-5.1.1-04.
Without this bind, an attacker presenting their own valid WRPAC could pair it with
someone else's valid WRPRC and inherit that party's registered scope.
An identifier missing on either side is a failure, never a match: comparing two
absent values would make None == None a successful bind — a fail-open hole
exactly where the check exists to close one.
match_trade_name additionally requires the WRPRC's name to equal the WRPAC's
trade_name. It is off by default: the two are free-text and legitimately differ
in punctuation or legal suffix, so a mismatch is weak evidence and would produce
false rejections. Note the comparison is intentionally on the identifier, which is
registry-controlled.
access is a :class:~openvc.rp_cert.RelyingPartyAccessCertificate (or anything
exposing entity_identifier / trade_name). Raises
:class:RpRegistrationError on any mismatch; returns None on success.
Source code in src/openvc/rp_registration.py
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | |
check_request_within_registration(registration, dcql_query, *, intended_use_id=None)
Check a presentation request against the registered scope: every credential and
attribute the DCQL query asks for must appear in the WRPRC's credentials.
dcql_query is the OpenID4VP 1.0 dcql_query the relying party sent — the same
object :func:openvc.verify_vp_token consumes. For each of its credential queries
this requires a registered entry of the same format whose meta covers the
requested one, and every requested claim path to fall inside that entry's
registered paths (a registered container covers its members; see
:func:_path_covered).
A WRPRC carries one intended use (clause 5.2.4 flattens TS5's nested model), so
intended_use_id is an optional assertion rather than a selector: pass it and the
certificate's own intended_use_id must equal it. Note the claim is itself
optional in the profile — a WRPRC that omits it cannot satisfy this assertion.
Fail-closed by construction. A request that names no claims is asking for everything in that credential and is refused unless the registration is equally unrestricted; a registered entry that lists no claim paths grants no attributes. Both readings deny rather than widen — the opposite default would turn an incomplete registration into a blanket entitlement.
This is an authorization check on top of verification: call it only on a WRPRC
that :func:verify_rp_registration_certificate accepted and
:func:check_matches_access_certificate bound to the requesting party. Raises
:class:RpRegistrationError on anything out of scope; returns None on success.
Source code in src/openvc/rp_registration.py
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 | |