Documentation
¶
Overview ¶
Package pseb reads and verifies Pakistan Software Export Board (PSEB) registration certificates.
PSEB is the Pakistani government body that registers IT and IT-enabled services companies and freelancers as software exporters. Every PSEB registration certificate is issued as a PDF that carries a QR code. That QR code does not just link to a web page: it encodes a verification URL whose final path segment is a signed JSON Web Token (JWT). The JWT's claims contain the certificate's core data: the registration number, the registration type (company or freelancer), and issued-at / expiry timestamps.
This package provides three things:
- ExtractCertificate reads the QR code out of a certificate PDF and returns the encoded verification URL, the raw JWT, and the claims decoded from it.
- ExtractCNIC reads the holder's 13-digit CNIC from a certificate PDF's text layer. The CNIC is printed on freelancer certificates but is not carried in the QR code, the JWT claims, or the verification response.
- Client.Verify submits a JWT to the PSEB portal's verification endpoint and returns the certificate data the portal reports for it, including the registered entity's name and whether the certificate is currently valid.
Trust model ¶
PSEB certificates are signed with HS256, a symmetric (shared-secret) algorithm. Because the signing secret is held by PSEB and not published, the signature cannot be verified offline by third parties. This package therefore uses the JWT only to read claims locally; authoritative validity comes from Client.Verify, which calls the PSEB portal. (If PSEB ever switches to an asymmetric scheme such as EdDSA/ES256 and publishes a public key, offline verification would become possible.)
Extracting from a PDF ¶
pdf, _ := os.ReadFile("pseb_cert.pdf")
cert, err := pseb.ExtractCertificate(pdf)
if err != nil {
// no QR code, no JWT, or malformed token
}
fmt.Println(cert.RegistrationNumber, cert.Type, cert.JWTExpiresAt)
Reading the CNIC ¶
pdf, _ := os.ReadFile("pseb_freelancer_cert.pdf")
cnic, err := pseb.ExtractCNIC(pdf)
if err != nil {
// no CNIC in the PDF's text layer
}
fmt.Println(cnic) // 3520122720739
Verifying against the PSEB portal ¶
client := pseb.New()
result, err := client.Verify(ctx, cert.JWT)
if err != nil {
// transport error or the portal rejected the certificate
}
fmt.Println(result.Name, result.IsValid)
Index ¶
Constants ¶
const DefaultBaseURL = "https://api.techdestination.com"
DefaultBaseURL is the base URL of the PSEB portal API used to verify certificates. It is the default host a Client created with New targets.
Variables ¶
var ( // ErrNoQRCode is returned by [ExtractCertificate] when the PDF contains no // image that decodes as a QR code. ErrNoQRCode = errors.New("no QR code found in PDF") // ErrNoJWT is returned by [ExtractCertificate] when the QR code was decoded // but does not contain a JWT-shaped token. ErrNoJWT = errors.New("no JWT found in QR code") // ErrNoCNIC is returned by [ExtractCNIC] when no CNIC can be found in the // PDF's text layer. ErrNoCNIC = errors.New("no CNIC found in PDF") )
var ErrCertificateInvalid = errors.New("PSEB reports certificate is not valid")
ErrCertificateInvalid is returned by Client.Verify when the PSEB portal reports that the certificate is not valid (its "isValid" flag is false). The accompanying VerificationResult is still returned and populated so callers can inspect the details PSEB reported.
Functions ¶
func ExtractCNIC ¶
ExtractCNIC reads a PSEB certificate PDF and returns the holder's 13-digit CNIC (the Pakistani national identity number printed on freelancer certificates).
Unlike the data returned by ExtractCertificate, the CNIC is not encoded in the QR code, the JWT claims, or the PSEB verification response - it appears only as text printed on the certificate. It is therefore recovered from the PDF's text layer: each page's content streams are parsed for their text-showing operators (Tj / TJ) to reconstruct the visible text, and the CNIC is located within it.
Matching prefers a value that follows a "CNIC" label, accepting both the bare (3520122720739) and dash-separated (35201-2272073-9) forms and normalizing to 13 digits. If the label cannot be located it falls back to any standalone run of exactly 13 digits, which is unambiguous on a PSEB certificate (the registration number, phone, and fax never form 13 consecutive digits).
It returns ErrNoCNIC if no CNIC can be found. This relies on the certificate having a real text layer (as current PSEB certificates do); it does not perform OCR on image-only (scanned) PDFs.
Types ¶
type Certificate ¶
type Certificate struct {
// PSEBHostedVerificationURL is the URL encoded in the certificate's QR code.
// It points at the PSEB portal's public verification page for this
// certificate and embeds the JWT as its final path segment, e.g.
// https://portal.techdestination.com/verify-certificate/<jwt>.
PSEBHostedVerificationURL string `json:"pseb_hosted_verification_url"`
// JWT is the raw, compact JSON Web Token taken from the verification URL.
// It is the signed credential that encodes the certificate's data and is the
// value to pass to [Client.Verify].
JWT string `json:"jwt"`
// RegistrationNumber is the PSEB registration number assigned to the holder,
// e.g. "Z-25-17156/25". This is the identifier printed on the certificate
// that uniquely identifies the registered software exporter.
RegistrationNumber string `json:"registration_number"`
// Type is the registration type (company or freelancer).
Type CertificateType `json:"type"`
// IssuedAt is the JWT "iat" (issued-at) claim, normalized to UTC. It is when
// the certificate's verification token was issued.
IssuedAt time.Time `json:"issued_at"`
// JWTExpiresAt is the JWT "exp" (expiry) claim, normalized to UTC. This is
// the expiry of the verification token itself, which PSEB issues with a
// short (~90-day) lifetime, and is NOT the end of the registration's
// validity period. The registration's actual expiry is only reported by the
// portal (see [VerificationResult.RegistrationExpiresAt]) and is not
// available offline from the JWT alone.
JWTExpiresAt time.Time `json:"jwt_expires_at"`
}
Certificate is a PSEB registration certificate as read from the QR code printed on the certificate PDF. The fields below come from the QR's verification URL and the claims of the signed JWT it carries; they are not independently verified (see Client.Verify for that).
func ExtractCertificate ¶
func ExtractCertificate(pdf []byte) (*Certificate, error)
ExtractCertificate reads a PSEB certificate PDF, decodes the QR code printed on it, and returns the Certificate it encodes.
It extracts the images embedded in the PDF, decodes the first one that is a readable QR code, parses the verification URL to capture the JWT, and decodes the JWT's claims to populate the registration number, type, and timestamps. The JWT signature is not checked here; use Client.Verify to confirm a certificate's authenticity and current validity with PSEB.
PSEB embeds the QR as a very small, full-bleed raster (roughly two pixels per module, with no surrounding quiet zone). Off-the-shelf QR readers cannot lock onto a symbol that small at any scale, so decoding falls back to reconstructing the module grid directly; see [decodeQRFromImage] for details.
It returns ErrNoQRCode if no QR code can be decoded from the PDF, or ErrNoJWT if the decoded QR code does not contain a JWT.
type CertificateType ¶
type CertificateType string
CertificateType identifies the kind of PSEB registration a certificate represents. PSEB registers two kinds of software exporters: companies and freelancers. The value mirrors the "type" claim in the certificate JWT verbatim, so it is safe to compare against the constants below but may hold an unrecognized value if PSEB introduces new types.
const ( // CertificateTypeCompany is a PSEB registration issued to a company // (a registered legal entity, e.g. a "(PRIVATE) LIMITED" company). CertificateTypeCompany CertificateType = "company" // CertificateTypeFreelancer is a PSEB registration issued to an individual // software exporter (a freelancer). CertificateTypeFreelancer CertificateType = "freelancer" )
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client verifies PSEB certificates against the PSEB portal's verification endpoint. It is safe for concurrent use and should be reused across calls.
func New ¶
func New(opts ...httpr.ClientOption) *Client
New creates a Client that talks to the PSEB portal verification API.
By default it targets DefaultBaseURL. Pass httpr client options to customize the underlying HTTP behavior, for example httpr.BaseURL(...) to point at a different host or httpr.HTTPClient(...) to supply a custom *http.Client (e.g. one with a timeout, or a recording transport in tests).
func (*Client) Verify ¶
Verify submits a PSEB certificate JWT to the PSEB portal and returns the certificate data the portal reports for it.
The token is the compact JWT extracted from a certificate's QR code (see Certificate.JWT). Verify first checks that the token is a well-formed JWT, then POSTs it to the portal's verification endpoint. Because PSEB certificates are signed with a secret only PSEB holds, this network call is what authoritatively establishes a certificate's authenticity and validity; the returned VerificationResult.IsValid reflects PSEB's own determination.
It returns an error if the token is not a valid JWT, if the request fails, or if the portal responds with a non-2xx status. If the portal responds successfully but reports the certificate as not valid, Verify returns the populated result together with ErrCertificateInvalid; match it with errors.Is to distinguish an invalid certificate from a transport failure.
type VerificationResult ¶
type VerificationResult struct {
// RegistrationNumber is the PSEB registration number of the holder,
// e.g. "Z-25-17156/25".
RegistrationNumber string `json:"registration_number"`
// Type is the registration type (company or freelancer).
Type CertificateType `json:"type"`
// Name is the registered name of the software exporter as recorded by PSEB,
// e.g. the legal company name "OUTSENTIA (PRIVATE) LIMITED". This is not
// present in the certificate JWT and is only available via verification.
Name string `json:"name"`
// IssuedAt is the JWT "iat" (issued-at) timestamp, normalized to UTC. Note
// this is when the certificate's token was issued, which may differ from the
// start of the registration's validity window (see ValidFrom).
IssuedAt time.Time `json:"issued_at"`
// JWTExpiresAt is the JWT "exp" timestamp, normalized to UTC. This is the
// expiry of the certificate's verification token (PSEB issues it with a
// short, ~90-day lifetime), NOT the end of the registration's validity
// period. For the registration's actual expiry use RegistrationExpiresAt.
JWTExpiresAt time.Time `json:"jwt_expires_at"`
// ValidFrom is the start of the registration's validity window as reported
// by PSEB, a coarse human-readable label such as "May 2026". It is empty if
// the portal did not report it.
ValidFrom string `json:"valid_from"`
// ValidTill is the end of the registration's validity window as reported by
// PSEB, a coarse human-readable label such as "Apr 2027". This is the
// certificate's real expiry (distinct from the token's JWTExpiresAt) and is
// empty if the portal did not report it.
ValidTill string `json:"valid_till"`
// RegistrationExpiresAt is ValidTill parsed into a timestamp, normalized to
// UTC. PSEB only reports month granularity, so this is set to the first
// instant of the reported month (e.g. "Apr 2027" becomes 2027-04-01
// 00:00:00 UTC). It is the zero time if ValidTill is empty or unparseable.
RegistrationExpiresAt time.Time `json:"registration_expires_at"`
// IsValid reports whether PSEB considers the certificate currently valid.
// This is the authoritative validity signal: it reflects PSEB's own check
// (signature and expiry) rather than any local inspection of the JWT.
IsValid bool `json:"is_valid"`
}
VerificationResult is the certificate data the PSEB portal returns when a certificate JWT is verified. Unlike the claims read locally from the JWT in Certificate, these values are reported by PSEB and include the registered entity's name and an authoritative validity flag.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
gensample
command
Command gensample generates synthetic PSEB certificate PDFs used as test fixtures.
|
Command gensample generates synthetic PSEB certificate PDFs used as test fixtures. |
|
pseb
command
Command pseb reads a PSEB registration certificate PDF, prints the data encoded in its QR code, and verifies it against the PSEB portal.
|
Command pseb reads a PSEB registration certificate PDF, prints the data encoded in its QR code, and verifies it against the PSEB portal. |