mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 02:09:38 +02:00
Sketch out yubikey-based public key decryption.
Note: Requires a modified copy of Bouncy Castle with the following changes: * BcPublicKeyDataDecryptorFactory.unwrapSessionData(): make public * org.bouncycastle.openpgp.operator.bc.RFC6637KDFCalculator: Make class public
This commit is contained in:
parent
9f0a9ccfa6
commit
b0c3533b8c
1 changed files with 61 additions and 17 deletions
|
@ -1,24 +1,47 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package org.pgpainless.yubikey
|
package org.pgpainless.yubikey
|
||||||
|
|
||||||
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
import com.yubico.yubikit.core.smartcard.SmartCardConnection
|
||||||
import com.yubico.yubikit.openpgp.KeyRef
|
import com.yubico.yubikit.openpgp.KeyRef
|
||||||
import com.yubico.yubikit.openpgp.OpenPgpSession
|
import com.yubico.yubikit.openpgp.OpenPgpSession
|
||||||
|
import org.bouncycastle.bcpg.ECDHPublicBCPGKey
|
||||||
import org.bouncycastle.bcpg.KeyIdentifier
|
import org.bouncycastle.bcpg.KeyIdentifier
|
||||||
|
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags
|
||||||
|
import org.bouncycastle.crypto.params.KeyParameter
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKey
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPPad
|
||||||
|
import org.bouncycastle.openpgp.operator.RFC6637Utils
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory
|
||||||
|
import org.bouncycastle.openpgp.operator.bc.RFC6637KDFCalculator
|
||||||
import org.pgpainless.decryption_verification.HardwareSecurity
|
import org.pgpainless.decryption_verification.HardwareSecurity
|
||||||
|
|
||||||
class YubikeyDataDecryptorFactory(
|
class YubikeyDataDecryptorFactory(
|
||||||
smartCardConnection: SmartCardConnection,
|
|
||||||
callback: HardwareSecurity.DecryptionCallback,
|
callback: HardwareSecurity.DecryptionCallback,
|
||||||
keyIdentifier: KeyIdentifier,
|
keyIdentifier: KeyIdentifier,
|
||||||
) : HardwareSecurity.HardwareDataDecryptorFactory(keyIdentifier, callback) {
|
) : HardwareSecurity.HardwareDataDecryptorFactory(keyIdentifier, callback) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createDecryptorFromConnection(smartCardConnection: SmartCardConnection): HardwareSecurity.HardwareDataDecryptorFactory {
|
fun createDecryptorFromConnection(
|
||||||
|
smartCardConnection: SmartCardConnection,
|
||||||
|
pubkey: PGPPublicKey
|
||||||
|
): HardwareSecurity.HardwareDataDecryptorFactory {
|
||||||
val openpgpSession = OpenPgpSession(smartCardConnection)
|
val openpgpSession = OpenPgpSession(smartCardConnection)
|
||||||
val fingerprintBytes = openpgpSession.getData(KeyRef.DEC.fingerprint)
|
val fingerprintBytes = openpgpSession.getData(KeyRef.DEC.fingerprint)
|
||||||
val decKeyIdentifier = KeyIdentifier(fingerprintBytes)
|
val decKeyIdentifier = KeyIdentifier(fingerprintBytes)
|
||||||
val rsa = true
|
|
||||||
|
if (!decKeyIdentifier.matches(pubkey.keyIdentifier)) {
|
||||||
|
throw IllegalArgumentException("Fingerprint mismatch.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val isRSAKey = pubkey.algorithm == PublicKeyAlgorithmTags.RSA_GENERAL
|
||||||
|
|| pubkey.algorithm == PublicKeyAlgorithmTags.RSA_SIGN
|
||||||
|
|| pubkey.algorithm == PublicKeyAlgorithmTags.RSA_ENCRYPT
|
||||||
|
|
||||||
val callback = object : HardwareSecurity.DecryptionCallback {
|
val callback = object : HardwareSecurity.DecryptionCallback {
|
||||||
override fun decryptSessionKey(
|
override fun decryptSessionKey(
|
||||||
|
@ -27,25 +50,49 @@ class YubikeyDataDecryptorFactory(
|
||||||
sessionKeyData: ByteArray,
|
sessionKeyData: ByteArray,
|
||||||
pkeskVersion: Int
|
pkeskVersion: Int
|
||||||
): ByteArray {
|
): ByteArray {
|
||||||
|
// TODO: Move user pin verification somewhere else
|
||||||
openpgpSession.verifyUserPin("asdasd".toCharArray(), true)
|
openpgpSession.verifyUserPin("asdasd".toCharArray(), true)
|
||||||
if(rsa) {
|
|
||||||
val decryptedSessionKey = openpgpSession.decrypt(sessionKeyData)
|
|
||||||
|
|
||||||
|
if(isRSAKey) {
|
||||||
|
// easy
|
||||||
|
val decryptedSessionKey = openpgpSession.decrypt(sessionKeyData)
|
||||||
return decryptedSessionKey
|
return decryptedSessionKey
|
||||||
} else {
|
} else {
|
||||||
/*
|
// meh...
|
||||||
val secret = openpgpSession.decrypt(sessionKeyData)
|
val ecPubKey: ECDHPublicBCPGKey = pubkey.publicKeyPacket.key as ECDHPublicBCPGKey
|
||||||
val hashAlgorithm: Int = ecPubKey.getHashAlgorithm().toInt()
|
// split session data into peer key and encrypted session key
|
||||||
val symmetricKeyAlgorithm: Int = ecPubKey.getSymmetricKeyAlgorithm().toInt()
|
|
||||||
|
// peer key
|
||||||
|
val pLen =
|
||||||
|
((((sessionKeyData[0].toInt() and 0xff) shl 8) + (sessionKeyData[1].toInt() and 0xff)) + 7) / 8
|
||||||
|
checkRange(2 + pLen + 1, sessionKeyData)
|
||||||
|
val pEnc = ByteArray(pLen)
|
||||||
|
System.arraycopy(sessionKeyData, 2, pEnc, 0, pLen)
|
||||||
|
|
||||||
|
// encrypted session key
|
||||||
|
val keyLen = sessionKeyData[pLen + 2].toInt() and 0xff
|
||||||
|
checkRange(2 + pLen + 1 + keyLen, sessionKeyData)
|
||||||
|
val keyEnc = ByteArray(keyLen)
|
||||||
|
System.arraycopy(sessionKeyData, 2 + pLen + 1, keyEnc, 0, keyLen)
|
||||||
|
|
||||||
|
// perform ECDH key agreement via the Yubikey
|
||||||
|
val secret = openpgpSession.decrypt(pEnc)
|
||||||
|
|
||||||
|
// Use the shared key to decrypt the session key
|
||||||
|
val hashAlgorithm: Int = ecPubKey.hashAlgorithm.toInt()
|
||||||
|
val symmetricKeyAlgorithm: Int = ecPubKey.symmetricKeyAlgorithm.toInt()
|
||||||
val userKeyingMaterial = RFC6637Utils.createUserKeyingMaterial(
|
val userKeyingMaterial = RFC6637Utils.createUserKeyingMaterial(
|
||||||
this.pgpPrivKey.getPublicKeyPacket(),
|
pubkey.publicKeyPacket,
|
||||||
BcKeyFingerprintCalculator(),
|
BcKeyFingerprintCalculator(),
|
||||||
)
|
)
|
||||||
val rfc6637KDFCalculator = RFC6637KDFCalculator(
|
val rfc6637KDFCalculator =
|
||||||
BcPGPDigestCalculatorProvider()[hashAlgorithm], symmetricKeyAlgorithm,
|
RFC6637KDFCalculator(
|
||||||
)
|
BcPGPDigestCalculatorProvider()[hashAlgorithm],
|
||||||
|
symmetricKeyAlgorithm,
|
||||||
|
)
|
||||||
val key =
|
val key =
|
||||||
KeyParameter(rfc6637KDFCalculator.createKey(secret, userKeyingMaterial))
|
KeyParameter(rfc6637KDFCalculator.createKey(secret, userKeyingMaterial))
|
||||||
|
|
||||||
return PGPPad.unpadSessionData(
|
return PGPPad.unpadSessionData(
|
||||||
BcPublicKeyDataDecryptorFactory.unwrapSessionData(
|
BcPublicKeyDataDecryptorFactory.unwrapSessionData(
|
||||||
keyEnc,
|
keyEnc,
|
||||||
|
@ -53,14 +100,11 @@ class YubikeyDataDecryptorFactory(
|
||||||
key,
|
key,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
*/
|
|
||||||
throw UnsupportedOperationException("ECDH decryption is not yet implemented.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return YubikeyDataDecryptorFactory(smartCardConnection, callback, decKeyIdentifier)
|
return YubikeyDataDecryptorFactory(callback, decKeyIdentifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue