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
|
||
|
}
|
||
|
}
|