Skip to content

Instantly share code, notes, and snippets.

@grrowl
Created November 19, 2016 02:35
Show Gist options
  • Save grrowl/ca94e47a6da2062e9bd6dad211588597 to your computer and use it in GitHub Desktop.
Save grrowl/ca94e47a6da2062e9bd6dad211588597 to your computer and use it in GitHub Desktop.
React component to provide ed25519 key generation, signing and verification
import React, { Component, PropTypes } from 'react'
import elliptic, { eddsa as EdDSA } from 'elliptic'
function toHex(arr) {
return elliptic.utils.toHex(arr).toUpperCase()
}
function fromHex(hex) {
return elliptic.utils.toArray(hex, 'hex')
}
export default class Signature extends Component {
constructor(props) {
super(props);
// Create and initialize EdDSA context
this.ec = new EdDSA('ed25519');
this.state = {
privateKey: null,
publicKey: null,
}
this._identity = null
this.signMessage = this.signMessage.bind(this)
this.verifyMessage = this.verifyMessage.bind(this)
this.generateKey = this.generateKey.bind(this)
}
componentWillMount() {
// if the keys are set in localStorage, initiate our state and this.key
if (localStorage && localStorage['privateKey'] && localStorage['publicKey']) {
const key = this.key = this.ec.keyFromSecret(fromHex(localStorage['privateKey']))
this.setState({
privateKey: toHex(key.getSecret()),
publicKey: toHex(key.getPublic())
})
}
}
generateKey() {
let secret;
if (window.crypto && window.crypto.getRandomValues) {
secret = new Uint8Array(256)
window.crypto.getRandomValues(secret)
} else {
console.warn('Warning: Using insecure methods to generate private key')
secret = []
for (let i = 0; i < 256; i++) {
secret.push(Math.random() * 9007199254740991) // aka Number.MAX_SAFE_INTEGER
}
}
const key = this.key = this.ec.keyFromSecret(secret)
// Save keys to localStorage
localStorage['privateKey'] = toHex(key.getSecret())
localStorage['publicKey'] = toHex(key.getPublic())
// Create key pair from secret
this.setState({
privateKey: toHex(key.getSecret()),
publicKey: toHex(key.getPublic())
})
}
// Sign message (must be an array, or it'll be treated as a hex sequence)
codifyMessage(message) {
message.split('').map(m => m.charCodeAt(0))
}
// signs a message with your own key
signMessage(message) {
// return signature
if (this.key) {
// ~1ms on my machine
return this.key.sign(this.codifyMessage(message)).toHex()
}
return null
}
// verify message against our private key
verifyOwnMessage(message, signature) {
// Verify signature
this.key.verify(message, signature)
}
// verify with no private key
verifyMessage(message, signature, publicKey) {
// Import public key
const key = this.ec.keyFromPublic(publicKey, 'hex')
// Verify signature
// 7~10ms on my machine
return key.verify(this.codifyMessage(message), signature)
}
render() {
const
signatureProps = {
publicKey: this.state.publicKey,
privateKey: this.state.privateKey,
generateKey: this.generateKey,
signMessage: this.signMessage,
verifyMessage: this.verifyMessage,
}
return (
<div>
{
React.Children.map(
this.props.children,
(child => React.cloneElement(child, signatureProps))
)
}
</div>
)
}
}
Signature.propTypes = {
children: PropTypes.node.isRequired,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment