Last active
January 1, 2022 17:12
-
-
Save 0x4f53/ea53ea1e9b5fef93301c898d9b7b379a to your computer and use it in GitHub Desktop.
Python script to compare Java LazySodium signed messages with PyNaCl signed messages using Keypairs from both locations. Designed for Keyspace.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
(c) 2021 Keyspace | |
Compares a LazySodium / LibSodium output from Keyspace Android with PyNaCl's output and checks if LazySodium's signatures match PyNaCl's signature. | |
Note: LazySodium output on Android and PyNaCl output from this script must be the same due to determinism. | |
______________________________________________________________________________________________________________________________________________________ | |
.-------> Public key -----> Verify data | |
Seed ----> Generate keypair ----| |_______________________________. | |
| on Android | | | |
| | | | |
| '-------> Secret key -----> Signed data | | |
| | | | |
| | | | |
| | | | |
| V | | |
| .-----> Verify key ---> [[ Verify data ]] <---' | |
'----> Generate keypair ------| ^ | |
in Python | | | |
| | | |
| | | |
'----> Signing key -----> Signed data | |
""" | |
# All of the below values come from Keyspace Android's logcat | |
hardenedSeed = "6123cc8cb10eb9a23585183428b2d692c58407a05e558362c00811b05ee21eb2" # hardened seed from argon2i(bip39[seed]) output, to be used as input to keypair generation function | |
lazySodiumPrivateKey = "6123CC8CB10EB9A23585183428B2D692C58407A05E558362C00811B05EE21EB2629E8A3DFF89FD68A2D6721F0F41E032624140A8D13EE865A3E4AA46E3406D3C" | |
lazySodiumPublicKey = "629E8A3DFF89FD68A2D6721F0F41E032624140A8D13EE865A3E4AA46E3406D3C" | |
data = """{"expiry":1641032384056,"token":"e12de5ab-0891-45d1-adb0-a65f50491100","signedToken":"f0b5308927b9611574c3e865203719b85eee98aa243c00829f8b6f14e1731d5a"}""" # the actual message to sign. | |
androidSignedData = "4F46A31E6576F801D5670B9C741384EFC16E990721DF1051FC17AF91EE9CA7A1BFFCE6F8FB089593A84E8DC82AEBA26795242779686F6075F11BF09FC971E7037B22657870697279223A313634313033323338343035362C22746F6B656E223A2265313264653561622D303839312D343564312D616462302D613635663530343931313030222C227369676E6564546F6B656E223A2266306235333038393237623936313135373463336538363532303337313962383565656539386161323433633030383239663862366631346531373331643561227D" | |
import json | |
from nacl import encoding | |
from nacl.encoding import HexEncoder, Base64Encoder | |
from nacl.signing import SigningKey, VerifyKey, SignedMessage | |
from nacl.public import PrivateKey, PublicKey | |
androidSignedDataAsBytes = HexEncoder.decode((bytes(androidSignedData, encoding='utf-8')).decode()) | |
androidSignature = androidSignedData[0:128] | |
androidSignatureAsBytes = HexEncoder.decode((bytes(androidSignature, encoding='utf-8')).decode()) | |
dataAsBytes = bytes(data, encoding='utf-8') | |
print ("Data:\n" + data) | |
print ("Seed:\n" + hardenedSeed + "\n") | |
# On LazySodium Android, the private key from the seed was: | |
print ("Android LazySodium Private Key:\n" + lazySodiumPrivateKey) | |
print ("Android LazySodium Public Key:\n" + lazySodiumPublicKey) | |
private_key = SigningKey(bytes(hardenedSeed, encoding='utf-8'), encoder=HexEncoder) # Generate a signing/secret/private key from the Android generated hardened seed | |
signed = private_key.sign(bytes(data, encoding='utf-8')) # Sign message with the signing/secret/private key in PyNaCl | |
public_key = VerifyKey(bytes(lazySodiumPublicKey, encoding='utf-8'), encoder=HexEncoder) # Generate a verification/public key from the Android public key hexString | |
verificationStatus = public_key.verify(dataAsBytes, androidSignatureAsBytes) | |
print ("\nPyNaCl message from signature: \n" + str (signed.message) ) | |
print ("Android pasted message from logcat:\n" + str (dataAsBytes) ) | |
if signed.message == dataAsBytes: print ("\n[ ✓ ] Bytes match") | |
else: print ("\n[ x ] Error: Bytes do not match") | |
print ("\nPyNaCl produced signature: \n" + str (signed.signature) ) | |
print ("Android pasted signature from logcat:\n" + str (androidSignatureAsBytes) ) | |
if signed == androidSignedDataAsBytes: print ("\n[ ✓ ] Bytes match") | |
else: print ("\n[ x ] Error: Bytes do not match") | |
print("\n**********************************************************************\n") | |
if verificationStatus: | |
print ("[ ✓ ] MESSAGE VERIFIED! :)") | |
else: | |
print ("[ x ] INVALID / FORGED SIGNATURE! :(") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Screenshot
Edit: Error fixed. Turns out, I was using sealed boxes on instead of signing in LazySodium Android. :)
Changes
Before
cryptoBoxSeedPair()
cryptoSign()
cryptoSignVerify()
After
cryptoSignSeedPair()
cryptoSign()
(changed to combined mode)cryptoSignDetached()
cryptoSignVerifyDetached()
(detached verification function works for both combined mode and detached mode signatures)