Skip to content

Commit

Permalink
Merge pull request #8 from transmute-industries/feat/hpke-experiments
Browse files Browse the repository at this point in the history
[WIP ]Add HPKE examples
  • Loading branch information
OR13 authored Nov 14, 2023
2 parents 4bcb0ac + d688966 commit f931e5e
Show file tree
Hide file tree
Showing 29 changed files with 994 additions and 104 deletions.
80 changes: 80 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"eslint": "^8.30.0",
"hpke-js": "^1.2.4",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"typescript": "^4.9.4"
Expand Down
4 changes: 2 additions & 2 deletions src/detachPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import * as cbor from 'cbor-web'

import { DetachedSignature } from './types/DetachedSignature'

import { typedArrayToBuffer } from './utils'


const detachPayload = async (attachedSignature: Uint8Array): Promise<DetachedSignature> => {
const decoded = cbor.decodeFirstSync(attachedSignature)
const payload = decoded.value[2]
decoded.value[2] = typedArrayToBuffer(new Uint8Array())
decoded.value[2] = null
cbor.encode(decoded)
const signature = new Uint8Array(await cbor.encodeAsync(decoded))
return { payload, signature }
Expand Down
123 changes: 28 additions & 95 deletions src/diagnostic.ts
Original file line number Diff line number Diff line change
@@ -1,107 +1,40 @@
import * as cbor from 'cbor-web'

// const COSE_Sign1_TAG = 18
function toHexString(byteArray: Uint8Array) {
return Array.prototype.map
.call(byteArray, function (byte) {
return ('0' + (byte & 0xff).toString(16)).slice(-2)
const alternateDiagnostic = async (
data: any
) => {
const decoded = await cbor.decode(data)
if (decoded.tag === 18 && decoded.value[2] === null) {
decoded.value[2] = 'nil'
const data2 = await cbor.encode(decoded)
let text = await cbor.diagnose(data2, {
separator: '\n'
})
.join('')
}
text = text.replace(/"nil"/gm, 'nil')

const prettyHeaderKey = (k: string) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ({
[`1`]: 'alg',
[`3`]: 'content_type',
[`4`]: 'kid',
// new
[`100`]: 'inclusion-proof',
[`200`]: 'consistency-proof',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any)[`${k}`]
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const prettyHeaderValue = (v: any) => {
const value = ({
[`-7`]: '"ES256"',
[`-35`]: '"ES384"',
[`-36`]: '"ES512"',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any)[`${v}`]
return value ? value : `h'${toHexString(new TextEncoder().encode(v))}'`
}
text = text.replace(/\(/gm, '(\n')
text = text.replace(/\)/gm, '\n)')

const diagnosticProtectedHeader = (data: Uint8Array) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const decoded = cbor.decode(data, { dictionary: 'map' } as any)
const lines = []
for (const [k, v] of decoded.entries()) {
lines.push(` # "${prettyHeaderKey(k)}" : ${prettyHeaderValue(v)}`)
lines.push(` # ${k} : ${v}`)
}
return ` # Protected Header
h'${toHexString(data)}',
# {
${lines.join(',\n')}
# }
`
}
text = text.replace(/\[/gm, '[\n')
text = text.replace(/\]/gm, '\n]')

const diagnosticData = (data: Uint8Array) => {
return `h'${toHexString(data)}'`
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const diagnosticUnprotectedHeader = (decoded: any) => {
if (!decoded.entries) {
return ' # Unprotected Header\n {},\n'
}
const lines = []
for (const [k, v] of decoded.entries()) {
lines.push(
` # "${prettyHeaderKey(k)}" : "${prettyHeaderValue(v)}"
${k} : ${prettyHeaderValue(v)} `,
)
}
return ` # Unprotected Header
{
${lines.join(',\n')}
},
`
}
text = text.replace(/, /gm, ',\n')

const default_options = {
decode_payload: true,
detached_payload: false,
}
const alternateDiagnostic = async (
data: Uint8Array,
options = default_options,
) => {
let diagnostic = ''
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { tag, value } = cbor.decode(data, { dictionary: 'map' } as any)
const unprotectedHeader = diagnosticUnprotectedHeader(value[1])
diagnostic += `# COSE_Sign1\n${tag}([\n\n`
diagnostic += diagnosticProtectedHeader(value[0])
diagnostic += '\n'
diagnostic += unprotectedHeader
diagnostic += '\n'
if (options.detached_payload) {
diagnostic += ' ' + '# Detached Payload\n'
} else {
diagnostic += ' ' + '# Protected Payload\n'
diagnostic += ' ' + diagnosticData(value[2]) + ',\n'
if (options.decode_payload) {
diagnostic += ' ' + '# ' + new TextDecoder().decode(value[2]) + '\n'
}
return text
}
let text = await cbor.diagnose(data, {
separator: '\n'
})
text = text.replace(/\{/gm, '{\n')
text = text.replace(/\}/gm, '\n}')

text = text.replace(/\[/gm, '[\n')
text = text.replace(/\]/gm, '\n]')

text = text.replace(/, /gm, ',\n')

return text

diagnostic += '\n'
diagnostic += ' ' + '# Signature\n'
diagnostic += ' ' + diagnosticData(value[3]) + '\n'
diagnostic += `])`
return diagnostic
}

export default alternateDiagnostic
2 changes: 1 addition & 1 deletion src/key/beautify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const beautify = (coseKey: CoseKeyMap): string => {
break
}
case 3: {
lines.push(addComment(`${indentSpaces}${key}: ${value},`, 'Algorithm'))
lines.push(addComment(`${indentSpaces}${key}: ${value || 'TBD'},`, 'Algorithm'))
break
}
case -1: {
Expand Down
2 changes: 1 addition & 1 deletion src/key/exportJWK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const coseKeyToJwk = (coseKey: CoseKeyMap): Record<string, unknown> => {
break
}
case 2: {
jwk.kid = textDecoder.decode(value as Buffer)
jwk.kid = typeof value === 'string' ? value : textDecoder.decode(value as Buffer)
break
}
case 3: {
Expand Down
12 changes: 9 additions & 3 deletions src/key/importJWK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import keyUtils from './keyUtils'

const jwkToCoseKey = (jwk: Record<string, unknown>): CoseKeyMap => {
const coseKey = new Map();
const textEncoder = new TextEncoder()
for (const [key, value] of Object.entries(jwk)) {
const coseKeyParam = keyUtils.parameters.toCOSE.get(key)
switch (key) {
Expand All @@ -16,8 +15,7 @@ const jwkToCoseKey = (jwk: Record<string, unknown>): CoseKeyMap => {
break
}
case 'kid': {
const asBstr = textEncoder.encode(value as string)
coseKey.set(coseKeyParam, typedArrayToBuffer(asBstr))
coseKey.set(coseKeyParam, value)
break
}
case 'alg': {
Expand Down Expand Up @@ -45,6 +43,14 @@ const jwkToCoseKey = (jwk: Record<string, unknown>): CoseKeyMap => {
coseKey.set(coseKeyParam, typedArrayToBuffer(jose.base64url.decode(value as string)))
break
}
case 'use': {
// console.log('skipping JWK use property when importing as COSE Key')
break
}
case 'key_ops': {
// console.log('skipping JWK use property when importing as COSE Key')
break
}
case 'x5c': {
const items = (value as string[] || []).map((item: string) => {
return typedArrayToBuffer(jose.base64url.decode(item as string))
Expand Down
1 change: 1 addition & 0 deletions src/key/keyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const algsMap = new Map([
[-7, 'ES256'],
[-35, 'ES384'],
[-36, 'ES512'],
[-55555, 'HPKE-Base-P256-SHA256-AES128GCM']
]);

const algorithms = {
Expand Down
2 changes: 1 addition & 1 deletion src/rfc/beautify/beautifyCoseSign1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ ${addComment(`18(`, 'COSE Sign 1')}
[
${addComment(protectedHeaderLine, 'Protected')}
${unprotectedHeaderLines}
${addComment(payloadLine, decoded.value[2].length > 0 ? `Payload` : `Detached payload`)}
${addComment(payloadLine, decoded.value[2] !== null ? `Payload` : `Detached payload`)}
${addComment(signatureLine, 'Signature')}
]
)
Expand Down
3 changes: 3 additions & 0 deletions src/rfc/beautify/bufferToTruncatedBstr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@


export const bufferToTruncatedBstr = (thing: ArrayBuffer | Buffer | any) => {
if (thing === null) {
return 'nil'
}
const buf = Buffer.from(thing)
const line = `h'${buf.toString('hex').toLowerCase()}'`
return line.replace(/h'(.{8}).+(.{8})'/g, `h'$1...$2'`).trim()
Expand Down
11 changes: 11 additions & 0 deletions test/hpke/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 🔥 Experimental HPKE Stuff 🔥

## JOSE

- [1 layer](./jose/direct.md)
- [2 layer](./jose/wrap.md)

## COSE

- [1 layer](./cose/direct.md)
- [2 layer](./cose/wrap.md)
Loading

0 comments on commit f931e5e

Please sign in to comment.