182 lines
5.3 KiB
Go
182 lines
5.3 KiB
Go
package users
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"go-nkode/customer"
|
|
"go-nkode/models"
|
|
"go-nkode/util"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type UserCipherKeys struct {
|
|
AlphaKey []uint64
|
|
SetKey []uint64
|
|
PassKey []uint64
|
|
MaskKey []uint64
|
|
Salt []byte
|
|
MaxNKodeLen int
|
|
}
|
|
|
|
func NewUserCipherKeys(keypadSize models.KeypadSize, setVals []uint64, maxNKodeLen int) (*UserCipherKeys, error) {
|
|
if len(setVals) != keypadSize.AttrsPerKey {
|
|
return nil, errors.New(fmt.Sprintf("setVals len != attrsPerKey, %d, %d", len(setVals), keypadSize.AttrsPerKey))
|
|
}
|
|
|
|
setKey, err := util.GenerateRandomNonRepeatingUint64(keypadSize.AttrsPerKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
setKey, err = util.XorLists(setKey, setVals)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
alphakey, _ := util.GenerateRandomNonRepeatingUint64(keypadSize.AttrsPerKey)
|
|
passKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
|
|
maskKey, _ := util.GenerateRandomNonRepeatingUint64(maxNKodeLen)
|
|
salt, _ := util.RandomBytes(10)
|
|
userCipherKeys := UserCipherKeys{
|
|
AlphaKey: alphakey,
|
|
PassKey: passKey,
|
|
MaskKey: maskKey,
|
|
SetKey: setKey,
|
|
Salt: salt,
|
|
MaxNKodeLen: maxNKodeLen,
|
|
}
|
|
return &userCipherKeys, nil
|
|
}
|
|
|
|
func (u *UserCipherKeys) PadUserMask(userMask []uint64, setVals []uint64) ([]uint64, error) {
|
|
if len(userMask) > u.MaxNKodeLen {
|
|
return nil, errors.New("user mask length exceeds max nkode length")
|
|
}
|
|
paddedUserMask := make([]uint64, len(userMask))
|
|
copy(paddedUserMask, userMask)
|
|
for i := len(userMask); i < u.MaxNKodeLen; i++ {
|
|
paddedUserMask = append(paddedUserMask, setVals[i%len(setVals)])
|
|
}
|
|
return paddedUserMask, nil
|
|
}
|
|
|
|
func (u *UserCipherKeys) ValidPassword(hashedPassword []byte, passcodeAttrIdx []int, customerAttrs customer.CustomerAttributes) error {
|
|
passcodeCipher := u.encipherCode(passcodeAttrIdx, customerAttrs)
|
|
passwordDigest, err := u.saltAndDigest(passcodeCipher)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = bcrypt.CompareHashAndPassword(hashedPassword, passwordDigest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u *UserCipherKeys) EncipherSaltHashCode(passcodeAttrIdx []int, customerAttrs customer.CustomerAttributes) ([]byte, error) {
|
|
passcodeCipher := u.encipherCode(passcodeAttrIdx, customerAttrs)
|
|
|
|
passcodeDigest, err := u.saltAndDigest(passcodeCipher)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return u.hashPasscode(passcodeDigest)
|
|
}
|
|
|
|
func (u *UserCipherKeys) encipherCode(passcodeAttrIdx []int, customerAttrs customer.CustomerAttributes) []uint64 {
|
|
passcodeLen := len(passcodeAttrIdx)
|
|
|
|
passcodeCipher := make([]uint64, u.MaxNKodeLen)
|
|
|
|
for idx := 0; idx < passcodeLen; idx++ {
|
|
attrIdx := passcodeAttrIdx[idx]
|
|
alpha := u.AlphaKey[attrIdx]
|
|
attrVal := customerAttrs.AttrVals[idx]
|
|
pass := u.PassKey[idx]
|
|
passcodeCipher[idx] = alpha ^ pass ^ attrVal
|
|
}
|
|
return passcodeCipher
|
|
}
|
|
|
|
func (u *UserCipherKeys) saltAndDigest(passcode []uint64) ([]byte, error) {
|
|
passcodeBytes := util.Uint64ArrToByteArr(passcode)
|
|
passcodeBytes = append(passcodeBytes, u.Salt...)
|
|
h := sha256.New()
|
|
passcodeSha, err := h.Write(passcodeBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
passcodeDigest := uint64(passcodeSha)
|
|
return util.Uint64ArrToByteArr([]uint64{passcodeDigest}), nil
|
|
}
|
|
|
|
func (u *UserCipherKeys) hashPasscode(passcodeDigest []byte) ([]byte, error) {
|
|
|
|
hashedPassword, err := bcrypt.GenerateFromPassword(passcodeDigest, bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return hashedPassword, nil
|
|
}
|
|
func (u *UserCipherKeys) EncipherMask(passcodeSet []uint64, customerAttrs customer.CustomerAttributes) (string, error) {
|
|
paddedPasscodeSets, err := u.PadUserMask(passcodeSet, customerAttrs.SetVals)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
cipheredMask := make([]uint64, len(paddedPasscodeSets))
|
|
for idx := range paddedPasscodeSets {
|
|
setIdx := customerAttrs.IndexOfSet(paddedPasscodeSets[idx])
|
|
setKeyVal := u.SetKey[setIdx]
|
|
maskKeyVal := u.MaskKey[idx]
|
|
setVal := paddedPasscodeSets[idx]
|
|
cipheredMask[idx] = setKeyVal ^ maskKeyVal ^ setVal
|
|
}
|
|
mask := util.EncodeBase64Str(cipheredMask)
|
|
return mask, nil
|
|
}
|
|
|
|
func (u *UserCipherKeys) DecipherMask(mask string, setVals []uint64, passcodeLen int) ([]uint64, error) {
|
|
decodedMask, err := util.DecodeBase64Str(mask)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
decipheredMask, err := util.XorLists(decodedMask, u.MaskKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
setKeyRandComponent, err := util.XorLists(setVals, u.SetKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
passcodeSet := make([]uint64, passcodeLen)
|
|
for idx, setCipher := range decipheredMask[:passcodeLen] {
|
|
setIdx := util.IndexOf(setKeyRandComponent, setCipher)
|
|
passcodeSet[idx] = setVals[setIdx]
|
|
}
|
|
return passcodeSet, nil
|
|
}
|
|
|
|
func (u *UserCipherKeys) EncipherNKode(passcodeAttrIdx []int, customerAttrs customer.CustomerAttributes) (*models.EncipheredNKode, error) {
|
|
code, err := u.EncipherSaltHashCode(passcodeAttrIdx, customerAttrs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
passcodeSet := make([]uint64, len(passcodeAttrIdx))
|
|
|
|
for idx := range passcodeSet {
|
|
passcodeAttr := customerAttrs.AttrVals[passcodeAttrIdx[idx]]
|
|
passcodeSet[idx], err = customerAttrs.GetAttrSetVal(passcodeAttr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
mask, err := u.EncipherMask(passcodeSet, customerAttrs)
|
|
encipheredCode := models.EncipheredNKode{
|
|
Code: string(code),
|
|
Mask: mask,
|
|
}
|
|
return &encipheredCode, nil
|
|
}
|