// NewYubiKey takes the key and initial OTP and returns an // authenticator. func NewYubiKey(key []byte, initialOTP string) (*Authenticator, error) { pub, otp, err := yubikey.ParseOTPString(initialOTP) if err != nil { return nil, err } tmpKey := yubikey.NewKey(key) token, err := otp.Parse(tmpKey) if err != nil { return nil, err } util.Zero(tmpKey[:]) config := &YubiKeyConfig{ Counter: getTokenCounter(token), Key: key, Public: pub, } defer util.Zero(config.Key[:]) auth := &Authenticator{ Type: TypeYubiKey, Last: initialOTP, Secret: config.Bytes(), } return auth, nil }
// UnmarshalSecretStore decrypts and parses the secret store contained // in the input byte slice. func UnmarshalSecretStore(in, passphrase []byte, m secret.ScryptMode) (*SecretStore, bool) { if len(in) < saltSize { return nil, false } salt := in[:saltSize] enc := in[saltSize:] key := secret.DeriveKeyStrength(passphrase, salt, m) if key == nil { return nil, false } defer util.Zero(key[:]) data, ok := secret.Decrypt(key, enc) if !ok { util.Errorf("decrypt fails") return nil, false } defer util.Zero(data) var store SecretStore err := json.Unmarshal(data, &store) if err != nil { util.Errorf("encrypt fails") return nil, false } store.passphrase = make([]byte, len(passphrase)) copy(store.passphrase, passphrase) return &store, true }
// MarshalSecretStore serialises and encrypts the data store to a byte // slice suitable for writing to disk. func MarshalSecretStore(s *SecretStore, m secret.ScryptMode) ([]byte, bool) { if !s.Valid() { return nil, false } out, err := json.Marshal(s) if err != nil { return nil, false } defer util.Zero(out) salt := util.RandBytes(saltSize) if salt == nil { return nil, false } key := secret.DeriveKeyStrength(s.passphrase, salt, m) if key == nil { return nil, false } defer util.Zero(key[:]) enc, ok := secret.Encrypt(key, out) if !ok { return nil, false } defer s.Zero() enc = append(salt, enc...) return enc, true }
// Zero clears out the secret. The discussion for the util.Zero // function contains a more in-depth discussion on the security of // this. func (r *SecretRecord) Zero() { if r == nil { return } util.Zero(r.Secret) for k := range r.Metadata { util.Zero(r.Metadata[k]) } }
func loadStore(path string, m secret.ScryptMode) *store.SecretStore { passphrase, err := util.PassPrompt("Secrets passphrase> ") if err != nil { util.Errorf("Failed to read passphrase: %v", err) return nil } var passwords *store.SecretStore if ok, _ := util.Exists(path); ok { defer util.Zero(passphrase) fileData, err := util.ReadFile(path) if err != nil { util.Errorf("%v", err) return nil } var ok bool passwords, ok = store.UnmarshalSecretStore(fileData, passphrase, m) if !ok { return nil } return passwords } util.Errorf("could not find %s", path) return nil }
func initStore(path string, m secret.ScryptMode) error { passphrase, err := util.PassPrompt("Secrets passphrase> ") if err != nil { util.Errorf("Failed to read passphrase: %v", err) return err } if len(passphrase) == 0 { return fmt.Errorf("No passphrase provided.") } defer util.Zero(passphrase) passwords := store.NewSecretStore(passphrase) if passwords == nil { return fmt.Errorf("failed to create store") } fmt.Println("creating store...") fileData, ok := store.MarshalSecretStore(passwords, m) if !ok { return fmt.Errorf("failed to marshal store") } err = util.WriteFile(fileData, path) if err != nil { return err } passwords, ok = store.UnmarshalSecretStore(fileData, passphrase, m) if !ok { err = fmt.Errorf("failed to unmarshal store") } return err }
func storeSingleSecret(label string) error { r, ok := session.Store.Store[label] if ok { answer, err := util.ReadLine(label + " exists. Overwrite secret (y/n)? ") if err != nil { return err } answer = strings.ToLower(answer) if answer != "y" && answer != "yes" { fmt.Println("Not overwriting.") return nil } } else { r = new(store.SecretRecord) } password, err := readpass.PasswordPromptBytes("New password: "******"no password entered") } util.Zero(r.Secret) r.Secret = password r.Timestamp = time.Now().Unix() session.Store.Timestamp = r.Timestamp session.Store.Store[label] = r session.Dirty = true return nil }
// UnmarshalPrivate parses a byte slice into a private key. func UnmarshalPrivate(in []byte) (*PrivateKey, error) { priv := PrivateKey{ D: new([32]byte), S: new([64]byte), PublicKey: &PublicKey{ E: new([32]byte), V: new([32]byte), }, } var mkey struct { D []byte S []byte E []byte V []byte } dec := tlv.NewDecoder(in) err := dec.Decode(&mkey.D) if err != nil { return nil, err } err = dec.Decode(&mkey.S) if err != nil { return nil, err } err = dec.Decode(&mkey.E) if err != nil { return nil, err } err = dec.Decode(&mkey.V) if err != nil { return nil, err } copy(priv.D[:], mkey.D) copy(priv.S[:], mkey.S) copy(priv.E[:], mkey.E) copy(priv.V[:], mkey.V) util.Zero(mkey.D) util.Zero(mkey.S) util.Zero(mkey.E) util.Zero(mkey.V) return &priv, nil }
func TestPeerLookup(t *testing.T) { bob.PeerLookup = func(k *[ed25519.PublicKeySize]byte) bool { return false } conn := testio.NewBufferConn() sk, _, err := bob.NewSession() if err != nil { t.Fatalf("%v", err) } conn.WritePeer(sk[:]) _, err = carol.Dial(conn) if err != nil { t.Fatalf("%v", err) } var csk [SessionKeySize]byte _, err = conn.ReadClient(csk[:]) if err != nil { t.Fatalf("%v", err) } _, ok := bob.VerifySessionKey(&csk) if ok { t.Fatal("carol should not be trusted by bob") } bob.PeerLookup = func(k *[ed25519.PublicKeySize]byte) bool { return true } conn = testio.NewBufferConn() sk, _, err = bob.NewSession() if err != nil { t.Fatalf("%v", err) } conn.WritePeer(sk[:]) _, err = carol.Dial(conn) if err != nil { t.Fatalf("%v", err) } util.Zero(csk[:]) _, err = conn.ReadClient(csk[:]) if err != nil { t.Fatalf("%v", err) } _, ok = bob.VerifySessionKey(&csk) if !ok { t.Fatal("carol should be trusted by bob") } bob.PeerLookup = nil }
// DecryptAndVerify decrypts the message and verifies its signature. func DecryptAndVerify(priv *PrivateKey, pub *PublicKey, enc []byte) ([]byte, bool) { if !priv.Valid() || !pub.Valid() { return nil, false } if len(enc) < overhead { return nil, false } out, ok := decrypt(priv, enc) if !ok { return nil, false } var m, s []byte dec := tlv.NewDecoder(out) err := dec.Decode(&m) if err != nil { return nil, false } err = dec.Decode(&s) if err != nil { util.Zero(m) return nil, false } if dec.Length() != 0 { util.Zero(m) return nil, false } if len(s) != ed25519.SignatureSize { util.Zero(m) return nil, false } if !Verify(pub, m, s) { util.Zero(m) return nil, false } return m, true }
// GenerateKey returns a randomly generated secretbox key. Typically, // you should use DeriveKey to get a key from a passphrase // instead. Returns nil on failure. func GenerateKey() *[KeySize]byte { var key [KeySize]byte rb := util.RandBytes(KeySize) if rb == nil || len(rb) != KeySize { return nil } defer util.Zero(rb) copy(key[:], rb) return &key }
// DeriveKey applies Scrypt with very strong parameters to generate an // encryption key from a passphrase and salt. func DeriveKey(passphrase []byte, salt []byte) *[KeySize]byte { rawKey, err := scrypt.Key(passphrase, salt, scryptParams.N, scryptParams.r, scryptParams.p, KeySize) if err != nil { return nil } var key [KeySize]byte copy(key[:], rawKey) util.Zero(rawKey) return &key }
// Decrypt opens the secured message using the private key. func Decrypt(priv *PrivateKey, enc []byte) (message []byte, ok bool) { out, ok := decrypt(priv, enc) if !ok { return nil, false } defer util.Zero(out) var m []byte dec := tlv.NewDecoder(out) err := dec.Decode(&m) if err != nil { return nil, false } if dec.Length() != 0 { util.Zero(m) return nil, false } return m, true }
// DeriveKeyStrength applies Scrypt using the given work parameters // to generate an encryption key from a passphrase and salt. func DeriveKeyStrength(passphrase []byte, salt []byte, m ScryptMode) *[KeySize]byte { s := scryptMode[m] rawKey, err := scrypt.Key(passphrase, salt, s.N, s.r, s.p, KeySize) if err != nil { return nil } var key [KeySize]byte copy(key[:], rawKey) util.Zero(rawKey) return &key }
func loadStore(path string) *store.KeyStore { // If keystore is newly created, we'll want to write it to // disk before leaving this function. var flush bool if exists, _ := util.Exists(path); !exists { flush = true } passphrase, err := util.PassPrompt("keystore passphrase> ") if err != nil { util.Errorf("%v", err) return nil } defer util.Zero(passphrase) keystore, ok := store.LoadKeyStore(path, true) if !ok { fmt.Printf("error in LoadKeyStore") return nil } if !keystore.Valid(false) { fmt.Println("keystore not valid") return nil } if !flush { if !keystore.Unlock(passphrase) { return nil } return keystore } if !keystore.LockWith(passphrase) { util.Errorf("Failed to set initial passphrase.") return nil } else if !keystore.Unlock(passphrase) { util.Errorf("Flushing keystore failed.") return nil } out, err := keystore.Dump() if err != nil { log.Printf("WARNING: failed to dump keystore: %v", err) return nil } err = ioutil.WriteFile(path, out, 0644) if err != nil { log.Printf("WARNING: failed to write keystore: %v", err) } return keystore }
// UnlockKey recovers the secured private key with the passphrase. func UnlockKey(locked, passphrase []byte) (*PrivateKey, bool) { if len(locked) <= saltSize { return nil, false } salt := locked[:saltSize] locked = locked[saltSize:] key := secret.DeriveKey(passphrase, salt) defer util.Zero(key[:]) out, ok := secret.Decrypt(key, locked) if !ok { return nil, false } defer util.Zero(out) priv, err := UnmarshalPrivate(out) if err != nil { return nil, false } return priv, true }
func editEntry(ps *store.SecretStore, cfg *config) error { title := cfg.Args[0] if !ps.Has(title) { return errors.New("entry not found") } tmp, err := ioutil.TempFile("", "cu_journal") if err != nil { return err } fileName := tmp.Name() tmp.Close() defer os.Remove(fileName) err = ioutil.WriteFile(fileName, ps.Store[title].Secret, 0600) defer func() { err := os.Remove(fileName) if err != nil { fmt.Println("*** WARNING ***") fmt.Println("FAILED TO REMOVE TEMPORARY FILE", fileName) fmt.Println("You should remove this yourself.") fmt.Printf("\nThe reason: %v\n", err) } }() editor := cfg.Editor if editor == "" { editor = defaultEditor } args := strings.Split(editor, " ") args = append(args, fileName) cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { return err } fileData, err := ioutil.ReadFile(fileName) if err != nil { return err } util.Zero(ps.Store[title].Secret) ps.Store[title].Secret = fileData return nil }
// LockKey secures the private key with the passphrase, using Scrypt // and NaCl's secretbox. func LockKey(priv *PrivateKey, passphrase []byte) ([]byte, bool) { out, err := MarshalPrivate(priv) if err != nil { return nil, false } defer util.Zero(out) salt := util.RandBytes(saltSize) if salt == nil { return nil, false } key := secret.DeriveKey(passphrase, salt) defer util.Zero(key[:]) out, ok := secret.Encrypt(key, out) if !ok { return nil, false } out = append(salt, out...) return out, true }
// EncryptFile securely stores the encoded blob under the filename. func EncryptFile(filename string, passphrase, encoded []byte) (err error) { salt := util.RandBytes(SaltSize) if salt == nil { err = errors.New("password: failed to generate new salt") return } defer util.Zero(encoded) key := DeriveKey(passphrase, salt) data, ok := Encrypt(key, encoded) if !ok { data = nil err = errors.New("password: failed to encrypt data") return } data = append(salt, data...) err = ioutil.WriteFile(filename, data, 0600) return }
// ValidateYubiKey takes an Authenticator that is presumed to be a // YubiKey authenticator and attempts to validate the given OTP // using it. The YubiKey authenticator will always need to be updated // when successful to account for changes in the counter, and to // update the last OTP. func ValidateYubiKey(auth *Authenticator, otp string) (bool, error) { if (auth == nil) || (auth.Type != TypeYubiKey) { return false, ErrInvalidAuthenticator } if auth.Last == otp { return false, ErrValidationFail } config, err := ParseYubiKeyConfig(auth.Secret) if err != nil { return false, ErrInvalidAuthenticator } tmpKey := yubikey.NewKey(config.Key) defer util.Zero(tmpKey[:]) pub, ykOTP, err := yubikey.ParseOTPString(otp) if err != nil { return false, ErrValidationFail } if !bytes.Equal(pub, config.Public) { return false, ErrValidationFail } userToken, err := ykOTP.Parse(tmpKey) if err != nil { return false, ErrValidationFail } if getTokenCounter(userToken) < config.Counter { return false, ErrValidationFail } config.Counter = getTokenCounter(userToken) auth.Last = otp auth.Secret = config.Bytes() return true, nil }
func passwd(args []string) error { newPass, err := util.PassPrompt("New password: "******"Confirm: ") if err != nil { return err } if !bytes.Equal(confirmPass, newPass) { return errors.New("passwords don't match") } util.Zero(confirmPass) session.Store.ChangePassword(newPass) fmt.Println("[+] Password updated.") return nil }
func unlockStore(ks *store.KeyStore) bool { if !ks.Locked() { return true } passphrase, err := util.PassPrompt("keystore passphrase> ") if err != nil { util.Errorf("%v", err) return false } defer util.Zero(passphrase) if !ks.Locked() && ks.PrivateKey == nil { if !ks.LockWith(passphrase) { util.Errorf("Failed to set initial passphrase.") return false } } if !ks.Unlock(passphrase) { util.Errorf("Unlock failed (bad passphrase?)") return false } return true }
// Zero wipes the key. The session is no longer valid, and should be // discarded. func (s *Session) Zero() { util.Zero(s.shared) }
// Zero clears out the private key. The public key components will // remain intact. func (priv *PrivateKey) Zero() { util.Zero(priv.D[:]) util.Zero(priv.S[:]) priv.D = nil priv.S = nil }
// Zero wipes the Authenticator's secret. func (a *Authenticator) Zero() { util.Zero(a.Secret) }
func main() { flArmour := flag.Bool("a", false, "armour output") flOutDir := flag.String("o", ".", "output directory") flOutfile := flag.String("f", "passcrypt.enc", "pack file") flShowManifest := flag.Bool("l", false, "list the files in the archive") flUnpack := flag.Bool("u", false, "unpack the archive") flag.BoolVar(&verbose, "v", false, "verbose mode") flVersion := flag.Bool("V", false, "display version and exit") flag.Parse() if *flVersion { fmt.Println("passcrypt version", util.VersionString()) os.Exit(0) } if *flUnpack || *flShowManifest { if flag.NArg() != 1 { util.Errorf("Only one file may be unpacked at a time.\n") os.Exit(1) } in, err := ioutil.ReadFile(flag.Arg(0)) if err != nil { util.Errorf("%v\n", err) os.Exit(1) } if p, _ := pem.Decode(in); p != nil { if p.Type != header { util.Errorf("Wrong header for archive.\n") os.Exit(1) } in = p.Bytes } if len(in) <= saltLength { util.Errorf("Invalid archive.\n") os.Exit(1) } salt := in[:saltLength] in = in[saltLength:] passphrase, err := readpass.PasswordPromptBytes("Password: "******"%v\n", err) os.Exit(1) } key := secret.DeriveKey(passphrase, salt) if key == nil { util.Errorf("Failed to derive key.n\n") os.Exit(1) } in, ok := secret.Decrypt(key, in) if !ok { util.Errorf("Decryption failed.\n") os.Exit(1) } defer util.Zero(in) if *flUnpack { err = unpackFiles(in, *flOutDir) if err != nil { util.Errorf("%v\n", err) os.Exit(1) } } else if *flShowManifest { var files []File _, err := asn1.Unmarshal(in, &files) if err != nil { util.Errorf("%v\n", err) os.Exit(1) } fmt.Println("Manifest for", flag.Arg(0)) fmt.Printf("\n") for _, file := range files { fmt.Printf("\t%s", file.Path) if os.FileMode(file.Mode).IsDir() { fmt.Printf("/") } fmt.Printf("\n") } } return } if flag.NArg() == 0 { return } passphrase, err := readpass.PasswordPromptBytes("Password: "******"%v\n", err) os.Exit(1) } salt := util.RandBytes(saltLength) if salt == nil { util.Errorf("Failed to generate a random salt.\n") os.Exit(1) } key := secret.DeriveKey(passphrase, salt) if key == nil { util.Errorf("Failed to derive key.n\n") os.Exit(1) } out, err := packFiles(flag.Args()) if err != nil { util.Errorf("%v\n", err) os.Exit(1) } var ok bool out, ok = secret.Encrypt(key, out) if !ok { util.Errorf("Encryption failed.\n") os.Exit(1) } out = append(salt, out...) if *flArmour { p := &pem.Block{ Type: header, Bytes: out, } out = pem.EncodeToMemory(p) } err = ioutil.WriteFile(*flOutfile, out, 0644) if err != nil { util.Errorf("%v\n", err) os.Exit(1) } }
// Zero wipes the sensitive data from the store. See the discussion of // util.Zero for a more in-depth discussion on the subject. func (s *SecretStore) Zero() { for k := range s.Store { s.Store[k].Zero() } util.Zero(s.passphrase) }
// ChangePassword changes the password for the SecretStore; this will // take effect the next time the password store is marshalled. func (s *SecretStore) ChangePassword(newPass []byte) { util.Zero(s.passphrase) s.passphrase = make([]byte, len(newPass)) copy(s.passphrase, newPass) }