Skip to content

Commit 6587a08

Browse files
committed
lvs: Add an example
1 parent 4206fab commit 6587a08

13 files changed

+386
-4
lines changed

docs/src/app.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,16 @@ If there is a conflict, the earlier an argument is listed the higher priority it
6767
All other related parameters will be ignored. The Keychain will not be used.
6868
+ **no_signature** (*bool*) - not signed. Not recommended.
6969
+ **digest_sha256** (*bool*) - using SHA-256 digest to protect integrity only. ``False`` by default.
70+
+ **cert** (:any:`NonStrictName`) - using the speficied Certificate to sign this packet.
71+
The Key name will be derived from the certificate name.
7072
+ **key** - using the specified Key to sign this packet.
7173
Either a Key object or the :any:`NonStrictName` of a Key is acceptable.
74+
KeyLocator will be set to the default Certificate name of this Key unless specified.
7275
+ **identity** - using the default Key of the specified Identity to sign this packet.
7376
Either an Identity object or the :any:`NonStrictName` of an Identity is acceptable.
7477
The default Identity will be used if all of the above arguments are omitted.
78+
+ **key_locator** (:any:`NonStrictName`) - using the specified KeyLocator Name regardless of which
79+
Key is used.
7580

7681
Reference
7782
---------

examples/lvs/consumer.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import os
2+
import sys
3+
import logging
4+
from ndn.utils import timestamp
5+
from ndn.encoding import Name, Component, InterestParam
6+
from ndn.security import TpmFile, KeychainSqlite3
7+
from ndn.app import NDNApp, InterestNack, InterestTimeout, InterestCanceled, ValidationFailure
8+
from ndn.app_support.light_versec import compile_lvs, Checker, DEFAULT_USER_FNS, lvs_validator
9+
10+
11+
logging.basicConfig(format='[{asctime}]{levelname}:{message}',
12+
datefmt='%Y-%m-%d %H:%M:%S',
13+
level=logging.INFO,
14+
style='{')
15+
16+
lvs_text = r'''
17+
#KEY: "KEY"/_/_/_
18+
#site: "lvs-test"
19+
#article: #site/"article"/author/post/_version & {_version: $eq_type("v=0")} <= #author
20+
#author: #site/"author"/author/"KEY"/_/admin/_ <= #admin
21+
#admin: #site/"admin"/admin/#KEY <= #root
22+
#root: #site/#KEY
23+
'''
24+
25+
26+
def main():
27+
basedir = os.path.dirname(os.path.abspath(sys.argv[0]))
28+
tpm_path = os.path.join(basedir, 'privKeys')
29+
pib_path = os.path.join(basedir, 'pib.db')
30+
keychain = KeychainSqlite3(pib_path, TpmFile(tpm_path))
31+
32+
trust_anchor = keychain['/lvs-test'].default_key().default_cert()
33+
print(f'Trust anchor name: {Name.to_str(trust_anchor.name)}')
34+
35+
lvs_model = compile_lvs(lvs_text)
36+
checker = Checker(lvs_model, DEFAULT_USER_FNS)
37+
app = NDNApp(keychain=keychain)
38+
validator = lvs_validator(checker, app, trust_anchor.data)
39+
app.data_validator = validator
40+
41+
async def fetch_interest(article: str):
42+
try:
43+
name = Name.from_str(f'/lvs-test/article/xinyu/{article}')
44+
print(f'Sending Interest {Name.to_str(name)}')
45+
data_name, meta_info, content = await app.express_interest(
46+
name, must_be_fresh=True, can_be_prefix=True, lifetime=6000)
47+
print(f'Received Data Name: {Name.to_str(data_name)}')
48+
print(meta_info)
49+
print(bytes(content).decode() if content else None)
50+
except InterestNack as e:
51+
print(f'Nacked with reason={e.reason}')
52+
except InterestTimeout:
53+
print(f'Timeout')
54+
except InterestCanceled:
55+
print(f'Canceled')
56+
except ValidationFailure:
57+
print(f'Data failed to validate')
58+
59+
async def ndn_main():
60+
await fetch_interest('hello')
61+
await fetch_interest('world')
62+
63+
app.shutdown()
64+
65+
app.run_forever(ndn_main())
66+
67+
68+
if __name__ == '__main__':
69+
main()

examples/lvs/ls-certificates.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyndnsec --path . --tpm tpm-file --tpm-path privKeys ls -vvv

examples/lvs/pib.db

32 KB
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MHcCAQEEIF7ZR5mKgKfnBQ6XMPDijYNH0UKNjLPnBbqBBXi1jb+xoAoGCCqGSM49
2+
AwEHoUQDQgAENFhT28Rsqhfaej7PcfHIy5Sf9oU5JzRuhp/NuyURonOx0I9ely8g
3+
XbaLqeYq6asyPBaqIp+US8Z1FPIbGTKTxA==
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MHcCAQEEIPyFI6RdO9vhZeFAKJbovhMYht7Ex7gGxsrFfWNZJofYoAoGCCqGSM49
2+
AwEHoUQDQgAEALGvaZ7m2uRLAYA/J0PFIUkDoyuRyW+kGmL8yY2b2xO6+0RhiF4S
3+
iuY1pv980c0c3lVccdIP++pk02xt9KIcTQ==
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MHcCAQEEIPzqwLIknTXhBbVUTPFtLIpUrpqL0+qPDswmWLR/beLgoAoGCCqGSM49
2+
AwEHoUQDQgAE6OHeZi1CDis43xtih2zU/fv7KfFsa4l/k74NyIBSTXm/cYfmQIzF
3+
nPK8IHqPfm43/PzaQltgN7LU1GI/hTPSSg==

examples/lvs/producer.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import os
2+
import sys
3+
import logging
4+
from ndn.utils import timestamp
5+
from ndn.encoding import Name, Component
6+
from ndn.security import TpmFile, KeychainSqlite3
7+
from ndn.app import NDNApp
8+
from ndn.app_support.light_versec import compile_lvs, Checker, DEFAULT_USER_FNS
9+
10+
11+
logging.basicConfig(format='[{asctime}]{levelname}:{message}',
12+
datefmt='%Y-%m-%d %H:%M:%S',
13+
level=logging.INFO,
14+
style='{')
15+
16+
lvs_text = r'''
17+
#KEY: "KEY"/_/_/_
18+
#site: "lvs-test"
19+
#article: #site/"article"/author/post/_version & {_version: $eq_type("v=0")} <= #author
20+
#author: #site/"author"/author/"KEY"/_/admin/_ <= #admin
21+
#admin: #site/"admin"/admin/#KEY <= #root
22+
#root: #site/#KEY
23+
'''
24+
25+
26+
def main():
27+
basedir = os.path.dirname(os.path.abspath(sys.argv[0]))
28+
tpm_path = os.path.join(basedir, 'privKeys')
29+
pib_path = os.path.join(basedir, 'pib.db')
30+
keychain = KeychainSqlite3(pib_path, TpmFile(tpm_path))
31+
32+
trust_anchor = keychain['/lvs-test'].default_key().default_cert()
33+
admin_cert = keychain['/lvs-test/admin/ndn'].default_key().default_cert()
34+
author_cert = keychain['/lvs-test/author/xinyu'].default_key().default_cert()
35+
print(f'Trust anchor name: {Name.to_str(trust_anchor.name)}')
36+
print(f'Admin name: {Name.to_str(admin_cert.name)}')
37+
print(f'Author name: {Name.to_str(author_cert.name)}')
38+
39+
lvs_model = compile_lvs(lvs_text)
40+
checker = Checker(lvs_model, DEFAULT_USER_FNS)
41+
# The following manual checks are listed for demonstration only.
42+
# In real implementation they are automatically done
43+
root_of_trust = checker.root_of_trust()
44+
print(f'LVS model root of trust: {root_of_trust}')
45+
print(f'LVS model user functions provided: {checker.validate_user_fns()}')
46+
ta_matches = sum((m[0] for m in checker.match(trust_anchor.name)), start=[])
47+
assert len(ta_matches) > 0
48+
assert root_of_trust.issubset(ta_matches)
49+
print(f'Trust anchor matches the root of trust: OK')
50+
51+
app = NDNApp(keychain=keychain)
52+
53+
@app.route('/lvs-test/article/xinyu/hello')
54+
def on_interest(name, param, _app_param):
55+
print(f'>> I: {Name.to_str(name)}, {param}')
56+
content = "Hello,".encode()
57+
data_name = name + [Component.from_version(timestamp())]
58+
app.put_data(data_name, content=content, freshness_period=10000)
59+
print(f'<< D: {Name.to_str(data_name)}')
60+
print(f'Content: {content.decode()}')
61+
print('')
62+
63+
@app.route('/lvs-test/article/xinyu/world')
64+
def on_interest(name, param, _app_param):
65+
print(f'>> I: {Name.to_str(name)}, {param}')
66+
content = "world!".encode()
67+
data_name = name + [Component.from_version(timestamp())]
68+
app.put_data(data_name, content=content, freshness_period=10000)
69+
print(f'<< D: {Name.to_str(data_name)}')
70+
print(f'Content: {content.decode()}')
71+
print('')
72+
73+
@app.route(trust_anchor.name)
74+
def on_interest(name, param, _app_param):
75+
print(f'>> I: {Name.to_str(name)}, {param}')
76+
app.put_raw_packet(trust_anchor.data)
77+
print(f'<< D: {Name.to_str(trust_anchor.name)}')
78+
print('')
79+
80+
@app.route(admin_cert.name)
81+
def on_interest(name, param, _app_param):
82+
print(f'>> I: {Name.to_str(name)}, {param}')
83+
app.put_raw_packet(admin_cert.data)
84+
print(f'<< D: {Name.to_str(admin_cert.name)}')
85+
print('')
86+
87+
@app.route(author_cert.name)
88+
def on_interest(name, param, _app_param):
89+
print(f'>> I: {Name.to_str(name)}, {param}')
90+
app.put_raw_packet(author_cert.data)
91+
print(f'<< D: {Name.to_str(author_cert.name)}')
92+
print('')
93+
94+
print('Start serving ...')
95+
app.run_forever()
96+
97+
98+
if __name__ == '__main__':
99+
main()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# -----------------------------------------------------------------------------
2+
# This piece of work is inspired by Pollere' VerSec:
3+
# https://github.com/pollere/DCT
4+
# But this code is implemented independently without using any line of the
5+
# original one, and released under Apache License.
6+
#
7+
# Copyright (C) 2019-2022 The python-ndn authors
8+
#
9+
# This file is part of python-ndn.
10+
#
11+
# Licensed under the Apache License, Version 2.0 (the "License");
12+
# you may not use this file except in compliance with the License.
13+
# You may obtain a copy of the License at
14+
#
15+
# http://www.apache.org/licenses/LICENSE-2.0
16+
#
17+
# Unless required by applicable law or agreed to in writing, software
18+
# distributed under the License is distributed on an "AS IS" BASIS,
19+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
# See the License for the specific language governing permissions and
21+
# limitations under the License.
22+
# -----------------------------------------------------------------------------
23+
import logging
24+
from ...encoding import BinaryStr, SignaturePtrs, FormalName, parse_data, Name
25+
from ...app import NDNApp, Validator
26+
from ...security import union_checker
27+
from ...security.validator.cascade_validator import CascadeChecker, PublicKeyStorage, MemoryKeyStorage
28+
from .checker import Checker
29+
30+
__all__ = ['lvs_validator']
31+
32+
33+
def lvs_validator(checker: Checker, app: NDNApp, trust_anchor: BinaryStr,
34+
storage: PublicKeyStorage = MemoryKeyStorage()) -> Validator:
35+
async def validate_name(name: FormalName, sig_ptrs: SignaturePtrs) -> bool:
36+
if (not sig_ptrs.signature_info or not sig_ptrs.signature_info.key_locator
37+
or not sig_ptrs.signature_info.key_locator.name):
38+
return False
39+
cert_name = sig_ptrs.signature_info.key_locator.name
40+
logging.debug(f'LVS Checking {Name.to_str(name)} <- {Name.to_str(cert_name)} ...')
41+
return checker.check(name, cert_name)
42+
43+
def sanity_check():
44+
root_of_trust = checker.root_of_trust()
45+
if not checker.validate_user_fns():
46+
raise ValueError('Missing user functions for LVS validator')
47+
cert_name, _, _, _ = parse_data(trust_anchor)
48+
ta_matches = sum((m[0] for m in checker.match(cert_name)), start=[])
49+
if not ta_matches or not root_of_trust.issubset(ta_matches):
50+
raise ValueError('Trust anchor does not match all roots of trust of LVS model')
51+
52+
sanity_check()
53+
cas_checker = CascadeChecker(app, trust_anchor, storage)
54+
ret = union_checker(validate_name, cas_checker)
55+
cas_checker.next_level = ret
56+
return ret

src/ndn/bin/sec/cmd_sign_cert.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,22 @@ def execute(args: argparse.Namespace):
6060
return -1
6161
# Note: Do we need to check the validity of sign_req?
6262

63-
key_loc = args.key_locator
64-
v, id_name, key_name, _ = infer_obj_name(key_loc)
63+
key_loc = Name.normalize(args.key_locator)
64+
v, id_name, key_name, cert_name = infer_obj_name(key_loc)
6565
try:
6666
if v == 0:
6767
signer = kc.get_signer({'identity': id_name})
68-
else:
68+
elif v == 1:
6969
signer = kc.get_signer({'key': key_name})
70+
else:
71+
signer = kc.get_signer({'cert': cert_name})
7072
except KeyError:
7173
if v == 0:
7274
print(f'Specified identity does not exist: {Name.to_str(id_name)}')
73-
else:
75+
elif v == 1:
7476
print(f'Specified key does not exist: {Name.to_str(key_name)}')
77+
else:
78+
print(f'Specified certificate does not exist: {Name.to_str(cert_name)}')
7579
return -2
7680
# Currently python-ndn does not support putting a certificate name into KeyLocator
7781

0 commit comments

Comments
 (0)