@ -0,0 +1,3 @@ |
|||||
|
keys.json |
||||
|
keys |
||||
|
web |
@ -0,0 +1 @@ |
|||||
|
# darkID-login-library-example |
@ -0,0 +1,75 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"fmt" |
||||
|
"net/http" |
||||
|
|
||||
|
"github.com/cryptoballot/rsablind" |
||||
|
"github.com/fatih/color" |
||||
|
|
||||
|
"gopkg.in/mgo.v2/bson" |
||||
|
) |
||||
|
|
||||
|
type User struct { |
||||
|
Id bson.ObjectId `json:"id" bson:"_id,omitempty"` |
||||
|
Email string `json:"email"` |
||||
|
Password string `json:"password"` |
||||
|
Token string `json:"token"` |
||||
|
} |
||||
|
|
||||
|
func Index(w http.ResponseWriter, r *http.Request) { |
||||
|
fmt.Fprintln(w, "clientApp") |
||||
|
} |
||||
|
|
||||
|
func Signup(w http.ResponseWriter, r *http.Request) { |
||||
|
|
||||
|
decoder := json.NewDecoder(r.Body) |
||||
|
var user User |
||||
|
err := decoder.Decode(&user) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
defer r.Body.Close() |
||||
|
|
||||
|
fmt.Print("user signup: ") |
||||
|
fmt.Println(user) |
||||
|
|
||||
|
jResp, err := json.Marshal(user) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
fmt.Fprintln(w, string(jResp)) |
||||
|
} |
||||
|
|
||||
|
func Login(w http.ResponseWriter, r *http.Request) { |
||||
|
|
||||
|
decoder := json.NewDecoder(r.Body) |
||||
|
var key Key |
||||
|
err := decoder.Decode(&key) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
defer r.Body.Close() |
||||
|
//TODO check if the user password exists in the database
|
||||
|
|
||||
|
fmt.Print("key login: ") |
||||
|
fmt.Println(key) |
||||
|
token, err := newToken() |
||||
|
check(err) |
||||
|
|
||||
|
//validate if the pubK darkID is in the blockchain
|
||||
|
|
||||
|
//verify that the darkID is signed
|
||||
|
if err := rsablind.VerifyBlindSignature(key.ServerVerifier, key.Hashed, key.UnblindedSig); err != nil { |
||||
|
fmt.Println(err) |
||||
|
} else { |
||||
|
color.Green("blind signature verified") |
||||
|
} |
||||
|
|
||||
|
/*jResp, err := json.Marshal(token) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
}*/ |
||||
|
fmt.Fprintln(w, string(token)) |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
{ |
||||
|
"port": "5000" |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"log" |
||||
|
"runtime" |
||||
|
) |
||||
|
|
||||
|
func check(err error) { |
||||
|
if err != nil { |
||||
|
_, fn, line, _ := runtime.Caller(1) |
||||
|
log.Println(line) |
||||
|
log.Println(fn) |
||||
|
log.Println(err) |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"crypto/sha256" |
||||
|
"encoding/base64" |
||||
|
) |
||||
|
|
||||
|
func hash(s string) string { |
||||
|
h := sha256.New() |
||||
|
h.Write([]byte(s)) |
||||
|
return base64.URLEncoding.EncodeToString(h.Sum(nil)) |
||||
|
} |
@ -0,0 +1,183 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"crypto/rsa" |
||||
|
"crypto/x509" |
||||
|
"encoding/asn1" |
||||
|
"encoding/json" |
||||
|
"encoding/pem" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"os" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/fatih/color" |
||||
|
) |
||||
|
|
||||
|
type Key struct { |
||||
|
ID string `json:"id"` |
||||
|
PrivK string `json:"privK"` //path of the PrivK file
|
||||
|
PubK string `json:"pubK"` //path of the PubK file
|
||||
|
Date time.Time `json:"date"` |
||||
|
Hashed []byte `json:"hashed"` |
||||
|
UnblindedSig []byte `json:"unblindedsig"` |
||||
|
Verified bool `json:"verified"` |
||||
|
ServerVerifier *rsa.PublicKey `json:"serververifier"` |
||||
|
SignerID string `json:"signerid"` |
||||
|
BlockchainRef string `json:"blockchainref"` |
||||
|
} |
||||
|
|
||||
|
func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string { |
||||
|
privkey_bytes := x509.MarshalPKCS1PrivateKey(privkey) |
||||
|
privkey_pem := pem.EncodeToMemory( |
||||
|
&pem.Block{ |
||||
|
Type: "RSA PRIVATE KEY", |
||||
|
Bytes: privkey_bytes, |
||||
|
}, |
||||
|
) |
||||
|
return string(privkey_pem) |
||||
|
} |
||||
|
|
||||
|
func ParseRsaPrivateKeyFromPemStr(privPEM string) (*rsa.PrivateKey, error) { |
||||
|
block, _ := pem.Decode([]byte(privPEM)) |
||||
|
if block == nil { |
||||
|
return nil, errors.New("failed to parse PEM block containing the key") |
||||
|
} |
||||
|
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return priv, nil |
||||
|
} |
||||
|
|
||||
|
func ExportRsaPublicKeyAsPemStr(pubkey rsa.PublicKey) (string, error) { |
||||
|
asn1Bytes, err := asn1.Marshal(pubkey) |
||||
|
check(err) |
||||
|
pubkey_pem := pem.EncodeToMemory( |
||||
|
&pem.Block{ |
||||
|
Type: "PUBLIC KEY", |
||||
|
Bytes: asn1Bytes, |
||||
|
}, |
||||
|
) |
||||
|
color.Red("pubkey_pem") |
||||
|
fmt.Println(pubkey_pem) |
||||
|
return string(pubkey_pem), nil |
||||
|
} |
||||
|
|
||||
|
func ParseRsaPublicKeyFromPemStr(pubPEM string) (pub rsa.PublicKey, err error) { |
||||
|
pemBlock, _ := pem.Decode([]byte(pubPEM)) |
||||
|
_, err = asn1.Unmarshal(pemBlock.Bytes, &pub) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func savePEMKey(fileName string, key *rsa.PrivateKey) { |
||||
|
outFile, err := os.Create(fileName) |
||||
|
check(err) |
||||
|
defer outFile.Close() |
||||
|
|
||||
|
var privateKey = &pem.Block{ |
||||
|
Type: "PRIVATE KEY", |
||||
|
Bytes: x509.MarshalPKCS1PrivateKey(key), |
||||
|
} |
||||
|
|
||||
|
err = pem.Encode(outFile, privateKey) |
||||
|
check(err) |
||||
|
} |
||||
|
func savePublicPEMKey(fileName string, pubkey rsa.PublicKey) { |
||||
|
asn1Bytes, err := asn1.Marshal(pubkey) |
||||
|
check(err) |
||||
|
|
||||
|
var pemkey = &pem.Block{ |
||||
|
Type: "PUBLIC KEY", |
||||
|
Bytes: asn1Bytes, |
||||
|
} |
||||
|
|
||||
|
pemfile, err := os.Create(fileName) |
||||
|
check(err) |
||||
|
defer pemfile.Close() |
||||
|
|
||||
|
err = pem.Encode(pemfile, pemkey) |
||||
|
check(err) |
||||
|
} |
||||
|
func openPEMKey(path string) (key *rsa.PrivateKey, err error) { |
||||
|
b, err := ioutil.ReadFile(path) |
||||
|
if err != nil { |
||||
|
fmt.Print(err) |
||||
|
} |
||||
|
key, err = ParseRsaPrivateKeyFromPemStr(string(b)) |
||||
|
return |
||||
|
} |
||||
|
func openPublicPEMKey(path string) (key rsa.PublicKey, err error) { |
||||
|
b, err := ioutil.ReadFile(path) |
||||
|
if err != nil { |
||||
|
fmt.Print(err) |
||||
|
} |
||||
|
key, err = ParseRsaPublicKeyFromPemStr(string(b)) |
||||
|
return |
||||
|
} |
||||
|
func readKeys() []Key { |
||||
|
path := keysDir + "/keys.json" |
||||
|
var keys []Key |
||||
|
|
||||
|
file, err := ioutil.ReadFile(path) |
||||
|
check(err) |
||||
|
content := string(file) |
||||
|
json.Unmarshal([]byte(content), &keys) |
||||
|
|
||||
|
return keys |
||||
|
} |
||||
|
|
||||
|
func saveKeys(keys []Key) { |
||||
|
jsonKeys, err := json.Marshal(keys) |
||||
|
check(err) |
||||
|
err = ioutil.WriteFile(keysDir+"/keys.json", jsonKeys, 0644) |
||||
|
check(err) |
||||
|
} |
||||
|
func saveKey(k Key) { |
||||
|
fmt.Println(k) |
||||
|
keys := readKeys() |
||||
|
for i, key := range keys { |
||||
|
if key.ID == k.ID { |
||||
|
keys[i] = k |
||||
|
} |
||||
|
} |
||||
|
saveKeys(keys) |
||||
|
} |
||||
|
func getKeyByKeyID(keyID string) (k Key) { |
||||
|
keys := readKeys() |
||||
|
for _, key := range keys { |
||||
|
if key.ID == keyID { |
||||
|
k = key |
||||
|
} |
||||
|
} |
||||
|
return k |
||||
|
} |
||||
|
func removeKey(keyID string, originalKeys []Key) (keys []Key) { |
||||
|
for _, key := range originalKeys { |
||||
|
if key.ID != keyID { |
||||
|
keys = append(keys, key) |
||||
|
} |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
func getServerPubK(url string) ownrsa.RSAPublicKey { |
||||
|
r, err := http.Get(url + "/") |
||||
|
check(err) |
||||
|
fmt.Println(r) |
||||
|
|
||||
|
decoder := json.NewDecoder(r.Body) |
||||
|
//var sigmaString string
|
||||
|
var pubK ownrsa.RSAPublicKey |
||||
|
err = decoder.Decode(&pubK) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
defer r.Body.Close() |
||||
|
color.Blue("received server pubK:") |
||||
|
fmt.Println(pubK) |
||||
|
return pubK |
||||
|
} |
||||
|
*/ |
@ -0,0 +1,24 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"io" |
||||
|
"log" |
||||
|
"os" |
||||
|
"strings" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func savelog() { |
||||
|
timeS := time.Now().String() |
||||
|
_ = os.Mkdir("logs", os.ModePerm) |
||||
|
//next 3 lines are to avoid windows filesystem errors
|
||||
|
timeS = strings.Replace(timeS, " ", "_", -1) |
||||
|
timeS = strings.Replace(timeS, ".", "-", -1) |
||||
|
timeS = strings.Replace(timeS, ":", "-", -1) |
||||
|
logFile, err := os.OpenFile("logs/log-"+timeS+".log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
mw := io.MultiWriter(os.Stdout, logFile) |
||||
|
log.SetOutput(mw) |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"log" |
||||
|
"net/http" |
||||
|
|
||||
|
"github.com/fatih/color" |
||||
|
"github.com/gorilla/handlers" |
||||
|
) |
||||
|
|
||||
|
const keysDir = "keys" |
||||
|
const keysize = 2048 |
||||
|
const hashize = 1536 |
||||
|
|
||||
|
func main() { |
||||
|
color.Blue("Starting darkID-libarary-login-example") |
||||
|
|
||||
|
readConfig("config.json") |
||||
|
fmt.Println(config) |
||||
|
|
||||
|
/*//create keys directory
|
||||
|
_ = os.Mkdir(keysDir, os.ModePerm)*/ |
||||
|
|
||||
|
initializeToken() |
||||
|
|
||||
|
//run thw webserver
|
||||
|
go GUI() |
||||
|
|
||||
|
//run API
|
||||
|
log.Println("api server running") |
||||
|
log.Print("port: ") |
||||
|
log.Println(config.Port) |
||||
|
router := NewRouter() |
||||
|
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Access-Control-Allow-Origin"}) |
||||
|
originsOk := handlers.AllowedOrigins([]string{"*"}) |
||||
|
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"}) |
||||
|
log.Fatal(http.ListenAndServe(":"+config.Port, handlers.CORS(originsOk, headersOk, methodsOk)(router))) |
||||
|
} |
||||
|
|
||||
|
func GUI() { |
||||
|
//here, run webserver
|
||||
|
log.Println("webserver in port " + "8080") |
||||
|
http.Handle("/", http.FileServer(http.Dir("./web"))) |
||||
|
http.ListenAndServe(":"+"8080", nil) |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"io/ioutil" |
||||
|
) |
||||
|
|
||||
|
//Config reads the config
|
||||
|
type Config struct { |
||||
|
Port string `json:"port"` |
||||
|
} |
||||
|
|
||||
|
var config Config |
||||
|
|
||||
|
func readConfig(path string) { |
||||
|
file, err := ioutil.ReadFile(path) |
||||
|
check(err) |
||||
|
content := string(file) |
||||
|
json.Unmarshal([]byte(content), &config) |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"log" |
||||
|
"net/http" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/gorilla/mux" |
||||
|
) |
||||
|
|
||||
|
type Route struct { |
||||
|
Name string |
||||
|
Method string |
||||
|
Pattern string |
||||
|
HandlerFunc http.HandlerFunc |
||||
|
} |
||||
|
|
||||
|
func Logger(inner http.Handler, name string) http.Handler { |
||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||
|
start := time.Now() |
||||
|
|
||||
|
inner.ServeHTTP(w, r) |
||||
|
|
||||
|
log.Printf( |
||||
|
"%s\t%s\t%s\t%s", |
||||
|
r.Method, |
||||
|
r.RequestURI, |
||||
|
name, |
||||
|
time.Since(start), |
||||
|
) |
||||
|
}) |
||||
|
} |
||||
|
func NewRouter() *mux.Router { |
||||
|
router := mux.NewRouter().StrictSlash(true) |
||||
|
for _, route := range routes { |
||||
|
var handler http.Handler |
||||
|
handler = route.HandlerFunc |
||||
|
handler = Logger(handler, route.Name) |
||||
|
|
||||
|
router. |
||||
|
Methods(route.Method). |
||||
|
Path(route.Pattern). |
||||
|
Name(route.Name). |
||||
|
Handler(handler) |
||||
|
} |
||||
|
return router |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
package main |
||||
|
|
||||
|
type Routes []Route |
||||
|
|
||||
|
var routes = Routes{ |
||||
|
Route{ |
||||
|
"Index", |
||||
|
"GET", |
||||
|
"/", |
||||
|
Index, |
||||
|
}, |
||||
|
Route{ |
||||
|
"Signup", |
||||
|
"POST", |
||||
|
"/signup", |
||||
|
Signup, |
||||
|
}, |
||||
|
Route{ |
||||
|
"Login", |
||||
|
"POST", |
||||
|
"/login", |
||||
|
Login, |
||||
|
}, |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
echo "" |
||||
|
echo "sending the signup, response:" |
||||
|
curl -X POST http://127.0.0.1:3130/signup -d '{"email": "user1@e.com", "password": "user1"}' |
||||
|
|
||||
|
echo "" |
||||
|
echo "sending the login, response:" |
||||
|
curl -X POST http://127.0.0.1:3130/login -d '{"email": "user1@e.com", "password": "user1"}' |
||||
|
|
||||
|
|
||||
|
echo "" |
||||
|
echo "send pubK and m to blind sign" |
||||
|
echo "json to send to the serverIDsigner:" |
||||
|
echo '{"pubKstring": {"e": "65537", "n": "139093"}, "m": "hola"}' |
||||
|
echo "serverIDsigner response:" |
||||
|
BLINDSIGNED=$(curl -X POST http://127.0.0.1:3130/blindsign -d '{"pubKstring": {"e": "65537", "n": "139093"}, "m": "hola"}') |
||||
|
echo "$BLINDSIGNED" |
||||
|
|
||||
|
echo "" |
||||
|
echo "send blindsigned to the serverIDsigner to verify" |
||||
|
curl -X POST http://127.0.0.1:3130/verifysign -d '{"m": "hola", "mSigned": "131898 40373 107552 34687"}' |
@ -0,0 +1,49 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"time" |
||||
|
|
||||
|
jwt "github.com/dgrijalva/jwt-go" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
signingKey = "this is the secret signing key" |
||||
|
) |
||||
|
|
||||
|
var createdToken string |
||||
|
|
||||
|
func initializeToken() { |
||||
|
var err error |
||||
|
createdToken, err = newToken() |
||||
|
if err != nil { |
||||
|
fmt.Println("Creating token failed") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func newToken() (string, error) { |
||||
|
signingKeyB := []byte(signingKey) |
||||
|
// Create the token
|
||||
|
token := jwt.New(jwt.SigningMethodHS256) |
||||
|
// Set some claims
|
||||
|
claims := make(jwt.MapClaims) |
||||
|
claims["foo"] = "bar" |
||||
|
claims["exp"] = time.Now().Add(time.Hour * 72).Unix() |
||||
|
token.Claims = claims |
||||
|
|
||||
|
// Sign and get the complete encoded token as a string
|
||||
|
tokenString, err := token.SignedString(signingKeyB) |
||||
|
return tokenString, err |
||||
|
} |
||||
|
|
||||
|
func parseToken(myToken string, myKey string) { |
||||
|
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) { |
||||
|
return []byte(myKey), nil |
||||
|
}) |
||||
|
|
||||
|
if err == nil && token.Valid { |
||||
|
fmt.Println("Your token is valid. I like your style.") |
||||
|
} else { |
||||
|
fmt.Println("This token is terrible! I cannot accept this.") |
||||
|
} |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
[{"constant":true,"inputs":[],"name":"getDarkID","outputs":[{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ID","outputs":[{"name":"pubK","type":"string"},{"name":"date","type":"string"},{"name":"hashed","type":"string"},{"name":"unblindedSig","type":"string"},{"name":"serverVerifier","type":"string"},{"name":"signerID","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_pubK","type":"string"},{"name":"_hashed","type":"string"},{"name":"_unblindedSig","type":"string"},{"name":"_serverVerifier","type":"string"},{"name":"_signerID","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] |
@ -0,0 +1,52 @@ |
|||||
|
- start node and blockchain syncing in testnet |
||||
|
``` |
||||
|
geth --testnet --datadir ~/eth-testnet |
||||
|
``` |
||||
|
|
||||
|
- create first account (wallet) |
||||
|
``` |
||||
|
geth --testnet --datadir ~/eth-testnet account new |
||||
|
``` |
||||
|
|
||||
|
- mine ethers in testnet |
||||
|
``` |
||||
|
geth --testnet --datadir ~/eth-testnet --mine |
||||
|
``` |
||||
|
|
||||
|
- serve dapp running on localhost:8000 via RPC |
||||
|
``` |
||||
|
geth --testnet --datadir ~/eth-testnet --rpc --rpccorsdomain "http://localhost:8000" --rpcapi eth,web3,personal |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
- generate ABI from contract.sol |
||||
|
``` |
||||
|
solcjs --abi HelloWorldContract.sol |
||||
|
``` |
||||
|
|
||||
|
- generate the byte code |
||||
|
``` |
||||
|
solcjs --bin HelloWorldContract.sol |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
- deploy contract via geth js console |
||||
|
- open geth console |
||||
|
``` |
||||
|
geth --testnet --datadir ~/eth-testnet console |
||||
|
``` |
||||
|
|
||||
|
- in the console create the contract using ABI and BYTE CODE result from compilation |
||||
|
```js |
||||
|
var abi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}]; |
||||
|
var code = '0x60606040527f48656c6c6f20576f726c64000000000000000000000000000000000000000000600090600019169055341561003957600080fd5b5b609d806100486000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314603d575b600080fd5b3415604757600080fd5b604d606b565b60405180826000191660001916815260200191505060405180910390f35b600054815600a165627a7a72305820f4c510e24a238337d5334b5b38a44e88ea53ef40f26aeb96eba4609cb72827cd0029'; |
||||
|
web3.personal.unlockAccount(eth.accounts[0], 'PASSWORD'); |
||||
|
var inputdata = ["pub", "h", "u", "sv", "signerid"]; |
||||
|
var contract = web3.eth.contract(abi).new(inputdata,{ from: eth.accounts[0], data: code, gas: 1000000 }); |
||||
|
web3.personal.lockAccount(eth.accounts[0]); |
||||
|
``` |
||||
|
|
||||
|
- call the method in the contract |
||||
|
```js |
||||
|
contract.name() |
||||
|
``` |