Skip to content

Commit

Permalink
crypto module
Browse files Browse the repository at this point in the history
  • Loading branch information
moliva committed Jan 22, 2020
1 parent f1a4735 commit 7fa9557
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 6 deletions.
8 changes: 8 additions & 0 deletions crypto/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package(default_visibility = ["//visibility:public"])

exports_files(
[
"crypto.package.json",
],
visibility = ["//visibility:public"],
)
7 changes: 7 additions & 0 deletions crypto/crypto.package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "decentraland-crypto",
"description": "Decentraland Cryptography",
"dependencies": {
"eth-crypto": "^1.5.0"
}
}
125 changes: 125 additions & 0 deletions crypto/src/Authenticator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { hash, sign, recover } from "eth-crypto";
import { AuthChain, EthAddress, AuthLinkType, IdentityType, AuditInfo, AuthLink, Signature } from "./types";

export class Authenticator {
/** Validate that the signature belongs to the Ethereum address */
static async validateSignature(expectedFinalAuthority: string, authChain: AuthChain): Promise<boolean> {
let currentAuthority: string = "";
authChain.forEach(authLink => {
const validator: ValidatorType = getValidatorByType(authLink.type);
const { error, nextAuthority } = validator(currentAuthority, authLink);
if (error) {
return false;
}
currentAuthority = nextAuthority ?? "";
});
return currentAuthority === expectedFinalAuthority;
}

static createEthereumMessageHash(msg: string) {
let msgWithPrefix: string = `\x19Ethereum Signed Message:\n${msg.length}${msg}`;
const msgHash = hash.keccak256(msgWithPrefix);
return msgHash;
}

static createSimpleAuthChain(finalPayload: string, ownerAddress: EthAddress, signature: Signature): AuthChain {
return [
{
type: AuthLinkType.SIGNER,
payload: ownerAddress,
signature: ""
},
{
type: AuthLinkType.ECDSA_SIGNED_ENTITY,
payload: finalPayload,
signature: signature
}
];
}

static createAuthChain(ownerIdentity: IdentityType, ephemeralIdentity: IdentityType, ephemeralMinutesDuration: number, entityId: string): AuthChain {
let expiration = new Date();
expiration.setMinutes(expiration.getMinutes() + ephemeralMinutesDuration);

const ephemeralMessage = `Decentraland Login\nEphemeral address: ${ephemeralIdentity.address}\nExpiration: ${expiration}`;
const firstSignature = Authenticator.createSignature(ownerIdentity, ephemeralMessage);
const secondSignature = Authenticator.createSignature(ephemeralIdentity, entityId);

const authChain: AuthChain = [
{ type: AuthLinkType.SIGNER, payload: ownerIdentity.address, signature: "" },
{ type: AuthLinkType.ECDSA_EPHEMERAL, payload: ephemeralMessage, signature: firstSignature },
{ type: AuthLinkType.ECDSA_SIGNED_ENTITY, payload: entityId, signature: secondSignature }
];

return authChain;
}

static createSignature(identity: IdentityType, message: string) {
return sign(identity.privateKey, Authenticator.createEthereumMessageHash(message));
}

static ownerAddress(auditInfo: AuditInfo): EthAddress {
if (auditInfo.authChain.length > 0) {
if (auditInfo.authChain[0].type === AuthLinkType.SIGNER) {
return auditInfo.authChain[0].payload;
}
}
return "Invalid-Owner-Address";
}
}

type ValidatorType = (authority: string, authLink: AuthLink) => { error?: boolean; nextAuthority?: string };

const SIGNER_VALIDATOR: ValidatorType = (authority: string, authLink: AuthLink) => {
return { nextAuthority: authLink.payload };
};

const ECDSA_SIGNED_ENTITY_VALIDATOR: ValidatorType = (authority: string, authLink: AuthLink) => {
try {
const signerAddress = recover(authLink.signature, Authenticator.createEthereumMessageHash(authLink.payload));
if (authority.toLocaleLowerCase() === signerAddress.toLocaleLowerCase()) {
return { nextAuthority: authLink.payload };
}
} catch (e) {
// console.error(e)
}
return { error: true };
};

const ECDSA_EPHEMERAL_VALIDATOR: ValidatorType = (authority: string, authLink: AuthLink) => {
try {
// authLink payload structure: <human-readable message>\nEphemeral address: <ephemeral-eth-address>\nExpiration: <timestamp>
// authLink payload example : Decentraland Login\nEphemeral address: 0x123456\nExpiration: 2020-01-20T22:57:11.334Z
const payloadParts: string[] = authLink.payload.split("\n");
const ephemeralAddress: string = payloadParts[1].substring("Ephemeral address: ".length);
const expirationString: string = payloadParts[2].substring("Expiration: ".length);
const expiration = Date.parse(expirationString);

if (expiration > Date.now()) {
const signerAddress = recover(authLink.signature, Authenticator.createEthereumMessageHash(authLink.payload));
if (authority.toLocaleLowerCase() === signerAddress.toLocaleLowerCase()) {
return { nextAuthority: ephemeralAddress };
}
}
} catch (e) {
// console.error(e)
}
return { error: true };
};

const ERROR_VALIDATOR: ValidatorType = (authority: string, authLink: AuthLink) => {
return { error: true };
};

function getValidatorByType(type: AuthLinkType): ValidatorType {
switch (type) {
case AuthLinkType.SIGNER:
return SIGNER_VALIDATOR;
case AuthLinkType.ECDSA_EPHEMERAL:
return ECDSA_EPHEMERAL_VALIDATOR;
case AuthLinkType.ECDSA_SIGNED_ENTITY:
return ECDSA_SIGNED_ENTITY_VALIDATOR;
default:
return ERROR_VALIDATOR;
}
}
33 changes: 33 additions & 0 deletions crypto/src/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])

load("@npm_bazel_typescript//:index.bzl", "ts_library")
load("//tools/npm:package.bzl", "dataform_npm_package")
load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package")

ts_library(
name = "src",
srcs = glob(["**/*.ts"]),
module_name = "decentraland-crypto",
deps = [
"@npm//@types",
"@npm//eth-crypto",
],
)

dataform_npm_package(
name = "dpackage",
package_layers = [
"//:common.package.json",
"//crypto:crypto.package.json",
],
version = "0.0.1",
deps = [],
)

npm_package(
name = "package",
deps = [
":dpackage_gen_package_json",
":src",
],
)
49 changes: 49 additions & 0 deletions crypto/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
export type Signature = string;
export type EthAddress = string;

export type IdentityType = {
privateKey: string;
publicKey: string;
address: string;
};

export type AuthChain = AuthLink[];

export type AuthLink = {
type: AuthLinkType;
payload: string;
signature: Signature;
};

export enum AuthLinkType {
SIGNER = "SIGNER",
ECDSA_EPHEMERAL = "ECDSA_EPHEMERAL",
ECDSA_SIGNED_ENTITY = "ECDSA_SIGNED_ENTITY"
}

export type AuditInfo = {
version: EntityVersion;
deployedTimestamp: Timestamp;

authChain: AuthChain;

overwrittenBy?: EntityId;

isBlacklisted?: boolean;
blacklistedContent?: ContentFileHash[];

originalMetadata?: {
// This is used for migrations
originalVersion: EntityVersion;
data: any;
};
};
export enum EntityVersion {
V2 = "v2",
V3 = "v3"
}

export type Timestamp = number;

export type EntityId = ContentFileHash;
export type ContentFileHash = string;
4 changes: 2 additions & 2 deletions tools/npm/package.bzl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package")
load("//:version.bzl", "LH_VERSION")

def dataform_npm_package(name, deps, srcs = [], package_layers = []):
def dataform_npm_package(name, deps, srcs = [], package_layers = [], version = LH_VERSION):
native.genrule(
name = name + "_gen_package_json",
srcs = package_layers,
tools = ["//tools/json-merge:bin"],
outs = ["package.json"],
cmd = "$(location //tools/json-merge:bin) --output-path $(OUTS) --layer-paths $(SRCS) --substitutions '{{ \"$$LH_VERSION\": \"{lh_version}\" }}'".format(lh_version = LH_VERSION),
cmd = "$(location //tools/json-merge:bin) --output-path $(OUTS) --layer-paths $(SRCS) --substitutions '{{ \"$$LH_VERSION\": \"{lh_version}\" }}'".format(lh_version = version),
)

npm_package(
Expand Down
9 changes: 5 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
"baseUrl": ".",
"lib": ["es2017", "dom", "esnext.asynciterable", "esnext"],
"paths": {
"@katalyst/content/*": ["content/src/*"],
"@katalyst/test-helpers/*": ["content/test/helpers/*"],
"decentraland-katalyst-contracts/*": ["contracts/*"],
},
"@katalyst/content/*": ["content/src/*"],
"@katalyst/test-helpers/*": ["content/test/helpers/*"],
"decentraland-katalyst-contracts/*": ["contracts/*"],
"decentraland-crypto/*": ["crypto/src/*"]
}
},
"exclude": ["node_modules", "bazel-*", "**/node_modules/*", "dist"]
}

0 comments on commit 7fa9557

Please sign in to comment.