/* * Copyright (c) 2014, Yawning Angel * 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. * * 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. */ package obfs4 import ( "encoding/json" "fmt" "io/ioutil" "os" "path" "strconv" "git.torproject.org/pluggable-transports/goptlib.git" "git.torproject.org/pluggable-transports/obfs4.git/common/csrand" "git.torproject.org/pluggable-transports/obfs4.git/common/drbg" "git.torproject.org/pluggable-transports/obfs4.git/common/ntor" ) const ( stateFile = "obfs4_state.json" ) type jsonServerState struct { NodeID string `json:"node-id"` PrivateKey string `json:"private-key"` PublicKey string `json:"public-key"` DrbgSeed string `json:"drbg-seed"` IATMode int `json:"iat-mode"` } type obfs4ServerState struct { nodeID *ntor.NodeID identityKey *ntor.Keypair drbgSeed *drbg.Seed iatMode int } func serverStateFromArgs(stateDir string, args *pt.Args) (*obfs4ServerState, error) { var js jsonServerState var nodeIDOk, privKeyOk, seedOk bool js.NodeID, nodeIDOk = args.Get(nodeIDArg) js.PrivateKey, privKeyOk = args.Get(privateKeyArg) js.DrbgSeed, seedOk = args.Get(seedArg) iatStr, iatOk := args.Get(iatArg) if !privKeyOk && !nodeIDOk && !seedOk && !iatOk { if err := jsonServerStateFromFile(stateDir, &js); err != nil { return nil, err } } else if !privKeyOk { return nil, fmt.Errorf("missing argument '%s'", privateKeyArg) } else if !nodeIDOk { return nil, fmt.Errorf("missing argument '%s'", nodeIDArg) } else if !seedOk { return nil, fmt.Errorf("missing argument '%s'", seedArg) } else if !iatOk { // Disable IAT if not specified. return nil, fmt.Errorf("missing argument '%s'", iatArg) } else { // Parse and validate the iat-mode argument. iatMode, err := strconv.Atoi(iatStr) if err != nil { return nil, fmt.Errorf("malformed iat-mode '%s'", iatStr) } js.IATMode = iatMode } return serverStateFromJSONServerState(&js) } func serverStateFromJSONServerState(js *jsonServerState) (*obfs4ServerState, error) { var err error st := new(obfs4ServerState) if st.nodeID, err = ntor.NodeIDFromHex(js.NodeID); err != nil { return nil, err } if st.identityKey, err = ntor.KeypairFromHex(js.PrivateKey); err != nil { return nil, err } if st.drbgSeed, err = drbg.SeedFromHex(js.DrbgSeed); err != nil { return nil, err } if js.IATMode < iatNone || js.IATMode > iatParanoid { return nil, fmt.Errorf("invalid iat-mode '%d'", js.IATMode) } st.iatMode = js.IATMode return st, nil } func jsonServerStateFromFile(stateDir string, js *jsonServerState) error { fPath := path.Join(stateDir, stateFile) f, err := ioutil.ReadFile(fPath) if err != nil { if os.IsNotExist(err) { if err = newJSONServerState(stateDir, js); err == nil { return nil } } return err } if err = json.Unmarshal(f, js); err != nil { return fmt.Errorf("failed to load statefile '%s': %s", fPath, err) } return nil } func newJSONServerState(stateDir string, js *jsonServerState) (err error) { // Generate everything a server needs, using the cryptographic PRNG. var st obfs4ServerState rawID := make([]byte, ntor.NodeIDLength) if err = csrand.Bytes(rawID); err != nil { return } if st.nodeID, err = ntor.NewNodeID(rawID); err != nil { return } if st.identityKey, err = ntor.NewKeypair(false); err != nil { return } if st.drbgSeed, err = drbg.NewSeed(); err != nil { return } st.iatMode = iatNone // Encode it into JSON format and write the state file. js.NodeID = st.nodeID.Hex() js.PrivateKey = st.identityKey.Private().Hex() js.PublicKey = st.identityKey.Public().Hex() js.DrbgSeed = st.drbgSeed.Hex() js.IATMode = st.iatMode var encoded []byte if encoded, err = json.Marshal(js); err != nil { return } if err = ioutil.WriteFile(path.Join(stateDir, stateFile), encoded, 0600); err != nil { return err } return nil }