{"id":23191665,"url":"https://github.com/sentryco/passkeylib","last_synced_at":"2025-04-05T06:44:08.781Z","repository":{"id":267492683,"uuid":"901428727","full_name":"sentryco/PasskeyLib","owner":"sentryco","description":"Simplifies passkey storage and validation","archived":false,"fork":false,"pushed_at":"2025-01-27T19:37:00.000Z","size":193,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-02-10T14:38:09.009Z","etag":null,"topics":["authentication","cryptokit","encryption","passkey","signing","swift","validation","verification"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sentryco.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":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-10T16:34:37.000Z","updated_at":"2025-01-27T19:37:04.000Z","dependencies_parsed_at":"2024-12-10T17:44:48.725Z","dependency_job_id":"9e79ae0b-9612-4390-9045-a8be3ea693d8","html_url":"https://github.com/sentryco/PasskeyLib","commit_stats":null,"previous_names":["sentryco/passkeylib"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentryco%2FPasskeyLib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentryco%2FPasskeyLib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentryco%2FPasskeyLib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentryco%2FPasskeyLib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sentryco","download_url":"https://codeload.github.com/sentryco/PasskeyLib/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299789,"owners_count":20916186,"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","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":["authentication","cryptokit","encryption","passkey","signing","swift","validation","verification"],"created_at":"2024-12-18T12:18:24.468Z","updated_at":"2025-04-05T06:44:08.464Z","avatar_url":"https://github.com/sentryco.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Tests](https://github.com/sentryco/PasskeyLib/actions/workflows/Tests.yml/badge.svg)](https://github.com/sentryco/PasskeyLib/actions/workflows/Tests.yml)\n[![codebeat badge](https://codebeat.co/badges/180de22b-8712-452f-ab9a-ccdcbf9a558e)](https://codebeat.co/projects/github-com-sentryco-passkeylib-main)\n\n# PasskeyLib\n\n\u003e Simplifies passkey storage and validation in Swift.\n\n## Features\n\n- 🔑 **Passkey Data Object**: Easily store passkey data.\n- 🔏 **Passkey Signing and Verification**: Sign, validate, assert, and verify passkeys with simple APIs.\n\n## Installation\n\nAdd **PasskeyLib** to your project using Swift Package Manager:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/sentryco/PasskeyLib\", from: \"1.0.0\")\n]\n```\n\n## Usage Examples\n\n### Generating a New Passkey\n\nInteract with the `PKSigner` class to create a signature using a private key.\n\n```swift\nimport PasskeyLib\nimport CryptoKit\n\n// Example challenge data that needs to be signed\nlet challengeString = \"your-challenge-string\"\nguard let challengeData = challengeString.data(using: .utf8) else {\n    print(\"Failed to convert the challenge string to Data.\")\n    return\n}\n\n// Retrieve or generate your private key\n// For this example, we'll generate a new private key\nlet privateKey = P256.Signing.PrivateKey()\nlet privateKeyPEM = privateKey.pemRepresentation\n\n// Sign the challenge data using the private key\nif let signature = PKSigner.signWithPrivateKey(challengeData, privateKey: privateKeyPEM) {\n    // Signature generated successfully\n    // The signature can now be sent to the server for verification\n    print(\"Signature: \\(signature.base64URLEncodedString())\")\n} else {\n    // Failed to generate the signature\n    print(\"Failed to generate signature.\")\n}\n```\n\n### Verifying a Passkey\n\nThe server can verify the signature against the stored public key using the `PKValidator` class.\n\n```swift\nimport PasskeyLib\n\n// Replace with your actual base64-encoded public key string\nlet publicKey = \"your-public-key-string\"\n\n// Replace with the base64URL-encoded signature you received\nguard let signature = Data(base64URLEncoded: \"your-received-signature-string\") else {\n    print(\"Invalid signature data.\")\n    return\n}\n\n// The challenge data that was originally sent to the client\nlet challengeData = Data(\"your-challenge-string\".utf8)\n\n// Verify the signature\nlet isValid = PKValidator.verifySignature(signature, publicKey: publicKey, challenge: challengeData)\n\nif isValid {\n    print(\"Authentication successful.\")\n} else {\n    print(\"Authentication failed.\")\n}\n```\n\n### Creating an Assertion Credential\n\nGenerate an assertion credential using the `PKData` object.\n\n```swift\nimport PasskeyLib\nimport CryptoKit\n\n// Prepare your client data hash (e.g., SHA256 hash of your client data)\nlet clientDataJSON = \"{\\\"challenge\\\":\\\"your-challenge-value\\\"}\".data(using: .utf8)!\nlet clientDataHash = SHA256.hash(data: clientDataJSON)\n\n// Initialize your PKData instance with your passkey details\nlet pkData = PKData(\n    credentialID: \"your-credential-id-base64url\",\n    relyingParty: \"example.com\",\n    username: \"user@example.com\",\n    userHandle: \"your-user-handle-base64url\",\n    privateKey: \"your-private-key-string\"\n)\n\ndo {\n    // Generate the assertion credential\n    let assertionCredential = try pkData.getAssertionCredential(clientDataHash: clientDataHash)\n    // Use the assertionCredential as needed, e.g., send it to your server\n} catch {\n    print(\"Failed to create assertion credential: \\(error)\")\n}\n```\n\n## PKData JSON Structure\n\nBelow is an example of a `PKData` object represented in JSON format. Note that some fields are base64-encoded to represent binary data.\n\n```json\n{\n    \"credentialID\": \"Y3JlZGVudGlhbC1pZC1leGFtcGxl\",\n    \"relyingParty\": \"securebank.com\",\n    \"username\": \"john.doe\",\n    \"userHandle\": \"dXNlci1oYW5kbGUtc2FtcGxl\",\n    \"privateKey\": \"cHJpdmF0ZS1rZXktZXhhbXBsZQ==\"\n}\n```\n\n**Explanation:**\n\n- `credentialID`: Base64-encoded string of `\"credential-id-example\"`.\n- `relyingParty`: The domain of the relying party, e.g., `\"securebank.com\"`.\n- `username`: The username associated with the passkey.\n- `userHandle`: Base64-encoded string of `\"user-handle-sample\"`.\n- `privateKey`: Base64-encoded string of `\"private-key-example\"`.\n\n**Note:** When parsing this JSON, remember to decode the base64-encoded fields (`credentialID`, `userHandle`, `privateKey`) to obtain the original data.\n\n## Configuration\n\nSet the following in your extension's `Info.plist`:\n\n```xml\n\u003ckey\u003eProvidesPasskeys\u003c/key\u003e\n\u003ctrue/\u003e\n```\n\nIn the Credential Provider / AutoFill extension:\n\n```xml\nInfo.plist \u003e NSExtension \u003e NSExtensionAttributes \u003e ASCredentialProviderExtensionCapabilities \u003e ProvidesPasskeys = YES\nInfo.plist \u003e NSExtension \u003e NSExtensionAttributes \u003e ASCredentialProviderExtensionCapabilities \u003e ProvidesPasswords = YES\n```\n\n**Implement passkey support in AutoFill extension:**\n\nTo support passkeys, implement the following methods in your `CredentialProviderViewController`:\n\n```swift\noverride func prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest) {\n    // Handle passkey registration\n}\n\noverride func prepareInterface(forPasskeyAssertion assertionRequest: ASCredentialRequest) {\n    // Handle passkey assertion\n}\n\noverride func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {\n    // Handle the preparation of the credential list\n}\n\noverride func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {\n    // Provide a credential without user interaction\n}\n\noverride func prepareInterface(forExtensionConfiguration configuration: ASCredentialProviderExtensionConfiguration) {\n    // Handle the preparation of the interface for the extension configuration\n}\n```\n\n### Resources: \n- Early u2f swift lib: https://github.com/al45tair/u2f-swift\n- Interesting ble+u2f experiement by ledger: https://github.com/LedgerHQ/u2f-ble-test-ios/tree/master\n- Comprehensive project on passkey autenticator (also has BLE): https://github.com/kryptco/krypton-ios\n- Apples passkey api: https://developer.apple.com/documentation/authenticationservices/asauthorizationwebbrowserpublickeycredentialmanager\n- Apples passkey autofill api: https://developer.apple.com/documentation/authenticationservices/ascredentialproviderviewcontroller\n- Apple on passkeys: https://developer.apple.com/documentation/authenticationservices/public-private-key-authentication\n- Very small sample project for passkeys https://github.com/hansemannn/iOS16-Passkeys-Sample/tree/main\n- Interesting webhauthn + passkeys. comprehensive + has nice gifs: https://github.com/lyokato/WebAuthnKit-iOS/tree/develop\n- Lots of passkey code for swift: https://github.com/tkhq/swift-sdk/blob/35a3f203d406eaaf64cc647f4c001deddb69c365/Sources/Shared/PasskeyManager.swift\n- AttestationObj etc: https://github.com/Dashlane/apple-apps/tree/e66b4b162ac898c751b9ca0403dcb792cf70a566/Packages/Foundation/AppleWebAuthn/Sources/WebAuthn\n- More AttestationObj: https://github.com/ForgeRock/forgerock-ios-sdk/tree/57567fd627c10cd3432cb53749f851abec755a67/FRAuth/FRAuth/WebAuthn/Authenticator\n- Fido2 code for iOS (comprehensive): https://github.com/dqj1998/dFido2Lib-ios\n\n### Todo: \n- An interesting aspect with passkey is that we can use a secondary device to authenticate on. challnage response. So we could build in a sort of require secondary device challange feature. Exclusive.\n- Investigate if we can broadcast receipt from another device. If that works, we could build in passkey support for chrome. by scanning the qr in chrome. and then broadcasting from iPhone etc.\n- Write unit tests based on the codebase. Use AI to suggest areas that are testable and write test code etc ✅\n- Add problem / solution to readme\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsentryco%2Fpasskeylib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsentryco%2Fpasskeylib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsentryco%2Fpasskeylib/lists"}