{"id":30345657,"url":"https://github.com/block/picocert","last_synced_at":"2025-08-18T14:09:12.629Z","repository":{"id":307235728,"uuid":"1016933210","full_name":"block/picocert","owner":"block","description":null,"archived":false,"fork":false,"pushed_at":"2025-07-16T16:57:33.000Z","size":31,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-30T07:19:18.658Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/block.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":"GOVERNANCE.md","roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-09T18:50:15.000Z","updated_at":"2025-07-28T12:30:58.000Z","dependencies_parsed_at":"2025-07-30T07:20:05.327Z","dependency_job_id":"716a35a3-6459-41c8-87ea-8301491cfda8","html_url":"https://github.com/block/picocert","commit_stats":null,"previous_names":["block/picocert"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/block/picocert","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/block%2Fpicocert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/block%2Fpicocert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/block%2Fpicocert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/block%2Fpicocert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/block","download_url":"https://codeload.github.com/block/picocert/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/block%2Fpicocert/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271005119,"owners_count":24683262,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-18T14:09:11.959Z","updated_at":"2025-08-18T14:09:12.618Z","avatar_url":"https://github.com/block.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# picocert: Minimal Certificate Library\n\n`picocert` is a minimal certificate library for handling compact X.509-like certificates, designed for embedded systems. It provides certificate chain validation and data signature verification using ECDSA (P-256) and SHA-256.\n\nThe library consists of:\n\n- **C Library**: Core certificate validation and signature verification (header-only `picocert.h`)\n- **Go Package**: Certificate management tools and CLI (`pkg/picocert`, `cmd/picocert`)\n- **CLI Tool**: Command-line interface for certificate operations\n- **PKI Scripts**: Helper scripts for setting up certificate hierarchies\n\n## Certificate Structure in Memory\n\nCertificates are represented by the `picocert_t` struct (see `picocert.h`):\n\n```c\ntypedef struct {\n  uint8_t version;\n  char issuer[PICOCERT_MAX_NAME_LEN];\n  char subject[PICOCERT_MAX_NAME_LEN];\n  uint64_t valid_from;  // Any monotonic timestamp, e.g. a unix timestamp or counter\n  uint64_t valid_to;    // Ditto\n  picocert_curve_t curve; // Only PICOCERT_P256 supported\n  picocert_hash_t hash;   // Only PICOCERT_SHA256 supported\n  uint32_t reserved;      // Must be zero\n  uint8_t public_key[PICOCERT_MAX_PUBKEY_LEN]; // Uncompressed ECC public key\n  uint8_t signature[PICOCERT_MAX_SIG_LEN];     // ECDSA signature\n} PACKED picocert_t;\n```\n\n**Field layout:**\n\n| Field         | Size (bytes) | Description                        |\n|-------------- |--------------|------------------------------------|\n| version       | 1            | Certificate version                |\n| issuer        | 32           | Issuer name (null-terminated)      |\n| subject       | 32           | Subject name (null-terminated)     |\n| valid_from    | 8            | Start of validity                  |\n| valid_to      | 8            | End of validity                    |\n| curve         | 1            | Curve ID (0 = P-256)               |\n| hash          | 1            | Hash ID (0 = SHA-256)              |\n| reserved      | 4            | Reserved, must be zero             |\n| public_key    | 65           | Uncompressed ECC public key (SEC1) |\n| signature     | 64           | ECDSA signature raw (r\\|\\|s)       |\n\nTotal size: **184 bytes** (packed)\n\n## C Library\n\nThe C library is header-only and uses a context for dependency injection of function pointers.\n\n### API\n\nThe main API functions are:\n\n```c\n// Initialize context with crypto functions\nstatic picocert_err_t picocert_init_context(picocert_context_t* ctx,\n                                            picocert_hash_fn_t hash_fn,\n                                            picocert_ecc_verify_fn_t ecc_verify_fn,\n                                            picocert_time_fn_t time_fn);\n\n// Verify a hash with certificate chain validation\nstatic picocert_err_t picocert_verify_hash_and_validate_chain(picocert_context_t* ctx,\n                                                             const picocert_t* cert_chain,\n                                                             const uint32_t chain_len,\n                                                             const uint8_t hash[HASH_SHA256_DIGEST_SIZE],\n                                                             const uint8_t signature[ECC_SIG_SIZE]);\n\n// Verify hash with a single certificate (no chain validation)\nstatic picocert_err_t picocert_verify_hash(picocert_context_t* ctx,\n                                           const picocert_t* cert,\n                                           const uint8_t hash[HASH_SHA256_DIGEST_SIZE],\n                                           const uint8_t signature[ECC_SIG_SIZE]);\n\n// Validate certificate chain (without data verification)\nstatic picocert_err_t picocert_validate_cert_chain(picocert_context_t* ctx,\n                                                   const picocert_t* cert_chain,\n                                                   const uint32_t chain_len);\n\n// Validate a single certificate against its issuer\nstatic picocert_err_t picocert_validate_cert(picocert_context_t* ctx,\n                                             const picocert_t* issuer,\n                                             const picocert_t* subject);\n```\n\n#### Example\n\n```c\n#include \"picocert.h\"\n\n// Example hash function (you must provide your own)\nbool my_sha256_hash(const uint8_t* data, uint32_t data_len,\n                    uint8_t* digest, uint32_t digest_len) {\n    return true;\n}\n\n// Example ECC verification function (you must provide your own)\nbool my_ecc_verify(const uint8_t* key, size_t key_size,\n                   const uint8_t* hash, uint32_t hash_len,\n                   const uint8_t* signature) {\n    return true;\n}\n\n// Time callback for certificate validity checking\nuint64_t get_current_time(void) {\n    return (uint64_t)time(NULL);\n}\n\npicocert_context_t ctx;\npicocert_init_context(\u0026ctx, my_sha256_hash, my_ecc_verify, get_current_time);\n\npicocert_t certs[2]; // [0] = leaf, [1] = root\n// ... fill certs, hash the data, and obtain signature ...\nuint8_t data_hash[HASH_SHA256_DIGEST_SIZE] = { ... };\nuint8_t signature[ECC_SIG_SIZE] = { ... };\n\npicocert_err_t err = picocert_verify_hash_and_validate_chain(\u0026ctx, certs, 2, data_hash, signature);\nif (err == PICOCERT_OK) {\n    // Data is valid and cert chain is trusted\n} else {\n    // Handle error\n}\n```\n### Error Codes\n\n| Error Code                      | Value | Meaning                           |\n|---------------------------------|-------|-----------------------------------|\n| PICOCERT_OK                     | 0     | Success                           |\n| PICOCERT_ERR_INVALID            | 1     | Invalid argument                  |\n| PICOCERT_ERR_EXPIRED            | 2     | Certificate expired/not yet valid |\n| PICOCERT_ERR_SIGNATURE          | 3     | Signature verification failed     |\n| PICOCERT_ERR_ISSUER             | 4     | Issuer/subject mismatch           |\n| PICOCERT_ERR_VERSION            | 5     | Version mismatch                  |\n| PICOCERT_ERR_RESERVED           | 6     | Reserved field nonzero            |\n| PICOCERT_ERR_NOT_SELF_SIGNED    | 7     | Root not self-signed              |\n| PICOCERT_ERR_INVALID_FORMAT     | 8     | Invalid certificate format        |\n| PICOCERT_ERR_CONTEXT_NOT_INITIALIZED | 9 | Context not properly initialized  |\n| PICOCERT_ERR_HASH_FAILED        | 10    | Hash computation failed           |\n| PICOCERT_ERR_UNSUPPORTED_CURVE  | 11    | Unsupported curve                 |\n| PICOCERT_ERR_UNSUPPORTED_HASH   | 12    | Unsupported hash algorithm        |\n| PICOCERT_ERR_INVALID_VALIDITY_PERIOD | 13 | Invalid validity period          |\n| PICOCERT_ERR_CHAIN_TOO_LONG     | 14    | Certificate chain too long        |\n| PICOCERT_ERR_UNKNOWN            | 255   | Unknown error                     |\n\n## CLI Tool\n\nThe `picocert` command-line tool provides certificate management functionality:\n\n### Installation\n\n```bash\ncd cmd/picocert\ngo build -o picocert\n```\n\n### Commands\n\n#### Issue Certificates\n\n```bash\n# Create a self-signed root certificate\npicocert issue --subject \"MyRoot\" --validity_in_days 3650 --self_signed\n\n# Create a certificate signed by an issuer\npicocert issue --subject \"MyLeaf\" --validity_in_days 365 \\\n  --issuer root.pct --issuer_key root.priv.der\n```\n\n#### Sign Binary Files\n\n```bash\n# Sign a binary file\npicocert sign --key private.priv.der --binary firmware.bin --output firmware.sig\n\n# Sign and print signature to stdout\npicocert sign --key private.priv.der --binary firmware.bin\n```\n\n#### Verify Signatures\n\n```bash\n# Verify a signed binary\npicocert verify --cert certificate.pct --binary firmware.bin --signature firmware.sig\n```\n\n### Global Flags\n\n- `--quiet, -q`: Suppress output messages\n\n## Setting Up a Three-Tier PKI\n\nUse the included script to set up a PKI hierarchy:\n\n```bash\n# Set up a three-tier PKI for firmware signing\n./three-tier-pki.sh ./cmd/picocert/picocert firmware\n\n# This creates:\n# - firmware-root.pct/priv.der (Root CA, 10-year validity)\n# - firmware-intermediate.pct/priv.der (Intermediate CA, 6-year validity)\n# - firmware-leaf.pct/priv.der (Leaf certificate, 4-year validity)\n```\n\n**⚠️ Warning**: The script uses long validity periods. Consider your own needs for production use.\n\n## Go Package\n\nThe Go package `github.com/block/picocert/pkg/picocert` provides high-level certificate operations.\n\n### Go Installation\n\n```bash\ngo get github.com/block/picocert/pkg/picocert\n```\n\n### Usage\n\n#### Certificate Issuance\n\n```go\npackage main\n\nimport (\n    \"time\"\n    \"github.com/block/picocert/pkg/picocert\"\n)\n\nfunc main() {\n    // Issue a self-signed certificate\n    validFrom := uint64(time.Now().Unix())\n    validTo := validFrom + 365*24*60*60 // 1 year\n\n    cert, err := picocert.Issue(nil, \"MyDevice\", validFrom, validTo)\n    if err != nil {\n        panic(err)\n    }\n\n    // cert.Cert contains the certificate\n    // cert.PrivateKey contains the PKCS#8 encoded private key\n}\n```\n\n#### Certificate Signing\n\n```go\n// Issue a certificate signed by an issuer\nissuerCert := \u0026picocert.CertificateWithKey{\n    Cert:       issuerCertificate,\n    PrivateKey: issuerPrivateKeyBytes,\n}\n\nsignedCert, err := picocert.Issue(issuerCert, \"SubjectName\", validFrom, validTo)\nif err != nil {\n    panic(err)\n}\n```\n\n#### Data Signing and Verification\n\n```go\n// Sign data\nprivateKey, err := picocert.ParsePrivateKey(privateKeyBytes)\nif err != nil {\n    panic(err)\n}\n\nsignature, err := picocert.Sign(privateKey, data)\nif err != nil {\n    panic(err)\n}\n\n// Verify signature\nerr = picocert.Verify(\u0026certificate, data, signature)\nif err != nil {\n    // Verification failed\n}\n```\n\n#### Certificate Chain Validation\n\n```go\n// Validate a certificate chain\nchain := []picocert.Certificate{leafCert, intermediateCert, rootCert}\n\nerr := picocert.ValidateCertChain(chain)\nif err != nil {\n    // Chain validation failed\n}\n\n// Verify data against the chain\nerr = picocert.VerifyAndValidateChain(chain, data, signature)\nif err != nil {\n    // Verification or validation failed\n}\n```\n\n#### Parsing Certificates and Keys\n\n```go\n// Parse certificate from bytes\ncert, err := picocert.ParseCertificate(certBytes)\nif err != nil {\n    panic(err)\n}\n\n// Parse private key from PKCS#8 format\nprivateKey, err := picocert.ParsePrivateKey(keyBytes)\nif err != nil {\n    panic(err)\n}\n\n// Convert certificate to bytes\ncertBytes := cert.ToBytes()\n```\n\n### Go Package Types\n\n```go\ntype Certificate struct {\n    Version   uint8\n    Issuer    [32]byte\n    Subject   [32]byte\n    ValidFrom uint64\n    ValidTo   uint64\n    Curve     Curve\n    Hash      Hash\n    Reserved  uint32\n    PubKey    [65]byte\n    Signature [64]byte\n}\n\ntype CertificateWithKey struct {\n    Cert       Certificate\n    PrivateKey []byte  // PKCS#8 encoded\n}\n\n// Constants\nconst (\n    P256 Curve = 0     // ECDSA P-256\n    Sha256 Hash = 0    // SHA-256\n)\n```\n\n## Notes\n\n- Only ECDSA P-256 and SHA-256 are supported.\n- All fields are packed; no padding.\n- Names are fixed-length, null-terminated strings.\n- Public key is uncompressed (0x04 | X | Y, 65 bytes).\n- Signature is raw ECDSA (r||s, 64 bytes).\n- Timestamps are Unix epoch seconds.\n- Certificate data is stored in little-endian format for embedded compatibility.\n\n## File Formats\n\n- **Certificates**: Binary format (`.pct` extension), 184 bytes each\n- **Private Keys**: PKCS#8 DER format (`.priv.der` extension)\n- **Signatures**: Raw binary, 64 bytes (r||s format)\n\n## Security Considerations\n\n- Private keys should be stored securely and never transmitted in plaintext\n- Consider using hardware security modules (HSMs) for root CA key storage\n- Implement proper key rotation and certificate renewal procedures\n- Validate certificate chains completely before trusting signatures\n- Use appropriate validity periods for your security requirements\n- Provide secure implementations of the required cryptographic functions\n- The library enforces a maximum chain length to prevent DoS\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblock%2Fpicocert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblock%2Fpicocert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblock%2Fpicocert/lists"}