Add jsonLD signing functions

main
Bob Mottram 2020-06-15 13:37:53 +01:00
parent a4350228de
commit 8245c35f46
5 changed files with 209 additions and 2 deletions

View File

@ -23,6 +23,7 @@ sudo pacman -S tor python-pip python-pysocks python-pycryptodome \
imagemagick python-pillow python-requests \
perl-image-exiftool python-numpy python-dateutil \
certbot flake8
suso pip3 install pyLD
```
Or on Debian:
@ -34,6 +35,7 @@ sudo apt install -y \
python3-crypto python3-cryptodome \
python3-dateutil python3-pil.imagetk
python3-idna python3-requests \
python3-pyld \
libimage-exiftool-perl python3-flake8 \
certbot nginx
```

View File

@ -4,7 +4,7 @@ You will need python version 3.7 or later.
On a Debian based system:
sudo apt install -y tor python3-socks imagemagick python3-numpy python3-setuptools python3-crypto python3-cryptodome python3-dateutil python3-pil.imagetk python3-idna python3-requests python3-flake8 libimage-exiftool-perl certbot nginx
sudo apt install -y tor python3-socks imagemagick python3-numpy python3-setuptools python3-crypto python3-cryptodome python3-dateutil python3-pil.imagetk python3-idna python3-requests python3-flake8 python3-pyld libimage-exiftool-perl certbot nginx
The following instructions install Epicyon to the /opt directory. It's not essential that it be installed there, and it could be in any other preferred directory.

149
jsonldsig.py 100644
View File

@ -0,0 +1,149 @@
__filename__ = "jsonldsig.py"
__author__ = "Bob Mottram"
__credits__ = ['Based on ' +
'https://github.com/WebOfTrustInfo/ld-signatures-python']
__license__ = "AGPL3+"
__version__ = "1.1.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from copy import deepcopy
from datetime import datetime
import pytz
try:
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256
from Cryptodome.Signature import pkcs1_15 as PKCS1_v1_5
except ImportError:
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from pyld import jsonld
import base64
import json
def b64safeEncode(payload):
"""
b64 url safe encoding with the padding removed.
"""
return base64.urlsafe_b64encode(payload).rstrip(b'=')
def b64safeDecode(payload):
"""
b64 url safe decoding with the padding added.
"""
return base64.urlsafe_b64decode(payload + b'=' * (4 - len(payload) % 4))
def normalizeJson(payload):
return json.dumps(payload, separators=(',', ':'),
sort_keys=True).encode('utf-8')
def signRs256(payload, private_key):
"""
Produce a RS256 signature of the payload
"""
key = RSA.importKey(private_key)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(SHA256.new(payload))
return signature
def verifyRs256(payload, signature, public_key):
"""
Verifies a RS256 signature
"""
key = RSA.importKey(public_key)
verifier = PKCS1_v1_5.new(key)
return verifier.verify(SHA256.new(payload), signature)
def signJws(payload, private_key):
""" Prepare payload to sign
"""
header = {
'alg': 'RS256',
'b64': False,
'crit': ['b64']
}
normalizedJson = normalizeJson(header)
encodedHeader = b64safeEncode(normalizedJson)
preparedPayload = b'.'.join([encodedHeader, payload])
signature = signRs256(preparedPayload, private_key)
encodedSignature = b64safeEncode(signature)
jwsSignature = b'..'.join([encodedHeader, encodedSignature])
return jwsSignature
def verifyJws(payload, jws_signature, public_key):
# remove the encoded header from the signature
encodedHeader, encodedSignature = jws_signature.split(b'..')
signature = b64safeDecode(encodedSignature)
payload = b'.'.join([encodedHeader, payload])
return verifyRs256(payload, signature, public_key)
def jsonldNormalize(jldDocument: str):
"""
Normalize and hash the json-ld document
"""
options = {
'algorithm': 'URDNA2015',
'format': 'application/nquads'
}
normalized = jsonld.normalize(jldDocument, options=options)
normalizedHash = SHA256.new(data=normalized.encode('utf-8')).digest()
return normalizedHash
def jsonldSign(jldDocument: {}, privateKeyPem: str) -> {}:
"""
Produces a signed JSON-LD document with a Json Web Signature
"""
jldDocument = deepcopy(jldDocument)
normalizedJldHash = jsonldNormalize(jldDocument)
jwsSignature = signJws(normalizedJldHash, privateKeyPem)
# construct the signature document and add it to jsonld
signature = {
'type': 'RsaSignatureSuite2017',
'created': datetime.now(tz=pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
'signatureValue': jwsSignature.decode('utf-8')
}
jldDocument.update({'signature': signature})
return jldDocument
def jsonldVerify(signedJldDocument: {}, publicKeyPem: str) -> bool:
"""
Verifies the Json Web Signature of a signed JSON-LD Document
"""
signedJldDocument = deepcopy(signedJldDocument)
signature = signedJldDocument.pop('signature')
jwsSignature = signature['signatureValue'].encode('utf-8')
normalizedJldHash = jsonldNormalize(signedJldDocument)
return verifyJws(normalizedJldHash, jwsSignature, publicKeyPem)
def testSignJsonld(jldDocument: {}, privateKeyPem: str,
expectedJldDocumentSigned=None):
signedJldDocument = jsonldSign(jldDocument, privateKeyPem)
# pop the created time key since its dynamic
signedJldDocument['signature'].pop('created')
if expectedJldDocumentSigned:
assert signedJldDocument == expectedJldDocumentSigned
else:
return signedJldDocument

View File

@ -47,6 +47,7 @@ from follow import sendFollowRequest
from person import createPerson
from person import setDisplayNickname
from person import setBio
# from person import generateRSAKey
from skills import setSkillLevel
from roles import setRole
from roles import outboxDelegate
@ -70,6 +71,8 @@ from content import replaceContentDuplicates
from content import removeTextFormatting
from theme import setCSSparam
from semantic import isAccusatory
from jsonldsig import testSignJsonld
from jsonldsig import jsonldVerify
testServerAliceRunning = False
testServerBobRunning = False
@ -1809,8 +1812,61 @@ def testRemoveTextFormatting():
assert(resultStr == '<p>Text with formatting</p>')
def testJsonld():
print("testJsonld")
jldDocument = {
"description": "My json document",
"numberField": 83582,
"object": {
"content": "Some content"
}
}
# privateKeyPem, publicKeyPem = generateRSAKey()
privateKeyPem = '-----BEGIN RSA PRIVATE KEY-----\n' \
'MIIEowIBAAKCAQEAod9iHfIn4ugY/2byFrFjUprrFLkkH5bCrjiBq2/MdHFg99IQ\n' \
'7li2x2mg5fkBMhU5SJIxlN8kiZMFq7JUXSA97Yo4puhVubqTSHihIh6Xn2mTjTgs\n' \
'zNo9SBbmN3YiyBPTcr0rF4jGWZAduJ8u6i7Eky2QH+UBKyUNRZrcfoVq+7grHUIA\n' \
'45pE7vAfEEWtgRiw32Nwlx55N3hayHax0y8gMdKEF/vfYKRLcM7rZgEASMtlCpgy\n' \
'fsyHwFCDzl/BP8AhP9u3dM+SEundeAvF58AiXx1pKvBpxqttDNAsKWCRQ06/WI/W\n' \
'2Rwihl9yCjobqRoFsZ/cTEi6FG9AbDAds5YjTwIDAQABAoIBAERL3rbpy8Bl0t43\n' \
'jh7a+yAIMvVMZBxb3InrV3KAug/LInGNFQ2rKnsaawN8uu9pmwCuhfLc7yqIeJUH\n' \
'qaadCuPlNJ/fWQQC309tbfbaV3iv78xejjBkSATZfIqb8nLeQpGflMXaNG3na1LQ\n' \
'/tdZoiDC0ZNTaNnOSTo765oKKqhHUTQkwkGChrwG3Js5jekV4zpPMLhUafXk6ksd\n' \
'8XLlZdCF3RUnuguXAg2xP/duxMYmTCx3eeGPkXBPQl0pahu8/6OtBoYvBrqNdQcx\n' \
'jnEtYX9PCqDY3hAXW9GWsxNfu02DKhWigFHFNRUQtMI++438+QIfzXPslE2bTQIt\n' \
'0OXUlwECgYEAxTKUZ7lwIBb5XKPJq53RQmX66M3ArxI1RzFSKm1+/CmxvYiN0c+5\n' \
'2Aq62WEIauX6hoZ7yQb4zhdeNRzinLR7rsmBvIcP12FidXG37q9v3Vu70KmHniJE\n' \
'TPbt5lHQ0bNACFxkar4Ab/JZN4CkMRgJdlcZ5boYNmcGOYCvw9izuM8CgYEA0iQ1\n' \
'khIFZ6fCiXwVRGvEHmqSnkBmBHz8MY8fczv2Z4Gzfq3Tlh9VxpigK2F2pFt7keWc\n' \
'53HerYFHFpf5otDhEyRwA1LyIcwbj5HopumxsB2WG+/M2as45lLfWa6KO73OtPpU\n' \
'wGZYW+i/otdk9eFphceYtw19mxI+3lYoeI8EjYECgYBxOtTKJkmCs45lqkp/d3QT\n' \
'2zjSempcXGkpQuG6KPtUUaCUgxdj1RISQj792OCbeQh8PDZRvOYaeIKInthkQKIQ\n' \
'P/Z1yVvIQUvmwfBqZmQmR6k1bFLJ80UiqFr7+BiegH2RD3Q9cnIP1aly3DPrWLD+\n' \
'OY9OQKfsfQWu+PxzyTeRMwKBgD8Zjlh5PtQ8RKcB8mTkMzSq7bHFRpzsZtH+1wPE\n' \
'Kp40DRDp41H9wMTsiZPdJUH/EmDh4LaCs8nHuu/m3JfuPtd/pn7pBjntzwzSVFji\n' \
'bW+jwrJK1Gk8B87pbZXBWlLMEOi5Dn/je37Fqd2c7f0DHauFHq9AxsmsteIPXwGs\n' \
'eEKBAoGBAIzJX/5yFp3ObkPracIfOJ/U/HF1UdP6Y8qmOJBZOg5s9Y+JAdY76raK\n' \
'0SbZPsOpuFUdTiRkSI3w/p1IuM5dPxgCGH9MHqjqogU5QwXr3vLF+a/PFhINkn1x\n' \
'lozRZjDcF1y6xHfExotPC973UZnKEviq9/FqOsovZpvSQkzAYSZF\n' \
'-----END RSA PRIVATE KEY-----'
publicKeyPem = '-----BEGIN PUBLIC KEY-----\n' \
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAod9iHfIn4ugY/2byFrFj\n' \
'UprrFLkkH5bCrjiBq2/MdHFg99IQ7li2x2mg5fkBMhU5SJIxlN8kiZMFq7JUXSA9\n' \
'7Yo4puhVubqTSHihIh6Xn2mTjTgszNo9SBbmN3YiyBPTcr0rF4jGWZAduJ8u6i7E\n' \
'ky2QH+UBKyUNRZrcfoVq+7grHUIA45pE7vAfEEWtgRiw32Nwlx55N3hayHax0y8g\n' \
'MdKEF/vfYKRLcM7rZgEASMtlCpgyfsyHwFCDzl/BP8AhP9u3dM+SEundeAvF58Ai\n' \
'Xx1pKvBpxqttDNAsKWCRQ06/WI/W2Rwihl9yCjobqRoFsZ/cTEi6FG9AbDAds5Yj\n' \
'TwIDAQAB\n' \
'-----END PUBLIC KEY-----'
signedDocument = testSignJsonld(jldDocument, privateKeyPem)
assert(signedDocument)
assert(jsonldVerify(signedDocument, publicKeyPem))
def runAllTests():
print('Running tests...')
testJsonld()
testRemoveTextFormatting()
testAccusatory()
testWebLinks()

View File

@ -1264,7 +1264,7 @@
<p class="intro">You will need python version 3.7 or later.</p>
<p class="intro">On a Debian based system:</p>
<div class="shell">
<p>sudo apt install -y tor python3-socks imagemagick python3-numpy python3-setuptools python3-crypto python3-cryptodome python3-dateutil python3-pil.imagetk python3-idna python3-requests python3-flake8 libimage-exiftool-perl certbot nginx</p>
<p>sudo apt install -y tor python3-socks imagemagick python3-numpy python3-setuptools python3-crypto python3-cryptodome python3-dateutil python3-pil.imagetk python3-idna python3-requests python3-flake8 python3-pyld libimage-exiftool-perl certbot nginx</p>
</div>
<p class="intro">