├── .github └── workflows │ └── CI.yml ├── .gitignore ├── .gitmodules ├── .kotlin-js-store └── yarn.lock ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── .gitignore └── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── samples └── native │ ├── .gitignore │ ├── README.md │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── runner.kt │ └── nativeMain │ └── kotlin │ └── app.kt ├── secure-random ├── .gitignore ├── api │ ├── android │ │ └── secure-random.api │ └── jvm │ │ └── secure-random.api ├── build.gradle.kts └── src │ ├── androidMain │ └── AndroidManifest.xml │ ├── commonMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ ├── -CommonPlatform.kt │ │ ├── SecureRandom.kt │ │ └── internal │ │ └── -SecureRandom.kt │ ├── commonTest │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ ├── EnsureFilledHelper.kt │ │ └── SecureRandomUnitTest.kt │ ├── darwinMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── internal │ │ └── SecRandomDelegate.kt │ ├── jsMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── SecureRandom.kt │ ├── jvmAndroidMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── SecureRandom.kt │ ├── linuxAndroidMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── internal │ │ ├── -LinuxAndroidPlatform.kt │ │ ├── GetRandom.kt │ │ ├── SecRandomDelegate.kt │ │ ├── SecRandomSynchronized.kt │ │ └── URandom.kt │ ├── linuxAndroidTest │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── internal │ │ ├── FillCompletelyUnitTest.kt │ │ └── URandomUnitTest.kt │ ├── linuxX64Test │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── internal │ │ └── SecRandomSynchronizedUnitTest.kt │ ├── mingwMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ └── internal │ │ ├── -MingwPlatform.kt │ │ └── SecRandomDelegate.kt │ ├── nativeMain │ └── kotlin │ │ └── io │ │ └── matthewnelson │ │ └── secure │ │ └── random │ │ ├── SecureRandom.kt │ │ └── internal │ │ ├── -Cinterop.kt │ │ └── SecRandomDelegate.kt │ └── wasmMain │ └── kotlin │ └── io │ └── matthewnelson │ └── secure │ └── random │ └── internal │ └── SecRandomDelegate.kt ├── settings.gradle.kts └── tools └── check-publication ├── .gitignore ├── build.gradle.kts └── src ├── androidMain └── AndroidManifest.xml └── commonMain └── kotlin └── io └── matthewnelson └── tools └── check └── publication └── Stub.kt /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [ pull_request ] 4 | 5 | env: 6 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError -XX:MetaspaceSize=1g" 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [ macos-latest, ubuntu-latest, windows-latest ] 14 | job: [ test ] 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - name: Checkout Repo 20 | uses: actions/checkout@v3 21 | with: 22 | submodules: true 23 | 24 | - name: Validate Gradle Wrapper 25 | uses: gradle/wrapper-validation-action@v1 26 | 27 | - name: Setup Caches [ Gradle Wrapper ] 28 | uses: actions/cache@v1 29 | with: 30 | path: ~/.gradle/wrapper 31 | key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.*') }} 32 | restore-keys: ${{ runner.os }}-gradle-wrapper- 33 | 34 | - name: Setup Caches [ Konan ] 35 | uses: actions/cache@v1 36 | with: 37 | path: ~/.konan 38 | key: ${{ runner.os }}-konan-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('kotlin-components/includeBuild/dependencies/src/main/kotlin/versions.kt') }} 39 | restore-keys: ${{ runner.os }}-konan- 40 | 41 | - name: Setup Caches [ Gradle Caches ] 42 | uses: actions/cache@v1 43 | with: 44 | path: ~/.gradle/caches 45 | key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle.kts') }}-${{ hashFiles('kotlin-components/includeBuild/dependencies/src/main/kotlin/versions.kt') }} 46 | restore-keys: ${{ runner.os }}-gradle-caches- 47 | 48 | - name: Setup JDK 49 | uses: actions/setup-java@v3.4.0 50 | with: 51 | distribution: 'zulu' 52 | java-version: 11 53 | 54 | - name: Run macOS Tests 55 | if: matrix.os == 'macos-latest' && matrix.job == 'test' 56 | run: > 57 | ./gradlew check --stacktrace 58 | -PKMP_TARGETS="JVM,JS,IOS_ARM32,IOS_ARM64,IOS_X64,IOS_SIMULATOR_ARM64,MACOS_ARM64,MACOS_X64,TVOS_ARM64,TVOS_X64,TVOS_SIMULATOR_ARM64,WATCHOS_ARM32,WATCHOS_ARM64,WATCHOS_DEVICE_ARM64,WATCHOS_X64,WATCHOS_X86,WATCHOS_SIMULATOR_ARM64,WASM_32" 59 | 60 | - name: Run Linux Tests 61 | if: matrix.os == 'ubuntu-latest' && matrix.job == 'test' 62 | run: > 63 | ./gradlew check --stacktrace 64 | -PKMP_TARGETS="JVM,JS,ANDROID,ANDROID_ARM32,ANDROID_ARM64,ANDROID_X64,ANDROID_X86,LINUX_ARM32HFP,LINUX_ARM64,LINUX_MIPS32,LINUX_MIPSEL32,LINUX_X64,WASM_32" 65 | 66 | - name: Run Windows Tests 67 | if: matrix.os == 'windows-latest' && matrix.job == 'test' 68 | run: > 69 | ./gradlew check --stacktrace 70 | -PKMP_TARGETS="JVM,JS,MINGW_X64,MINGW_X86,WASM_32" 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | /build 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/ 42 | 43 | # Keystore files 44 | # Uncomment the following lines if you do not want to check your keystore files in. 45 | #*.jks 46 | #*.keystore 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | .cxx/ 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | # google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | 67 | # Version control 68 | vcs.xml 69 | 70 | # lint 71 | lint/intermediates/ 72 | lint/generated/ 73 | lint/outputs/ 74 | lint/tmp/ 75 | # lint/reports/ 76 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "kotlin-components"] 2 | path = kotlin-components 3 | url = https://github.com/05nelsonm/kotlin-components.git 4 | -------------------------------------------------------------------------------- /.kotlin-js-store/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@ungap/promise-all-settled@1.1.2": 6 | version "1.1.2" 7 | resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" 8 | integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== 9 | 10 | ansi-colors@4.1.1: 11 | version "4.1.1" 12 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" 13 | integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== 14 | 15 | ansi-regex@^5.0.1: 16 | version "5.0.1" 17 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 18 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 19 | 20 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 21 | version "4.3.0" 22 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 23 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 24 | dependencies: 25 | color-convert "^2.0.1" 26 | 27 | anymatch@~3.1.2: 28 | version "3.1.3" 29 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 30 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 31 | dependencies: 32 | normalize-path "^3.0.0" 33 | picomatch "^2.0.4" 34 | 35 | argparse@^2.0.1: 36 | version "2.0.1" 37 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 38 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 39 | 40 | balanced-match@^1.0.0: 41 | version "1.0.2" 42 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 43 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 44 | 45 | binary-extensions@^2.0.0: 46 | version "2.2.0" 47 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 48 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 49 | 50 | brace-expansion@^1.1.7: 51 | version "1.1.11" 52 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 53 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 54 | dependencies: 55 | balanced-match "^1.0.0" 56 | concat-map "0.0.1" 57 | 58 | brace-expansion@^2.0.1: 59 | version "2.0.1" 60 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 61 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 62 | dependencies: 63 | balanced-match "^1.0.0" 64 | 65 | braces@~3.0.2: 66 | version "3.0.2" 67 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 68 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 69 | dependencies: 70 | fill-range "^7.0.1" 71 | 72 | browser-stdout@1.3.1: 73 | version "1.3.1" 74 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 75 | integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== 76 | 77 | buffer-from@^1.0.0: 78 | version "1.1.2" 79 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" 80 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 81 | 82 | camelcase@^6.0.0: 83 | version "6.3.0" 84 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" 85 | integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== 86 | 87 | chalk@^4.1.0: 88 | version "4.1.2" 89 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 90 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 91 | dependencies: 92 | ansi-styles "^4.1.0" 93 | supports-color "^7.1.0" 94 | 95 | chokidar@3.5.3: 96 | version "3.5.3" 97 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 98 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 99 | dependencies: 100 | anymatch "~3.1.2" 101 | braces "~3.0.2" 102 | glob-parent "~5.1.2" 103 | is-binary-path "~2.1.0" 104 | is-glob "~4.0.1" 105 | normalize-path "~3.0.0" 106 | readdirp "~3.6.0" 107 | optionalDependencies: 108 | fsevents "~2.3.2" 109 | 110 | cliui@^7.0.2: 111 | version "7.0.4" 112 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" 113 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== 114 | dependencies: 115 | string-width "^4.2.0" 116 | strip-ansi "^6.0.0" 117 | wrap-ansi "^7.0.0" 118 | 119 | color-convert@^2.0.1: 120 | version "2.0.1" 121 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 122 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 123 | dependencies: 124 | color-name "~1.1.4" 125 | 126 | color-name@~1.1.4: 127 | version "1.1.4" 128 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 129 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 130 | 131 | concat-map@0.0.1: 132 | version "0.0.1" 133 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 134 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 135 | 136 | debug@4.3.4: 137 | version "4.3.4" 138 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 139 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 140 | dependencies: 141 | ms "2.1.2" 142 | 143 | decamelize@^4.0.0: 144 | version "4.0.0" 145 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" 146 | integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== 147 | 148 | diff@5.0.0: 149 | version "5.0.0" 150 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" 151 | integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== 152 | 153 | emoji-regex@^8.0.0: 154 | version "8.0.0" 155 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 156 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 157 | 158 | escalade@^3.1.1: 159 | version "3.1.1" 160 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 161 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 162 | 163 | escape-string-regexp@4.0.0: 164 | version "4.0.0" 165 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 166 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 167 | 168 | fill-range@^7.0.1: 169 | version "7.0.1" 170 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 171 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 172 | dependencies: 173 | to-regex-range "^5.0.1" 174 | 175 | find-up@5.0.0: 176 | version "5.0.0" 177 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 178 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 179 | dependencies: 180 | locate-path "^6.0.0" 181 | path-exists "^4.0.0" 182 | 183 | flat@^5.0.2: 184 | version "5.0.2" 185 | resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" 186 | integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== 187 | 188 | format-util@1.0.5: 189 | version "1.0.5" 190 | resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" 191 | integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== 192 | 193 | fs.realpath@^1.0.0: 194 | version "1.0.0" 195 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 196 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 197 | 198 | fsevents@~2.3.2: 199 | version "2.3.2" 200 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 201 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 202 | 203 | get-caller-file@^2.0.5: 204 | version "2.0.5" 205 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 206 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 207 | 208 | glob-parent@~5.1.2: 209 | version "5.1.2" 210 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 211 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 212 | dependencies: 213 | is-glob "^4.0.1" 214 | 215 | glob@7.2.0: 216 | version "7.2.0" 217 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" 218 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== 219 | dependencies: 220 | fs.realpath "^1.0.0" 221 | inflight "^1.0.4" 222 | inherits "2" 223 | minimatch "^3.0.4" 224 | once "^1.3.0" 225 | path-is-absolute "^1.0.0" 226 | 227 | has-flag@^4.0.0: 228 | version "4.0.0" 229 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 230 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 231 | 232 | he@1.2.0: 233 | version "1.2.0" 234 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 235 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 236 | 237 | inflight@^1.0.4: 238 | version "1.0.6" 239 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 240 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 241 | dependencies: 242 | once "^1.3.0" 243 | wrappy "1" 244 | 245 | inherits@2: 246 | version "2.0.4" 247 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 248 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 249 | 250 | is-binary-path@~2.1.0: 251 | version "2.1.0" 252 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 253 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 254 | dependencies: 255 | binary-extensions "^2.0.0" 256 | 257 | is-extglob@^2.1.1: 258 | version "2.1.1" 259 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 260 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 261 | 262 | is-fullwidth-code-point@^3.0.0: 263 | version "3.0.0" 264 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 265 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 266 | 267 | is-glob@^4.0.1, is-glob@~4.0.1: 268 | version "4.0.3" 269 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 270 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 271 | dependencies: 272 | is-extglob "^2.1.1" 273 | 274 | is-number@^7.0.0: 275 | version "7.0.0" 276 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 277 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 278 | 279 | is-plain-obj@^2.1.0: 280 | version "2.1.0" 281 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" 282 | integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== 283 | 284 | is-unicode-supported@^0.1.0: 285 | version "0.1.0" 286 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" 287 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== 288 | 289 | js-yaml@4.1.0: 290 | version "4.1.0" 291 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 292 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 293 | dependencies: 294 | argparse "^2.0.1" 295 | 296 | locate-path@^6.0.0: 297 | version "6.0.0" 298 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 299 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 300 | dependencies: 301 | p-locate "^5.0.0" 302 | 303 | log-symbols@4.1.0: 304 | version "4.1.0" 305 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 306 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== 307 | dependencies: 308 | chalk "^4.1.0" 309 | is-unicode-supported "^0.1.0" 310 | 311 | minimatch@5.0.1: 312 | version "5.0.1" 313 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" 314 | integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== 315 | dependencies: 316 | brace-expansion "^2.0.1" 317 | 318 | minimatch@^3.0.4: 319 | version "3.1.2" 320 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 321 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 322 | dependencies: 323 | brace-expansion "^1.1.7" 324 | 325 | mocha@10.0.0: 326 | version "10.0.0" 327 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" 328 | integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== 329 | dependencies: 330 | "@ungap/promise-all-settled" "1.1.2" 331 | ansi-colors "4.1.1" 332 | browser-stdout "1.3.1" 333 | chokidar "3.5.3" 334 | debug "4.3.4" 335 | diff "5.0.0" 336 | escape-string-regexp "4.0.0" 337 | find-up "5.0.0" 338 | glob "7.2.0" 339 | he "1.2.0" 340 | js-yaml "4.1.0" 341 | log-symbols "4.1.0" 342 | minimatch "5.0.1" 343 | ms "2.1.3" 344 | nanoid "3.3.3" 345 | serialize-javascript "6.0.0" 346 | strip-json-comments "3.1.1" 347 | supports-color "8.1.1" 348 | workerpool "6.2.1" 349 | yargs "16.2.0" 350 | yargs-parser "20.2.4" 351 | yargs-unparser "2.0.0" 352 | 353 | ms@2.1.2: 354 | version "2.1.2" 355 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 356 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 357 | 358 | ms@2.1.3: 359 | version "2.1.3" 360 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 361 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 362 | 363 | nanoid@3.3.3: 364 | version "3.3.3" 365 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" 366 | integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== 367 | 368 | normalize-path@^3.0.0, normalize-path@~3.0.0: 369 | version "3.0.0" 370 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 371 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 372 | 373 | once@^1.3.0: 374 | version "1.4.0" 375 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 376 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 377 | dependencies: 378 | wrappy "1" 379 | 380 | p-limit@^3.0.2: 381 | version "3.1.0" 382 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 383 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 384 | dependencies: 385 | yocto-queue "^0.1.0" 386 | 387 | p-locate@^5.0.0: 388 | version "5.0.0" 389 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 390 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 391 | dependencies: 392 | p-limit "^3.0.2" 393 | 394 | path-exists@^4.0.0: 395 | version "4.0.0" 396 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 397 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 398 | 399 | path-is-absolute@^1.0.0: 400 | version "1.0.1" 401 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 402 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 403 | 404 | picomatch@^2.0.4, picomatch@^2.2.1: 405 | version "2.3.1" 406 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 407 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 408 | 409 | randombytes@^2.1.0: 410 | version "2.1.0" 411 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 412 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 413 | dependencies: 414 | safe-buffer "^5.1.0" 415 | 416 | readdirp@~3.6.0: 417 | version "3.6.0" 418 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 419 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 420 | dependencies: 421 | picomatch "^2.2.1" 422 | 423 | require-directory@^2.1.1: 424 | version "2.1.1" 425 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 426 | integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== 427 | 428 | safe-buffer@^5.1.0: 429 | version "5.2.1" 430 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 431 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 432 | 433 | serialize-javascript@6.0.0: 434 | version "6.0.0" 435 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" 436 | integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== 437 | dependencies: 438 | randombytes "^2.1.0" 439 | 440 | source-map-support@0.5.21: 441 | version "0.5.21" 442 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" 443 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 444 | dependencies: 445 | buffer-from "^1.0.0" 446 | source-map "^0.6.0" 447 | 448 | source-map@^0.6.0: 449 | version "0.6.1" 450 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 451 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 452 | 453 | string-width@^4.1.0, string-width@^4.2.0: 454 | version "4.2.3" 455 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 456 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 457 | dependencies: 458 | emoji-regex "^8.0.0" 459 | is-fullwidth-code-point "^3.0.0" 460 | strip-ansi "^6.0.1" 461 | 462 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 463 | version "6.0.1" 464 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 465 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 466 | dependencies: 467 | ansi-regex "^5.0.1" 468 | 469 | strip-json-comments@3.1.1: 470 | version "3.1.1" 471 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 472 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 473 | 474 | supports-color@8.1.1: 475 | version "8.1.1" 476 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 477 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== 478 | dependencies: 479 | has-flag "^4.0.0" 480 | 481 | supports-color@^7.1.0: 482 | version "7.2.0" 483 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 484 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 485 | dependencies: 486 | has-flag "^4.0.0" 487 | 488 | to-regex-range@^5.0.1: 489 | version "5.0.1" 490 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 491 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 492 | dependencies: 493 | is-number "^7.0.0" 494 | 495 | workerpool@6.2.1: 496 | version "6.2.1" 497 | resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" 498 | integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== 499 | 500 | wrap-ansi@^7.0.0: 501 | version "7.0.0" 502 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 503 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 504 | dependencies: 505 | ansi-styles "^4.0.0" 506 | string-width "^4.1.0" 507 | strip-ansi "^6.0.0" 508 | 509 | wrappy@1: 510 | version "1.0.2" 511 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 512 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 513 | 514 | y18n@^5.0.5: 515 | version "5.0.8" 516 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 517 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 518 | 519 | yargs-parser@20.2.4: 520 | version "20.2.4" 521 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" 522 | integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== 523 | 524 | yargs-parser@^20.2.2: 525 | version "20.2.9" 526 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" 527 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== 528 | 529 | yargs-unparser@2.0.0: 530 | version "2.0.0" 531 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" 532 | integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== 533 | dependencies: 534 | camelcase "^6.0.0" 535 | decamelize "^4.0.0" 536 | flat "^5.0.2" 537 | is-plain-obj "^2.1.0" 538 | 539 | yargs@16.2.0: 540 | version "16.2.0" 541 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" 542 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== 543 | dependencies: 544 | cliui "^7.0.2" 545 | escalade "^3.1.1" 546 | get-caller-file "^2.0.5" 547 | require-directory "^2.1.1" 548 | string-width "^4.2.0" 549 | y18n "^5.0.5" 550 | yargs-parser "^20.2.2" 551 | 552 | yocto-queue@^0.1.0: 553 | version "0.1.0" 554 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 555 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 556 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Version 0.1.3 (2023-03-07) 4 | - Adds `Deprecated` annotations to `SecureRandom` and `SecRandomCopyException` 5 | - Moves the project to [KotlinCrypto][url-1] organization at [THIS][url-2] 6 | repository. 7 | 8 | ## Version 0.1.2 (2023-01-16) 9 | - Add support for `js-browser` and `js-node` ([#41][pr-41]) 10 | 11 | ## Version 0.1.1 (2023-01-16) 12 | - Fixes `fillCompletely` function not updating internal index properly, 13 | resulting in an endless loop if more than 2 invocations were needed. ([#40][pr-40]) 14 | 15 | ## Version 0.1.0 (2023-01-16) 16 | - Initial Release 17 | 18 | [url-1]: https://github.com/KotlinCrypto/ 19 | [url-2]: https://github.com/KotlinCrypto/secure-random 20 | [pr-40]: https://github.com/05nelsonm/secure-random/pull/40 21 | [pr-41]: https://github.com/05nelsonm/secure-random/pull/41 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # secure-random 2 | 3 | This project has been moved to the [KotlinCrypto][1] organization 4 | and can be found [HERE][2]. No further updates to this project 5 | will be made, and it **should not** be used. 6 | 7 | ### Migration 8 | 9 | If you are already utilizing this project, you can follow the steps below 10 | to migrate to [KotlinCrypto/secure-random][2] 11 | 12 | 1. Add the replacement dependency + update this project's dependency to the 13 | latest release (to bring in the `Deprecation` notice). 14 | ```kotlin 15 | dependencies { 16 | implementation("org.kotlincrypto:secure-random:0.1.0") 17 | 18 | // TODO: Remove after replacing imports in source code 19 | implementation("io.matthewnelson.kotlin-components:secure-random:0.1.3") 20 | } 21 | ``` 22 | 2. Use the `ReplaceWith` deprecation feature to replace imports for `SecureRandom` and `SecRandomCopyException` 23 | 24 | ![image](https://user-images.githubusercontent.com/44778092/223445774-3dd67218-9d6e-446e-be80-643b4d0282db.png) 25 | 26 | 3. Remove dependency `io.matthewnelson.kotlin-components:secure-random` from your project. 27 | 28 | [1]: https://github.com/KotlinCrypto/ 29 | [2]: https://github.com/KotlinCrypto/secure-random 30 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | import io.matthewnelson.kotlin.components.kmp.util.configureYarn 17 | 18 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 19 | buildscript { 20 | 21 | repositories { 22 | mavenCentral() 23 | google() 24 | gradlePluginPortal() 25 | } 26 | 27 | dependencies { 28 | classpath(pluginDeps.kotlin.gradle) 29 | classpath(pluginDeps.android.gradle) 30 | classpath(pluginDeps.mavenPublish) 31 | 32 | // NOTE: Do not place your application dependencies here; they belong 33 | // in the individual module build.gradle.kts files 34 | } 35 | } 36 | 37 | allprojects { 38 | 39 | repositories { 40 | mavenCentral() 41 | google() 42 | gradlePluginPortal() 43 | } 44 | 45 | } 46 | 47 | configureYarn { rootYarn, _ -> 48 | rootYarn.apply { 49 | lockFileDirectory = project.rootDir.resolve(".kotlin-js-store") 50 | } 51 | } 52 | 53 | plugins { 54 | id(pluginId.kmp.publish) 55 | id(pluginId.kotlin.binaryCompat) version(versions.gradle.binaryCompat) 56 | } 57 | 58 | kmpPublish { 59 | setupRootProject( 60 | versionName = "0.1.3", 61 | // 1.0.0-alpha1 == 01_00_00_11 62 | // 1.0.0-alpha2 == 01_00_00_12 63 | // 1.0.0-beta1 == 01_00_00_21 64 | // 1.0.0-rc1 == 01_00_00_31 65 | // 1.0.0 == 01_00_00_99 66 | // 1.0.1 == 01_00_01_99 67 | // 1.1.1 == 01_01_01_99 68 | // 1.15.1 == 01_15_01_99 69 | versionCode = /*00_0 */1_03_99, 70 | pomInceptionYear = 2023, 71 | ) 72 | } 73 | 74 | @Suppress("LocalVariableName") 75 | apiValidation { 76 | val KMP_TARGETS = findProperty("KMP_TARGETS") as? String 77 | val CHECK_PUBLICATION = findProperty("CHECK_PUBLICATION") as? String 78 | val KMP_TARGETS_ALL = System.getProperty("KMP_TARGETS_ALL") != null 79 | val TARGETS = KMP_TARGETS?.split(',') 80 | 81 | if (CHECK_PUBLICATION != null) { 82 | ignoredProjects.add("check-publication") 83 | } else { 84 | val JVM = TARGETS?.contains("JVM") != false 85 | val ANDROID = TARGETS?.contains("ANDROID") != false 86 | 87 | // Only check if building both Android and Jvm 88 | if (!(KMP_TARGETS_ALL || (ANDROID && JVM))) { 89 | ignoredProjects.add("secure-random") 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | plugins { 17 | `kotlin-dsl` 18 | } 19 | 20 | repositories { 21 | gradlePluginPortal() 22 | } 23 | 24 | dependencies { 25 | implementation(files("../kotlin-components/includeBuild/dependencies/build/libs/dependencies-SNAPSHOT.jar")) 26 | } 27 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 2 | org.gradle.parallel=true 3 | org.gradle.caching=true 4 | 5 | android.disableAutomaticComponentCreation=true 6 | android.enableJetifier=true 7 | android.useAndroidX=true 8 | 9 | kotlin.code.style=official 10 | kotlin.js.compiler=both 11 | 12 | kotlin.mpp.enableCompatibilityMetadataVariant=true 13 | kotlin.mpp.stability.nowarn=true 14 | 15 | kotlin.native.binary.memoryModel=experimental 16 | kotlin.native.ignoreDisabledTargets=true 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/05nelsonm/secure-random/0caa48fb6c03004d0bcd242c0fdc394ea8e39192/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Oct 31 12:37:08 EDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStorePath=wrapper/dists 5 | zipStoreBase=GRADLE_USER_HOME 6 | 7 | # https://gradle.org/release-checksums/ 8 | distributionSha256Sum=312eb12875e1747e05c2f81a4789902d7e4ec5defbd1eefeaccc08acf096505d 9 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip 10 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /samples/native/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /samples/native/README.md: -------------------------------------------------------------------------------- 1 | # SecureRandom native app 2 | 3 | Simple native application that will use `SecureRandom` to generate and 4 | then print random bytes. 5 | 6 | ### Running 7 | 8 | On Linux: 9 | ``` 10 | ./gradlew :samples:native:runDebugExecutableLinuxX64 -PKMP_TARGETS=LINUX_X64 11 | ``` 12 | 13 | On macOS X: 14 | ``` 15 | ./gradlew :samples:native:runDebugExecutableMacosX64 -PKMP_TARGETS=MACOS_X64 16 | ``` 17 | 18 | On macOS M1-2: 19 | ``` 20 | ./gradlew :samples:native:runDebugExecutableMacosArm64 -PKMP_TARGETS=MACOS_ARM64 21 | ``` 22 | 23 | On Windows: 24 | ``` 25 | ./gradlew :samples:native:runDebugExecutableMingwX64 -PKMP_TARGETS=MINGW_X64 26 | ``` 27 | -------------------------------------------------------------------------------- /samples/native/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.matthewnelson.kotlin.components.kmp.KmpTarget 2 | import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget 3 | 4 | /* 5 | * Copyright (c) 2023 Matthew Nelson 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * https://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | plugins { 20 | id(pluginId.kmp.configuration) 21 | } 22 | 23 | kmpConfiguration { 24 | 25 | fun KotlinNativeTarget.setup() { 26 | binaries { 27 | executable { 28 | entryPoint = "main" 29 | } 30 | } 31 | } 32 | 33 | val osName = System.getProperty("os.name") 34 | when { 35 | osName.startsWith("Windows", true) -> { 36 | KmpTarget.NonJvm.Native.Mingw.X64(target = { setup() }) 37 | } 38 | osName == "Mac OS X" -> { 39 | KmpTarget.NonJvm.Native.Unix.Darwin.Macos.X64(target = { setup() }) 40 | } 41 | osName.contains("Mac", true) -> { 42 | KmpTarget.NonJvm.Native.Unix.Darwin.Macos.Arm64(target = { setup() }) 43 | } 44 | osName == "Linux" -> { 45 | KmpTarget.NonJvm.Native.Unix.Linux.X64(target = { setup() }) 46 | } 47 | else -> null 48 | }?.let { target -> 49 | setupMultiplatform( 50 | targets = setOf(target), 51 | commonMainSourceSet = { 52 | dependencies { 53 | implementation(project(":secure-random")) 54 | } 55 | } 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /samples/native/src/commonMain/kotlin/runner.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | import io.matthewnelson.secure.random.SecRandomCopyException 18 | import io.matthewnelson.secure.random.SecureRandom 19 | 20 | fun runSecureRandom() { 21 | val sRandom = SecureRandom() 22 | 23 | for (i in 10..20) { 24 | val bytes = try { 25 | sRandom.nextBytesOf(i).toList() 26 | } catch (e: SecRandomCopyException) { 27 | e.printStackTrace() 28 | return 29 | } 30 | 31 | println("$i: $bytes") 32 | } 33 | 34 | listOf( 35 | 500, 36 | 5000, 37 | 50000, 38 | ).forEach { i -> 39 | 40 | try { 41 | sRandom.nextBytesOf(i) 42 | } catch (e: SecRandomCopyException) { 43 | e.printStackTrace() 44 | return 45 | } 46 | 47 | println("$i: omitted (success)") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /samples/native/src/nativeMain/kotlin/app.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | fun main() { 18 | // To prevent 134 exit status https://youtrack.jetbrains.com/issue/KTOR-1220 19 | Platform.isMemoryLeakCheckerActive = false 20 | 21 | runSecureRandom() 22 | } 23 | -------------------------------------------------------------------------------- /secure-random/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /secure-random/api/android/secure-random.api: -------------------------------------------------------------------------------- 1 | public final class io/matthewnelson/secure/random/BuildConfig { 2 | public static final field BUILD_TYPE Ljava/lang/String; 3 | public static final field DEBUG Z 4 | public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; 5 | public fun ()V 6 | } 7 | 8 | public final class io/matthewnelson/secure/random/SecRandomCopyException : java/lang/RuntimeException { 9 | public fun ()V 10 | public fun (Ljava/lang/String;)V 11 | public fun (Ljava/lang/String;Ljava/lang/Throwable;)V 12 | public fun (Ljava/lang/Throwable;)V 13 | } 14 | 15 | public final class io/matthewnelson/secure/random/SecureRandom : java/security/SecureRandom { 16 | public fun ()V 17 | public final fun nextBytesCopyTo ([B)V 18 | public final fun nextBytesOf (I)[B 19 | } 20 | 21 | -------------------------------------------------------------------------------- /secure-random/api/jvm/secure-random.api: -------------------------------------------------------------------------------- 1 | public final class io/matthewnelson/secure/random/SecRandomCopyException : java/lang/RuntimeException { 2 | public fun ()V 3 | public fun (Ljava/lang/String;)V 4 | public fun (Ljava/lang/String;Ljava/lang/Throwable;)V 5 | public fun (Ljava/lang/Throwable;)V 6 | } 7 | 8 | public final class io/matthewnelson/secure/random/SecureRandom : java/security/SecureRandom { 9 | public fun ()V 10 | public final fun nextBytesCopyTo ([B)V 11 | public final fun nextBytesOf (I)[B 12 | } 13 | 14 | -------------------------------------------------------------------------------- /secure-random/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | import io.matthewnelson.kotlin.components.kmp.KmpTarget 17 | import io.matthewnelson.kotlin.components.kmp.util.* 18 | 19 | plugins { 20 | id(pluginId.kmp.configuration) 21 | id(pluginId.kmp.publish) 22 | } 23 | 24 | kmpConfiguration { 25 | setupMultiplatform(targets= 26 | setOf( 27 | KmpTarget.Jvm.Android( 28 | buildTools = versions.android.buildTools, 29 | compileSdk = versions.android.sdkCompile, 30 | 31 | // https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html 32 | minSdk = versions.android.sdkMin19, // KitKat (4.4) 33 | 34 | namespace = "io.matthewnelson.secure.random", 35 | compileSourceOption = JavaVersion.VERSION_1_8, 36 | compileTargetOption = JavaVersion.VERSION_1_8, 37 | kotlinJvmTarget = JavaVersion.VERSION_1_8, 38 | target = { 39 | publishLibraryVariants("release") 40 | } 41 | ), 42 | KmpTarget.Jvm.Jvm(kotlinJvmTarget = JavaVersion.VERSION_1_8), 43 | KmpTarget.NonJvm.JS.DEFAULT, 44 | KmpTarget.NonJvm.Native.Unix.Darwin.Watchos.DeviceArm64.DEFAULT, 45 | ) + 46 | KmpTarget.NonJvm.Native.Android.ALL_DEFAULT + 47 | 48 | // TODO: Implement (Issue #36) 49 | // Also uncomment in :tools:check-publication build.gradle.kts 50 | // KmpTarget.NonJvm.Native.Wasm.ALL_DEFAULT + 51 | 52 | KmpTarget.NonJvm.Native.Unix.Darwin.Ios.ALL_DEFAULT + 53 | KmpTarget.NonJvm.Native.Unix.Darwin.Macos.ALL_DEFAULT + 54 | KmpTarget.NonJvm.Native.Unix.Darwin.Tvos.ALL_DEFAULT + 55 | KmpTarget.NonJvm.Native.Unix.Darwin.Watchos.ALL_DEFAULT + 56 | KmpTarget.NonJvm.Native.Unix.Linux.ALL_DEFAULT + 57 | KmpTarget.NonJvm.Native.Mingw.ALL_DEFAULT, 58 | 59 | commonTestSourceSet = { 60 | dependencies { 61 | implementation(kotlin("test")) 62 | } 63 | }, 64 | 65 | kotlin = { 66 | explicitApi() 67 | 68 | val linuxMain = sourceSetLinuxMain 69 | val androidNativeMain = sourceSetAndroidNativeMain 70 | 71 | // If either linux or androidNative sources are available 72 | if (linuxMain != null || androidNativeMain != null) { 73 | sourceSets { 74 | val linuxAndroidMain by creating { 75 | dependsOn(sourceSetNativeMain!!) 76 | } 77 | val linuxAndroidTest by creating { 78 | dependsOn(sourceSetNativeTest!!) 79 | } 80 | 81 | linuxMain?.apply { 82 | dependsOn(linuxAndroidMain) 83 | } 84 | sourceSetLinuxTest { 85 | dependsOn(linuxAndroidTest) 86 | } 87 | androidNativeMain?.apply { 88 | dependsOn(linuxAndroidMain) 89 | } 90 | sourceSetAndroidNativeTest { 91 | dependsOn(linuxAndroidTest) 92 | } 93 | } 94 | } 95 | 96 | sourceSetLinuxX64Test { 97 | dependencies { 98 | implementation(depsTest.kotlin.coroutines) 99 | } 100 | } 101 | } 102 | ) 103 | } 104 | 105 | kmpPublish { 106 | setupModule( 107 | pomDescription = "Kotlin Components' SecureRandom Component", 108 | ) 109 | } 110 | -------------------------------------------------------------------------------- /secure-random/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | -------------------------------------------------------------------------------- /secure-random/src/commonMain/kotlin/io/matthewnelson/secure/random/-CommonPlatform.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | import kotlin.contracts.ExperimentalContracts 19 | import kotlin.contracts.InvocationKind 20 | import kotlin.contracts.contract 21 | 22 | @OptIn(ExperimentalContracts::class) 23 | @Suppress("NOTHING_TO_INLINE") 24 | internal inline fun ByteArray?.ifNotNullOrEmpty(block: ByteArray.() -> Unit) { 25 | contract { 26 | callsInPlace(block, InvocationKind.AT_MOST_ONCE) 27 | } 28 | 29 | if (this == null || this.isEmpty()) return 30 | block.invoke(this) 31 | } 32 | 33 | @Deprecated( 34 | message = """ 35 | Project moved to the https://github.com/KotlinCrypto organization. 36 | 37 | Step 1: Add dependency 'org.kotlincrypto:secure-random:0.1.0' to your project. 38 | Step 2: Use the 'ReplaceWith' feature on import. 39 | Step 3: Remove dependency 'io.matthewnelson.kotlin-components:secure-random'. 40 | 41 | See more at https://github.com/05nelsonm/secure-random/blob/master/README.md#migration 42 | """, 43 | replaceWith = ReplaceWith( 44 | expression = "SecRandomCopyException", 45 | imports = [ "org.kotlincrypto.SecRandomCopyException" ] 46 | ), 47 | level = DeprecationLevel.WARNING, 48 | ) 49 | public class SecRandomCopyException: RuntimeException { 50 | public constructor(): super() 51 | public constructor(message: String?): super(message) 52 | public constructor(message: String?, cause: Throwable?): super(message, cause) 53 | public constructor(cause: Throwable?): super(cause) 54 | } 55 | -------------------------------------------------------------------------------- /secure-random/src/commonMain/kotlin/io/matthewnelson/secure/random/SecureRandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | @Deprecated( 19 | message = """ 20 | Project moved to the https://github.com/KotlinCrypto organization. 21 | 22 | Step 1: Add dependency 'org.kotlincrypto:secure-random:0.1.0' to your project. 23 | Step 2: Use the 'ReplaceWith' feature on import. 24 | Step 3: Remove dependency 'io.matthewnelson.kotlin-components:secure-random'. 25 | 26 | See more at https://github.com/05nelsonm/secure-random/blob/master/README.md#migration 27 | """, 28 | replaceWith = ReplaceWith( 29 | expression = "SecureRandom", 30 | imports = [ "org.kotlincrypto.SecureRandom" ] 31 | ), 32 | level = DeprecationLevel.WARNING, 33 | ) 34 | public expect class SecureRandom() { 35 | 36 | /** 37 | * Returns a [ByteArray] of size [count], filled with 38 | * securely generated random data. 39 | * 40 | * @throws [IllegalArgumentException] if [count] is negative. 41 | * @throws [SecRandomCopyException] if [nextBytesCopyTo] failed. 42 | * */ 43 | @Throws(IllegalArgumentException::class, SecRandomCopyException::class) 44 | public fun nextBytesOf(count: Int): ByteArray 45 | 46 | /** 47 | * Fills a [ByteArray] with securely generated random data. 48 | * Does nothing if [bytes] is null or empty. 49 | * 50 | * @throws [SecRandomCopyException] if procurement of securely random data failed. 51 | * */ 52 | @Throws(SecRandomCopyException::class) 53 | public fun nextBytesCopyTo(bytes: ByteArray?) 54 | } 55 | -------------------------------------------------------------------------------- /secure-random/src/commonMain/kotlin/io/matthewnelson/secure/random/internal/-SecureRandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.SecRandomCopyException 19 | import io.matthewnelson.secure.random.SecureRandom 20 | 21 | @Suppress("NOTHING_TO_INLINE") 22 | @Throws(IllegalArgumentException::class, SecRandomCopyException::class) 23 | internal inline fun SecureRandom.commonNextBytesOf(count: Int): ByteArray { 24 | require(count >= 0) { "count cannot be negative" } 25 | val bytes = ByteArray(count) 26 | nextBytesCopyTo(bytes) 27 | return bytes 28 | } 29 | -------------------------------------------------------------------------------- /secure-random/src/commonTest/kotlin/io/matthewnelson/secure/random/EnsureFilledHelper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | import kotlin.test.assertTrue 19 | 20 | /** 21 | * Test helper to extend for some platforms which have fallback 22 | * implementations if something is not available. 23 | * */ 24 | abstract class EnsureFilledHelper { 25 | 26 | protected abstract val sRandom: SecureRandom 27 | 28 | // https://github.com/briansmith/ring/blob/main/tests/rand_tests.rs 29 | open fun givenByteArray_whenNextBytes_thenIsFilledWithData() { 30 | val linuxLimit = 256 31 | val webLimit = 65536 32 | 33 | val sizes = listOf( 34 | 1, 35 | 2, 36 | 3, 37 | 96, 38 | linuxLimit - 1, 39 | linuxLimit, 40 | linuxLimit + 1, 41 | linuxLimit * 2, 42 | 511, 43 | 512, 44 | 513, 45 | 4096, 46 | webLimit - 1, 47 | webLimit, 48 | webLimit + 1, 49 | webLimit * 2, 50 | ) 51 | 52 | for (size in sizes) { 53 | val bytes = ByteArray(size) 54 | val emptyByte = bytes[0] 55 | 56 | sRandom.nextBytesCopyTo(bytes) 57 | 58 | var emptyCount = 0 59 | bytes.forEach { 60 | if (it == emptyByte) { 61 | emptyCount++ 62 | } 63 | } 64 | 65 | // Some indices will remain empty so cannot check if all were 66 | // filled. Must adjust our limit depending on size to mitigate 67 | // false positives. 68 | val emptyLimit = when { 69 | size < 10 -> 0.5f 70 | size < 200 -> 0.04F 71 | size < 1000 -> 0.03F 72 | size < 10000 -> 0.01F 73 | else -> 0.0075F 74 | }.let { pctErr -> 75 | (size * pctErr).toInt() 76 | } 77 | 78 | val message = "size=$size,emptyLimit=$emptyLimit,emptyCount=$emptyCount" 79 | // println(message) 80 | 81 | assertTrue( 82 | actual = emptyCount <= emptyLimit, 83 | message = message 84 | ) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /secure-random/src/commonTest/kotlin/io/matthewnelson/secure/random/SecureRandomUnitTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | import kotlin.test.Test 19 | import kotlin.test.assertTrue 20 | import kotlin.test.fail 21 | 22 | /** 23 | * See [EnsureFilledHelper] 24 | * */ 25 | class SecureRandomUnitTest: EnsureFilledHelper() { 26 | 27 | override val sRandom = SecureRandom() 28 | 29 | @Test 30 | fun givenNextBytesOf_whenCountNegative_thenThrows() { 31 | try { 32 | sRandom.nextBytesOf(-1) 33 | fail() 34 | } catch (_: IllegalArgumentException) { 35 | // pass 36 | } 37 | } 38 | 39 | @Test 40 | fun givenNextBytesOf_whenCount0_thenReturnsEmpty() { 41 | assertTrue(sRandom.nextBytesOf(0).isEmpty()) 42 | } 43 | 44 | @Test 45 | override fun givenByteArray_whenNextBytes_thenIsFilledWithData() { 46 | super.givenByteArray_whenNextBytes_thenIsFilledWithData() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /secure-random/src/darwinMain/kotlin/io/matthewnelson/secure/random/internal/SecRandomDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | @file:Suppress("UnnecessaryOptInAnnotation") 17 | 18 | package io.matthewnelson.secure.random.internal 19 | 20 | import io.matthewnelson.secure.random.SecRandomCopyException 21 | import kotlinx.cinterop.Pinned 22 | import kotlinx.cinterop.addressOf 23 | import kotlinx.cinterop.UnsafeNumber 24 | import kotlinx.cinterop.convert 25 | import platform.Security.SecRandomCopyBytes 26 | import platform.Security.kSecRandomDefault 27 | 28 | /** 29 | * https://developer.apple.com/documentation/security/1399291-secrandomcopybytes 30 | * */ 31 | internal actual abstract class SecRandomDelegate private actual constructor() { 32 | 33 | @Throws(SecRandomCopyException::class) 34 | internal actual abstract fun nextBytesCopyTo(bytes: Pinned, size: Int) 35 | 36 | internal actual companion object: SecRandomDelegate() { 37 | 38 | @OptIn(UnsafeNumber::class) 39 | @Throws(SecRandomCopyException::class) 40 | actual override fun nextBytesCopyTo(bytes: Pinned, size: Int) { 41 | // kSecRandomDefault is synonymous to NULL 42 | val errno: Int = SecRandomCopyBytes(kSecRandomDefault, size.toUInt().convert(), bytes.addressOf(0)) 43 | if (errno != 0) { 44 | throw errnoToSecRandomCopyException(errno) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /secure-random/src/jsMain/kotlin/io/matthewnelson/secure/random/SecureRandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | import io.matthewnelson.secure.random.internal.commonNextBytesOf 19 | 20 | @Deprecated( 21 | message = """ 22 | Project moved to the https://github.com/KotlinCrypto organization. 23 | 24 | Step 1: Add dependency 'org.kotlincrypto:secure-random:0.1.0' to your project. 25 | Step 2: Use the 'ReplaceWith' feature on import. 26 | Step 3: Remove dependency 'io.matthewnelson.kotlin-components:secure-random'. 27 | 28 | See more at https://github.com/05nelsonm/secure-random/blob/master/README.md#migration 29 | """, 30 | replaceWith = ReplaceWith( 31 | expression = "SecureRandom", 32 | imports = [ "org.kotlincrypto.SecureRandom" ] 33 | ), 34 | level = DeprecationLevel.WARNING, 35 | ) 36 | public actual class SecureRandom public actual constructor() { 37 | 38 | /** 39 | * Returns a [ByteArray] of size [count], filled with 40 | * securely generated random data. 41 | * 42 | * @throws [IllegalArgumentException] if [count] is negative. 43 | * @throws [SecRandomCopyException] if [nextBytesCopyTo] failed. 44 | * */ 45 | public actual fun nextBytesOf(count: Int): ByteArray = commonNextBytesOf(count) 46 | 47 | /** 48 | * Fills a [ByteArray] with securely generated random data. 49 | * Does nothing if [bytes] is null or empty. 50 | * 51 | * Node: https://nodejs.org/api/crypto.html#cryptorandomfillsyncbuffer-offset-size 52 | * Browser: https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues 53 | * 54 | * @throws [SecRandomCopyException] if procurement of securely random data failed. 55 | * */ 56 | public actual fun nextBytesCopyTo(bytes: ByteArray?) { 57 | bytes.ifNotNullOrEmpty { 58 | try { 59 | if (isNode) { 60 | _require("crypto").randomFillSync(this) 61 | } else { 62 | global.crypto.getRandomValues(this) 63 | } 64 | 65 | Unit 66 | } catch (t: Throwable) { 67 | throw SecRandomCopyException("Failed to obtain bytes", t) 68 | } 69 | } 70 | } 71 | 72 | private companion object { 73 | private val isNode: Boolean by lazy { 74 | val runtime: String? = try { 75 | // May not be available, but should be preferred 76 | // method of determining runtime environment. 77 | js("(globalThis.process.release.name)") as String 78 | } catch (_: Throwable) { 79 | null 80 | } 81 | 82 | when (runtime) { 83 | null -> { 84 | js("(typeof global !== 'undefined' && ({}).toString.call(global) == '[object global]')") as Boolean 85 | } 86 | "node" -> true 87 | else -> false 88 | } 89 | } 90 | 91 | private val global: dynamic by lazy { 92 | js("((typeof global !== 'undefined') ? global : self)") 93 | } 94 | 95 | @Suppress("FunctionName", "UNUSED_PARAMETER") 96 | private fun _require(name: String): dynamic = js("require(name)") 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /secure-random/src/jvmAndroidMain/kotlin/io/matthewnelson/secure/random/SecureRandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | import io.matthewnelson.secure.random.internal.commonNextBytesOf 19 | 20 | @Deprecated( 21 | message = """ 22 | Project moved to the https://github.com/KotlinCrypto organization. 23 | 24 | Step 1: Add dependency 'org.kotlincrypto:secure-random:0.1.0' to your project. 25 | Step 2: Use the 'ReplaceWith' feature on import. 26 | Step 3: Remove dependency 'io.matthewnelson.kotlin-components:secure-random'. 27 | 28 | See more at https://github.com/05nelsonm/secure-random/blob/master/README.md#migration 29 | """, 30 | replaceWith = ReplaceWith( 31 | expression = "SecureRandom", 32 | imports = [ "org.kotlincrypto.SecureRandom" ] 33 | ), 34 | level = DeprecationLevel.WARNING, 35 | ) 36 | public actual class SecureRandom public actual constructor(): java.security.SecureRandom() { 37 | 38 | /** 39 | * Returns a [ByteArray] of size [count], filled with 40 | * securely generated random data. 41 | * 42 | * @throws [IllegalArgumentException] if [count] is negative. 43 | * */ 44 | @Throws(IllegalArgumentException::class) 45 | public actual fun nextBytesOf(count: Int): ByteArray = commonNextBytesOf(count) 46 | 47 | /** 48 | * Fills a [ByteArray] with securely generated random data. 49 | * Does nothing if [bytes] is null or empty. 50 | * 51 | * @see [java.security.SecureRandom.nextBytes] 52 | * */ 53 | public actual fun nextBytesCopyTo(bytes: ByteArray?) { 54 | bytes.ifNotNullOrEmpty { 55 | super.nextBytes(this) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidMain/kotlin/io/matthewnelson/secure/random/internal/-LinuxAndroidPlatform.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import kotlinx.cinterop.ByteVar 19 | import kotlinx.cinterop.CPointer 20 | import kotlinx.cinterop.Pinned 21 | import kotlinx.cinterop.addressOf 22 | import platform.posix.EINTR 23 | import platform.posix.errno 24 | 25 | /** 26 | * [GetRandom.getrandom] and [URandom.readBytesTo] return 27 | * values (if positive) indicate the number of bytes that were 28 | * put into the [ByteArray]. If it is less than the [size], 29 | * this ensures that the call is repeated until it has been 30 | * completely filled. 31 | * */ 32 | internal inline fun Pinned.fillCompletely( 33 | size: Int, 34 | block: (ptr: CPointer, length: Int) -> Int 35 | ) { 36 | var index = 0 37 | while (index < size) { 38 | 39 | val remainder = size - index 40 | val result = block.invoke(addressOf(index), remainder) 41 | 42 | if (result < 0) { 43 | when (val err = errno) { 44 | EINTR -> continue // Retry 45 | else -> throw errnoToSecRandomCopyException(err) 46 | } 47 | } else { 48 | index += result 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidMain/kotlin/io/matthewnelson/secure/random/internal/GetRandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | @file:Suppress("SpellCheckingInspection", "UnnecessaryOptInAnnotation") 17 | 18 | package io.matthewnelson.secure.random.internal 19 | 20 | import io.matthewnelson.secure.random.SecRandomCopyException 21 | import kotlinx.cinterop.* 22 | import platform.posix.* 23 | 24 | /** 25 | * Helper for: 26 | * - Determining if system has [getrandom] available. 27 | * - Utilizing the [getrandom] syscall to obtain random 28 | * data from /dev/urandom. 29 | * 30 | * Must always check that [isAvailable] returns true before 31 | * calling [getrandom]. 32 | * 33 | * https://man7.org/linux/man-pages/man2/getrandom.2.html 34 | * 35 | * @see [SecRandomSynchronized] 36 | * @see [isAvailable] 37 | * @see [getrandom] 38 | * */ 39 | internal class GetRandom private constructor(): SecRandomSynchronized() { 40 | 41 | internal companion object { 42 | private const val NO_FLAGS: UInt = 0U 43 | // https://docs.piston.rs/dev_menu/libc/constant.SYS_getrandom.html 44 | private const val SYS_getrandom: Long = 318L 45 | // https://docs.piston.rs/dev_menu/libc/constant.GRND_NONBLOCK.html 46 | private const val GRND_NONBLOCK: UInt = 0x0001U 47 | 48 | internal val instance = GetRandom() 49 | } 50 | 51 | /** 52 | * Performs a non-blocking check via [syscall] to check 53 | * availability of [getrandom] on the system. 54 | * */ 55 | internal fun isAvailable(): Boolean { 56 | return synchronizedRemember { buf, size -> 57 | @OptIn(UnsafeNumber::class) 58 | val result = getrandom(buf, size.toULong().convert(), GRND_NONBLOCK) 59 | if (result < 0) { 60 | when (errno) { 61 | ENOSYS, // No kernel support 62 | EPERM, // Blocked by seccomp 63 | -> false 64 | else 65 | -> true 66 | } 67 | } else { 68 | true 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Must always call [isAvailable] beforehand to ensure 75 | * availability on the system. 76 | * */ 77 | @OptIn(UnsafeNumber::class) 78 | @Throws(SecRandomCopyException::class) 79 | internal fun getrandom(buf: Pinned, buflen: Int) { 80 | buf.fillCompletely(buflen) { ptr, len -> 81 | getrandom(ptr, len.toULong().convert(), NO_FLAGS) 82 | } 83 | } 84 | 85 | @OptIn(UnsafeNumber::class) 86 | private fun getrandom( 87 | buf: CPointer, 88 | buflen: size_t, 89 | flags: u_int, 90 | ): Int { 91 | return syscall(SYS_getrandom.convert(), buf, buflen, flags).convert() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidMain/kotlin/io/matthewnelson/secure/random/internal/SecRandomDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.SecRandomCopyException 19 | import kotlinx.cinterop.Pinned 20 | 21 | internal actual abstract class SecRandomDelegate private actual constructor() { 22 | 23 | @Throws(SecRandomCopyException::class) 24 | internal actual abstract fun nextBytesCopyTo(bytes: Pinned, size: Int) 25 | 26 | internal actual companion object: SecRandomDelegate() { 27 | 28 | @Throws(SecRandomCopyException::class) 29 | actual override fun nextBytesCopyTo(bytes: Pinned, size: Int) { 30 | if (GetRandom.instance.isAvailable()) { 31 | GetRandom.instance.getrandom(bytes, size) 32 | } else { 33 | DelegateURandom.nextBytesCopyTo(bytes, size) 34 | } 35 | } 36 | } 37 | 38 | // Alternative fall back if getrandom is unavailable. 39 | // It is here so that it can be tested via constructor 40 | // injection into SecureRandom. 41 | internal object DelegateURandom: SecRandomDelegate() { 42 | 43 | @Throws(SecRandomCopyException::class) 44 | override fun nextBytesCopyTo(bytes: Pinned, size: Int) { 45 | URandom.instance.readBytesTo(bytes, size) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidMain/kotlin/io/matthewnelson/secure/random/internal/SecRandomSynchronized.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import kotlinx.cinterop.ByteVar 19 | import kotlinx.cinterop.CPointer 20 | import kotlinx.cinterop.addressOf 21 | import kotlinx.cinterop.usePinned 22 | import kotlin.native.concurrent.AtomicInt 23 | 24 | internal abstract class SecRandomSynchronized { 25 | 26 | private companion object { 27 | private const val TRUE = 1 28 | private const val FALSE = 0 29 | private const val UNKNOWN = -1 30 | } 31 | 32 | private val result = AtomicInt(UNKNOWN) 33 | 34 | /** 35 | * Synchronized execution of [action] such that it is only ever 36 | * invoked once, and it's response stored in [result] which 37 | * will be returned henceforth. 38 | * */ 39 | protected fun synchronizedRemember( 40 | action: (buf: CPointer, size: Int) -> Boolean 41 | ): Boolean { 42 | when (result.value) { 43 | TRUE -> return true 44 | FALSE -> return false 45 | else -> { 46 | val buf = ByteArray(1) 47 | 48 | // Use the hashCode as the id for obtaining 49 | // the lock for this invocation of 50 | // synchronizedRemember 51 | val lockId = buf.hashCode() 52 | 53 | try { 54 | return executeActionMaybe(buf, lockId, action) 55 | } finally { 56 | // Ensure the lock is reset back to UNKNOWN 57 | // if it was not set to t/f so that it can 58 | // be obtained again 59 | result.compareAndSet(lockId, UNKNOWN) 60 | } 61 | } 62 | } 63 | } 64 | 65 | private fun executeActionMaybe( 66 | buf: ByteArray, 67 | lockId: Int, 68 | action: (buf: CPointer, size: Int) -> Boolean 69 | ): Boolean { 70 | 71 | // Acquire the lock or return another invocation 72 | // instance's result. 73 | while (true) { 74 | when (result.value) { 75 | TRUE -> return true 76 | FALSE -> return false 77 | else -> { 78 | if (result.compareAndSet(UNKNOWN, lockId)) { 79 | break 80 | } 81 | } 82 | } 83 | } 84 | 85 | // Lock obtained. Execute and store result. 86 | val result: Boolean = buf.usePinned { pinned -> 87 | action.invoke(pinned.addressOf(0), buf.size) 88 | } 89 | 90 | this.result.compareAndSet(lockId, if (result) TRUE else FALSE) 91 | 92 | return result 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidMain/kotlin/io/matthewnelson/secure/random/internal/URandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | @file:Suppress("SpellCheckingInspection", "UnnecessaryOptInAnnotation") 17 | 18 | package io.matthewnelson.secure.random.internal 19 | 20 | import io.matthewnelson.secure.random.SecRandomCopyException 21 | import kotlinx.cinterop.* 22 | import platform.posix.* 23 | import kotlin.native.concurrent.AtomicInt 24 | 25 | /** 26 | * Helper for: 27 | * - Ensuring /dev/random has been seeded 28 | * - Obtaining bytes from /dev/urandom 29 | * 30 | * Polling of /dev/random via [ensureSeeded] is always called 31 | * to ensure that no data is read from /dev/urandom until there 32 | * is adequate randomness generated. [ensureSeeded] only ever 33 | * polls /dev/random once, afterwhich it does nothing. 34 | * 35 | * @see [SecRandomSynchronized] 36 | * @see [readBytesTo] 37 | * */ 38 | internal class URandom private constructor(): SecRandomSynchronized() { 39 | 40 | internal companion object { 41 | private const val INFINITE_TIMEOUT = -1 42 | 43 | internal val instance = URandom() 44 | } 45 | 46 | private val lock = Lock() 47 | 48 | /** 49 | * Reads bytes from /dev/urandom of specified [buflen] into [buf]. 50 | * 51 | * @see [withReadOnlyFD] 52 | * @see [fillCompletely] 53 | * */ 54 | @Throws(SecRandomCopyException::class) 55 | internal fun readBytesTo(buf: Pinned, buflen: Int) { 56 | ensureSeeded() 57 | 58 | lock.withLock { 59 | @OptIn(UnsafeNumber::class) 60 | withReadOnlyFD("/dev/urandom") { fd -> 61 | buf.fillCompletely(buflen) { ptr, length -> 62 | read(fd, ptr, length.toULong().convert()).convert() 63 | } 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Polls /dev/random once and only once to ensure /dev/urandom 70 | * is ready to read from. Any sucessive calls to [ensureSeeded] 71 | * will block until [synchronizedRemember]'s lambda is executed. 72 | * */ 73 | private fun ensureSeeded() { 74 | synchronizedRemember { _, _ -> 75 | 76 | // Will only ever be invoked once 77 | withReadOnlyFD("/dev/random") { fd -> 78 | memScoped { 79 | val pollFd = nativeHeap.alloc() 80 | pollFd.apply { 81 | this.fd = fd 82 | events = POLLIN.convert() 83 | revents = 0 84 | } 85 | 86 | while (true) { 87 | @OptIn(UnsafeNumber::class) 88 | val result = poll(pollFd.ptr, 1, INFINITE_TIMEOUT) 89 | if (result >= 0) { 90 | break 91 | } 92 | when (val err = errno) { 93 | EINTR, 94 | EAGAIN -> continue 95 | else -> throw errnoToSecRandomCopyException(err) 96 | } 97 | } 98 | } 99 | } 100 | 101 | true 102 | } 103 | } 104 | 105 | /** 106 | * Opens the file descriptor for [path] and then closes 107 | * it once [block] completes. 108 | * */ 109 | private fun withReadOnlyFD(path: String, block: (fd: Int) -> Unit) { 110 | val fd = open(path, O_RDONLY, null) 111 | if (fd == -1) throw errnoToSecRandomCopyException(errno) 112 | 113 | try { 114 | block.invoke(fd) 115 | } finally { 116 | close(fd) 117 | } 118 | } 119 | 120 | private inner class Lock { 121 | 122 | // 0: Unlocked 123 | // *: Locked 124 | private val lock = AtomicInt(0) 125 | 126 | fun withLock(block: () -> T): T { 127 | val lockId = Any().hashCode() 128 | 129 | try { 130 | while (true) { 131 | if (lock.compareAndSet(0, lockId)) { 132 | break 133 | } 134 | } 135 | 136 | return block.invoke() 137 | } finally { 138 | lock.compareAndSet(lockId, 0) 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidTest/kotlin/io/matthewnelson/secure/random/internal/FillCompletelyUnitTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import kotlinx.cinterop.set 19 | import kotlinx.cinterop.usePinned 20 | import kotlin.test.Test 21 | import kotlin.test.assertEquals 22 | import kotlin.test.assertTrue 23 | 24 | class FillCompletelyUnitTest { 25 | 26 | @Test 27 | fun givenFillCompletely_whenMoreThan2InvocationsNeeded_thenUpdatesIndexCorrectly() { 28 | listOf( 29 | 9, 30 | 21, 31 | 42, 32 | 81, 33 | ).forEach { size -> 34 | // Must be multiple of 3 to simulate more than 3 successive 35 | // invocations of fillCompletely.block invocation. 36 | assertTrue(size %3 == 0) 37 | 38 | val bytes = ByteArray(size) 39 | 40 | val fillSize = size / 3 41 | 42 | var invocationCount = 0 43 | bytes.usePinned { pinned -> 44 | pinned.fillCompletely(size) { ptr, _ -> 45 | for (i in 0..fillSize) { 46 | ptr[i] = 1 47 | } 48 | 49 | invocationCount++ 50 | fillSize 51 | } 52 | } 53 | 54 | bytes.forEach { byte -> 55 | assertEquals(1, byte) 56 | } 57 | 58 | assertEquals(3, invocationCount) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /secure-random/src/linuxAndroidTest/kotlin/io/matthewnelson/secure/random/internal/URandomUnitTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.EnsureFilledHelper 19 | import io.matthewnelson.secure.random.SecureRandom 20 | import kotlin.test.Test 21 | 22 | class URandomUnitTest: EnsureFilledHelper() { 23 | 24 | override val sRandom: SecureRandom = SecureRandom(SecRandomDelegate.DelegateURandom) 25 | 26 | @Test 27 | override fun givenByteArray_whenNextBytes_thenIsFilledWithData() { 28 | if (!GetRandom.instance.isAvailable()) { 29 | // commonTest will fall back to use URandom, and we 30 | // do not need to double test it here. 31 | return 32 | } 33 | 34 | super.givenByteArray_whenNextBytes_thenIsFilledWithData() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /secure-random/src/linuxX64Test/kotlin/io/matthewnelson/secure/random/internal/SecRandomSynchronizedUnitTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import kotlinx.coroutines.* 19 | import kotlinx.coroutines.sync.Mutex 20 | import kotlinx.coroutines.sync.withLock 21 | import kotlinx.coroutines.test.runTest 22 | import kotlin.native.concurrent.AtomicInt 23 | import kotlin.test.Test 24 | import kotlin.test.assertEquals 25 | 26 | @OptIn(ExperimentalCoroutinesApi::class) 27 | class SecRandomSynchronizedUnitTest { 28 | 29 | private class TestSynchronized: SecRandomSynchronized() { 30 | private val lock = Mutex(locked = true) 31 | val invocationCount = AtomicInt(0) 32 | val loadedCount = AtomicInt(0) 33 | 34 | suspend fun load() { 35 | val calledBefore = invocationCount.value == 0 36 | 37 | lock.withLock { } 38 | 39 | synchronizedRemember { _, _ -> 40 | // lambda should only ever be invoked once and 41 | // then always return the remembered value. 42 | throw IllegalArgumentException("synchronizedRemember was invoked multiple times") 43 | } 44 | 45 | if (calledBefore) { 46 | loadedCount.increment() 47 | } 48 | } 49 | 50 | fun trigger() { 51 | synchronizedRemember { _, _ -> 52 | 53 | lock.unlock() 54 | 55 | runBlocking { 56 | delay(500L) 57 | } 58 | 59 | true 60 | } 61 | 62 | invocationCount.increment() 63 | } 64 | } 65 | 66 | @Test 67 | fun givenPollingResult_whenMultipleInvocations_thenOnlyPollsOnce() = runTest { 68 | val testSynchronized = TestSynchronized() 69 | 70 | val jobs = mutableListOf() 71 | 72 | // load up the test class which will suspend 73 | // until trigger is pulled 74 | val repeatTimes = 100 75 | repeat(repeatTimes) { 76 | 77 | launch { 78 | testSynchronized.load() 79 | }.let { job -> 80 | jobs.add(job) 81 | } 82 | } 83 | 84 | delay(500L) 85 | 86 | // Trigger it from background thread which 87 | // will block for 500ms, ensuring that all 88 | // the load is put onto synchronizedRemember 89 | launch(Dispatchers.Default) { 90 | testSynchronized.trigger() 91 | }.join() 92 | 93 | // Wait for everyone to finish 94 | for (job in jobs) { 95 | job.join() 96 | } 97 | 98 | assertEquals(1, testSynchronized.invocationCount.value) 99 | assertEquals(repeatTimes, testSynchronized.loadedCount.value) 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /secure-random/src/mingwMain/kotlin/io/matthewnelson/secure/random/internal/-MingwPlatform.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import kotlinx.cinterop.ByteVarOf 19 | import kotlinx.cinterop.allocArray 20 | import kotlinx.cinterop.memScoped 21 | import kotlinx.cinterop.toKStringFromUtf8 22 | import platform.windows.DWORD 23 | import platform.windows.FORMAT_MESSAGE_FROM_SYSTEM 24 | import platform.windows.FORMAT_MESSAGE_IGNORE_INSERTS 25 | import platform.windows.LANG_NEUTRAL 26 | import platform.windows.SUBLANG_DEFAULT 27 | import platform.windows.FormatMessageA 28 | 29 | /* https://github.com/square/okio/blob/master/okio/src/mingwX64Main/kotlin/okio/-Windows.kt */ 30 | internal fun errorToString(error: DWORD): String { 31 | memScoped { 32 | val messageMaxSize = 2048 33 | val message = allocArray>(messageMaxSize) 34 | FormatMessageA( 35 | dwFlags = (FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS).toUInt(), 36 | lpSource = null, 37 | dwMessageId = error, 38 | dwLanguageId = (SUBLANG_DEFAULT * 1024 + LANG_NEUTRAL).toUInt(), // MAKELANGID macro. 39 | lpBuffer = message, 40 | nSize = messageMaxSize.toUInt(), 41 | Arguments = null 42 | ) 43 | return message.toKStringFromUtf8().trim() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /secure-random/src/mingwMain/kotlin/io/matthewnelson/secure/random/internal/SecRandomDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.SecRandomCopyException 19 | import kotlinx.cinterop.Pinned 20 | import kotlinx.cinterop.addressOf 21 | import kotlinx.cinterop.convert 22 | import kotlinx.cinterop.reinterpret 23 | import platform.windows.BCRYPT_USE_SYSTEM_PREFERRED_RNG 24 | import platform.windows.BCryptGenRandom 25 | import platform.windows.STATUS_INVALID_HANDLE 26 | import platform.windows.STATUS_INVALID_PARAMETER 27 | 28 | /** 29 | * https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom 30 | * */ 31 | internal actual abstract class SecRandomDelegate private actual constructor() { 32 | 33 | @Throws(SecRandomCopyException::class) 34 | internal actual abstract fun nextBytesCopyTo(bytes: Pinned, size: Int) 35 | 36 | internal actual companion object: SecRandomDelegate() { 37 | 38 | @Throws(SecRandomCopyException::class) 39 | actual override fun nextBytesCopyTo(bytes: Pinned, size: Int) { 40 | val status = BCryptGenRandom( 41 | null, 42 | bytes.addressOf(0).reinterpret(), 43 | size.toULong().convert(), 44 | BCRYPT_USE_SYSTEM_PREFERRED_RNG, 45 | ).toUInt() 46 | 47 | when (status) { 48 | STATUS_INVALID_HANDLE, 49 | STATUS_INVALID_PARAMETER -> throw SecRandomCopyException(errorToString(status)) 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /secure-random/src/nativeMain/kotlin/io/matthewnelson/secure/random/SecureRandom.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random 17 | 18 | import io.matthewnelson.secure.random.internal.commonNextBytesOf 19 | import io.matthewnelson.secure.random.internal.SecRandomDelegate 20 | import kotlinx.cinterop.usePinned 21 | 22 | @Deprecated( 23 | message = """ 24 | Project moved to the https://github.com/KotlinCrypto organization. 25 | 26 | Step 1: Add dependency 'org.kotlincrypto:secure-random:0.1.0' to your project. 27 | Step 2: Use the 'ReplaceWith' feature on import. 28 | Step 3: Remove dependency 'io.matthewnelson.kotlin-components:secure-random'. 29 | 30 | See more at https://github.com/05nelsonm/secure-random/blob/master/README.md#migration 31 | """, 32 | replaceWith = ReplaceWith( 33 | expression = "SecureRandom", 34 | imports = [ "org.kotlincrypto.SecureRandom" ] 35 | ), 36 | level = DeprecationLevel.WARNING, 37 | ) 38 | public actual class SecureRandom { 39 | 40 | private val delegate: SecRandomDelegate 41 | 42 | public actual constructor(): this(SecRandomDelegate) 43 | internal constructor(delegate: SecRandomDelegate) { this.delegate = delegate } 44 | 45 | /** 46 | * Returns a [ByteArray] of size [count], filled with 47 | * securely generated random data. 48 | * 49 | * @throws [IllegalArgumentException] if [count] is negative. 50 | * @throws [SecRandomCopyException] if [nextBytesCopyTo] failed. 51 | * */ 52 | @Throws(IllegalArgumentException::class, SecRandomCopyException::class) 53 | public actual fun nextBytesOf(count: Int): ByteArray = commonNextBytesOf(count) 54 | 55 | /** 56 | * Fills a [ByteArray] with securely generated random data. 57 | * Does nothing if [bytes] is null or empty. 58 | * 59 | * @throws [SecRandomCopyException] if procurement of securely random data failed. 60 | * */ 61 | @Throws(SecRandomCopyException::class) 62 | public actual fun nextBytesCopyTo(bytes: ByteArray?) { 63 | bytes.ifNotNullOrEmpty { 64 | usePinned { pinned -> 65 | delegate.nextBytesCopyTo(pinned, size) 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /secure-random/src/nativeMain/kotlin/io/matthewnelson/secure/random/internal/-Cinterop.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.SecRandomCopyException 19 | import kotlinx.cinterop.toKStringFromUtf8 20 | import platform.posix.strerror 21 | 22 | internal fun errnoToSecRandomCopyException(errno: Int): SecRandomCopyException { 23 | val message = strerror(errno)?.toKStringFromUtf8() ?: "errno: $errno" 24 | return SecRandomCopyException(message) 25 | } 26 | -------------------------------------------------------------------------------- /secure-random/src/nativeMain/kotlin/io/matthewnelson/secure/random/internal/SecRandomDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.SecRandomCopyException 19 | import kotlinx.cinterop.Pinned 20 | 21 | internal expect abstract class SecRandomDelegate private constructor() { 22 | 23 | @Throws(SecRandomCopyException::class) 24 | internal abstract fun nextBytesCopyTo(bytes: Pinned, size: Int) 25 | 26 | internal companion object: SecRandomDelegate { 27 | 28 | override fun nextBytesCopyTo(bytes: Pinned, size: Int) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /secure-random/src/wasmMain/kotlin/io/matthewnelson/secure/random/internal/SecRandomDelegate.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.secure.random.internal 17 | 18 | import io.matthewnelson.secure.random.SecRandomCopyException 19 | import kotlinx.cinterop.Pinned 20 | 21 | internal actual abstract class SecRandomDelegate private actual constructor() { 22 | 23 | @Throws(SecRandomCopyException::class) 24 | internal actual abstract fun nextBytesCopyTo(bytes: Pinned, size: Int) 25 | 26 | internal actual companion object: SecRandomDelegate() { 27 | 28 | actual override fun nextBytesCopyTo(bytes: Pinned, size: Int) { 29 | // TODO: Implement (Issue #36) 30 | throw SecRandomCopyException("Not yet implemented") 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "secure-random" 2 | 3 | includeBuild("kotlin-components/includeBuild/dependencies") 4 | includeBuild("kotlin-components/includeBuild/kmp") 5 | 6 | @Suppress("PrivatePropertyName") 7 | private val CHECK_PUBLICATION: String? by settings 8 | if (CHECK_PUBLICATION != null) { 9 | include(":tools:check-publication") 10 | } else { 11 | include(":secure-random") 12 | include(":samples:native") 13 | } 14 | -------------------------------------------------------------------------------- /tools/check-publication/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /tools/check-publication/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | import io.matthewnelson.kotlin.components.kmp.KmpTarget 17 | import io.matthewnelson.kotlin.components.kmp.publish.isSnapshotVersion 18 | import io.matthewnelson.kotlin.components.kmp.publish.kmpPublishRootProjectConfiguration 19 | import io.matthewnelson.kotlin.components.kmp.util.includeSnapshotsRepoIfTrue 20 | import io.matthewnelson.kotlin.components.kmp.util.includeStagingRepoIfTrue 21 | 22 | plugins { 23 | id(pluginId.kmp.configuration) 24 | id(pluginId.kmp.publish) 25 | } 26 | 27 | val pConfig = kmpPublishRootProjectConfiguration!! 28 | 29 | includeSnapshotsRepoIfTrue(pConfig.isSnapshotVersion) 30 | includeStagingRepoIfTrue(!pConfig.isSnapshotVersion) 31 | 32 | kmpConfiguration { 33 | setupMultiplatform(targets= 34 | setOf( 35 | KmpTarget.Jvm.Android( 36 | buildTools = versions.android.buildTools, 37 | compileSdk = versions.android.sdkCompile, 38 | minSdk = versions.android.sdkMin19, // KitKat (4.4) 39 | namespace = "io.matthewnelson.tools.check.publication", 40 | compileSourceOption = JavaVersion.VERSION_1_8, 41 | compileTargetOption = JavaVersion.VERSION_1_8, 42 | kotlinJvmTarget = JavaVersion.VERSION_1_8, 43 | mainSourceSet = { 44 | dependencies { 45 | implementation("${pConfig.group}:secure-random-android:${pConfig.versionName}") 46 | } 47 | } 48 | ), 49 | KmpTarget.Jvm.Jvm( 50 | kotlinJvmTarget = JavaVersion.VERSION_1_8, 51 | mainSourceSet = { 52 | dependencies { 53 | implementation("${pConfig.group}:secure-random-jvm:${pConfig.versionName}") 54 | } 55 | } 56 | ), 57 | KmpTarget.NonJvm.JS.DEFAULT, 58 | KmpTarget.NonJvm.Native.Unix.Darwin.Watchos.DeviceArm64.DEFAULT, 59 | ) + 60 | KmpTarget.NonJvm.Native.Android.ALL_DEFAULT + 61 | // KmpTarget.NonJvm.Native.Wasm.ALL_DEFAULT + 62 | KmpTarget.NonJvm.Native.Unix.Darwin.Ios.ALL_DEFAULT + 63 | KmpTarget.NonJvm.Native.Unix.Darwin.Macos.ALL_DEFAULT + 64 | KmpTarget.NonJvm.Native.Unix.Darwin.Tvos.ALL_DEFAULT + 65 | KmpTarget.NonJvm.Native.Unix.Darwin.Watchos.ALL_DEFAULT + 66 | KmpTarget.NonJvm.Native.Unix.Linux.ALL_DEFAULT + 67 | KmpTarget.NonJvm.Native.Mingw.ALL_DEFAULT, 68 | 69 | commonMainSourceSet = { 70 | dependencies { 71 | implementation("${pConfig.group}:secure-random:${pConfig.versionName}") 72 | } 73 | }, 74 | ) 75 | } 76 | -------------------------------------------------------------------------------- /tools/check-publication/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | -------------------------------------------------------------------------------- /tools/check-publication/src/commonMain/kotlin/io/matthewnelson/tools/check/publication/Stub.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Matthew Nelson 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | package io.matthewnelson.tools.check.publication 17 | 18 | internal fun stub() { /* no-op */ } 19 | --------------------------------------------------------------------------------