Skip to content

Commit

Permalink
add crypto sign/verify features
Browse files Browse the repository at this point in the history
  • Loading branch information
dmonad committed Feb 6, 2023
1 parent b983ce1 commit 6762a69
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 4 deletions.
64 changes: 60 additions & 4 deletions crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ const toBinary = data => typeof data === 'string' ? string.encodeUtf8(data) : da
*
* @param {string | Uint8Array} secret
* @param {string | Uint8Array} salt
* @param {Object} options
* @param {boolean} [options.extractable]
* @param {Object} opts
* @param {boolean} [opts.extractable]
* @param {Array<'sign'|'verify'|'encrypt'|'decrypt'>} [opts.usages]
* @return {PromiseLike<CryptoKey>}
*/
export const deriveSymmetricKey = (secret, salt, { extractable = false } = {}) => {
export const deriveSymmetricKey = (secret, salt, { extractable = false, usages = ['encrypt', 'decrypt'] } = {}) => {
const binSecret = toBinary(secret)
const binSalt = toBinary(salt)
return webcrypto.subtle.importKey(
Expand All @@ -46,11 +47,26 @@ export const deriveSymmetricKey = (secret, salt, { extractable = false } = {}) =
length: 256
},
extractable,
['encrypt', 'decrypt']
usages
)
)
}

/**
* @param {Object} opts
* @param {boolean} [opts.extractable]
* @param {Array<'sign'|'verify'|'encrypt'|'decrypt'>} [opts.usages]
*/
export const generateAsymmetricKey = ({ extractable = false, usages = ['sign', 'verify'] } = {}) =>
webcrypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-384'
},
extractable,
usages
)

/**
* @experimental The API is not final!
*
Expand Down Expand Up @@ -102,3 +118,43 @@ export const decrypt = (data, key) => {
}

export const exportKey = webcrypto.subtle.exportKey.bind(webcrypto.subtle)

/**
* @experimental The API is not final!
*
* Sign a message
*
* @param {Uint8Array} data
* @param {CryptoKey} privateKey
* @return {PromiseLike<Uint8Array>} signature
*/
export const sign = (data, privateKey) =>
webcrypto.subtle.sign(
{
name: 'ECDSA',
hash: { name: 'SHA-384' }
},
privateKey,
data
).then(signature => new Uint8Array(signature))

/**
* @experimental The API is not final!
*
* Sign a message
*
* @param {Uint8Array} signature
* @param {Uint8Array} data
* @param {CryptoKey} publicKey
* @return {PromiseLike<boolean>} signature
*/
export const verify = (signature, data, publicKey) =>
webcrypto.subtle.verify(
{
name: 'ECDSA',
hash: { name: 'SHA-384' }
},
publicKey,
signature,
data
)
16 changes: 16 additions & 0 deletions crypto.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,19 @@ export const testConsistentKeyGeneration = async _tc => {
const jwk = await cryptutils.exportKey('jwk', key)
t.compare(jwk, expectedJwk)
}

/**
* @param {t.TestCase} tc
*/
export const testSigning = async tc => {
await t.measureTimeAsync('time to sign & verify 2 messages', async () => {
const keypair = await cryptutils.generateAsymmetricKey({ extractable: true })
const keypair2 = await cryptutils.generateAsymmetricKey({ extractable: true })
const data = prng.uint8Array(tc.prng, 100)
const signature = await cryptutils.sign(data, keypair.privateKey)
const result = await cryptutils.verify(signature, data, keypair.publicKey)
const result2 = await cryptutils.verify(signature, data, keypair2.publicKey)
t.assert(result, 'verification works using the correct key')
t.assert(!result2, 'verification fails using the incorrect key')
})
}

0 comments on commit 6762a69

Please sign in to comment.