Skip to content

Commit

Permalink
feature: add crypto functions and tests with string instead of UInt8A…
Browse files Browse the repository at this point in the history
…rray
  • Loading branch information
bekolb committed Dec 21, 2018
1 parent a969a07 commit b29490e
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 62 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"dependencies": {
"@polkadot/api": "^0.33.19",
"@polkadot/util": "^0.33.13",
"@polkadot/util": "^0.33.22",
"@polkadot/util-crypto": "^0.33.13",
"ajv": "^6.6.1"
},
Expand Down
9 changes: 9 additions & 0 deletions prototype-sdk.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
73 changes: 69 additions & 4 deletions src/crypto/Crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ describe('Crypto', () => {
const alice = Identity.buildFromMnemonic()
const bob = Identity.buildFromMnemonic()

const message = new Uint8Array(string.stringToU8a('This is a test'))
const messageStr = 'This is a test'
const message = new Uint8Array(string.stringToU8a(messageStr))

it('should sign and verify', () => {
it('should sign and verify (UInt8Array)', () => {
const signature = Crypto.sign(message, alice.signKeyPair.secretKey)
expect(signature).not.toBeFalsy()
expect(Crypto.verify(message, signature, alice.signKeyPair.publicKey)).toBe(
Expand All @@ -28,8 +29,23 @@ describe('Crypto', () => {
).toBe(false)
})

it('should sign and verify (string)', () => {
const signature = Crypto.signStr(messageStr, alice.signSecretKeyAsHex)
expect(signature).not.toBeFalsy()
expect(Crypto.verify(messageStr, signature, alice.signPublicKeyAsHex)).toBe(
true
)

expect(Crypto.verify(messageStr, signature, bob.signPublicKeyAsHex)).toBe(
false
)
expect(Crypto.verify('0x000000', signature, alice.signPublicKeyAsHex)).toBe(
false
)
})

// https://polkadot.js.org/common/examples/util-crypto/01_encrypt_decrypt_message_nacl/
it('should encrypt and decrypt symmetrical using random secret key', () => {
it('should encrypt and decrypt symmetrical using random secret key (UInt8Array)', () => {
const secret = new Uint8Array([
0,
1,
Expand Down Expand Up @@ -67,6 +83,32 @@ describe('Crypto', () => {
const data = Crypto.encryptSymmetric(message, secret)
expect(data).not.toBeFalsy()
expect(Crypto.decryptSymmetric(data, secret)).toEqual(message)
const dataWithNonce = Crypto.encryptSymmetric(message, secret, data.nonce)
expect(Crypto.decryptSymmetric(dataWithNonce, secret)).toEqual(message)
})

// https://polkadot.js.org/common/examples/util-crypto/01_encrypt_decrypt_message_nacl/
it('should encrypt and decrypt symmetrical using random secret key (string)', () => {
const secret =
'0x000102030405060708090A0B0C0D0E0F' + '101112131415161718191A1B1C1D1E1F'

const data = Crypto.encryptSymmetricAsStr(messageStr, secret)
expect(data).not.toBeFalsy()
expect(Crypto.decryptSymmetricStr(data, secret)).toEqual(messageStr)
expect(
Crypto.decryptSymmetricStr(
{ encrypted: '0x000102030405060708090A0B0C0D0E0F', nonce: data.nonce },
secret
)
).toEqual(null)
const dataWithNonce = Crypto.encryptSymmetricAsStr(
messageStr,
secret,
data.nonce
)
expect(Crypto.decryptSymmetricStr(dataWithNonce, secret)).toEqual(
messageStr
)
})

it('should hash', () => {
Expand All @@ -78,9 +120,32 @@ describe('Crypto', () => {
Crypto.hash(message)
)
expect(Crypto.hash('123')).not.toEqual(Crypto.hash(message))
expect(Crypto.hashStr('123')).not.toEqual(Crypto.hashStr(message))
})

it('should encrypt and decrypt asymmetrical (string)', () => {
const encrypted = Crypto.encryptAsymmetricAsStr(
messageStr,
alice.boxPublicKeyAsHex,
bob.boxSecretKeyAsHex
)
expect(encrypted).not.toEqual(messageStr)

const decrypted = Crypto.decryptAsymmetricAsStr(
encrypted,
bob.boxPublicKeyAsHex,
alice.boxSecretKeyAsHex
)
expect(decrypted).toEqual(messageStr)
const decryptedFalse = Crypto.decryptAsymmetricAsStr(
encrypted,
bob.boxPublicKeyAsHex,
bob.boxSecretKeyAsHex
)
expect(decryptedFalse).toEqual(false)
})

it('should encrypt and decrypt asymmetrical', () => {
it('should encrypt and decrypt asymmetrical (UInt8Array)', () => {
const encrypted = Crypto.encryptAsymmetric(
message,
alice.boxKeyPair.publicKey,
Expand Down
157 changes: 133 additions & 24 deletions src/crypto/Crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import { default as naclEncrypt } from '@polkadot/util-crypto/nacl/encrypt'
import { default as naclSign } from '@polkadot/util-crypto/nacl/sign'
import { default as naclVerify } from '@polkadot/util-crypto/nacl/verify'
import nacl from 'tweetnacl'
import {
isString,
stringToU8a,
u8aToHex,
u8aToString,
u8aToU8a,
} from '@polkadot/util'

export type CryptoInput = Buffer | Uint8Array | string

export type EncryptedSymmetric = {
encrypted: Uint8Array
Expand All @@ -18,53 +27,153 @@ export type EncryptedAsymmetric = {
nonce: Uint8Array
}

export function sign(message: Uint8Array, secretKey: Uint8Array): Uint8Array {
return naclSign(message, secretKey)
export type EncryptedSymmetricString = {
encrypted: string
nonce: string
}

export type EncryptedAsymmetricString = {
box: string
nonce: string
}

export function coToUInt8(
input: CryptoInput,
rawConvert?: boolean
): Uint8Array {
if (rawConvert && isString(input)) {
return stringToU8a(input)
}
return u8aToU8a(input)
}

export function sign(message: CryptoInput, secretKey: CryptoInput): Uint8Array {
return naclSign(coToUInt8(message), coToUInt8(secretKey))
}

export function signStr(message: CryptoInput, secretKey: CryptoInput): string {
return u8aToHex(naclSign(coToUInt8(message), coToUInt8(secretKey)))
}

export function verify(
message: Uint8Array,
signature: Uint8Array,
publicKey: Uint8Array
message: CryptoInput,
signature: CryptoInput,
publicKey: CryptoInput
): boolean {
return naclVerify(message, signature, publicKey)
return naclVerify(
coToUInt8(message),
coToUInt8(signature),
coToUInt8(publicKey)
)
}

export function encryptSymmetric(
message: Uint8Array,
secret: Uint8Array,
nonce?: Uint8Array
message: CryptoInput,
secret: CryptoInput,
nonce?: CryptoInput
): EncryptedSymmetric {
return naclEncrypt(message, secret, nonce)
return naclEncrypt(
coToUInt8(message, true),
coToUInt8(secret),
nonce ? coToUInt8(nonce) : undefined
)
}

export function encryptSymmetricAsStr(
message: CryptoInput,
secret: CryptoInput,
inputNonce?: CryptoInput
): EncryptedSymmetricString {
const result = naclEncrypt(
coToUInt8(message, true),
coToUInt8(secret),
inputNonce ? coToUInt8(inputNonce) : undefined
)
const nonce: string = u8aToHex(result.nonce)
const encrypted: string = u8aToHex(result.encrypted)
return { encrypted, nonce }
}

export function decryptSymmetric(
data: EncryptedSymmetric,
secret: Uint8Array
data: EncryptedSymmetric | EncryptedSymmetricString,
secret: CryptoInput
): Uint8Array | null {
return naclDecrypt(data.encrypted, data.nonce, secret)
return naclDecrypt(
coToUInt8(data.encrypted),
coToUInt8(data.nonce),
coToUInt8(secret)
)
}

export function decryptSymmetricStr(
data: EncryptedSymmetric | EncryptedSymmetricString,
secret: CryptoInput
): string | null {
const result = naclDecrypt(
coToUInt8(data.encrypted),
coToUInt8(data.nonce),
coToUInt8(secret)
)
return result ? u8aToString(result) : null
}

export function hash(value: Buffer | Uint8Array | string): Uint8Array {
export function hash(value: CryptoInput): Uint8Array {
return keccakAsU8a(value)
}

export function hashStr(value: CryptoInput): string {
return u8aToHex(hash(value))
}

export function encryptAsymmetric(
message: Uint8Array,
publicKeyA: Uint8Array,
secretKeyB: Uint8Array
message: CryptoInput,
publicKeyA: CryptoInput,
secretKeyB: CryptoInput
): EncryptedAsymmetric {
const nonce = nacl.randomBytes(24)
const box = nacl.box(message, nonce, publicKeyA, secretKeyB)
const encrypted = { box, nonce }
return encrypted
const box = nacl.box(
coToUInt8(message, true),
nonce,
coToUInt8(publicKeyA),
coToUInt8(secretKeyB)
)
return { box, nonce }
}

export function encryptAsymmetricAsStr(
message: CryptoInput,
publicKeyA: CryptoInput,
secretKeyB: CryptoInput
): EncryptedAsymmetricString {
const encrypted = encryptAsymmetric(message, publicKeyA, secretKeyB)
const box: string = u8aToHex(encrypted.box)
const nonce: string = u8aToHex(encrypted.nonce)
return { box, nonce }
}

export function decryptAsymmetric(
data: EncryptedAsymmetric,
publicKeyB: Uint8Array,
secretKeyA: Uint8Array
data: EncryptedAsymmetric | EncryptedAsymmetricString,
publicKeyB: CryptoInput,
secretKeyA: CryptoInput
): Uint8Array | false {
const decrypted = nacl.box.open(data.box, data.nonce, publicKeyB, secretKeyA)
const decrypted = nacl.box.open(
coToUInt8(data.box),
coToUInt8(data.nonce),
coToUInt8(publicKeyB),
coToUInt8(secretKeyA)
)
return decrypted
}

export function decryptAsymmetricAsStr(
data: EncryptedAsymmetric | EncryptedAsymmetricString,
publicKeyB: CryptoInput,
secretKeyA: CryptoInput
): string | false {
const result = decryptAsymmetric(
data,
coToUInt8(publicKeyB),
coToUInt8(secretKeyA)
)
return result ? u8aToString(result) : false
}
16 changes: 16 additions & 0 deletions src/identity/Identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ export default class Identity {
return this._seedAsHex
}

get signPublicKeyAsHex(): string {
return u8aUtil.u8aToHex(this._signKeyPair.publicKey)
}

get signSecretKeyAsHex(): string {
return u8aUtil.u8aToHex(this._signKeyPair.secretKey)
}

get boxPublicKeyAsHex(): string {
return u8aUtil.u8aToHex(this._boxKeyPair.publicKey)
}

get boxSecretKeyAsHex(): string {
return u8aUtil.u8aToHex(this._boxKeyPair.secretKey)
}

public static buildFromMnemonic(phraseArg?: string) {
let phrase = phraseArg
if (phrase) {
Expand Down
3 changes: 2 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"interface-over-type-literal": false,
"no-console": false,
"no-parameter-reassignment": true,
"variable-name": false,
"ordered-imports": false,
"variable-name": false,
"quotemark": [true, "single"],
"object-literal-sort-keys": false,
"prettier": [true, ".prettierrc"]
Expand Down
Loading

0 comments on commit b29490e

Please sign in to comment.