commit 78673c0967d39afcd2f889321e696a4b4baa0515 Author: ston1th Date: Tue Apr 12 23:03:22 2016 +0200 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a58068d --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2016, ston1th +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of the authors and/or contributors may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2767240 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# gomp - MasterPassword written in Go + +gomp is the golang implementation of the MasterPassword algorithm. + +## Sources +* http://masterpasswordapp.com/algorithm.html +* https://github.com/Lyndir/MasterPassword diff --git a/gomp.go b/gomp.go new file mode 100644 index 0000000..ad42ce3 --- /dev/null +++ b/gomp.go @@ -0,0 +1,151 @@ +package mp + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/binary" + "golang.org/x/crypto/scrypt" + "runtime" + "sync" +) + +type TemplateType int + +const ( + MaximumSecurityPassword TemplateType = iota + LongPassword + MediumPassword + BasicPassword + ShortPassword + PIN + Name + Phrase +) + +const variant = "com.lyndir.masterpassword" + +var ( + typeMap = map[TemplateType][]string{ + MaximumSecurityPassword: { + "anoxxxxxxxxxxxxxxxxx", + "axxxxxxxxxxxxxxxxxno", + }, + LongPassword: { + "CvcvnoCvcvCvcv", + "CvcvCvcvnoCvcv", + "CvcvCvcvCvcvno", + "CvccnoCvcvCvcv", + "CvccCvcvnoCvcv", + "CvccCvcvCvcvno", + "CvcvnoCvccCvcv", + "CvcvCvccnoCvcv", + "CvcvCvccCvcvno", + "CvcvnoCvcvCvcc", + "CvcvCvcvnoCvcc", + "CvcvCvcvCvccno", + "CvccnoCvccCvcv", + "CvccCvccnoCvcv", + "CvccCvccCvcvno", + "CvcvnoCvccCvcc", + "CvcvCvccnoCvcc", + "CvcvCvccCvccno", + "CvccnoCvcvCvcc", + "CvccCvcvnoCvcc", + "CvccCvcvCvccno", + }, + MediumPassword: { + "CvcnoCvc", + "CvcCvcno", + }, + BasicPassword: { + "aaanaaan", + "aannaaan", + "aaannaaa", + }, + ShortPassword: { + "Cvcn", + }, + PIN: { + "nnnn", + }, + Name: { + "cvccvcvcv", + }, + Phrase: { + "cvcc cvc cvccvcv cvc", + "cvc cvccvcvcv cvcv", + "cv cvccv cvc cvcvccv", + }, + } + charMap = map[rune]string{ + 'V': "AEIOU", + 'C': "BCDFGHJKLMNPQRSTVWXYZ", + 'v': "aeiou", + 'c': "bcdfghjklmnpqrstvwxyz", + 'A': "AEIOUBCDFGHJKLMNPQRSTVWXYZ", + 'a': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz", + 'n': "0123456789", + 'o': "@&%?,=[]_:-+*$#!'^~;()/.", + 'x': "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()", + ' ': " ", + } +) + +func i2b(i int) (b []byte) { + b = make([]byte, 4) + binary.BigEndian.PutUint32(b, uint32(i)) + return +} + +type MP struct { + sync.Mutex + key []byte +} + +func NewMP(p, name []byte) (*MP, error) { + salt := []byte(variant) + salt = append(salt, i2b(len(name))...) + salt = append(salt, name...) + key, err := scrypt.Key(p, salt, 32768, 8, 2, 64) + defer func() { + for i := range p { + p[i] = byte(0) + } + for i := range name { + name[i] = byte(0) + } + }() + if err != nil { + return nil, err + } + return &MP{key: key}, nil +} + +func (mp *MP) PrintPassword(site string, counter int, tempType TemplateType) { + mp.Lock() + defer mp.Unlock() + salt := []byte(variant) + salt = append(salt, i2b(len(site))...) + salt = append(salt, []byte(site)...) + salt = append(salt, i2b(counter)...) + mac := hmac.New(sha256.New, mp.key) + mac.Write(salt) + seed := mac.Sum(nil) + templates := typeMap[tempType] + temp := templates[int(seed[0])%len(templates)] + for i, c := range temp { + chars := charMap[c] + print(string(chars[int(seed[i+1])%len(chars)])) + } + print("\n") +} + +func (mp *MP) DestroyKey() { + mp.Lock() + for i := range mp.key { + mp.key[i] = byte(0) + } + mp.key = nil + runtime.GC() + mp.Unlock() +} diff --git a/gomp_test.go b/gomp_test.go new file mode 100644 index 0000000..711ccd8 --- /dev/null +++ b/gomp_test.go @@ -0,0 +1,18 @@ +package mp + +import "testing" + +func test(t *testing.T) { + pw := []byte("1") + name := []byte("1") + mp, _ := NewMP(pw, name) + mp.PrintPassword("1", 1, MaximumSecurityPassword) + mp.PrintPassword("1", 1, LongPassword) + mp.PrintPassword("1", 1, MediumPassword) + mp.PrintPassword("1", 1, BasicPassword) + mp.PrintPassword("1", 1, ShortPassword) + mp.PrintPassword("1", 1, PIN) + mp.PrintPassword("1", 1, Name) + mp.DestroyKey() + mp.PrintPassword("1", 1, Phrase) +}