"""Validators for schema fields."""
from datetime import datetime
from base58 import alphabet
from marshmallow.validate import OneOf, Range, Regexp
from .util import epoch_to_str
B58 = alphabet if isinstance(alphabet, str) else alphabet.decode("ascii")
[docs]class IntEpoch(Range):
"""Validate value against (integer) epoch format."""
EXAMPLE = int(datetime.now().timestamp())
def __init__(self):
"""Initializer."""
super().__init__( # use 64-bit for Aries RFC compatibility
min=-9223372036854775808,
max=9223372036854775807,
error="Value {input} is not a valid integer epoch time",
)
[docs]class JSONWebToken(Regexp):
"""Validate JSON Web Token."""
EXAMPLE = (
"eyJhbGciOiJFZERTQSJ9."
"eyJhIjogIjAifQ."
"dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
)
PATTERN = r"^[-_a-zA-Z0-9]*\.[-_a-zA-Z0-9]*\.[-_a-zA-Z0-9]*$"
def __init__(self):
"""Initializer."""
super().__init__(
JSONWebToken.PATTERN, error="Value {input} is not a valid JSON Web token",
)
[docs]class DIDKey(Regexp):
"""Validate value against DID key specification."""
EXAMPLE = "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
PATTERN = rf"^did:key:z[{B58}]+$"
def __init__(self):
"""Initializer."""
super().__init__(
DIDKey.PATTERN, error="Value {input} is not in W3C did:key format"
)
[docs]class IndyDID(Regexp):
"""Validate value against indy DID."""
EXAMPLE = "WgWxqztrNooG92RXvxSTWv"
PATTERN = rf"^(did:sov:)?[{B58}]{{21,22}}$"
def __init__(self):
"""Initializer."""
super().__init__(
IndyDID.PATTERN,
error="Value {input} is not an indy decentralized identifier (DID)",
)
[docs]class IndyRawPublicKey(Regexp):
"""Validate value against indy (Ed25519VerificationKey2018) raw public key."""
EXAMPLE = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
PATTERN = rf"^[{B58}]{{43,44}}$"
def __init__(self):
"""Initializer."""
super().__init__(
IndyRawPublicKey.PATTERN,
error="Value {input} is not a raw Ed25519VerificationKey2018 key",
)
[docs]class IndyCredDefId(Regexp):
"""Validate value against indy credential definition identifier specification."""
EXAMPLE = "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag"
PATTERN = (
rf"^([{B58}]{{21,22}})" # issuer DID
f":3" # cred def id marker
f":CL" # sig alg
rf":(([1-9][0-9]*)|([{B58}]{{21,22}}:2:.+:[0-9.]+))" # schema txn / id
f":(.+)?$" # tag
)
def __init__(self):
"""Initializer."""
super().__init__(
IndyCredDefId.PATTERN,
error="Value {input} is not an indy credential definition identifier",
)
[docs]class IndyVersion(Regexp):
"""Validate value against indy version specification."""
EXAMPLE = "1.0"
PATTERN = rf"^[0-9.]+$"
def __init__(self):
"""Initializer."""
super().__init__(
IndyVersion.PATTERN,
error="Value {input} is not an indy version (use only digits and '.')",
)
[docs]class IndySchemaId(Regexp):
"""Validate value against indy schema identifier specification."""
EXAMPLE = "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0"
PATTERN = rf"^[{B58}]{{21,22}}:2:.+:[0-9.]+$"
def __init__(self):
"""Initializer."""
super().__init__(
IndySchemaId.PATTERN,
error="Value {input} is not an indy schema identifier",
)
[docs]class IndyRevRegId(Regexp):
"""Validate value against indy revocation registry identifier specification."""
EXAMPLE = f"WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0"
PATTERN = (
rf"^([{B58}]{{21,22}}):4:"
rf"([{B58}]{{21,22}}):3:"
rf"CL:(([1-9][0-9]*)|([{B58}]{{21,22}}:2:.+:[0-9.]+))(:.+)?:"
rf"CL_ACCUM:(.+$)"
)
def __init__(self):
"""Initializer."""
super().__init__(
IndyRevRegId.PATTERN,
error="Value {input} is not an indy revocation registry identifier",
)
[docs]class IndyPredicate(OneOf):
"""Validate value against indy predicate."""
EXAMPLE = ">="
def __init__(self):
"""Initializer."""
super().__init__(
choices=["<", "<=", ">=", ">"],
error="Value {input} must be one of {choices}",
)
[docs]class IndyISO8601DateTime(Regexp):
"""Validate value against ISO 8601 datetime format, indy profile."""
EXAMPLE = epoch_to_str(int(datetime.now().timestamp()))
PATTERN = (
r"^\d{4}-\d\d-\d\d[T ]\d\d:\d\d"
r"(?:\:(?:\d\d(?:\.\d{1,6})?))?(?:[+-]\d\d:?\d\d|Z|)$"
)
def __init__(self):
"""Initializer."""
super().__init__(
IndyISO8601DateTime.PATTERN,
error="Value {input} is not a date in valid format",
)
[docs]class Base64(Regexp):
"""Validate base64 value."""
EXAMPLE = "ey4uLn0="
PATTERN = r"^[a-zA-Z0-9+/]*={0,2}$"
def __init__(self):
"""Initializer."""
super().__init__(
Base64.PATTERN, error="Value {input} is not a valid base64 encoding",
)
[docs]class Base64URL(Regexp):
"""Validate base64 value."""
EXAMPLE = "ey4uLn0="
PATTERN = r"^[-_a-zA-Z0-9]*={0,2}$"
def __init__(self):
"""Initializer."""
super().__init__(
Base64URL.PATTERN, error="Value {input} is not a valid base64url encoding",
)
[docs]class Base64URLNoPad(Regexp):
"""Validate base64 value."""
EXAMPLE = "ey4uLn0"
PATTERN = r"^[-_a-zA-Z0-9]*$"
def __init__(self):
"""Initializer."""
super().__init__(
Base64URLNoPad.PATTERN,
error="Value {input} is not a valid unpadded base64url encoding",
)
[docs]class SHA256Hash(Regexp):
"""Validate (binhex-encoded) SHA256 value."""
EXAMPLE = "617a48c7c8afe0521efdc03e5bb0ad9e655893e6b4b51f0e794d70fba132aacb"
PATTERN = r"^[a-fA-F0-9+/]{64}$"
def __init__(self):
"""Initializer."""
super().__init__(
SHA256Hash.PATTERN,
error="Value {input} is not a valid (binhex-encoded) SHA-256 hash",
)
[docs]class Base58SHA256Hash(Regexp):
"""Validate value against base58 encoding of SHA-256 hash."""
EXAMPLE = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
PATTERN = rf"^[{B58}]{{43,44}}$"
def __init__(self):
"""Initializer."""
super().__init__(
Base58SHA256Hash.PATTERN,
error="Value {input} is not a base58 encoding of a SHA-256 hash",
)
[docs]class UUIDFour(Regexp):
"""Validate UUID4: 8-4-4-4-12 hex digits, the 13th of which being 4."""
EXAMPLE = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
PATTERN = (
r"[a-fA-F0-9]{8}-"
r"[a-fA-F0-9]{4}-"
r"4[a-fA-F0-9]{3}-"
r"[a-fA-F0-9]{4}-"
r"[a-fA-F0-9]{12}"
)
def __init__(self):
"""Initializer."""
super().__init__(
UUIDFour.PATTERN,
error="Value {input} is not UUID4 (8-4-4-4-12 hex digits with digit#13=4)",
)
# Instances for marshmallow schema specification
INT_EPOCH = {"validate": IntEpoch(), "example": IntEpoch.EXAMPLE}
JWS_HEADER_KID = {"validate": JWSHeaderKid(), "example": JWSHeaderKid.EXAMPLE}
JWT = {"validate": JSONWebToken(), "example": JSONWebToken.EXAMPLE}
DID_KEY = {"validate": DIDKey(), "example": DIDKey.EXAMPLE}
INDY_DID = {"validate": IndyDID(), "example": IndyDID.EXAMPLE}
INDY_RAW_PUBLIC_KEY = {
"validate": IndyRawPublicKey(),
"example": IndyRawPublicKey.EXAMPLE,
}
INDY_SCHEMA_ID = {"validate": IndySchemaId(), "example": IndySchemaId.EXAMPLE}
INDY_CRED_DEF_ID = {"validate": IndyCredDefId(), "example": IndyCredDefId.EXAMPLE}
INDY_REV_REG_ID = {"validate": IndyRevRegId(), "example": IndyRevRegId.EXAMPLE}
INDY_VERSION = {"validate": IndyVersion(), "example": IndyVersion.EXAMPLE}
INDY_PREDICATE = {"validate": IndyPredicate(), "example": IndyPredicate.EXAMPLE}
INDY_ISO8601_DATETIME = {
"validate": IndyISO8601DateTime(),
"example": IndyISO8601DateTime.EXAMPLE,
}
BASE64 = {"validate": Base64(), "example": Base64.EXAMPLE}
BASE64URL = {"validate": Base64URL(), "example": Base64URL.EXAMPLE}
BASE64URL_NO_PAD = {"validate": Base64URLNoPad(), "example": Base64URLNoPad.EXAMPLE}
SHA256 = {"validate": SHA256Hash(), "example": SHA256Hash.EXAMPLE}
BASE58_SHA256_HASH = {
"validate": Base58SHA256Hash(),
"example": Base58SHA256Hash.EXAMPLE,
}
UUID4 = {"validate": UUIDFour(), "example": UUIDFour.EXAMPLE}