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 \ imagemagick python-pillow python-requests \
perl-image-exiftool python-numpy python-dateutil \ perl-image-exiftool python-numpy python-dateutil \
certbot flake8 certbot flake8
suso pip3 install pyLD
``` ```
Or on Debian: Or on Debian:
@ -34,6 +35,7 @@ sudo apt install -y \
python3-crypto python3-cryptodome \ python3-crypto python3-cryptodome \
python3-dateutil python3-pil.imagetk python3-dateutil python3-pil.imagetk
python3-idna python3-requests \ python3-idna python3-requests \
python3-pyld \
libimage-exiftool-perl python3-flake8 \ libimage-exiftool-perl python3-flake8 \
certbot nginx certbot nginx
``` ```

View File

@ -4,7 +4,7 @@ You will need python version 3.7 or later.
On a Debian based system: 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. 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 createPerson
from person import setDisplayNickname from person import setDisplayNickname
from person import setBio from person import setBio
# from person import generateRSAKey
from skills import setSkillLevel from skills import setSkillLevel
from roles import setRole from roles import setRole
from roles import outboxDelegate from roles import outboxDelegate
@ -70,6 +71,8 @@ from content import replaceContentDuplicates
from content import removeTextFormatting from content import removeTextFormatting
from theme import setCSSparam from theme import setCSSparam
from semantic import isAccusatory from semantic import isAccusatory
from jsonldsig import testSignJsonld
from jsonldsig import jsonldVerify
testServerAliceRunning = False testServerAliceRunning = False
testServerBobRunning = False testServerBobRunning = False
@ -1809,8 +1812,61 @@ def testRemoveTextFormatting():
assert(resultStr == '<p>Text with formatting</p>') 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(): def runAllTests():
print('Running tests...') print('Running tests...')
testJsonld()
testRemoveTextFormatting() testRemoveTextFormatting()
testAccusatory() testAccusatory()
testWebLinks() testWebLinks()

View File

@ -1264,7 +1264,7 @@
<p class="intro">You will need python version 3.7 or later.</p> <p class="intro">You will need python version 3.7 or later.</p>
<p class="intro">On a Debian based system:</p> <p class="intro">On a Debian based system:</p>
<div class="shell"> <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> </div>
<p class="intro"> <p class="intro">