Skip to content

Commit

Permalink
Allow server to be marshaled to string
Browse files Browse the repository at this point in the history
  • Loading branch information
mcrute committed Jan 13, 2020
1 parent ed36aba commit 99a83be
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 3 deletions.
89 changes: 88 additions & 1 deletion srp.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,93 @@ type Server struct {
xM []byte
}

// Marshal returns a string encoding of the Server. This encoded string can be stored by the
// server for use later in the SRP process in the case that the client and server can not
// maintain a session and thus a live copy of the Server struct.
func (s *Server) Marshal() string {
return strings.Join([]string{
strconv.Itoa(s.s.FieldSize()),
strconv.FormatUint(uint64(s.s.h), 10),
hex.EncodeToString(s.i),
hex.EncodeToString(s.salt),
s.v.Text(10),
s.xB.Text(10),
hex.EncodeToString(s.xK),
hex.EncodeToString(s.xM),
}, ":")
}

// UnmarshalServer parses the encoded string generated by Marshal and returns a populated
// Server struct with the data if possible, otherwise it returns an error.
func UnmarshalServer(s string) (*Server, error) {
p := strings.Split(s, ":")
if len(p) != 8 {
return nil, fmt.Errorf("unmarshal: malformed fields exp 8, saw %d", len(p))
}

sz, err := strconv.Atoi(p[0])
if err != nil || sz <= 0 {
return nil, fmt.Errorf("unmarshal: malformed field size %s", p[0])
}
pf, ok := pflist[sz]
if !ok {
return nil, fmt.Errorf("unmarshal: invalid prime-field size: %d", sz)
}

h, err := strconv.Atoi(p[1])
if err != nil || h <= 0 {
return nil, fmt.Errorf("unmarshal: malformed field size %s", p[1])
}

hf := crypto.Hash(h)
if !hf.Available() {
return nil, fmt.Errorf("unmarshal: hash algorithm %d unavailable", h)
}

i, err := hex.DecodeString(p[2])
if err != nil {
return nil, fmt.Errorf("unmarshal: invalid identity: %s", p[2])
}

salt, err := hex.DecodeString(p[3])
if err != nil {
return nil, fmt.Errorf("unmarshal: invalid salt: %s", p[3])
}

v := atobi(p[4], 10)
if r := recover(); r != nil {
return nil, fmt.Errorf("unmarshal: invalid verifier: %s", p[4])
}

B := atobi(p[5], 10)
if r := recover(); r != nil {
return nil, fmt.Errorf("unmarshal: invalid ephemeral key B: %s", p[5])
}

K, err := hex.DecodeString(p[6])
if err != nil {
return nil, fmt.Errorf("unmarshal: invalid key: %s", p[6])
}

M, err := hex.DecodeString(p[7])
if err != nil {
return nil, fmt.Errorf("unmarshal: invalid M: %s", p[7])
}

return &Server{
s: &SRP{
h: hf,
pf: pf,
},
i: i,
salt: salt,
v: v,
xB: B,
xK: K,
xM: M,
}, nil
}

// NewServer constructs a Server instance for computing a shared secret.
func (s *SRP) NewServer(v *Verifier, A *big.Int) (*Server, error) {

Expand Down Expand Up @@ -595,7 +682,7 @@ func randbytes(n int) []byte {
// Generate and return a bigInt 'bits' bits in length
func randBigInt(bits int) *big.Int {
n := bits / 8
if (bits%8) != 0 {
if (bits % 8) != 0 {
n += 1
}
b := randbytes(n)
Expand Down
9 changes: 7 additions & 2 deletions srp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,21 @@ func (db *userdb) verify(t *testing.T, user, pass []byte, goodPw bool) {
sCreds := srv.Credentials()

// Server --> sends 'sCreds' to client
// Marshal the server for use later as-if the client can't remain connected
srv_m := srv.Marshal()

// Client generates a mutual auth and sends to server
mauth, err := c.Generate(sCreds)
assert(err == nil, "Client.Generate: %s", err)

// Client --> sends 'mauth' to server
// Unmarshal the previously marshaled server for use after the client reconnects
srv_um, err := UnmarshalServer(srv_m)
assert(err == nil, "UnmarshalServer: %s", err)

// Server validates the mutual authenticator and creates its proof of having derived
// the same key. This proof is sent to the client.
proof, ok := srv.ClientOk(mauth)
proof, ok := srv_um.ClientOk(mauth)
if goodPw {
assert(ok, "server: bad client proof")
} else {
Expand All @@ -128,7 +133,7 @@ func (db *userdb) verify(t *testing.T, user, pass []byte, goodPw bool) {
// mutual secret -- which should be identical

kc := c.RawKey()
ks := srv.RawKey()
ks := srv_um.RawKey()

assert(subtle.ConstantTimeCompare(kc, ks) == 1, "key mismatch;\nclient %x, server %x", kc, ks)
}
Expand Down

0 comments on commit 99a83be

Please sign in to comment.