Commits

Markus Tzoe committed 6235ae7

import from hg

Comments (0)

Files changed (1)

+// Copyright 2011 <chaishushan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Password Generator
+//   usage: pwdgen site_name
+package main
+
+import (
+	"bufio"
+	"crypto/md5"
+	"crypto/sha512"
+	"encoding/base64"
+	"encoding/xml"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+	"time"
+)
+
+//-----------------------------------------------------------------------------
+
+// version
+const (
+	majorVersion = 1
+	minorVersion = 1
+	patchVersion = 0
+)
+
+// flags
+var flagConfigFile = flag.String("f", "config.xml", "config file")
+var flagOutputFile = flag.String("o", "", "output file")
+var flagPassword = flag.String("p", "", "global password")
+var flagShowVersion = flag.Bool("v", false, "show version")
+
+func usage() {
+	fmt.Fprintf(os.Stderr,
+		"usage: pwdgen [options] site\n",
+	)
+	flag.PrintDefaults()
+	os.Exit(2)
+}
+
+func input(str string) string {
+	fmt.Printf("%s", str)
+	reader := bufio.NewReader(os.Stdin)
+	input, _ := reader.ReadString('\n')
+	return strings.TrimSpace(input)
+}
+
+func parseFlags() {
+	flag.Usage = usage
+	flag.Parse()
+
+	if *flagShowVersion {
+		fmt.Printf("pwdgen version %d.%d.%d\n",
+			majorVersion, minorVersion, patchVersion,
+		)
+		os.Exit(0)
+	}
+
+	// site_name
+	if flag.NArg() < 1 {
+		usage()
+	}
+	// password
+	if len(*flagPassword) <= 0 {
+		(*flagPassword) = input("Password: ")
+		if len(*flagPassword) <= 0 {
+			usage()
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// base64 --> base62
+
+// 'i' --> ii
+// '+' --> ip
+// '/' --> is
+// '=' --> ie
+func base64ToBase62(src []byte) []byte {
+	dst := make([]byte, 2*len(src))
+	dstLen := 0
+
+	for len(src) > 0 {
+		switch src[0] {
+		case 'i':
+			dst[dstLen+0] = 'i'
+			dst[dstLen+1] = 'i'
+			dstLen += 2
+		case '+':
+			dst[dstLen+0] = 'i'
+			dst[dstLen+1] = 'p'
+			dstLen += 2
+		case '/':
+			dst[dstLen+0] = 'i'
+			dst[dstLen+1] = 's'
+			dstLen += 2
+		case '=':
+			dst[dstLen+0] = 'i'
+			dst[dstLen+1] = 'e'
+			dstLen += 2
+
+		default:
+			dst[dstLen+0] = src[0]
+			dstLen += 1
+		}
+		src = src[1:]
+	}
+
+	return dst[:dstLen]
+}
+
+//-----------------------------------------------------------------------------
+
+// pwd config struct
+type PwdConfig struct {
+	XMLName xml.Name `xml:"config"`
+
+	Salt string `xml:"salt"`
+
+	Site []struct {
+		Name string `xml:"name,attr"`
+		Url  string `xml:"url,attr"`
+
+		Id     string `xml:"id"`
+		Salt   string `xml:"salt"`
+		Length int    `xml:"length"`
+
+		pwd string
+	} `xml:"site"`
+}
+
+// load config
+func NewPwdConfig(filename string) *PwdConfig {
+	r, err := os.OpenFile(filename, os.O_RDONLY, 0)
+	if err != nil {
+		log.Fatalf("Open config file '%s' fail\n", filename)
+	}
+	defer r.Close()
+
+	var config *PwdConfig
+	if err := xml.NewDecoder(r).Decode(&config); err != nil {
+		log.Fatalf("Read config file '%s' fail\n", filename)
+	}
+
+	return config
+}
+
+// gen password
+// base64(sha(md5(pwd+pwd_salt)+site_id+site_salt))[0:len]
+func (self *PwdConfig) GenPassword(idx int, pwd string) {
+	md5Hash := md5.New()
+	shaHash := sha512.New()
+
+	// md5(pwd+pwd_salt)
+	pwd_salt := self.Salt
+	md5Hash.Write([]byte(pwd + pwd_salt))
+	// md5(pwd+pwd_salt)+site_id+site_salt
+	str := fmt.Sprintf("%x%s%s", md5Hash.Sum(nil),
+		self.Site[idx].Id, self.Site[idx].Salt,
+	)
+	// sha(md5(pwd+pwd_salt)+site_id+site_salt)
+	shaHash.Write([]byte(str))
+
+	// base64/62(...)
+	encLen := base64.StdEncoding.EncodedLen(len(shaHash.Sum(nil)))
+	encBuf := make([]byte, encLen)
+	base64.StdEncoding.Encode(encBuf, shaHash.Sum(nil))
+	encBuf = base64ToBase62(encBuf)
+
+	// pwd[0:len]
+	length := self.Site[idx].Length
+	self.Site[idx].pwd = string(encBuf[0:length])
+}
+
+// gen all password
+func (self *PwdConfig) GenAllPassword(passord string) {
+	for i := 0; i < len(self.Site); i++ {
+		self.GenPassword(i, passord)
+		time.Sleep(1e7)
+	}
+}
+
+// save password
+func (self *PwdConfig) SavePassword(filename, site_name string) {
+	writer := os.Stdout
+	if len(filename) > 0 {
+		flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
+		file, err := os.OpenFile(filename, flag, 0666)
+		if err != nil {
+			log.Fatalf("Create file '%s' fail\n", filename)
+		}
+		defer file.Close()
+		writer = file
+	}
+
+	if strings.HasSuffix(filename, ".csv") {
+		// KeePass 1.x format
+		// http://keepass.info/
+		// "Account","Login Name","Password","Web Site","Comments"
+		for i := 0; i < len(self.Site); i++ {
+			if site_name == "*all" || site_name == self.Site[i].Name {
+				fmt.Fprintf(writer, `"%s","%s","%s","%s","%s"%s`,
+					self.Site[i].Name,
+					self.Site[i].Id,
+					self.Site[i].pwd,
+					self.Site[i].Url,
+					"", "\n",
+				)
+			}
+		}
+	} else {
+		for i := 0; i < len(self.Site); i++ {
+			if site_name == "*all" || site_name == self.Site[i].Name {
+				fmt.Fprintf(writer, "%s\n", self.Site[i].Url)
+				fmt.Fprintf(writer, "  id: %s\n", self.Site[i].Id)
+				fmt.Fprintf(writer, "  pd: %s\n", self.Site[i].pwd)
+				fmt.Fprintf(writer, "\n")
+			}
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+// usage: pwdgen site_name
+func main() {
+	parseFlags()
+
+	conf := NewPwdConfig(*flagConfigFile)
+	conf.GenAllPassword(*flagPassword)
+	conf.SavePassword(*flagOutputFile, flag.Arg(0))
+}
+
+//-----------------------------------------------------------------------------