blob: 4ebf6e61d18fb1afced86636d67d5aba90c25627 [file] [log] [blame]
Asim Shankarae8d4c52014-10-08 13:03:31 -07001package security
2
3import (
4 "crypto/ecdsa"
Asim Shankarae8d4c52014-10-08 13:03:31 -07005 "fmt"
6 "os"
7 "path"
8
9 "veyron.io/veyron/veyron2/security"
10)
11
gauthamt1e313bc2014-11-10 15:45:56 -080012const (
13 blessingStoreDataFile = "blessingstore.data"
14 blessingStoreSigFile = "blessingstore.sig"
15
16 blessingRootsDataFile = "blessingroots.data"
17 blessingRootsSigFile = "blessingroots.sig"
18
19 privateKeyFile = "privatekey.pem"
20)
Asim Shankarae8d4c52014-10-08 13:03:31 -070021
Asim Shankarae8d4c52014-10-08 13:03:31 -070022// NewPrincipal mints a new private key and generates a principal based on
23// this key, storing its BlessingRoots and BlessingStore in memory.
24func NewPrincipal() (security.Principal, error) {
gauthamta134eda2014-11-05 17:57:42 -080025 pub, priv, err := NewPrincipalKey()
Asim Shankarae8d4c52014-10-08 13:03:31 -070026 if err != nil {
27 return nil, err
28 }
29 return security.CreatePrincipal(security.NewInMemoryECDSASigner(priv), newInMemoryBlessingStore(pub), newInMemoryBlessingRoots())
30}
31
gauthamt98108162014-11-11 18:45:56 -080032// PrincipalStateSerializer is used to persist BlessingRoots/BlessingStore state for
33// a principal with the provided SerializerReaderWriters.
34type PrincipalStateSerializer struct {
35 BlessingRoots SerializerReaderWriter
36 BlessingStore SerializerReaderWriter
Srdjan Petrovicf07f4a02014-10-22 16:31:06 -070037}
38
gauthamt98108162014-11-11 18:45:56 -080039// NewPrincipalStateSerializer is a convenience function that returns a serializer
40// for BlessingStore and BlessingRoots given a directory location. We create the
41// directory if it does not already exist.
42func NewPrincipalStateSerializer(dir string) (*PrincipalStateSerializer, error) {
Srdjan Petrovicf07f4a02014-10-22 16:31:06 -070043 if err := mkDir(dir); err != nil {
44 return nil, err
45 }
gauthamt98108162014-11-11 18:45:56 -080046 return &PrincipalStateSerializer{
Ankur27c56fd2014-11-17 19:30:34 -080047 BlessingRoots: NewFileSerializer(path.Join(dir, blessingRootsDataFile), path.Join(dir, blessingRootsSigFile)),
48 BlessingStore: NewFileSerializer(path.Join(dir, blessingStoreDataFile), path.Join(dir, blessingStoreSigFile)),
gauthamt98108162014-11-11 18:45:56 -080049 }, nil
50}
51
52// NewPrincipalFromSigner creates a new principal using the provided Signer. If previously
53// persisted state is available, we use the serializers to populate BlessingRoots/BlessingStore
54// for the Principal. If provided, changes to the state are persisted and committed with the
55// same serializers. Otherwise, the state (ie: BlessingStore, BlessingRoots) is kept in memory.
56func NewPrincipalFromSigner(signer security.Signer, state *PrincipalStateSerializer) (security.Principal, error) {
57 if state == nil {
58 return security.CreatePrincipal(signer, newInMemoryBlessingStore(signer.PublicKey()), newInMemoryBlessingRoots())
59 }
60 serializationSigner, err := security.CreatePrincipal(signer, nil, nil)
61 if err != nil {
62 return nil, fmt.Errorf("failed to create serialization.Signer: %v", err)
63 }
64 blessingRoots, err := newPersistingBlessingRoots(state.BlessingRoots, serializationSigner)
65 if err != nil {
66 return nil, fmt.Errorf("failed to load BlessingRoots: %v", err)
67 }
68 blessingStore, err := newPersistingBlessingStore(state.BlessingStore, serializationSigner)
69 if err != nil {
70 return nil, fmt.Errorf("failed to load BlessingStore: %v", err)
71 }
72 return security.CreatePrincipal(signer, blessingStore, blessingRoots)
Srdjan Petrovicf07f4a02014-10-22 16:31:06 -070073}
74
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070075// LoadPersistentPrincipal reads state for a principal (private key, BlessingRoots, BlessingStore)
Asim Shankarae8d4c52014-10-08 13:03:31 -070076// from the provided directory 'dir' and commits all state changes to the same directory.
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070077// If private key file does not exist then an error 'err' is returned such that os.IsNotExist(err) is true.
Suharsh Sivakumar4684f4e2014-10-24 13:42:06 -070078// If private key file exists then 'passphrase' must be correct, otherwise PassphraseErr will be returned.
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070079func LoadPersistentPrincipal(dir string, passphrase []byte) (security.Principal, error) {
80 key, err := loadKeyFromDir(dir, passphrase)
81 if err != nil {
82 return nil, err
83 }
gauthamt98108162014-11-11 18:45:56 -080084 state, err := NewPrincipalStateSerializer(dir)
85 if err != nil {
86 return nil, err
87 }
88 return NewPrincipalFromSigner(security.NewInMemoryECDSASigner(key), state)
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -070089}
90
Ankur4704f5f2014-10-23 12:40:54 -070091// CreatePersistentPrincipal creates a new principal (private key, BlessingRoots,
92// BlessingStore) and commits all state changes to the provided directory.
93//
94// The generated private key is serialized and saved encrypted if the 'passphrase'
95// is non-nil, and unencrypted otherwise.
96//
97// If the directory has any preexisting principal data, CreatePersistentPrincipal
98// will return an error.
99//
100// The specified directory may not exist, in which case it gets created by this
101// function.
102func CreatePersistentPrincipal(dir string, passphrase []byte) (principal security.Principal, err error) {
Srdjan Petrovicf07f4a02014-10-22 16:31:06 -0700103 if err := mkDir(dir); err != nil {
104 return nil, err
Asim Shankarae8d4c52014-10-08 13:03:31 -0700105 }
Suharsh Sivakumar4e091882014-11-11 11:50:28 -0800106 key, err := initKey(dir, passphrase)
Asim Shankarae8d4c52014-10-08 13:03:31 -0700107 if err != nil {
Ankur4704f5f2014-10-23 12:40:54 -0700108 return nil, fmt.Errorf("failed to initialize private key: %v", err)
Asim Shankarae8d4c52014-10-08 13:03:31 -0700109 }
gauthamt98108162014-11-11 18:45:56 -0800110 state, err := NewPrincipalStateSerializer(dir)
111 if err != nil {
112 return nil, err
113 }
114 return NewPrincipalFromSigner(security.NewInMemoryECDSASigner(key), state)
Srdjan Petrovicf07f4a02014-10-22 16:31:06 -0700115}
116
Suharsh Sivakumar8a7fba42014-10-27 12:40:48 -0700117// InitDefaultBlessings uses the provided principal to create a self blessing for name 'name',
118// sets it as default on the principal's BlessingStore and adds it as root to the principal's BlessingRoots.
119func InitDefaultBlessings(p security.Principal, name string) error {
120 blessing, err := p.BlessSelf(name)
121 if err != nil {
122 return err
123 }
124 if err := p.BlessingStore().SetDefault(blessing); err != nil {
125 return err
126 }
127 if _, err := p.BlessingStore().Set(blessing, security.AllPrincipals); err != nil {
128 return err
129 }
130 if err := p.AddToRoots(blessing); err != nil {
131 return err
132 }
133 return nil
134}
135
Srdjan Petrovicf07f4a02014-10-22 16:31:06 -0700136func mkDir(dir string) error {
137 if finfo, err := os.Stat(dir); err == nil {
138 if !finfo.IsDir() {
139 return fmt.Errorf("%q is not a directory", dir)
140 }
141 } else if err := os.MkdirAll(dir, 0700); err != nil {
142 return fmt.Errorf("failed to create %q: %v", dir, err)
143 }
144 return nil
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700145}
146
147func loadKeyFromDir(dir string, passphrase []byte) (*ecdsa.PrivateKey, error) {
148 keyFile := path.Join(dir, privateKeyFile)
149 f, err := os.Open(keyFile)
150 if err != nil {
151 return nil, err
152 }
153 defer f.Close()
Ankur73e7a932014-10-24 15:57:03 -0700154 key, err := LoadPEMKey(f, passphrase)
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700155 if err != nil {
156 return nil, err
157 }
158 return key.(*ecdsa.PrivateKey), nil
159}
160
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700161func initKey(dir string, passphrase []byte) (*ecdsa.PrivateKey, error) {
Asim Shankarae8d4c52014-10-08 13:03:31 -0700162 keyFile := path.Join(dir, privateKeyFile)
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700163 f, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
Asim Shankarae8d4c52014-10-08 13:03:31 -0700164 if err != nil {
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700165 return nil, fmt.Errorf("failed to open %q for writing: %v", keyFile, err)
Asim Shankarae8d4c52014-10-08 13:03:31 -0700166 }
167 defer f.Close()
gauthamta134eda2014-11-05 17:57:42 -0800168 _, key, err := NewPrincipalKey()
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700169 if err != nil {
170 return nil, fmt.Errorf("failed to generate private key: %v", err)
Asim Shankarae8d4c52014-10-08 13:03:31 -0700171 }
Ankur73e7a932014-10-24 15:57:03 -0700172 if err := SavePEMKey(f, key, passphrase); err != nil {
Suharsh Sivakumaraca1c322014-10-21 11:27:32 -0700173 return nil, fmt.Errorf("failed to save private key to %q: %v", keyFile, err)
174 }
175 return key, nil
Asim Shankarae8d4c52014-10-08 13:03:31 -0700176}