Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
client
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Terraform modules
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
elixxir
client
Commits
0d9346db
Commit
0d9346db
authored
Mar 24, 2021
by
netrino
Browse files
Options
Downloads
Patches
Plain Diff
Initial vanitiy generator implementation
parent
eec4b550
No related branches found
No related tags found
No related merge requests found
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
api/client.go
+66
-37
66 additions, 37 deletions
api/client.go
api/user.go
+127
-0
127 additions, 0 deletions
api/user.go
cmd/init.go
+5
-0
5 additions, 0 deletions
cmd/init.go
cmd/root.go
+8
-2
8 additions, 2 deletions
cmd/root.go
with
206 additions
and
39 deletions
api/client.go
+
66
−
37
View file @
0d9346db
...
...
@@ -84,33 +84,12 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
protoUser
:=
createNewUser
(
rngStream
,
cmixGrp
,
e2eGrp
)
// Get current client version
currentVersion
,
err
:=
version
.
ParseVersion
(
SEMVER
)
if
err
!=
nil
{
return
errors
.
WithMessage
(
err
,
"Could not parse version string."
)
}
// Create Storage
passwordStr
:=
string
(
password
)
storageSess
,
err
:=
storage
.
New
(
storageDir
,
passwordStr
,
protoUser
,
currentVersion
,
cmixGrp
,
e2eGrp
,
rngStreamGen
)
err
=
checkVersionAndSetupStorage
(
def
,
storageDir
,
password
,
protoUser
,
cmixGrp
,
e2eGrp
,
rngStreamGen
,
false
,
registrationCode
)
if
err
!=
nil
{
return
err
}
// Save NDF to be used in the future
storageSess
.
SetBaseNDF
(
def
)
//store the registration code for later use
storageSess
.
SetRegCode
(
registrationCode
)
//move the registration state to keys generated
err
=
storageSess
.
ForwardRegistrationStatus
(
storage
.
KeyGenComplete
)
if
err
!=
nil
{
return
errors
.
WithMessage
(
err
,
"Failed to denote state "
+
"change in session"
)
}
//TODO: close the session
return
nil
}
...
...
@@ -135,29 +114,39 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, password [
protoUser
:=
createPrecannedUser
(
precannedID
,
rngStream
,
cmixGrp
,
e2eGrp
)
// Get current client version
c
urrentVersion
,
err
:=
version
.
ParseVersion
(
SEMVER
)
err
=
checkVersionAndSetupStorage
(
def
,
storageDir
,
password
,
protoUser
,
c
mixGrp
,
e2eGrp
,
rngStreamGen
,
true
,
""
)
if
err
!=
nil
{
return
errors
.
WithMessage
(
err
,
"Could not parse version string."
)
return
err
}
//TODO: close the session
return
nil
}
// Create Storage
passwordStr
:=
string
(
password
)
storageSess
,
err
:=
storage
.
New
(
storageDir
,
passwordStr
,
protoUser
,
currentVersion
,
cmixGrp
,
e2eGrp
,
rngStreamGen
)
// NewVanityClient creates a user with a receptionID that starts with the supplied prefix
// It creates client storage, generates keys, connects, and registers
// with the network. Note that this does not register a username/identity, but
// merely creates a new cryptographic identity for adding such information
// at a later date.
func
NewVanityClient
(
ndfJSON
,
storageDir
string
,
password
[]
byte
,
registrationCode
string
,
userIdPrefix
string
)
error
{
jww
.
INFO
.
Printf
(
"NewVanityClient()"
)
// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
rngStreamGen
:=
fastRNG
.
NewStreamGenerator
(
12
,
3
,
csprng
.
NewSystemRNG
)
rngStream
:=
rngStreamGen
.
GetStream
()
// Parse the NDF
def
,
err
:=
parseNDF
(
ndfJSON
)
if
err
!=
nil
{
return
err
}
cmixGrp
,
e2eGrp
:=
decodeGroups
(
def
)
// Save NDF to be used in the future
storageSess
.
SetBaseNDF
(
def
)
protoUser
:=
createNewVanityUser
(
rngStream
,
cmixGrp
,
e2eGrp
,
userIdPrefix
)
//move the registration state to indicate registered with permissioning
err
=
storageSess
.
ForwardRegistrationStatus
(
storage
.
PermissioningComplete
)
err
=
checkVersionAndSetupStorage
(
def
,
storageDir
,
password
,
protoUser
,
cmixGrp
,
e2eGrp
,
rngStreamGen
,
false
,
registrationCode
)
if
err
!=
nil
{
return
errors
.
WithMessage
(
err
,
"Failed to denote state "
+
"change in session"
)
return
err
}
//TODO: close the session
...
...
@@ -583,3 +572,43 @@ func decodeGroups(ndf *ndf.NetworkDefinition) (cmixGrp, e2eGrp *cyclic.Group) {
return
cmixGrp
,
e2eGrp
}
// checkVersionAndSetupStorage is common code shared by NewClient, NewPrecannedClient and NewVanityClient
// it checks client version and creates a new storage for user data
func
checkVersionAndSetupStorage
(
def
*
ndf
.
NetworkDefinition
,
storageDir
string
,
password
[]
byte
,
protoUser
user
.
User
,
cmixGrp
,
e2eGrp
*
cyclic
.
Group
,
rngStreamGen
*
fastRNG
.
StreamGenerator
,
isPrecanned
bool
,
registrationCode
string
)
error
{
// Get current client version
currentVersion
,
err
:=
version
.
ParseVersion
(
SEMVER
)
if
err
!=
nil
{
return
errors
.
WithMessage
(
err
,
"Could not parse version string."
)
}
// Create Storage
passwordStr
:=
string
(
password
)
storageSess
,
err
:=
storage
.
New
(
storageDir
,
passwordStr
,
protoUser
,
currentVersion
,
cmixGrp
,
e2eGrp
,
rngStreamGen
)
if
err
!=
nil
{
return
err
}
// Save NDF to be used in the future
storageSess
.
SetBaseNDF
(
def
)
if
!
isPrecanned
{
//store the registration code for later use
storageSess
.
SetRegCode
(
registrationCode
)
//move the registration state to keys generated
err
=
storageSess
.
ForwardRegistrationStatus
(
storage
.
KeyGenComplete
)
}
else
{
//move the registration state to indicate registered with permissioning
err
=
storageSess
.
ForwardRegistrationStatus
(
storage
.
PermissioningComplete
)
}
if
err
!=
nil
{
return
errors
.
WithMessage
(
err
,
"Failed to denote state "
+
"change in session"
)
}
return
nil
}
This diff is collapsed.
Click to expand it.
api/user.go
+
127
−
0
View file @
0d9346db
...
...
@@ -17,6 +17,10 @@ import (
"gitlab.com/xx_network/crypto/xx"
"gitlab.com/xx_network/primitives/id"
"math/rand"
"regexp"
"runtime"
"strings"
"sync"
)
const
(
...
...
@@ -134,3 +138,126 @@ func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.
ReceptionRSA
:
rsaKey
,
}
}
// createNewVanityUser generates an identity for cMix
// The identity's ReceptionID is not random but starts with the supplied prefix
func
createNewVanityUser
(
rng
csprng
.
Source
,
cmix
,
e2e
*
cyclic
.
Group
,
prefix
string
)
user
.
User
{
// CMIX Keygen
// FIXME: Why 256 bits? -- this is spec but not explained, it has
// to do with optimizing operations on one side and still preserves
// decent security -- cite this.
cMixKeyBytes
,
err
:=
csprng
.
GenerateInGroup
(
cmix
.
GetPBytes
(),
256
,
rng
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
// DH Keygen
// FIXME: Why 256 bits? -- this is spec but not explained, it has
// to do with optimizing operations on one side and still preserves
// decent security -- cite this. Why valid for BOTH e2e and cmix?
e2eKeyBytes
,
err
:=
csprng
.
GenerateInGroup
(
e2e
.
GetPBytes
(),
256
,
rng
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
// RSA Keygen (4096 bit defaults)
transmissionRsaKey
,
err
:=
rsa
.
GenerateKey
(
rng
,
rsa
.
DefaultRSABitLen
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
// Salt, UID, etc gen
transmissionSalt
:=
make
([]
byte
,
SaltSize
)
n
,
err
:=
csprng
.
NewSystemRNG
()
.
Read
(
transmissionSalt
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
if
n
!=
SaltSize
{
jww
.
FATAL
.
Panicf
(
"transmissionSalt size too small: %d"
,
n
)
}
transmissionID
,
err
:=
xx
.
NewID
(
transmissionRsaKey
.
GetPublic
(),
transmissionSalt
,
id
.
User
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
receptionRsaKey
,
err
:=
rsa
.
GenerateKey
(
rng
,
rsa
.
DefaultRSABitLen
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
var
mu
sync
.
Mutex
// just in case more than one go routine tries to access receptionSalt and receptionID
done
:=
make
(
chan
struct
{})
found
:=
make
(
chan
bool
)
wg
:=
&
sync
.
WaitGroup
{}
cores
:=
runtime
.
NumCPU
()
var
receptionSalt
[]
byte
var
receptionID
*
id
.
ID
pref
:=
prefix
ignoreCase
:=
false
// check if case-insensitivity is enabled
if
strings
.
HasPrefix
(
prefix
,
"(?i)"
)
{
pref
=
strings
.
ToLower
(
pref
[
4
:
])
ignoreCase
=
true
}
// Check if prefix contains valid Base64 characters
match
,
_
:=
regexp
.
MatchString
(
"^[A-Za-z0-9+/]+$"
,
pref
)
if
match
==
false
{
jww
.
FATAL
.
Panicf
(
"Prefix contains non-Base64 characters"
)
}
jww
.
INFO
.
Printf
(
"Vanity userID generation started. Prefix: %s Ignore-Case: %v NumCPU: %d"
,
pref
,
ignoreCase
,
cores
)
for
w
:=
0
;
w
<
cores
;
w
++
{
wg
.
Add
(
1
)
go
func
()
{
rSalt
:=
make
([]
byte
,
SaltSize
)
for
{
select
{
case
<-
done
:
defer
wg
.
Done
()
return
default
:
n
,
err
=
csprng
.
NewSystemRNG
()
.
Read
(
rSalt
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
if
n
!=
SaltSize
{
jww
.
FATAL
.
Panicf
(
"receptionSalt size too small: %d"
,
n
)
}
rID
,
err
:=
xx
.
NewID
(
receptionRsaKey
.
GetPublic
(),
rSalt
,
id
.
User
)
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
err
.
Error
())
}
id
:=
rID
.
String
()
if
ignoreCase
{
id
=
strings
.
ToLower
(
id
)
}
if
strings
.
HasPrefix
(
id
,
pref
)
{
mu
.
Lock
()
receptionID
=
rID
receptionSalt
=
rSalt
mu
.
Unlock
()
found
<-
true
defer
wg
.
Done
()
return
}
}
}
}()
}
// wait for a solution then close the done channel to signal the workers to exit
<-
found
close
(
done
)
wg
.
Wait
()
return
user
.
User
{
TransmissionID
:
transmissionID
.
DeepCopy
(),
TransmissionSalt
:
transmissionSalt
,
TransmissionRSA
:
transmissionRsaKey
,
ReceptionID
:
receptionID
.
DeepCopy
(),
ReceptionSalt
:
receptionSalt
,
ReceptionRSA
:
receptionRsaKey
,
Precanned
:
false
,
CmixDhPrivateKey
:
cmix
.
NewIntFromBytes
(
cMixKeyBytes
),
E2eDhPrivateKey
:
e2e
.
NewIntFromBytes
(
e2eKeyBytes
),
}
}
This diff is collapsed.
Click to expand it.
cmd/init.go
+
5
−
0
View file @
0d9346db
...
...
@@ -11,6 +11,7 @@ package cmd
import
(
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
jww
"github.com/spf13/jwalterweatherman"
)
...
...
@@ -29,5 +30,9 @@ var initCmd = &cobra.Command{
}
func
init
()
{
initCmd
.
Flags
()
.
StringP
(
"userid-prefix"
,
""
,
""
,
"Desired prefix of userID to brute force when running init command. Prepend (?i) for case-insensitive. Only Base64 characters are valid."
)
_
=
viper
.
BindPFlag
(
"userid-prefix"
,
initCmd
.
Flags
()
.
Lookup
(
"userid-prefix"
))
rootCmd
.
AddCommand
(
initCmd
)
}
This diff is collapsed.
Click to expand it.
cmd/root.go
+
8
−
2
View file @
0d9346db
...
...
@@ -306,7 +306,7 @@ func createClient() *api.Client {
storeDir
:=
viper
.
GetString
(
"session"
)
regCode
:=
viper
.
GetString
(
"regcode"
)
precannedID
:=
viper
.
GetUint
(
"sendid"
)
userIDprefix
:=
viper
.
GetString
(
"userid-prefix"
)
//create a new client if none exist
if
_
,
err
:=
os
.
Stat
(
storeDir
);
os
.
IsNotExist
(
err
)
{
// Load NDF
...
...
@@ -319,11 +319,17 @@ func createClient() *api.Client {
if
precannedID
!=
0
{
err
=
api
.
NewPrecannedClient
(
precannedID
,
string
(
ndfJSON
),
storeDir
,
[]
byte
(
pass
))
}
else
{
if
userIDprefix
!=
""
{
err
=
api
.
NewVanityClient
(
string
(
ndfJSON
),
storeDir
,
[]
byte
(
pass
),
regCode
,
userIDprefix
)
}
else
{
err
=
api
.
NewClient
(
string
(
ndfJSON
),
storeDir
,
[]
byte
(
pass
),
regCode
)
}
}
if
err
!=
nil
{
jww
.
FATAL
.
Panicf
(
"%+v"
,
err
)
}
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment