Skip to content

Commit f06a07b

Browse files
authored
Merge pull request #167 from tianyuan129/main
introduce inter-domain
2 parents b51b5db + 750dfad commit f06a07b

10 files changed

+894
-55
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ wasm_exec.js
1111
# Compiler folders
1212
.vscode
1313
.idea
14+
.gocache
1415

1516
# Temporary
1617
.env

std/security/cert_cache.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,46 @@ type certCacheEntry struct {
2121
expiry time.Time
2222
}
2323

24+
// CertListCache stores validated CertList Data packets keyed by prefix and full name.
25+
type CertListCache struct {
26+
cache sync.Map
27+
}
28+
29+
// NewCertListCache creates a new CertListCache.
30+
func NewCertListCache() *CertListCache {
31+
return &CertListCache{}
32+
}
33+
34+
// Get returns a cached CertList for the given prefix or full name.
35+
func (clc *CertListCache) Get(prefix enc.Name) (ndn.Data, bool) {
36+
if v, ok := clc.cache.Load(prefix.TlvStr()); ok {
37+
if data, ok := v.(ndn.Data); ok {
38+
return data, true
39+
}
40+
}
41+
return nil, false
42+
}
43+
44+
// Put stores a CertList, preferring newer versions.
45+
func (clc *CertListCache) Put(anchorKeyName enc.Name, data ndn.Data) {
46+
prefix, err := CertListPrefix(anchorKeyName)
47+
if err != nil {
48+
return
49+
}
50+
key := prefix.TlvStr()
51+
if v, ok := clc.cache.Load(key); ok {
52+
if old, ok := v.(ndn.Data); ok && !isCertListNewer(old, data) {
53+
return
54+
}
55+
}
56+
clc.cache.Store(key, data)
57+
clc.cache.Store(data.Name().TlvStr(), data)
58+
}
59+
60+
func isCertListNewer(old, new ndn.Data) bool {
61+
return CertListVersion(new.Name()) > CertListVersion(old.Name())
62+
}
63+
2464
// (AI GENERATED DESCRIPTION): Creates and returns a new, empty CertCache instance.
2565
func NewCertCache() *CertCache {
2666
return &CertCache{}

std/security/certificate.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package security
22

33
import (
4+
"fmt"
5+
"io"
46
"time"
57

68
enc "github.com/named-data/ndnd/std/encoding"
@@ -14,6 +16,9 @@ import (
1416
type SignCertArgs struct {
1517
// Signer is the private key used to sign the certificate.
1618
Signer ndn.Signer
19+
// SignerName sets the KeyLocator of the signature to a specific
20+
// name. Optional; defaults to the signer’s key name.
21+
SignerName enc.Name
1722
// Data is the CSR or Key to be signed.
1823
Data ndn.Data
1924
// IssuerId is the issuer ID to be included in the certificate name.
@@ -65,7 +70,22 @@ func SignCert(args SignCertArgs) (enc.Wire, error) {
6570
SigNotAfter: optional.Some(args.NotAfter),
6671
CrossSchema: args.CrossSchema,
6772
}
68-
cert, err := spec.Spec{}.MakeData(certName, cfg, enc.Wire{pk}, args.Signer)
73+
signer := args.Signer
74+
if len(args.SignerName) > 0 {
75+
locatorKey, err := KeyNameFromLocator(args.SignerName)
76+
if err != nil {
77+
return nil, err
78+
}
79+
if !locatorKey.Equal(args.Signer.KeyName()) {
80+
return nil, ndn.ErrInvalidValue{Item: "SignerName", Value: args.SignerName}
81+
}
82+
signer = &sig.ContextSigner{
83+
Signer: args.Signer,
84+
KeyLocatorName: args.SignerName,
85+
}
86+
}
87+
88+
cert, err := spec.Spec{}.MakeData(certName, cfg, enc.Wire{pk}, signer)
6989
if err != nil {
7090
return nil, err
7191
}
@@ -144,3 +164,54 @@ func getPubKey(data ndn.Data) ([]byte, enc.Name, error) {
144164
return nil, nil, ndn.ErrInvalidValue{Item: "Data.ContentType", Value: contentType}
145165
}
146166
}
167+
168+
// EncodeCertList encodes a list of certificate names as a TLV sequence of Name TLVs.
169+
func EncodeCertList(names []enc.Name) (enc.Wire, error) {
170+
if len(names) == 0 {
171+
return nil, ndn.ErrInvalidValue{Item: "CertList", Value: "empty"}
172+
}
173+
length := 0
174+
for _, n := range names {
175+
length += len(n.Bytes())
176+
}
177+
buf := make([]byte, length)
178+
pos := 0
179+
for _, n := range names {
180+
nb := n.Bytes()
181+
copy(buf[pos:], nb)
182+
pos += len(nb)
183+
}
184+
return enc.Wire{buf}, nil
185+
}
186+
187+
// DecodeCertList decodes the content of a CertList into certificate names.
188+
func DecodeCertList(content enc.Wire) ([]enc.Name, error) {
189+
reader := enc.NewWireView(content)
190+
names := make([]enc.Name, 0)
191+
for !reader.IsEOF() {
192+
typ, err := reader.ReadTLNum()
193+
if err != nil {
194+
return nil, err
195+
}
196+
l, err := reader.ReadTLNum()
197+
if err != nil {
198+
return nil, err
199+
}
200+
if typ != enc.TypeName {
201+
return nil, fmt.Errorf("unexpected TLV type %x in CertList", typ)
202+
}
203+
nameView := reader.Delegate(int(l))
204+
if nameView.Length() != int(l) {
205+
return nil, io.ErrUnexpectedEOF
206+
}
207+
name, err := nameView.ReadName()
208+
if err != nil {
209+
return nil, err
210+
}
211+
names = append(names, name)
212+
}
213+
if len(names) == 0 {
214+
return nil, ndn.ErrInvalidValue{Item: "CertList", Value: "empty"}
215+
}
216+
return names, nil
217+
}

std/security/certificate_test.go

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ func TestSignCertOther(t *testing.T) {
8282
aliceSigner := tu.NoErr(signer.UnmarshalSecret(aliceKeyData))
8383

8484
// self signed alice's key
85-
aliceCert, err := sec.SignCert(sec.SignCertArgs{
85+
aliceCertWire, err := sec.SignCert(sec.SignCertArgs{
8686
Signer: aliceSigner,
8787
Data: aliceKeyData,
8888
IssuerId: ISSUER,
8989
NotBefore: T1,
9090
NotAfter: T2,
9191
})
9292
require.NoError(t, err)
93-
aliceCertData, _, err := spec_2022.Spec{}.ReadData(enc.NewWireView(aliceCert))
93+
aliceCertData, _, err := spec_2022.Spec{}.ReadData(enc.NewWireView(aliceCertWire))
9494
require.NoError(t, err)
9595

9696
// parse existing certificate
@@ -136,3 +136,71 @@ func TestSignCertOther(t *testing.T) {
136136
require.Equal(t, 64, len(signature.SigValue())) // ed25519
137137
require.True(t, tu.NoErr(signer.ValidateData(newCert, newSigCov, aliceCertData)))
138138
}
139+
140+
func TestSignCertWithSignerCertName(t *testing.T) {
141+
tu.SetT(t)
142+
143+
aliceKey, _ := base64.StdEncoding.DecodeString(KEY_ALICE)
144+
aliceKeyData, _, _ := spec_2022.Spec{}.ReadData(enc.NewBufferView(aliceKey))
145+
aliceSigner := tu.NoErr(signer.UnmarshalSecret(aliceKeyData))
146+
147+
// self signed alice's key to obtain a certificate name for the KeyLocator
148+
aliceCertWire := tu.NoErr(sec.SignCert(sec.SignCertArgs{
149+
Signer: aliceSigner,
150+
Data: aliceKeyData,
151+
IssuerId: ISSUER,
152+
NotBefore: T1,
153+
NotAfter: T2,
154+
}))
155+
aliceCertData, _, _ := spec_2022.Spec{}.ReadData(enc.NewWireView(aliceCertWire))
156+
157+
// parse existing certificate
158+
rootCert, _ := base64.StdEncoding.DecodeString(CERT_ROOT)
159+
rootCertData, _, _ := spec_2022.Spec{}.ReadData(enc.NewBufferView(rootCert))
160+
161+
// sign root cert with alice's key but force the KeyLocator to use alice's cert name
162+
newCertB := tu.NoErr(sec.SignCert(sec.SignCertArgs{
163+
Signer: aliceSigner,
164+
SignerName: aliceCertData.Name(),
165+
Data: rootCertData,
166+
IssuerId: ISSUER,
167+
NotBefore: T1,
168+
NotAfter: T2,
169+
}))
170+
newCert, newSigCov, err := spec_2022.Spec{}.ReadData(enc.NewWireView(newCertB))
171+
require.NoError(t, err)
172+
173+
signature := newCert.Signature()
174+
require.Equal(t, aliceCertData.Name(), signature.KeyName())
175+
require.True(t, tu.NoErr(signer.ValidateData(newCert, newSigCov, aliceCertData)))
176+
177+
t.Run("mismatched signer cert name", func(t *testing.T) {
178+
_, err := sec.SignCert(sec.SignCertArgs{
179+
Signer: aliceSigner,
180+
SignerName: rootCertData.Name(), // wrong key name
181+
Data: rootCertData,
182+
IssuerId: ISSUER,
183+
NotBefore: T1,
184+
NotAfter: T2,
185+
})
186+
require.Error(t, err)
187+
})
188+
}
189+
190+
func TestEncodeDecodeCertList(t *testing.T) {
191+
tu.SetT(t)
192+
n1 := tu.NoErr(enc.NameFromStr("/ndn/alice/KEY/aa/self/v=1"))
193+
n2 := tu.NoErr(enc.NameFromStr("/ndn/alice/KEY/bb/ndn/v=2"))
194+
195+
wire, err := sec.EncodeCertList([]enc.Name{n1, n2})
196+
require.NoError(t, err)
197+
decoded, err := sec.DecodeCertList(wire)
198+
require.NoError(t, err)
199+
require.Equal(t, []enc.Name{n1, n2}, decoded)
200+
201+
_, err = sec.EncodeCertList(nil)
202+
require.Error(t, err)
203+
204+
_, err = sec.DecodeCertList(enc.Wire{[]byte{0x01, 0x02}})
205+
require.Error(t, err)
206+
}

std/security/name_convention.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,67 @@ func GetIdentityFromCertName(name enc.Name) (enc.Name, error) {
5353
}
5454
return GetIdentityFromKeyName(keyName)
5555
}
56+
57+
// CertListPrefix returns /<domain>/KEY/<keyid>/32=auth for the given key name.
58+
func CertListPrefix(keyName enc.Name) (enc.Name, error) {
59+
if _, err := GetIdentityFromKeyName(keyName); err != nil {
60+
return nil, err
61+
}
62+
return keyName.Append(enc.NewKeywordComponent("auth")), nil
63+
}
64+
65+
// CertListNameMatches checks whether the CertList name is under /<domain>/KEY/<keyid>/32=auth[/<version>].
66+
func CertListNameMatches(keyName, listName enc.Name) bool {
67+
prefix, err := CertListPrefix(keyName)
68+
if err != nil {
69+
return false
70+
}
71+
listName = stripImplicitDigest(listName)
72+
if len(listName) < len(prefix) {
73+
return false
74+
}
75+
if !prefix.Equal(listName.Prefix(len(prefix))) {
76+
return false
77+
}
78+
rest := listName[len(prefix):]
79+
if len(rest) == 0 {
80+
return true
81+
}
82+
if len(rest) == 1 && rest[0].IsVersion() {
83+
return true
84+
}
85+
return false
86+
}
87+
88+
// CertListVersion returns the version component on the CertList name, or zero if absent.
89+
func CertListVersion(name enc.Name) uint64 {
90+
name = stripImplicitDigest(name)
91+
if name.At(-1).IsVersion() {
92+
return name.At(-1).NumberVal()
93+
}
94+
return 0
95+
}
96+
97+
func stripImplicitDigest(name enc.Name) enc.Name {
98+
if name.At(-1).Typ == enc.TypeImplicitSha256DigestComponent {
99+
return name.Prefix(-1)
100+
}
101+
return name
102+
}
103+
104+
// KeyNameFromLocator extracts /<identity>/KEY/<keyid> from a KeyLocator name
105+
// that may be a key name, certificate name, or cert name without version.
106+
func KeyNameFromLocator(name enc.Name) (enc.Name, error) {
107+
name = stripImplicitDigest(name)
108+
if len(name) < 3 {
109+
return nil, ndn.ErrInvalidValue{Item: "KEY component"}
110+
} else {
111+
for i := len(name) - 2; i >= 0; i-- {
112+
comp := name.At(i)
113+
if comp.String() == "KEY" || comp.IsKeyword("KEY") {
114+
return name[:i+2], nil
115+
}
116+
}
117+
return nil, ndn.ErrInvalidValue{Item: "KEY component"}
118+
}
119+
}

std/security/name_convention_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,55 @@ func TestGetKeyNameFromCertName(t *testing.T) {
8282
_, err = sec.GetKeyNameFromCertName(enc.Name{})
8383
require.Error(t, err)
8484
}
85+
86+
func TestCertListNaming(t *testing.T) {
87+
tu.SetT(t)
88+
89+
keyName := tu.NoErr(enc.NameFromStr("/my/test/identity/KEY/kid"))
90+
prefix, err := sec.CertListPrefix(keyName)
91+
require.NoError(t, err)
92+
require.True(t, prefix.At(-1).IsKeyword("auth"))
93+
94+
// Exact prefix and version
95+
require.True(t, sec.CertListNameMatches(keyName, prefix))
96+
withVer := prefix.Append(enc.NewVersionComponent(1))
97+
require.True(t, sec.CertListNameMatches(keyName, withVer))
98+
// implicit digest stripped
99+
withDigest := withVer.Append(enc.Component{Typ: enc.TypeImplicitSha256DigestComponent})
100+
require.True(t, sec.CertListNameMatches(keyName, withDigest))
101+
102+
// Wrong auth type (generic, not keyword)
103+
wrongAuth := keyName.Append(enc.NewGenericComponent("auth"))
104+
require.False(t, sec.CertListNameMatches(keyName, wrongAuth))
105+
// Extra components or wrong key
106+
require.False(t, sec.CertListNameMatches(keyName, withVer.Append(enc.NewSegmentComponent(0))))
107+
require.False(t, sec.CertListNameMatches(tu.NoErr(enc.NameFromStr("/other/KEY/kid")), withVer))
108+
109+
// Version parsing
110+
require.Equal(t, uint64(1), sec.CertListVersion(withVer))
111+
require.Equal(t, uint64(1), sec.CertListVersion(withDigest))
112+
require.Equal(t, uint64(0), sec.CertListVersion(prefix))
113+
}
114+
115+
func TestAnchorKeyNameFromLocator(t *testing.T) {
116+
tu.SetT(t)
117+
118+
keyName := tu.NoErr(enc.NameFromStr("/my/test/identity/KEY/kid"))
119+
certName := keyName.Append(enc.NewGenericComponent("issuer"), enc.NewVersionComponent(5))
120+
issuerOnly := keyName.Append(enc.NewGenericComponent("issuer"))
121+
122+
got, err := sec.KeyNameFromLocator(keyName)
123+
require.NoError(t, err)
124+
require.Equal(t, keyName, got)
125+
126+
got, err = sec.KeyNameFromLocator(certName)
127+
require.NoError(t, err)
128+
require.Equal(t, keyName, got)
129+
130+
got, err = sec.KeyNameFromLocator(issuerOnly)
131+
require.NoError(t, err)
132+
require.Equal(t, keyName, got)
133+
134+
_, err = sec.KeyNameFromLocator(tu.NoErr(enc.NameFromStr("/wrong/components")))
135+
require.Error(t, err)
136+
}

0 commit comments

Comments
 (0)