{
  "openapi": "3.1.1",
  "info": {
    "title": "Key Derivation Lab API",
    "version": "1.0.0",
    "summary": "Derive keys with PBKDF2, scrypt, Argon2id and HKDF; compare costs with OWASP advice.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://keyderivationlab.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Derive keys with PBKDF2, scrypt, Argon2id and HKDF; compare costs with OWASP advice.",
        "description": "Derives keys from a password and salt with PBKDF2 (SHA-1/256/512), scrypt, Argon2id and HKDF using the parameters you choose, and compares their memory and work settings with the OWASP Password Storage recommendations. With JavaScript on, PBKDF2, HKDF and scrypt run locally in your browser and show real timings.\n\nMethodology:\n- PBKDF2 (RFC 8018) repeats HMAC with the chosen hash; its cost is the iteration count and it needs almost no memory, so GPUs attack it efficiently.\n- scrypt (RFC 7914) needs about 128 × N × r bytes of memory per derivation, which makes large-scale guessing expensive. Argon2id (RFC 9106, winner of the Password Hashing Competition) is configured by memory m, passes t and lanes p; OWASP recommends it first.\n- HKDF (RFC 5869) extracts and expands keys from input that is already random (for example a Diffie–Hellman secret); it is fast and must not be used to hash passwords.\n- \"Meets OWASP\" compares your parameters with the minimums of the OWASP Password Storage Cheat Sheet (Argon2id m/t combinations; scrypt N×r ≥ 2^17×8; PBKDF2 1,300,000 / 600,000 / 210,000 iterations for SHA-1 / SHA-256 / SHA-512).\n- Server and API results contain no timings, so the same input always gives the same result. With JavaScript on, the page derives PBKDF2 and HKDF with WebCrypto and scrypt with a built-in JavaScript implementation in your browser and shows how long each took on your device. Argon2 is not available in browsers, so it needs the server (you are asked first).\n\nNever reuse the example salts. For real password storage use a maintained library that encodes parameters and salt (e.g. the PHC string format).",
        "tags": [
          "crypto"
        ],
        "x-mcp-tool": "key_derivation_lab",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyDerivationLabInput"
              },
              "examples": {
                "example1": {
                  "summary": "OWASP defaults: PBKDF2-SHA-256, scrypt, Argon2id, HKDF",
                  "value": {
                    "password": "correct horse battery staple",
                    "salt": "c2FsdHNhbHRzYWx0c2FsdA==",
                    "saltFormat": "base64"
                  }
                },
                "example2": {
                  "summary": "RFC 7914 scrypt test vector (N = 1024, r = 8, p = 16)",
                  "value": {
                    "password": "password",
                    "salt": "NaCl",
                    "algorithms": [
                      "scrypt"
                    ],
                    "scryptLogN": 10,
                    "scryptR": 8,
                    "scryptP": 16,
                    "keyLength": 64
                  }
                },
                "example3": {
                  "summary": "PBKDF2 with too few iterations (RFC 7914 §11 vector)",
                  "value": {
                    "password": "passwd",
                    "salt": "salt",
                    "algorithms": [
                      "pbkdf2-sha256"
                    ],
                    "pbkdf2Iterations": 1,
                    "keyLength": 64
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyDerivationLabResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "KeyDerivationLabInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "password": {
            "title": "Password or input key material",
            "type": "string",
            "maxLength": 1024
          },
          "passwordFormat": {
            "title": "Password is",
            "type": "string",
            "enum": [
              "utf8",
              "hex",
              "base64"
            ],
            "default": "utf8"
          },
          "salt": {
            "title": "Salt",
            "description": "Use a random, unique salt of 16 bytes per password in real systems.",
            "type": "string",
            "maxLength": 1024
          },
          "saltFormat": {
            "title": "Salt is",
            "type": "string",
            "enum": [
              "utf8",
              "hex",
              "base64"
            ],
            "default": "utf8"
          },
          "algorithms": {
            "title": "Functions",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "pbkdf2-sha1",
                "pbkdf2-sha256",
                "pbkdf2-sha512",
                "scrypt",
                "argon2id",
                "hkdf-sha256",
                "hkdf-sha512"
              ]
            },
            "default": [
              "pbkdf2-sha256",
              "scrypt",
              "argon2id",
              "hkdf-sha256"
            ]
          },
          "keyLength": {
            "title": "Output length",
            "type": "integer",
            "minimum": 16,
            "maximum": 128,
            "default": 32,
            "x-unit": "bytes"
          },
          "pbkdf2Iterations": {
            "title": "PBKDF2 iterations",
            "description": "Empty = OWASP value for each hash (SHA-1 1,300,000; SHA-256 600,000; SHA-512 210,000).",
            "type": "integer",
            "minimum": 1,
            "maximum": 5000000
          },
          "scryptLogN": {
            "title": "scrypt cost N = 2^…",
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 17
          },
          "scryptR": {
            "title": "scrypt block size r",
            "type": "integer",
            "minimum": 1,
            "maximum": 32,
            "default": 8
          },
          "scryptP": {
            "title": "scrypt parallelism p",
            "type": "integer",
            "minimum": 1,
            "maximum": 16,
            "default": 1
          },
          "argon2MemoryKiB": {
            "title": "Argon2 memory",
            "type": "integer",
            "minimum": 8,
            "maximum": 262144,
            "default": 19456,
            "x-unit": "KiB"
          },
          "argon2Passes": {
            "title": "Argon2 passes (t)",
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "default": 2
          },
          "argon2Parallelism": {
            "title": "Argon2 parallelism (p)",
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "default": 1
          },
          "hkdfInfo": {
            "title": "HKDF info (context)",
            "type": "string",
            "maxLength": 1024
          },
          "outputEncoding": {
            "title": "Output",
            "type": "string",
            "enum": [
              "hex",
              "base64"
            ],
            "default": "hex"
          }
        },
        "required": [
          "password",
          "salt"
        ]
      },
      "KeyDerivationLabResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "passwordBytes",
              "saltBytes",
              "keyLength",
              "encoding",
              "argon2Available",
              "derivations"
            ],
            "properties": {
              "passwordBytes": {
                "type": "integer"
              },
              "saltBytes": {
                "type": "integer"
              },
              "keyLength": {
                "type": "integer"
              },
              "encoding": {
                "type": "string",
                "enum": [
                  "hex",
                  "base64"
                ]
              },
              "argon2Available": {
                "type": "boolean"
              },
              "derivations": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "algorithm",
                    "label",
                    "parameters",
                    "memoryBytes",
                    "meetsOwasp",
                    "owasp",
                    "hex",
                    "base64",
                    "note"
                  ],
                  "properties": {
                    "algorithm": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "parameters": {
                      "type": "string"
                    },
                    "memoryBytes": {
                      "type": "integer",
                      "description": "Approximate working memory the function needs."
                    },
                    "meetsOwasp": {
                      "type": [
                        "boolean",
                        "null"
                      ],
                      "description": "Parameters at or above the OWASP minimum (null = not a password hash)."
                    },
                    "owasp": {
                      "type": "string"
                    },
                    "hex": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "base64": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "note": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}