blob: 715c558043daad60111a76f8f678064121da91a1 [file] [log] [blame]
Jungho Ahnc3cfceb2015-09-28 14:56:20 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package discovery
6
7import (
8 "crypto/rand"
9 "encoding/binary"
10 "errors"
11 "fmt"
12 "strings"
13
14 "golang.org/x/crypto/nacl/secretbox"
15
16 "v.io/v23/security"
17)
18
19var (
20 // errNoPermission is the error returned by decrypt when there is no permission
21 // to decrypt an advertisement.
22 errNoPermission = errors.New("no permission")
23)
24
25// encrypt identity-based encrypts the service so that only users who match with one of
26// the given blessing patterns can decrypt it. Nil patterns means no encryption.
27func encrypt(ad *Advertisement, patterns []security.BlessingPattern) error {
28 if len(patterns) == 0 {
29 ad.EncryptionAlgorithm = NoEncryption
30 return nil
31 }
32
33 sharedKey, keys, err := newSharedKey(patterns)
34 if err != nil {
35 return err
36 }
37 ad.EncryptionAlgorithm = TestEncryption
38 ad.EncryptionKeys = keys
39
40 // We only encrypt addresses for now.
41 //
42 // TODO(jhahn): Revisit the scope of encryption.
43 encrypted := make([]string, len(ad.Addrs))
44 for i, addr := range ad.Addrs {
45 var n [24]byte
46 binary.LittleEndian.PutUint64(n[:], uint64(i))
47 encrypted[i] = string(secretbox.Seal(nil, []byte(addr), &n, sharedKey))
48 }
49 ad.Addrs = encrypted
50 return nil
51}
52
53// decrypt decrypts the service with the given blessing names.
54func decrypt(ad *Advertisement, names []string) error {
55 if ad.EncryptionAlgorithm == NoEncryption {
56 // Not encrypted.
57 return nil
58 }
59 if len(names) == 0 {
60 // No identifiers.
61 return errNoPermission
62 }
63
64 if ad.EncryptionAlgorithm != TestEncryption {
65 return fmt.Errorf("not supported encryption algorithm %v\n", ad.EncryptionAlgorithm)
66 }
67 sharedKey, err := decryptSharedKey(ad.EncryptionKeys, names)
68 if err != nil {
69 return err
70 }
71 if sharedKey == nil {
72 return errNoPermission
73 }
74
75 // We only encrypt addresses for now.
76 //
77 // Note that we should not modify the slice element directly here since the
78 // underlying plugins may cache services and the next plugin.Scan() may return
79 // the already decrypted addresses.
80 decrypted := make([]string, len(ad.Addrs))
81 for i, encrypted := range ad.Addrs {
82 var n [24]byte
83 binary.LittleEndian.PutUint64(n[:], uint64(i))
84 addr, ok := secretbox.Open(nil, []byte(encrypted), &n, sharedKey)
85 if !ok {
86 return errors.New("decryption error")
87 }
88 decrypted[i] = string(addr)
89 }
90 ad.Addrs = decrypted
91 return nil
92}
93
94// newSharedKey creates a new shared encryption key and identity-based encrypts
95// the shared key with the given blessing patterns.
96func newSharedKey(patterns []security.BlessingPattern) (*[32]byte, []EncryptionKey, error) {
97 var sharedKey [32]byte
98 if _, err := rand.Read(sharedKey[:]); err != nil {
99 return nil, nil, err
100 }
101
102 keys := make([]EncryptionKey, len(patterns))
103 // TODO(jhahn): Replace this fake with the real IBE.
104 for i, pattern := range patterns {
105 var k [32]byte
106 copy(k[:], pattern)
107 keys[i] = secretbox.Seal(nil, sharedKey[:], &[24]byte{}, &k)
108 }
109 return &sharedKey, keys, nil
110}
111
112// decryptSharedKey decrypts the identity-based encrypted shared key with the
113// given blessing names.
114func decryptSharedKey(keys []EncryptionKey, names []string) (*[32]byte, error) {
115 // TODO(jhahn): Replace this fake with the real IBE.
116 for _, name := range names {
117 for _, pattern := range prefixPatterns(name) {
118 var k [32]byte
119 copy(k[:], pattern)
120 for _, key := range keys {
121 decrypted, ok := secretbox.Open(nil, key, &[24]byte{}, &k)
122 if !ok {
123 continue
124 }
125 if len(decrypted) != 32 {
126 return nil, errors.New("shared key decryption error")
127 }
128 var sharedKey [32]byte
129 copy(sharedKey[:], decrypted)
130 return &sharedKey, nil
131 }
132 }
133 }
134 return nil, nil
135}
136
137// prefixPatterns returns blessing patterns that can be matched by the given name.
138func prefixPatterns(name string) []string {
139 patterns := []string{
140 name,
141 name + security.ChainSeparator + string(security.NoExtension),
142 }
143 for {
144 i := strings.LastIndex(name, security.ChainSeparator)
145 if i < 0 {
146 break
147 }
148 name = name[:i]
149 patterns = append(patterns, name)
150 }
151 return patterns
152}