import { ecdsa , jwk , parse } from './settings' ; import { sha256 } from './sha256' ; import { Buffer , crypto } from './shims' ; import { pubFromSoul } from './soul' ; import { verify } from './verify' ; const DEFAULT_OPTS = { encode : 'base64' } ; export function prep ( val , key , node , soul ) { return { '#' : soul , '.' : key , ':' : parse ( val ) , '>' : ( node && node . _ && node . _ [ '>' ] && node . _ [ '>' ] [ key ] ) || 0 } ; } export async function hashForSignature ( prepped ) { const hash = await sha256 ( typeof prepped === 'string' ? prepped : JSON . stringify ( prepped ) ) ; return hash . toString ( 'hex' ) ; } export function hashNodeKey ( node , key ) { const val = node && node [ key ] ; const parsed = parse ( val ) ; const soul = node && node . _ && node . _ [ '#' ] ; const prepped = prep ( parsed , key , node , soul ) ; return hashForSignature ( prepped ) ; } export async function signHash ( hash , pair , encoding = DEFAULT_OPTS . encode ) { const { pub , priv } = pair ; const token = jwk ( pub , priv ) ; const signKey = await crypto . subtle . importKey ( 'jwk' , token , ecdsa . pair , false , [ 'sign' ] ) ; const sig = await crypto . subtle . sign ( ecdsa . sign , signKey , new Uint8Array ( Buffer . from ( hash , 'hex' ) ) ) ; const res = Buffer . from ( sig , 'binary' ) . toString ( encoding ) ; return res ; } export async function sign ( data , pair , opt = DEFAULT_OPTS ) { if ( typeof data === 'undefined' ) { throw new Error ( '`undefined` not allowed.' ) ; } const json = parse ( data ) ; const encoding = opt . encode || DEFAULT_OPTS . encode ; const checkData = opt . check || json ; if ( json && ( ( json . s && json . m ) || ( json [ ':' ] && json [ '~' ] ) ) && ( await verify ( data , pair . pub ) ) ) { const parsed = parse ( checkData ) ; if ( opt . raw ) { return parsed ; } return 'SEA' + JSON . stringify ( parsed ) ; } const hash = await hashForSignature ( data ) ; const sig = await signHash ( hash , pair , encoding ) ; const r = { m : json , s : sig } ; if ( opt . raw ) { return r ; } return 'SEA' + JSON . stringify ( r ) ; } export async function signNodeValue ( node , key , pair , _encoding = DEFAULT_OPTS . encode ) { const data = node [ key ] ; const json = parse ( data ) ; if ( data && json && ( ( json . s && json . m ) || ( json [ ':' ] && json [ '~' ] ) ) ) { return json ; } const hash = await hashNodeKey ( node , key ) ; const sig = await signHash ( hash , pair ) ; return { ':' : parse ( node [ key ] ) , '~' : sig } ; } export async function signNode ( node , pair , encoding = DEFAULT_OPTS . encode ) { const signedNode = { _ : node . _ } ; const soul = node . _ && node . _ [ '#' ] ; for ( const key in node ) { if ( key === '_' ) { continue ; } if ( key === 'pub' && soul === `~${ pair . pub }` ) { signedNode [ key ] = node [ key ] ; continue ; } signedNode [ key ] = JSON . stringify ( await signNodeValue ( node , key , pair , encoding ) ) ; } return signedNode ; } export async function signGraph ( graph , pair , encoding = DEFAULT_OPTS . encode ) { const modifiedGraph = { ... graph } ; for ( const soul in graph ) { if ( ! soul ) { continue ; } const soulPub = pubFromSoul ( soul ) ; if ( soulPub !== pair . pub ) { continue ; } const node = graph [ soul ] ; if ( ! node ) { continue ; } modifiedGraph [ soul ] = await signNode ( node , pair , encoding ) ; } return modifiedGraph ; } export function graphSigner ( pair , encoding = DEFAULT_OPTS . encode ) { return ( graph ) => signGraph ( graph , pair , encoding ) ; }