Skip to content

Instantly share code, notes, and snippets.

@shanewholloway
Created June 20, 2018 16:18
Show Gist options
  • Save shanewholloway/6ed5a52fa985b1d23024daa001b6a51e to your computer and use it in GitHub Desktop.
Save shanewholloway/6ed5a52fa985b1d23024daa001b6a51e to your computer and use it in GitHub Desktop.
WebCrypto get compressed ECDH public key that is compatible with Node's ec.getPublicKey(null, 'compressed')
const _fromCharCode = String.fromCharCode
export function pack_base64(arr) {
let res=''
const u8 = new Uint8Array(arr.buffer || arr)
const len = u8.byteLength
for (let i=0; i<len; i++)
res += _fromCharCode(u8[i])
return window.btoa(res)
}
const _charCodeAt = ''.charCodeAt
export function unpack_base64(str_b64) {
const sz = window.atob(str_b64)
const len = sz.length
const res = new Uint8Array(len)
for (let i=0; i<len; i++)
res[i] = _charCodeAt.call(sz, i)
return res
}
import {pack_base64, unpack_base64} from './browser_util.jsy'
import ecdh_pubkey_compressed from './ecdh_pubkey_compressed.js'
function testWithAlg(namedCurve) {
const ec_alg = { name: 'ECDH', namedCurve }
const reexport = ecdh_raw =>
crypto.subtle
.importKey('raw', ecdh_raw, ec_alg, true, [])
.then(ecdh => crypto.subtle.exportKey('raw', ecdh))
.then(pack_base64)
return crypto.subtle
.generateKey(ec_alg, false, ['deriveKey', 'deriveBits'])
.then(ec => Promise.all([
crypto.subtle.exportKey('raw', ec.publicKey),
ecdh_pubkey_compressed(ec),
ecdh_pubkey_compressed(ec.publicKey),
]))
.then(lst => {
console.log(pack_base64(lst[1]))
return Promise.all(lst.map(reexport)) })
.then(lst => console.log(lst[0]===lst[1], lst[0]===lst[2]))
}
testWithAlg('P-256')
testWithAlg('P-384')
testWithAlg('P-521')
export default ecdh_pubkey_compressed
const supported_curves = { 'P-256': 1, 'P-384': 1, 'P-521': 1, }
export function ecdh_pubkey_compressed(ecdh) {
if (null != ecdh.publicKey)
ecdh = ecdh.publicKey
if (ecdh.type !== 'public' || ! ecdh.algorithm || ecdh.algorithm.name !== 'ECDH')
throw new TypeError('Expected a public ECDH CryptoKey')
if (! supported_curves[ecdh.algorithm.namedCurve] )
return Promise.resolve(
new Error('Unsupported ECDH curve for compression') )
return crypto.subtle.exportKey('raw', ecdh)
.then(ecdh_raw_pubkey_compressed)
}
export function ecdh_raw_pubkey_compressed(ec_raw_pubkey) {
const u8full = new Uint8Array(ec_raw_pubkey)
const len = u8full.byteLength
const u8 = u8full.slice(0, 1 + len >>> 1) // drop `y`
u8[0] = 0x2 | (u8full[len-1] & 0x01) // encode sign of `y` in first bit
return u8.buffer
}
@shanewholloway
Copy link
Author

Based on StackOverflow answer from Adria and inspecting the byte differences between ecdh.getPublicKey(null) and ecdh.getPublicKey(null, 'compressed') in NodeJS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment