|
| 1 | +-- SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) |
| 3 | +-- |
| 4 | +-- JanusKey ABI Foreign — C FFI declarations |
| 5 | +-- Maps Idris2 types to C-compatible foreign function interface |
| 6 | +-- Generated C headers go to generated/abi/januskey.h |
| 7 | + |
| 8 | +module JanusKey.ABI.Foreign |
| 9 | + |
| 10 | +import JanusKey.ABI.Types |
| 11 | +import JanusKey.ABI.Layout |
| 12 | +import Data.Vect |
| 13 | +import Data.So |
| 14 | + |
| 15 | +%default total |
| 16 | + |
| 17 | +-- ============================================================ |
| 18 | +-- C Type Mappings |
| 19 | +-- ============================================================ |
| 20 | + |
| 21 | +||| C-compatible error codes |
| 22 | +public export |
| 23 | +data CError : Type where |
| 24 | + JK_OK : CError -- 0 |
| 25 | + JK_ERR_NOT_INITIALIZED : CError -- 1 |
| 26 | + JK_ERR_INVALID_PATH : CError -- 2 |
| 27 | + JK_ERR_IO : CError -- 3 |
| 28 | + JK_ERR_CRYPTO : CError -- 4 |
| 29 | + JK_ERR_TX_NOT_ACTIVE : CError -- 5 |
| 30 | + JK_ERR_TX_CONFLICT : CError -- 6 |
| 31 | + JK_ERR_KEY_NOT_FOUND : CError -- 7 |
| 32 | + JK_ERR_KEY_REVOKED : CError -- 8 |
| 33 | + JK_ERR_OBLITERATION : CError -- 9 |
| 34 | + JK_ERR_ATTESTATION : CError -- 10 |
| 35 | + JK_ERR_BUFFER_TOO_SMALL : CError -- 11 |
| 36 | + |
| 37 | +||| Convert CError to numeric code for C |
| 38 | +public export |
| 39 | +errorCode : CError -> Int |
| 40 | +errorCode JK_OK = 0 |
| 41 | +errorCode JK_ERR_NOT_INITIALIZED = 1 |
| 42 | +errorCode JK_ERR_INVALID_PATH = 2 |
| 43 | +errorCode JK_ERR_IO = 3 |
| 44 | +errorCode JK_ERR_CRYPTO = 4 |
| 45 | +errorCode JK_ERR_TX_NOT_ACTIVE = 5 |
| 46 | +errorCode JK_ERR_TX_CONFLICT = 6 |
| 47 | +errorCode JK_ERR_KEY_NOT_FOUND = 7 |
| 48 | +errorCode JK_ERR_KEY_REVOKED = 8 |
| 49 | +errorCode JK_ERR_OBLITERATION = 9 |
| 50 | +errorCode JK_ERR_ATTESTATION = 10 |
| 51 | +errorCode JK_ERR_BUFFER_TOO_SMALL = 11 |
| 52 | + |
| 53 | +||| Proof: all error codes are non-negative |
| 54 | +public export |
| 55 | +errorCodeNonNeg : (e : CError) -> So (errorCode e >= 0) |
| 56 | +errorCodeNonNeg JK_OK = Oh |
| 57 | +errorCodeNonNeg JK_ERR_NOT_INITIALIZED = Oh |
| 58 | +errorCodeNonNeg JK_ERR_INVALID_PATH = Oh |
| 59 | +errorCodeNonNeg JK_ERR_IO = Oh |
| 60 | +errorCodeNonNeg JK_ERR_CRYPTO = Oh |
| 61 | +errorCodeNonNeg JK_ERR_TX_NOT_ACTIVE = Oh |
| 62 | +errorCodeNonNeg JK_ERR_TX_CONFLICT = Oh |
| 63 | +errorCodeNonNeg JK_ERR_KEY_NOT_FOUND = Oh |
| 64 | +errorCodeNonNeg JK_ERR_KEY_REVOKED = Oh |
| 65 | +errorCodeNonNeg JK_ERR_OBLITERATION = Oh |
| 66 | +errorCodeNonNeg JK_ERR_ATTESTATION = Oh |
| 67 | +errorCodeNonNeg JK_ERR_BUFFER_TOO_SMALL = Oh |
| 68 | + |
| 69 | +||| Proof: JK_OK is the only success code |
| 70 | +public export |
| 71 | +onlyOkIsSuccess : (e : CError) -> errorCode e = 0 -> e = JK_OK |
| 72 | +onlyOkIsSuccess JK_OK Refl = Refl |
| 73 | + |
| 74 | +-- ============================================================ |
| 75 | +-- Foreign Function Signatures |
| 76 | +-- These map to the C header generated/abi/januskey.h |
| 77 | +-- ============================================================ |
| 78 | + |
| 79 | +||| Opaque handle to a JanusKey instance |
| 80 | +public export |
| 81 | +data JKHandle : Type where |
| 82 | + MkJKHandle : (ptr : Int) -> JKHandle |
| 83 | + |
| 84 | +||| FFI: Initialize a JanusKey repository |
| 85 | +||| C: int jk_init(const char* path, jk_handle_t* out_handle) |
| 86 | +public export |
| 87 | +record JKInitArgs where |
| 88 | + constructor MkInitArgs |
| 89 | + repoPath : ValidPath |
| 90 | + |
| 91 | +||| FFI: Open an existing JanusKey repository |
| 92 | +||| C: int jk_open(const char* path, jk_handle_t* out_handle) |
| 93 | +public export |
| 94 | +record JKOpenArgs where |
| 95 | + constructor MkOpenArgs |
| 96 | + repoPath : ValidPath |
| 97 | + |
| 98 | +||| FFI: Execute a file operation |
| 99 | +||| C: int jk_execute(jk_handle_t handle, jk_op_kind_t op, |
| 100 | +||| const char* src, const char* dst) |
| 101 | +public export |
| 102 | +record JKExecuteArgs where |
| 103 | + constructor MkExecArgs |
| 104 | + handle : JKHandle |
| 105 | + op : OpKind |
| 106 | + src : ValidPath |
| 107 | + dst : Maybe ValidPath |
| 108 | + |
| 109 | +||| FFI: Undo the last operation |
| 110 | +||| C: int jk_undo(jk_handle_t handle) |
| 111 | +public export |
| 112 | +record JKUndoArgs where |
| 113 | + constructor MkUndoArgs |
| 114 | + handle : JKHandle |
| 115 | + |
| 116 | +||| FFI: Obliterate a file (irreversible secure deletion) |
| 117 | +||| C: int jk_obliterate(jk_handle_t handle, const char* path, |
| 118 | +||| jk_oblit_proof_t* out_proof) |
| 119 | +public export |
| 120 | +record JKObliterateArgs where |
| 121 | + constructor MkOblitArgs |
| 122 | + handle : JKHandle |
| 123 | + target : ValidPath |
| 124 | + |
| 125 | +||| FFI: Generate a new key |
| 126 | +||| C: int jk_generate_key(jk_handle_t handle, jk_algorithm_t algo, |
| 127 | +||| const char* passphrase, jk_key_id_t* out_id) |
| 128 | +public export |
| 129 | +record JKKeyGenArgs where |
| 130 | + constructor MkKeyGenArgs |
| 131 | + handle : JKHandle |
| 132 | + algorithm : KeyAlgorithm |
| 133 | + passphrase : String |
| 134 | + |
| 135 | +||| FFI: Rotate a key |
| 136 | +||| C: int jk_rotate_key(jk_handle_t handle, jk_key_id_t old_id, |
| 137 | +||| const char* new_passphrase, jk_key_id_t* out_new_id) |
| 138 | +public export |
| 139 | +record JKKeyRotateArgs where |
| 140 | + constructor MkKeyRotateArgs |
| 141 | + handle : JKHandle |
| 142 | + oldKeyId : KeyId |
| 143 | + newPassphrase : String |
| 144 | + |
| 145 | +||| FFI: Begin a transaction |
| 146 | +||| C: int jk_tx_begin(jk_handle_t handle, jk_tx_t* out_tx) |
| 147 | +||| FFI: Commit a transaction |
| 148 | +||| C: int jk_tx_commit(jk_handle_t handle, jk_tx_t tx) |
| 149 | +||| FFI: Rollback a transaction |
| 150 | +||| C: int jk_tx_rollback(jk_handle_t handle, jk_tx_t tx) |
| 151 | + |
| 152 | +-- ============================================================ |
| 153 | +-- Safety Contracts for FFI Boundary |
| 154 | +-- ============================================================ |
| 155 | + |
| 156 | +||| Contract: every FFI call returns a valid error code |
| 157 | +public export |
| 158 | +data ValidFFIReturn : Int -> Type where |
| 159 | + IsValid : So (code >= 0) -> So (code <= 11) -> ValidFFIReturn code |
| 160 | + |
| 161 | +||| Contract: init must be called before any other operation |
| 162 | +public export |
| 163 | +data Initialized : JKHandle -> Type where |
| 164 | + WasInitialized : Initialized h |
| 165 | + |
| 166 | +||| Contract: obliterate consumes the file linearly |
| 167 | +public export |
| 168 | +data ObliterateContract : JKHandle -> ValidPath -> Type where |
| 169 | + ||| After obliterate returns JK_OK, the file at path is |
| 170 | + ||| provably destroyed (3-pass overwrite + verification) |
| 171 | + OblitContract : (h : JKHandle) |
| 172 | + -> (p : ValidPath) |
| 173 | + -> (proof : ObliterationProof) |
| 174 | + -> ObliterateContract h p |
| 175 | + |
| 176 | +||| Contract: key generation produces unique IDs |
| 177 | +public export |
| 178 | +data KeyGenContract : JKHandle -> KeyAlgorithm -> Type where |
| 179 | + ||| Generated key ID is unique within this repository |
| 180 | + KeyGenUnique : (h : JKHandle) -> (a : KeyAlgorithm) -> (kid : KeyId) |
| 181 | + -> KeyGenContract h a |
0 commit comments