149 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package sprig
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto/dsa"
 | 
						|
	"crypto/ecdsa"
 | 
						|
	"crypto/elliptic"
 | 
						|
	"crypto/hmac"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/rsa"
 | 
						|
	"crypto/sha256"
 | 
						|
	"crypto/x509"
 | 
						|
	"encoding/asn1"
 | 
						|
	"encoding/binary"
 | 
						|
	"encoding/hex"
 | 
						|
	"encoding/pem"
 | 
						|
	"fmt"
 | 
						|
	"math/big"
 | 
						|
 | 
						|
	uuid "github.com/satori/go.uuid"
 | 
						|
	"golang.org/x/crypto/scrypt"
 | 
						|
)
 | 
						|
 | 
						|
func sha256sum(input string) string {
 | 
						|
	hash := sha256.Sum256([]byte(input))
 | 
						|
	return hex.EncodeToString(hash[:])
 | 
						|
}
 | 
						|
 | 
						|
// uuidv4 provides a safe and secure UUID v4 implementation
 | 
						|
func uuidv4() string {
 | 
						|
	return fmt.Sprintf("%s", uuid.NewV4())
 | 
						|
}
 | 
						|
 | 
						|
var master_password_seed = "com.lyndir.masterpassword"
 | 
						|
 | 
						|
var password_type_templates = map[string][][]byte{
 | 
						|
	"maximum": {[]byte("anoxxxxxxxxxxxxxxxxx"), []byte("axxxxxxxxxxxxxxxxxno")},
 | 
						|
	"long": {[]byte("CvcvnoCvcvCvcv"), []byte("CvcvCvcvnoCvcv"), []byte("CvcvCvcvCvcvno"), []byte("CvccnoCvcvCvcv"), []byte("CvccCvcvnoCvcv"),
 | 
						|
		[]byte("CvccCvcvCvcvno"), []byte("CvcvnoCvccCvcv"), []byte("CvcvCvccnoCvcv"), []byte("CvcvCvccCvcvno"), []byte("CvcvnoCvcvCvcc"),
 | 
						|
		[]byte("CvcvCvcvnoCvcc"), []byte("CvcvCvcvCvccno"), []byte("CvccnoCvccCvcv"), []byte("CvccCvccnoCvcv"), []byte("CvccCvccCvcvno"),
 | 
						|
		[]byte("CvcvnoCvccCvcc"), []byte("CvcvCvccnoCvcc"), []byte("CvcvCvccCvccno"), []byte("CvccnoCvcvCvcc"), []byte("CvccCvcvnoCvcc"),
 | 
						|
		[]byte("CvccCvcvCvccno")},
 | 
						|
	"medium": {[]byte("CvcnoCvc"), []byte("CvcCvcno")},
 | 
						|
	"short":  {[]byte("Cvcn")},
 | 
						|
	"basic":  {[]byte("aaanaaan"), []byte("aannaaan"), []byte("aaannaaa")},
 | 
						|
	"pin":    {[]byte("nnnn")},
 | 
						|
}
 | 
						|
 | 
						|
var template_characters = map[byte]string{
 | 
						|
	'V': "AEIOU",
 | 
						|
	'C': "BCDFGHJKLMNPQRSTVWXYZ",
 | 
						|
	'v': "aeiou",
 | 
						|
	'c': "bcdfghjklmnpqrstvwxyz",
 | 
						|
	'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ",
 | 
						|
	'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz",
 | 
						|
	'n': "0123456789",
 | 
						|
	'o': "@&%?,=[]_:-+*$#!'^~;()/.",
 | 
						|
	'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()",
 | 
						|
}
 | 
						|
 | 
						|
func derivePassword(counter uint32, password_type, password, user, site string) string {
 | 
						|
	var templates = password_type_templates[password_type]
 | 
						|
	if templates == nil {
 | 
						|
		return fmt.Sprintf("cannot find password template %s", password_type)
 | 
						|
	}
 | 
						|
 | 
						|
	var buffer bytes.Buffer
 | 
						|
	buffer.WriteString(master_password_seed)
 | 
						|
	binary.Write(&buffer, binary.BigEndian, uint32(len(user)))
 | 
						|
	buffer.WriteString(user)
 | 
						|
 | 
						|
	salt := buffer.Bytes()
 | 
						|
	key, err := scrypt.Key([]byte(password), salt, 32768, 8, 2, 64)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Sprintf("failed to derive password: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	buffer.Truncate(len(master_password_seed))
 | 
						|
	binary.Write(&buffer, binary.BigEndian, uint32(len(site)))
 | 
						|
	buffer.WriteString(site)
 | 
						|
	binary.Write(&buffer, binary.BigEndian, counter)
 | 
						|
 | 
						|
	var hmacv = hmac.New(sha256.New, key)
 | 
						|
	hmacv.Write(buffer.Bytes())
 | 
						|
	var seed = hmacv.Sum(nil)
 | 
						|
	var temp = templates[int(seed[0])%len(templates)]
 | 
						|
 | 
						|
	buffer.Truncate(0)
 | 
						|
	for i, element := range temp {
 | 
						|
		pass_chars := template_characters[element]
 | 
						|
		pass_char := pass_chars[int(seed[i+1])%len(pass_chars)]
 | 
						|
		buffer.WriteByte(pass_char)
 | 
						|
	}
 | 
						|
 | 
						|
	return buffer.String()
 | 
						|
}
 | 
						|
 | 
						|
func generatePrivateKey(typ string) string {
 | 
						|
	var priv interface{}
 | 
						|
	var err error
 | 
						|
	switch typ {
 | 
						|
	case "", "rsa":
 | 
						|
		// good enough for government work
 | 
						|
		priv, err = rsa.GenerateKey(rand.Reader, 4096)
 | 
						|
	case "dsa":
 | 
						|
		key := new(dsa.PrivateKey)
 | 
						|
		// again, good enough for government work
 | 
						|
		if err = dsa.GenerateParameters(&key.Parameters, rand.Reader, dsa.L2048N256); err != nil {
 | 
						|
			return fmt.Sprintf("failed to generate dsa params: %s", err)
 | 
						|
		}
 | 
						|
		err = dsa.GenerateKey(key, rand.Reader)
 | 
						|
		priv = key
 | 
						|
	case "ecdsa":
 | 
						|
		// again, good enough for government work
 | 
						|
		priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	default:
 | 
						|
		return "Unknown type " + typ
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Sprintf("failed to generate private key: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return string(pem.EncodeToMemory(pemBlockForKey(priv)))
 | 
						|
}
 | 
						|
 | 
						|
type DSAKeyFormat struct {
 | 
						|
	Version       int
 | 
						|
	P, Q, G, Y, X *big.Int
 | 
						|
}
 | 
						|
 | 
						|
func pemBlockForKey(priv interface{}) *pem.Block {
 | 
						|
	switch k := priv.(type) {
 | 
						|
	case *rsa.PrivateKey:
 | 
						|
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
 | 
						|
	case *dsa.PrivateKey:
 | 
						|
		val := DSAKeyFormat{
 | 
						|
			P: k.P, Q: k.Q, G: k.G,
 | 
						|
			Y: k.Y, X: k.X,
 | 
						|
		}
 | 
						|
		bytes, _ := asn1.Marshal(val)
 | 
						|
		return &pem.Block{Type: "DSA PRIVATE KEY", Bytes: bytes}
 | 
						|
	case *ecdsa.PrivateKey:
 | 
						|
		b, _ := x509.MarshalECPrivateKey(k)
 | 
						|
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
 | 
						|
	default:
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
}
 |