fix android module name

import fixes

fix some imports

use `@atproto/jwk` over local zod schema

preview oauth client

rename package

alignment

more alignment

database implementation

cleanup some types

alignment

switch implementation for es256

half of `verifyJwt` for android

half of `verifyJwt` for iOS

rm log

`createJwt` android

`createJwt` ios

rm print

cleanup native impl

silence some warnings

reorg, finish web impl

follow the `CryptoImplementation`

move some more things around

normalize some names

rename func

add generate on android

add android dep

rm bool

add alg

better implementation

ios jwk generation

fix index.ts

add `getRandomValues()` on native

web `getRandomValues`

silence ts error for missing crypto on web

add `digest` for web

add `digest`

add module
This commit is contained in:
Hailey
2024-04-10 12:58:41 -07:00
parent 310d865440
commit 13607edd43
27 changed files with 1502 additions and 0 deletions
@@ -0,0 +1,93 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'
group = 'expo.modules.blueskyoauthclient'
version = '0.0.1'
buildscript {
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
if (expoModulesCorePlugin.exists()) {
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
}
// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
// Ensures backward compatibility
ext.getKotlinVersion = {
if (ext.has("kotlinVersion")) {
ext.kotlinVersion()
} else {
ext.safeExtGet("kotlinVersion", "1.8.10")
}
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
}
}
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
}
}
repositories {
maven {
url = mavenLocal().url
}
}
}
}
android {
compileSdkVersion safeExtGet("compileSdkVersion", 33)
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.majorVersion
}
}
namespace "expo.modules.blueskyoauthclient"
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 34)
versionCode 1
versionName "0.0.1"
}
lintOptions {
abortOnError false
}
publishing {
singleVariant("release") {
withSourcesJar()
}
}
}
repositories {
mavenCentral()
}
dependencies {
implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation "com.nimbusds:nimbus-jose-jwt:9.38-rc3"
}
@@ -0,0 +1,2 @@
<manifest>
</manifest>
@@ -0,0 +1,52 @@
package expo.modules.blueskyoauthclient
import com.nimbusds.jose.Algorithm
import java.security.KeyPairGenerator
import java.security.MessageDigest
import java.security.interfaces.ECPublicKey
import java.security.interfaces.ECPrivateKey
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jose.jwk.KeyUse
import java.util.UUID
class CryptoUtil {
fun digest(data: ByteArray): ByteArray {
val digest = MessageDigest.getInstance("sha256")
return digest.digest(data)
}
fun getRandomValues(byteLength: Int): ByteArray {
val random = ByteArray(byteLength)
java.security.SecureRandom().nextBytes(random)
return random
}
fun generateKeyPair(keyId: String?): Pair<String, String> {
val keyIdString = keyId ?: UUID.randomUUID().toString()
val keyPairGen = KeyPairGenerator.getInstance("EC")
keyPairGen.initialize(Curve.P_256.toECParameterSpec())
val keyPair = keyPairGen.generateKeyPair()
val publicKey = keyPair.public as ECPublicKey
val privateKey = keyPair.private as ECPrivateKey
val publicJwk = ECKey.Builder(Curve.P_256, publicKey)
.keyUse(KeyUse.SIGNATURE)
.algorithm(Algorithm.parse("ES256"))
.keyID(keyIdString)
.build()
val privateJwk = ECKey.Builder(Curve.P_256, publicKey)
.privateKey(privateKey)
.keyUse(KeyUse.SIGNATURE)
.keyID(keyIdString)
.algorithm(Algorithm.parse("ES256"))
.build()
return Pair(
publicJwk.toString(),
privateJwk.toString()
)
}
}
@@ -0,0 +1,35 @@
package expo.modules.blueskyoauthclient
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class ExpoBlueskyOAuthClientModule : Module() {
override fun definition() = ModuleDefinition {
Name("ExpoBlueskyOAuthClient")
AsyncFunction("digest") { value: ByteArray ->
return@AsyncFunction CryptoUtil().digest(value)
}
Function("getRandomValues") { byteLength: Int ->
return@Function CryptoUtil().getRandomValues(byteLength)
}
AsyncFunction("generateKeyPair") { keyId: String? ->
val res = CryptoUtil().generateKeyPair(keyId)
return@AsyncFunction mapOf(
"publicKey" to res.first,
"privateKey" to res.second
)
}
AsyncFunction("createJwt") { jwkString: String, headerString: String, payloadString: String ->
return@AsyncFunction JWTUtil().createJwt(jwkString, headerString, payloadString)
}
AsyncFunction("verifyJwt") { jwkString: String, tokenString: String, options: String? ->
return@AsyncFunction JWTUtil().verifyJwt(jwkString, tokenString, options)
}
}
}
@@ -0,0 +1,35 @@
package expo.modules.blueskyoauthclient
import com.nimbusds.jose.JWSHeader
import com.nimbusds.jose.crypto.ECDSASigner
import com.nimbusds.jose.crypto.ECDSAVerifier
import com.nimbusds.jose.jwk.ECKey
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
class JWTUtil {
fun createJwt(jwkString: String, headerString: String, payloadString: String): String {
val key = ECKey.parse(jwkString)
val header = JWSHeader.parse(headerString)
val payload = JWTClaimsSet.parse(payloadString)
val signer = ECDSASigner(key)
val jwt = SignedJWT(header, payload)
jwt.sign(signer)
return jwt.serialize()
}
fun verifyJwt(jwkString: String, tokenString: String, options: String?): Boolean {
return try {
val key = ECKey.parse(jwkString)
val jwt = SignedJWT.parse(tokenString)
val verifier = ECDSAVerifier(key)
jwt.verify(verifier)
} catch(e: Exception) {
false
}
}
}