{"id":20037987,"url":"https://github.com/perfectlysoft/perfect-crypto","last_synced_at":"2025-05-05T06:31:54.672Z","repository":{"id":17429339,"uuid":"81219752","full_name":"PerfectlySoft/Perfect-Crypto","owner":"PerfectlySoft","description":"Cryptographic Operations","archived":false,"fork":false,"pushed_at":"2022-05-11T10:40:01.000Z","size":128,"stargazers_count":28,"open_issues_count":7,"forks_count":20,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-30T17:15:47.486Z","etag":null,"topics":["crypto","cryptography","security","server-side-swift","ssl","swift"],"latest_commit_sha":null,"homepage":null,"language":"Swift","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/PerfectlySoft.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}},"created_at":"2017-02-07T14:56:22.000Z","updated_at":"2023-08-21T02:29:34.000Z","dependencies_parsed_at":"2022-11-28T10:39:22.574Z","dependency_job_id":null,"html_url":"https://github.com/PerfectlySoft/Perfect-Crypto","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-Crypto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-Crypto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-Crypto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectlySoft%2FPerfect-Crypto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PerfectlySoft","download_url":"https://codeload.github.com/PerfectlySoft/Perfect-Crypto/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224429206,"owners_count":17309667,"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":["crypto","cryptography","security","server-side-swift","ssl","swift"],"created_at":"2024-11-13T10:24:32.004Z","updated_at":"2024-11-13T10:24:32.723Z","avatar_url":"https://github.com/PerfectlySoft.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Perfect-Crypto [简体中文](README.zh_CN.md)\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"http://perfect.org/get-involved.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"http://perfect.org/assets/github/perfect_github_2_0_0.jpg\" alt=\"Get Involed with Perfect!\" width=\"854\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Swift-5.2-orange.svg?style=flat\" alt=\"Swift 5.2\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/swift/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat\" alt=\"Platforms OS X | Linux\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"http://perfect.org/licensing.html\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat\" alt=\"License Apache\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\nDigest, cipher and encoding support for Perfect.\n\n## Building\n\nAdd this project as a dependency in your Package.swift file.\n\n```\n.package(url: \"https://github.com/PerfectlySoft/Perfect-Crypto.git\", from: \"4.0.0\")\n```\n\n## Linux Build Notes\n\nEnsure that you have installed libssl-dev. OpenSSL 1.0.2+ is required for this package. On Ubuntu 14 or some Debian distributions you will need to update your OpenSSL before this package will build.\n\n```\nsudo apt-get install openssl libssl-dev\n```\n\n## Overview\n\nThis package wraps up some of the functionality provided by OpenSSL and adds a Swift layer on top of it. The main features are:\n\n* Extensions for String, [UInt8] and UnsafeRawBufferPointer that provide simple encode, decode, digest and cipher operations.\n* JWT (JSON Web Token) generation and validation.\n* Generation of arbitrary amounts of random data.\n* Swift wrappers around OpenSSL BIOs, providing chainable, filterable byte IO sinks and sources.\n* Convenience functions for creating Strings given non-null terminated UTF8 containing UnsafeRawBufferPointer or [UInt8] objects.\n\n## Usage Examples\n\n### Encode/Decode Hex\n\n```swift\nlet testStr = \"Hello, world!\"\nguard let hexBytes = testStr.encode(.hex) else {\n\treturn\n}\n\nString(validatingUTF8: hexBytes) == \"48656c6c6f2c20776f726c6421\"\n\nguard let unHex = hexBytes.decode(.hex) else {\n\treturn\n}\n\nString(validatingUTF8: unHex) == testStr\n\n```\n\n### Encode/Decode Base 64\n\n```swift\nlet testStr = \"Hello, world!\"\nguard let baseBytes = testStr.encode(.base64) else {\n\treturn\n}\n\nString(validatingUTF8: baseBytes) == \"SGVsbG8sIHdvcmxkIQ==\"\n\nguard let unBase = baseBytes.decode(.base64) else {\n\treturn\n}\n\nString(validatingUTF8: unBase) == testStr\n```\n\n### Digest\n\n```swift\nlet testStr = \"Hello, world!\"\nlet testAnswer = \"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3\"\nguard let enc = testStr.digest(.sha256)?.encode(.hex) else {\n\treturn\n}\n\nString(validatingUTF8: enc) == testAnswer\n```\n\n### HMAC Sign/Verify\n\nThe following snippet will HMAC-SHA1 sign, encode as base64, then decode, and verify a data string. Replace usages of .sha1 or .base64 depending on your requirements.\n\n```swift\nlet password = \"this is a good pw\"\nlet data = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\t\nif let signed = data.sign(.sha1, key: HMACKey(password))?.encode(.base64),\n\tlet base64Str = String(validatingUTF8: signed),\n\t\n\tlet reRawData = base64Str.decode(.base64) {\n\t\n\tlet verifyResult = data.verify(.sha1, signature: reRawData, key: HMACKey(password))\n\tXCTAssert(verifyResult)\n} else {\n\tXCTAssert(false, \"Failed signing\")\n}\n```\n\n### Public API\n\n```swift\npublic extension String {\n\t/// Construct a string from a UTF8 character pointer.\n\t/// Character data does not need to be null terminated.\n\t/// The buffer's count indicates how many characters are to be converted.\n\t/// Returns nil if the data is invalid.\n\tinit?(validatingUTF8 ptr: UnsafeRawBufferPointer?)\n\t/// Construct a string from a UTF8 character array.\n\t/// The array's count indicates how many characters are to be converted.\n\t/// Returns nil if the data is invalid.\n\tinit?(validatingUTF8 a: [UInt8])\n}\n\npublic extension String {\n\t/// Decode the String into an array of bytes using the indicated encoding.\n\t/// The string's UTF8 characters are decoded.\n\tfunc decode(_ encoding: Encoding) -\u003e [UInt8]?\n\t/// Encode the String into an array of bytes using the indicated encoding.\n\t/// The string's UTF8 characters are decoded.\n\tfunc encode(_ encoding: Encoding) -\u003e [UInt8]?\n\t/// Perform the digest algorithm on the String's UTF8 bytes\n\tfunc digest(_ digest: Digest) -\u003e [UInt8]?\n\t/// Sign the String data into an array of bytes using the indicated algorithm and key.\n\tfunc sign(_ digest: Digest, key: Key) -\u003e [UInt8]?\n\t/// Verify the signature against the String data.\n\t/// Returns true if the signature is verified. Returns false otherwise.\n\tfunc verify(_ digest: Digest, signature: [UInt8], key: Key) -\u003e Bool\n\t/// Encrypt this buffer using the indicated cipher, password, and salt.\n\t/// The string's UTF8 characters are encoded.\n\t/// Resulting data is in PEM encoded CMS format.\n\tfunc encrypt(_ cipher: Cipher,\n\t             password: String,\n\t             salt: String,\n\t             keyIterations: Int = 2048,\n\t             keyDigest: Digest = .md5) -\u003e String?\n\t/// Decrypt this PEM encoded CMS buffer using the indicated password and salt.\n\t/// Resulting decrypted data must be valid UTF-8 characters or the operation will fail.\n\tfunc decrypt(_ cipher: Cipher,\n\t             password: String,\n\t             salt: String,\n\t             keyIterations: Int = 2048,\n\t             keyDigest: Digest = .md5) -\u003e String?\n}\n\npublic protocol Octal {}\nextension UInt8: Octal {}\n\npublic extension Array where Element: Octal {\n\t/// Encode the Array into An array of bytes using the indicated encoding.\n\tfunc encode(_ encoding: Encoding) -\u003e [UInt8]?\n\t/// Decode the Array into an array of bytes using the indicated encoding.\n\tfunc decode(_ encoding: Encoding) -\u003e [UInt8]?\n\t/// Digest the Array data into an array of bytes using the indicated algorithm.\n\tfunc digest(_ digest: Digest) -\u003e [UInt8]?\n\t/// Sign the Array data into an array of bytes using the indicated algorithm and key.\n\tfunc sign(_ digest: Digest, key: Key) -\u003e [UInt8]?\n\t/// Verify the array against the signature.\n\t/// Returns true if the signature is verified. Returns false otherwise.\n\tfunc verify(_ digest: Digest, signature: [UInt8], key: Key) -\u003e Bool\n\t/// Decrypt this buffer using the indicated cipher, key an iv (initialization vector).\n\tfunc encrypt(_ cipher: Cipher, key: [UInt8], iv: [UInt8]) -\u003e [UInt8]?\n\t/// Decrypt this buffer using the indicated cipher, key an iv (initialization vector).\n\tfunc decrypt(_ cipher: Cipher, key: [UInt8], iv: [UInt8]) -\u003e [UInt8]?\n\t/// Encrypt this buffer using the indicated cipher, password, and salt.\n\t/// Resulting data is PEM encoded CMS format.\n\tfunc encrypt(_ cipher: Cipher,\n\t             password: [UInt8],\n\t             salt: [UInt8],\n\t             keyIterations: Int = 2048,\n\t             keyDigest: Digest = .md5) -\u003e [UInt8]?\n\t/// Decrypt this PEM encoded CMS buffer using the indicated password and salt.\n\tfunc decrypt(_ cipher: Cipher,\n\t             password: [UInt8],\n\t             salt: [UInt8],\n\t             keyIterations: Int = 2048,\n\t             keyDigest: Digest = .md5) -\u003e [UInt8]?\n}\n\npublic extension UnsafeRawBufferPointer {\n\t/// Encode the buffer using the indicated encoding.\n\t/// The return value must be deallocated by the caller.\n\tfunc encode(_ encoding: Encoding) -\u003e UnsafeMutableRawBufferPointer?\n\t/// Decode the buffer using the indicated encoding.\n\t/// The return value must be deallocated by the caller.\n\tfunc decode(_ encoding: Encoding) -\u003e UnsafeMutableRawBufferPointer?\n\t/// Digest the buffer using the indicated algorithm.\n\t/// The return value must be deallocated by the caller.\n\tfunc digest(_ digest: Digest) -\u003e UnsafeMutableRawBufferPointer?\n\t/// Sign the buffer using the indicated algorithm and key.\n\t/// The return value must be deallocated by the caller.\n\tfunc sign(_ digest: Digest, key: Key) -\u003e UnsafeMutableRawBufferPointer?\n\t/// Verify the signature against the buffer.\n\t/// Returns true if the signature is verified. Returns false otherwise.\n\tfunc verify(_ digest: Digest, signature: UnsafeRawBufferPointer, key: Key) -\u003e Bool\n\t/// Encrypt this buffer using the indicated cipher, key and iv (initialization vector).\n\t/// Returns a newly allocated buffer which must be freed by the caller.\n\tfunc encrypt(_ cipher: Cipher, key: UnsafeRawBufferPointer, iv: UnsafeRawBufferPointer) -\u003e UnsafeMutableRawBufferPointer?\n\t/// Decrypt this buffer using the indicated cipher, key and iv (initialization vector).\n\t/// Returns a newly allocated buffer which must be freed by the caller.\n\tfunc decrypt(_ cipher: Cipher, key: UnsafeRawBufferPointer, iv: UnsafeRawBufferPointer) -\u003e UnsafeMutableRawBufferPointer?\n\t/// Encrypt this buffer to PEM encoded CMS format using the indicated cipher, password, and salt.\n\t/// Returns a newly allocated buffer which must be freed by the caller.\n\tfunc encrypt(_ cipher: Cipher,\n\t             password: UnsafeRawBufferPointer,\n\t             salt: UnsafeRawBufferPointer,\n\t             keyIterations: Int = 2048,\n\t             keyDigest: Digest = .md5) -\u003e UnsafeMutableRawBufferPointer?\n   \t/// Decrypt this PEM encoded CMS buffer using the indicated password and salt.\n\t/// Returns a newly allocated buffer which must be freed by the caller.\n\tfunc decrypt(_ cipher: Cipher,\n\t             password: UnsafeRawBufferPointer,\n\t             salt: UnsafeRawBufferPointer,\n\t             keyIterations: Int = 2048,\n\t             keyDigest: Digest = .md5) -\u003e UnsafeMutableRawBufferPointer?\n}\n\npublic extension UnsafeRawBufferPointer {\n\t/// Allocate memory for `size` bytes with word alignment from the encryption library's\n\t///\trandom number generator.\n\t///\n\t/// - Postcondition: The memory is allocated and initialized to random bits.\n\tstatic func allocateRandom(count size: Int) -\u003e UnsafeRawBufferPointer? \n}\n\npublic extension FixedWidthInteger {\n  /// get a random integer, i.e., signed or unsigned int8/16/32/64\n  public static var random: Self\n}\npublic extension Float {\n  /// get a random float\n  public static var random: Float\n}\npublic extension Double {\n  /// get a random double\n  public static var random: Double \n}\n```\n\n### JSON Web Tokens (JWT)\n\nThis crypto package provides an means for creating new JWT tokens and validating existing tokens.\n\nJSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA. Source: [JWT](https://jwt.io/introduction/).\n\nNew JWT tokens are created through the `JWTCreator` object.\n\n```swift\n/// Creates and signs new JWT tokens.\npublic struct JWTCreator {\n\t/// Creates a new JWT token given a payload.\n\t/// The payload can then be signed to generate a JWT token string.\n\tpublic init?(payload: [String:Any])\n\t/// Sign and return a new JWT token string using an HMAC key.\n\t/// Additional headers can be optionally provided.\n\t/// Throws a JWT.Error.signingError if there is a problem generating the token string.\n\tpublic func sign(alg: JWT.Alg, key: String, headers: [String:Any] = [:]) throws -\u003e String\n\t/// Sign and return a new JWT token string using the given key.\n\t/// Additional headers can be optionally provided.\n\t/// The key type must be compatible with the indicated `algo`.\n\t/// Throws a JWT.Error.signingError if there is a problem generating the token string.\n\tpublic func sign(alg: JWT.Alg, key: Key, headers: [String:Any] = [:]) throws -\u003e String\n}\n```\n\nExisting JWT tokens can be validated through the `JWTVerifier` object.\n\n```swift\n/// Accepts a JWT token string and verifies its structural validity and signature.\npublic struct JWTVerifier {\n\t/// The headers obtained from the token.\n\tpublic var header: [String:Any]\n\t/// The payload carried by the token.\n\tpublic var payload: [String:Any]\n\t/// Create a JWTVerifier given a source string in the \"aaaa.bbbb.cccc\" format.\n\t/// Returns nil if the given string is not a valid JWT.\n\t/// *Does not perform verification in this step.* Call `verify` with your key to validate.\n\t/// If verification succeeds then the `.headers` and `.payload` properties can be safely accessed.\n\tpublic init?(_ jwt: String)\n\t/// Verify the token based on the indicated algorithm and HMAC key.\n\t/// Throws a JWT.Error.verificationError if any aspect of the token is incongruent.\n\t/// Returns without any error if the token was able to be verified.\n\t/// The parameter `algo` must match the token's \"alg\" header.\n\tpublic func verify(algo: JWT.Alg, key: String) throws\n\t/// Verify the token based on the indicated algorithm and key.\n\t/// Throws a JWT.Error.verificationError if any aspect of the token is incongruent.\n\t/// Returns without any error if the token was able to be verified.\n\t/// The parameter `algo` must match the token's \"alg\" header.\n\t/// The key type must be compatible with the indicated `algo`.\n\tpublic func verify(algo: JWT.Alg, key: Key) throws\n}\n```\n\nThe following example will create and then verify a token using the \"HS256\" alg scheme.\n\n```swift\nlet name = \"John Doe\"\nlet tstPayload = [\"sub\": \"1234567890\", \"name\": name, \"admin\": true] as [String : Any]\nlet secret = \"secret\"\nguard let jwt1 = JWTCreator(payload: tstPayload) else {\n\treturn // fatal error\n}\nlet token = try jwt1.sign(alg: .hs256, key: secret)\nguard let jwt = JWTVerifier(token) else {\n  return // fatal error\n}\ntry jwt.verify(algo: .hs256, key: HMACKey(secret))\nlet fndName = jwt.payload[\"name\"] as? String\n// name == fndName!\n```\n\nIt's important to note that the JWTVerifier will verify that the token is cryptographically sound, but it **does not** validate payload claims such as iss(uer) or exp(iration). You can obtain these from the payload dictionary and validate according to the needs of your application. \n\n### Supported encodings, digests and ciphers\n\n```swift\n/// Available encoding methods.\npublic enum Encoding {\n\tcase base64\n\tcase hex\n}\n\n/// Available digest methods.\npublic enum Digest {\n\tcase md4\n\tcase md5\n\tcase sha\n\tcase sha1\n\tcase dss\n\tcase dss1\n\tcase ecdsa\n\tcase sha224\n\tcase sha256\n\tcase sha384\n\tcase sha512\n\tcase ripemd160\n\tcase whirlpool\n\t\n\tcase custom(String)\n}\n\n/// Available ciphers.\npublic enum Cipher {\n\tcase des_ecb\n\tcase des_ede\n\tcase des_ede3\n\tcase des_ede_ecb\n\tcase des_ede3_ecb\n\tcase des_cfb64\n\tcase des_cfb1\n\tcase des_cfb8\n\tcase des_ede_cfb64\n\tcase des_ede3_cfb1\n\tcase des_ede3_cfb8\n\tcase des_ofb\n\tcase des_ede_ofb\n\tcase des_ede3_ofb\n\tcase des_cbc\n\tcase des_ede_cbc\n\tcase des_ede3_cbc\n\tcase desx_cbc\n\tcase des_ede3_wrap\n\tcase rc4\n\tcase rc4_40\n\tcase rc4_hmac_md5\n\tcase rc2_ecb\n\tcase rc2_cbc\n\tcase rc2_40_cbc\n\tcase rc2_64_cbc\n\tcase rc2_cfb64\n\tcase rc2_ofb\n\tcase bf_ecb\n\tcase bf_cbc\n\tcase bf_cfb64\n\tcase bf_ofb\n\tcase cast5_ecb\n\tcase cast5_cbc\n\tcase cast5_cfb64\n\tcase cast5_ofb\n\tcase aes_128_ecb\n\tcase aes_128_cbc\n\tcase aes_128_cfb1\n\tcase aes_128_cfb8\n\tcase aes_128_cfb128\n\tcase aes_128_ofb\n\tcase aes_128_ctr\n\tcase aes_128_ccm\n\tcase aes_128_gcm\n\tcase aes_128_xts\n\tcase aes_128_wrap\n\tcase aes_192_ecb\n\tcase aes_192_cbc\n\tcase aes_192_cfb1\n\tcase aes_192_cfb8\n\tcase aes_192_cfb128\n\tcase aes_192_ofb\n\tcase aes_192_ctr\n\tcase aes_192_ccm\n\tcase aes_192_gcm\n\tcase aes_192_wrap\n\tcase aes_256_ecb\n\tcase aes_256_cbc\n\tcase aes_256_cfb1\n\tcase aes_256_cfb8\n\tcase aes_256_cfb128\n\tcase aes_256_ofb\n\tcase aes_256_ctr\n\tcase aes_256_ccm\n\tcase aes_256_gcm\n\tcase aes_256_xts\n\tcase aes_256_wrap\n\tcase aes_128_cbc_hmac_sha1\n\tcase aes_256_cbc_hmac_sha1\n\tcase aes_128_cbc_hmac_sha256\n\tcase aes_256_cbc_hmac_sha256\n\tcase camellia_128_ecb\n\tcase camellia_128_cbc\n\tcase camellia_128_cfb1\n\tcase camellia_128_cfb8\n\tcase camellia_128_cfb128\n\tcase camellia_128_ofb\n\tcase camellia_192_ecb\n\tcase camellia_192_cbc\n\tcase camellia_192_cfb1\n\tcase camellia_192_cfb8\n\tcase camellia_192_cfb128\n\tcase camellia_192_ofb\n\tcase camellia_256_ecb\n\tcase camellia_256_cbc\n\tcase camellia_256_cfb1\n\tcase camellia_256_cfb8\n\tcase camellia_256_cfb128\n\tcase camellia_256_ofb\n\tcase seed_ecb\n\tcase seed_cbc\n\tcase seed_cfb128\n\tcase seed_ofb\n\t\n\tcase custom(String)\n}\n```\n\n## Further Information\n\nFor more documentation, please visit [perfect.org](http://www.perfect.org/docs/crypto.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-crypto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperfectlysoft%2Fperfect-crypto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperfectlysoft%2Fperfect-crypto/lists"}