Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WebUSBでレイヤーが低まるWeb開発(Shibuya.XSS版)

Fadis
December 12, 2017

 WebUSBでレイヤーが低まるWeb開発(Shibuya.XSS版)

Chrome 61から使えるようになったWebUSB APIを使ってUSBデバイスと会話する方法を解説します。
これは2017年12月13日に行われた Shibuya.XSS techtalk #10 の発表資料です。
これは2017年9月23日に行われた 第3回 カーネル/VM探検隊@北陸での発表資料に加筆修正を行なったものです。

Fadis

December 12, 2017
Tweet

More Decks by Fadis

Other Decks in Programming

Transcript

  1. WebUSBͰ
    ௿·ΔWeb։ൃ
    ϨΠϠʔ͕
    NAOMASA MATSUBAYASHI
    ιʔείʔυ: https://goo.gl/cijdrQ
    (Shibuya.XSS൛)

    View full-size slide

  2. WebUSB API
    http://wicg.github.io/webusb

    View full-size slide

  3. ϗετʹ઀ଓ͞ΕͨUSBσόΠεͱϒϥ΢β͕
    OSͷσόΠευϥΠόΛհͣ͞ʹ
    ௚઀ձ࿩͢ΔͨΊͷJavaScriptͷAPI
    Χʔωϧ
    ϒϥ΢β 64#σόΠε
    WebUSB API
    libusbΛWeb͔Βୟ͚ΔΑ͏ʹͳͬͨͱࢥ͑͹
    ͍͍͍ͩͨ͋ͬͯΔ

    View full-size slide

  4. σόΠεΛऔಘ͢Δ
    return navigator.usb.requestDevice(
    { 'filters': [
    { 'vendorId': 0x1234, 'productId': 0x5678 }
    ] }
    ).then(device_ => {
    device = device_;
    return connect();
    }).catch(error => {
    console.log('઀ଓΤϥʔ: ' + error);
    });
    navigator.usb.requestDevieͰ
    ࢦఆͨ͠ϕϯμIDͱϓϩμΫτIDΛ࣋ͭ
    USBσόΠε΁ͷ઀ଓΛཁٻ͢Δ

    View full-size slide

  5. σόΠεΛऔಘ͢Δ
    ͜Μͳϓϩϯϓτ͕ग़ͯ͘Δ
    Ϣʔβ͸Webϖʔδʹ৮Βͤͯྑ͍σόΠεΛબͿ

    View full-size slide

  6. USBσόΠε
    σόΠε
    ΤϯυϙΠϯτ0
    ΤϯυϙΠϯτ1
    ίϯϑΟάϨʔγϣϯ0
    ΠϯλʔϑΣʔε0
    ΠϯλʔϑΣʔε1
    ΠϯλʔϑΣʔε2
    ΤϯυϙΠϯτ0
    ΤϯυϙΠϯτ1
    ΤϯυϙΠϯτ2
    ΤϯυϙΠϯτ3

    View full-size slide

  7. USBσόΠε
    σόΠε
    ΤϯυϙΠϯτ0
    ΤϯυϙΠϯτ1
    ίϯϑΟάϨʔγϣϯ0
    ΠϯλʔϑΣʔε0
    ΠϯλʔϑΣʔε1
    ΠϯλʔϑΣʔε2
    ΤϯυϙΠϯτ0
    ΤϯυϙΠϯτ1
    ΤϯυϙΠϯτ2
    ΤϯυϙΠϯτ3
    σʔλΛૹड৴͢Δͱ͖ʹ͸
    ͲͷΤϯυϙΠϯτͰձ࿩͢Δ͔Λࢦఆ͢Δ

    View full-size slide

  8. USBσόΠε
    σόΠε
    ΤϯυϙΠϯτ0
    ΤϯυϙΠϯτ1
    ίϯϑΟάϨʔγϣϯ0
    ΠϯλʔϑΣʔε0
    ΠϯλʔϑΣʔε1
    ΠϯλʔϑΣʔε2
    ΤϯυϙΠϯτ0
    ΤϯυϙΠϯτ1
    ΤϯυϙΠϯτ2
    ΤϯυϙΠϯτ3
    ΞϓϦέʔγϣϯ͸
    ΠϯλʔϑΣʔε୯ҐͰΤϯυϙΠϯτΛ઎༗͢Δ

    View full-size slide

  9. σόΠεʹσʔλΛૹΔ/໯͏
    device.controlTransferOut({
    'requestType': 'class',
    'recipient': 'interface',
    'request': 0x22,
    'value': 0x01,
    'index': 0x00
    }).then(result => {

    }
    ίϯτϩʔϧసૹ
    device.controlTransferIn({
    'requestType': 'standard',
    'recipient': 'device',
    'request': 0x06,
    'value': 0x0100,
    'index': 0x0000
    }, 0x12).then(result => {

    }
    σόΠεͷ৘ใͷऔಘ΍
    σόΠεͷॳظԽΛߦ͏

    View full-size slide

  10. ௨৴Ͱ͖Δঢ়ଶʹ͢Δ
    return device.open().then(() => {
    if(device.configuration === null) {
    return device.selectConfiguration(1);
    }
    }).then(() => {
    return device.claimInterface(1);
    }).then(() => {
    return device.claimInterface(0);
    });
    configurationΛબͿ
    USBσόΠε͸ෳ਺ͷػೳΛఏڙ͍ͯ͠Δ͜ͱ͕͋Δ
    ͲͷػೳΛ࢖͏͔ΛબͿͷ͕configurationͷબ୒
    ඞཁͳΠϯλʔϑΣʔεΛ઎༗͢Δ

    View full-size slide

  11. σόΠεʹσʔλΛૹΔ/໯͏
    device.transferIn(1, 1024).then(result => {
    something_on_recieved( result.data );
    }
    σόΠε͔Βड৴
    σόΠεʹૹ৴
    ΤϯυϙΠϯτID ड৴͢ΔόΠτ਺
    PromiseͰड৴݁ՌͱDataView͕ฦͬͯདྷΔ
    device.transferOut(2, data).then(result => {
    something_on_sent();
    }
    ΤϯυϙΠϯτID
    ૹ৴͢ΔArrayBuffer
    PromiseͰૹ৴݁Ռ͕ฦͬͯདྷΔ

    View full-size slide

  12. CDC ACM γϦΞϧ௨৴
    Webϒϥ΢βͷதͱ֎Ͱձ࿩ͯ͠ΈΑ͏

    View full-size slide

  13. USB GadgetυϥΠό
    ࢲ͸64#֎෇͚
    ϋʔυσΟεΫͰ͢
    ϋʔυσΟεΫ͕དྷͨ
    USBσόΠεͷϑϦΛ͢ΔυϥΠό

    View full-size slide

  14. dummy_udc
    ϋʔυσΟεΫ͕དྷͨ
    Ծ૝తͳUSBϗετͱσόΠεͷ૊Λ࡞Δ
    ࢲ͸64#֎෇͚
    ϋʔυσΟεΫͰ͢

    View full-size slide

  15. $ modprobe libcomposite
    $ modprobe dummy_hcd
    $ cd /sys/kernel/config/usb_gadget
    $ mkdir g1
    $ cd g1
    $ mkdir functions/acm.g1
    $ mkdir configs/c.1
    $ ln -s functions/acm.g1 configs/c.1
    $ echo 0x1234 >idVendor
    $ echo 0x5678 >idProduct
    $ echo dummy_udc.0 >UDC
    $ sleep 1
    $ modprobe -r cdc_acm
    $ agetty 115200 ttyGS0
    LinuxͷUSB GadgetͰ
    CDC ACMͷγϦΞϧ௨৴σόΠεΛ࡞Γ
    σόΠεଆͰgetty͢Δ
    ϗετଆυϥΠό͸Ξϯϩʔυ͓ͯ͘͠

    View full-size slide

  16. CDC ACM
    σόΠε
    ίϯϑΟάϨʔγϣϯ0
    CDC ACM Comm(0)
    CDC ACM Data(1)
    ੍ޚ৴߸(3)
    σʔλೖྗ(1)
    σʔλग़ྗ(2)
    σʔλೖग़ྗͷͨΊͷόϧΫΤϯυϙΠϯτΛ
    උ͑ΔΠϯλʔϑΣʔε͕1ͭ
    ੍ޚ৴߸ΛૹΔͨΊͷׂΓࠐΈΤϯυϙΠϯτΛ
    උ͑ΔΠϯλʔϑΣʔε͕1ͭ

    View full-size slide

  17. function flush() {
    writing = true;
    let buffer_ = buffer;
    buffer = '';
    device.transferOut(2, textEncoder.encode(buffer_)).then(
    result => {
    writing = false;
    if( buffer.length != 0 ) { flush(); }
    }).catch(error => {
    console.log(‘ૹ৴Τϥʔ: ' + error);
    writing = false;
    if( buffer.length != 0 ) { flush(); }
    });
    }
    t.open( document.getElementById('terminal') );
    t.on('key', function (key, ev) {
    if(device !== undefined) {
    if( !writing ) {
    buffer += key;
    flush();
    }
    else { buffer += key; }
    }
    });
    xterm.jsʹΩʔೖྗ͕͋ͬͨΒ
    ΤϯυϙΠϯτʹೖྗ಺༰Λྲྀ͢

    View full-size slide

  18. let readLoop = () => {
    if( device ) {
    device.transferIn(1, 1024).then(result => {
    let textDecoder = new TextDecoder();
    t.write(textDecoder.decode(result.data));
    readLoop();
    }, error => {
    console.log( error );
    readLoop();
    });
    }
    };
    ΤϯυϙΠϯτ1Ͱ͸
    σόΠε͔ΒͷσʔλΛ଴ͪड͚
    Կ͔ड͚औͬͨΒxterm.jsʹྲྀ͢

    View full-size slide

  19. USBΛ௨ͬͯ֎ͷੈքʹඈͼग़͢Α!
    http://fadis.github.io/webusb/minimal/

    View full-size slide

  20. USBϚεετϨʔδ
    USB֎෇͚ϋʔυσΟεΫ΍
    USBϑϥογϡϝϞϦͱձ࿩ͯ͠ΈΑ͏

    View full-size slide

  21. ͜ͷUSBϑϥογϡϝϞϦΛ
    ϒϥ΢β͔ΒಡΉ
    $ modprobe -r uas
    $ modprobe -r mass_storage
    ·ͣΧʔωϧ͕अຐΛ͠ͳ͍Α͏ʹ͓ͯ͘͠

    View full-size slide

  22. Command Block Wrapper
    SCSIίϚϯυ
    Command State Wrapper
    ίϚϯυͷ࣮ߦ݁Ռ
    ίϚϯυʹ෇ਵ͢Δ
    σʔλΛૹड৴
    Bulk Only Transport
    ࡉ͔͍ετϨʔδͷૢ࡞ํ๏͸SCSIͦͷ··

    View full-size slide

  23. σόΠε
    ίϯϑΟάϨʔγϣϯ0
    MSC(0)
    σʔλೖྗ(1)
    σʔλग़ྗ(2)
    σʔλೖग़ྗͷͨΊͷόϧΫΤϯυϙΠϯτΛ
    උ͑ΔΠϯλʔϑΣʔε͕1͚ͭͩ
    Bulk Only Transport

    View full-size slide

  24. Command Block Wrapper
    let cbw = ( tag, len, dir, command ) => {
    let data = new Uint8Array( 15 + 16 );
    data.set([
    0x55, 0x53, 0x42, 0x43, // USBC
    tag & 0xFF,
    ( tag >> 8 ) & 0xFF,
    ( tag >> 16 ) & 0xFF,
    ( tag >> 24 ) & 0xFF, // tag
    len & 0xFF,
    ( len >> 8 ) & 0xFF,
    ( len >> 16 ) & 0xFF,
    ( len >> 24 ) & 0xFF, // len
    dir << 7, // flags
    0, // LUN
    command.byteLength // command length
    ], 0);
    data.set( command, 15 );
    return device.transferOut( 2, data );
    }
    4$4*ίϚϯυʹ
    $#8ͷ
    ϔομΛ͚ͭͯ
    σόΠεʹ౤͛Δ

    View full-size slide

  25. Command State Wrapper
    let csw = () => {
    return device.transferIn( 1, 13 ).then(
    result => {
    let state = result.data.getUint8( 12 );
    return state == 0;
    }
    );
    }
    ίϚϯυͷ࣮ߦ݁ՌΛσόΠε͔Βड͚औΔ

    View full-size slide

  26. ετϨʔδΛಡΉͷʹඞཁͳSCSIίϚϯυ
    INQUIRY
    TEST UNIT READY
    State?
    READ CAPACITY(10)
    READ(10)
    OK
    *
    -6/ʹσόΠε͕
    ͋Δ͜ͱΛ֬ೝ
    σόΠε͕
    ར༻ՄೳʹͳΔͷΛ଴ͭ
    σόΠεͷ༰ྔͱ
    ηΫλαΠζΛऔಘ
    σόΠε͔Β
    σʔλΛಡΈग़͢

    View full-size slide

  27. let inquiry = () => {
    let command = new Uint8Array([
    // command LUN reserved reserved size reserved
    0x12, 0, 0, 0, 36, 0
    ]);
    return cbw( 1, 36, 1, command ).then( result => {
    return device.transferIn( 1, 36 );
    }).then( result => {
    return result.data;
    }).then( data => {
    return csw().then( stat => {
    return { 'status': stat, 'data': data }
    });
    });
    }
    ඞཁͳSCSIίϚϯυΛ࣮૷͍ͯ͘͠

    View full-size slide

  28. let test_unit_ready = () => {
    let command = new Uint8Array([
    // command LUN
    0x00, 0
    ]);
    return cbw( 1, 0, 1, command ).then( result => {
    return csw();
    }).then( stat => {
    if( stat ) {
    console.log( 'σόΠε͕ར༻ՄೳʹͳΓ·ͨ͠' );
    return true;
    }
    else {
    console.log( 'σόΠε͕ར༻ՄೳʹͳΔͷΛ଴͍ͬͯ·͢' );
    return async_sleep( 1000 ).then( test_unit_ready );
    }
    });
    }
    ඞཁͳSCSIίϚϯυΛ࣮૷͍ͯ͘͠

    View full-size slide

  29. let read_capacity = () => {
    let command = new Uint8Array([
    0x25, 0 // command LUN
    ]);
    return cbw( 1, 8, 1, command ).then( result => {
    return device.transferIn( 1, 8 )
    }).then( result => {
    let last_lba = result.data.getUint32( 0 );
    let block_len = result.data.getUint32( 4 );
    return {
    'last_logical_block_address': last_lba,
    'block_length': block_len
    };
    }).then( data => {
    return csw().then( stat => {
    return { 'status': stat, 'data': data }
    });
    });
    };
    ඞཁͳSCSIίϚϯυΛ࣮૷͍ͯ͘͠

    View full-size slide

  30. let read = ( offset, count ) => {
    let command = new Uint8Array([
    0x28, 0, // command LUN
    ( offset >> 24 ) & 0xFF, ( offset >> 16 ) & 0xFF,
    ( offset >> 8 ) & 0xFF, offset & 0xFF, // LBA
    0, // reserved
    ( count >> 8 ) & 0xFF, count & 0xFF, // length
    0 // reserved
    ]);
    let size = sector_size * count;
    return cbw( 1, size, 1, command ).then( result => {
    return device.transferIn( 1, sector_size * count )
    }).then( result => {
    return result.data;
    }).then( data => {
    return csw().then( stat => {
    return { 'status': stat, 'data': data }
    });
    });
    };
    ඞཁͳSCSIίϚϯυΛ࣮૷͍ͯ͘͠

    View full-size slide

  31. ϚελʔϒʔτϨίʔυ
    ύʔςΟγϣϯ
    ςʔϒϧͷ
    ͭΊͷΤϯτϦ
    ϒʔτγάχνϟ
    -#"͔Β
    ϒϩοΫΛऔಘ
    55 aa

    View full-size slide

  32. BIOSύϥϝʔλϒϩοΫ
    FAT32ͷϔομ
    46 41 54 33 32 20 20 20
    F A T 3 2 _ _ _

    View full-size slide

  33. FAT32
    let load_fat32 = partition => {
    return read( partition.at, 1 ).then( result => {
    let cluster_size = result.data.getUint8( 13 );
    let reserved_sector_size =
    result.data.getUint16( 14, true );
    let num_fats = result.data.getUint8( 16 );
    let rde_size = result.data.getUint16( 17, true );
    let fat_size = result.data.getUint32( 36, true );
    let rde = result.data.getUint32( 44, true );
    let fat_lba = partition.at + reserved_sector_size;
    return read( fat_lba, fat_size ).then( result => {
    let len = result.data.byteLength / 4;
    let fat = new Uint32Array( len );
    for( let i = 0; i != len; i++ ) {
    fat[ i ] = result.data.getUint32( i * 4, true );
    }
    let clusters_lba = fat_lba + num_fats * fat_size;
    let fsinfo = {
    'cluster_size': cluster_size, 'fat': fat,
    'clusters_lba': clusters_lba, 'rootdir_entry': rde
    BIOSύϥϝʔλϒϩοΫ͔Β
    ϑΝΠϧγεςϜΛಡΉͷʹඞཁͳ஋Λऔಘ
    ΫϥελαΠζ
    FATͷ਺ͱαΠζͱ։࢝ηΫλ
    ϧʔτσΟϨΫτϦ͕ॻ͔Ε͍ͯΔҐஔ

    View full-size slide

  34. FAT32
    result.data.getUint16( 14, true );
    let num_fats = result.data.getUint8( 16 );
    let rde_size = result.data.getUint16( 17, true );
    let fat_size = result.data.getUint32( 36, true );
    let rde = result.data.getUint32( 44, true );
    let fat_lba = partition.at + reserved_sector_size;
    return read( fat_lba, fat_size ).then( result => {
    let len = result.data.byteLength / 4;
    let fat = new Uint32Array( len );
    for( let i = 0; i != len; i++ ) {
    fat[ i ] = result.data.getUint32( i * 4, true );
    }
    let clusters_lba = fat_lba + num_fats * fat_size;
    let fsinfo = {
    'cluster_size': cluster_size, 'fat': fat,
    'clusters_lba': clusters_lba, 'rootdir_entry': rde
    };
    return load_fat32file(
    fsinfo, fsinfo.rootdir_entry, 0
    ).then( raw_root_dir => {
    let root_dir =
    parse_fat32directory( raw_root_dir );
    FATʹ͸
    ࠓಡΜͰ͍ΔΫϥελͷ࣍ʹಡΉ΂͖Ϋϥελ͕
    Ͳ͔͕͜ه࿥͞Ε͍ͯΔ
    FATΛಡΉ

    View full-size slide

  35. FAT32
    let fat = new Uint32Array( len );
    for( let i = 0; i != len; i++ ) {
    fat[ i ] = result.data.getUint32( i * 4, true );
    }
    let clusters_lba = fat_lba + num_fats * fat_size;
    let fsinfo = {
    'cluster_size': cluster_size, 'fat': fat,
    'clusters_lba': clusters_lba, 'rootdir_entry': rde
    };
    return load_fat32file(
    fsinfo, fsinfo.rootdir_entry, 0
    ).then( raw_root_dir => {
    let root_dir =
    parse_fat32directory( raw_root_dir );
    fsinfo[ 'rootdir' ] = root_dir;
    return fsinfo;
    });
    });
    });
    };
    ϧʔτσΟϨΫτϦΛಡΉ

    View full-size slide

  36. let get_fat32clusters_reversed =
    ( fsinfo, head ) => {
    let next = fsinfo.fat[ head ] & 0x0FFFFFFF;
    if( next >= 0x00000002 && next <= 0x0ffffff6 ) {
    let tail =
    get_fat32clusters_reversed( fsinfo, next );
    tail.push( head );
    return tail;
    }
    else return [ head ];
    };
    ΫϥελνΣΠϯ
    FAT͔ΒಡΉඞཁ͕͋ΔΫϥελΛௐ΂ͯ

    View full-size slide

  37. let get_fat32clusters = ( fsinfo, head ) => {
    let clusters = get_fat32clusters_reversed( fsinfo, head );
    clusters.reverse();
    let chunks = [ { 'at': clusters[ 0 ], 'length': 1 } ];
    for( let i = 1; i != clusters.length; i++ ) {
    if( clusters[ i - 1 ] + 1 == clusters[ i ] ) {
    chunks[ chunks.length - 1 ].length++;
    }
    else {
    chunks.push( { 'at': clusters[ i ], 'length': 1 } );
    }
    }
    return chunks;
    }
    ΫϥελνΣΠϯ
    ࿈ଓͨ͠Ϋϥελ͸·ͱΊͯ

    View full-size slide

  38. let load_fat32cluster = ( fsinfo, chunks, index, data ) => {
    let lba = fsinfo.clusters_lba +
    ( chunks[ index ].at - 2 ) * fsinfo.cluster_size;
    return read(
    lba, chunks[ index ].length * fsinfo.cluster_size
    ).then(
    result => {
    data.push( result.data );
    if( index + 1 < chunks.length ) {
    return load_fat32cluster(
    fsinfo, chunks, index + 1, data
    );
    }
    else return data;
    });
    }
    ΫϥελνΣΠϯ
    READ(10)

    View full-size slide

  39. let parse_fat32directory = ( data ) => {
    let files = [];
    let lfn = [];
    for( let offset = 0; offset != data.byteLength; offset +=
    32 ) {
    let entry = data.subarray( offset, offset + 32 );
    let attribute = entry[ 11 ];
    let sfn_head = entry[ 0 ];
    if( sfn_head == 0x00 ) {
    break;
    }
    else if( sfn_head == 0xE5 ) {
    continue;
    }
    else if( attribute == 0x0F ) {
    let fragment = new Uint8Array( 26 );
    fragment.set( entry.subarray( 1, 11 ), 0 );
    fragment.set( entry.subarray( 14, 26 ), 10 );
    fragment.set( entry.subarray( 28, 32 ), 22 );
    lfn.push( fragment );
    σΟϨΫτϦΤϯτϦ
    ͦΜͳʹෳࡶ͡Όͳ͍ͷͰؾ߹͍Ͱύʔε͢Δ

    View full-size slide

  40. ϒϥ΢βͰUSBϑϥογϡϝϞϦΛಡΉΑ!
    http://fadis.github.io/webusb/storage/

    View full-size slide

  41. USBϑϥογϡϝϞϦ͔ΒಡΜͩը૾Λ
    Webϖʔδʹදࣔ͢ΔΑ!
    http://fadis.github.io/webusb/storage/

    View full-size slide

  42. WebUSBͷηΩϡϦςΟ
    WebUSB͸USBσόΠεʹର͢Δ
    ͋ΒΏΔૢ࡞Λߦ͏ݖݶΛ
    ϦϞʔτ͔ΒඈΜͰདྷͨJavaScriptʹ༩͑Δ
    σόΠεʹΑͬͯ͸JavaScript͸
    σόΠεΛ৐ͬऔΔࣄ΋ഁյ͢Δࣄ΋Ͱ͖Δ
    ࠷௿Ͱ΋JavaScriptͷग़ॴ͕
    อূ͞Ε͍ͯͳ͍ͱාͯ͘ར༻Ͱ͖ͳ͍

    View full-size slide

  43. WebUSBͷηΩϡϦςΟ
    WebUSB͸SecureContextͰͷΈ࢖༻Ͱ͖Δ
    https://www.w3.org/TR/secure-contexts/
    TLS
    ҉߸Խ͞Ε͍ͯͯ
    εΫϦϓτΛڬΊͳ͍
    ฏจͰҾͬுΒΕͨ
    εΫϦϓτΛվ᜵ͯ͠΋
    ͦ͜Ͱ͸WebUSB͸ಈ͔ͳ͍
    ࠷ॳʹTLSͰऔಘͨ͠Webϖʔδ͔Β
    TLSΛհͯ͠ḷΓண͚ΔεΫϦϓτ͚͕ͩ
    USBσόΠεΛૢ࡞Ͱ͖Δ
    ͱ͜ΖͰͦͷWebϖʔδࣗମ͸৴པͰ͖Δ?

    View full-size slide

  44. WebUSBͷηΩϡϦςΟ
    https://example.org/
    ͳΜ͚ͩͲ
    USBσόΠεΛ࢖ΘͤͯΑ
    https://example.com/
    Ͱ࢖ΘΕΔҝʹ࡞ΒΕͨ
    WebUSBରԠσόΠεͰ͢
    μϝ
    ౰ॳWebUSB͸σόΠεʹ৘ใΛຒΊΔࣄͰ
    ҙਤ͠ͳ͍Webϖʔδʹ
    σόΠεΛ࢖ΘΕͳ͍Α͏ʹ͠Α͏ͱͨ͠

    View full-size slide

  45. WebUSB Platform Capability Descriptor
    WebUSBରԠσόΠεͰ͋Δ͜ͱΛࣔ͢σεΫϦϓλ
    https://wicg.github.io/webusb/

    View full-size slide

  46. Get URL
    σόΠεͷURLΛऔಘ͢Δҝͷ৽͍͠ϦΫΤετ
    ͜ͷURLͱಉ͡υϝΠϯͷWebϖʔδ͔Βͷ
    WebUSBϦΫΤετ͔͠ड͚෇͚ͳ͍
    https://wicg.github.io/webusb/

    View full-size slide

  47. WebαΠτAͰ
    ೝূ͢ΔͨΊͷ
    ΧʔυϦʔμʔ
    WebαΠτB༻Ͱ
    ೝূ͢ΔͨΊͷ
    ΧʔυϦʔμʔ
    WebαΠτC༻Ͱ
    ೝূ͢ΔͨΊͷ
    ΧʔυϦʔμʔ
    ͜Ε͸ศརͰ͸ͳ͍

    View full-size slide

  48. ౰ॳσόΠεʹURLຒΊࠐ΋͏ͱࢥ͚ͬͨͲ
    σόΠεͷϕϯμʔʹରԠͯ͠΋Β͏ͷ೉͍͠͠
    αʔυύʔςΟ͕σόΠε࢖͑ͳ͘ͳΔ͔Β΍Ίͨ
    ϢʔβʹϓϩϯϓτͰ֬ೝͱͬͯΔ͠
    ͦΕ͚ͩͰे෼ͳΜ͡Όͳ͍͔ͳ
    https://wicg.github.io/webusb/

    View full-size slide

  49. WebUSBͷηΩϡϦςΟ
    ͜͏ͯ͠σόΠε઀ଓϓϩϯϓτҎ֎ʹ
    ѱҙ͋ΔυϥΠόͷ࣮ߦΛ๷͙ज़͕ͳ͍
    WebUSB͕҆ఆ൛ͷChromeʹ࣮૷͞Εͨ
    https://developers.google.com/web/updates/2017/09/nic61

    View full-size slide

  50. WebUSBͷηΩϡϦςΟ
    ͜ͷϓϩϯϓτͷҙຯ͸
    Ӿཡதͷ8FCϖʔδ͕ಘମͷ஌Εͳ͍υϥΠόͰ
    64#σόΠεΛ޷͖উख͢Δࣄʹಉҙ͠·͔͢

    View full-size slide

  51. XSSͷ؍఺͔Βͷߟ࡯
    WebUSBࣗମʹ
    XSSͷҾ͖ۚͱͳΔ੬ऑੑ͸ݟ͔͍ͭͬͯͳ͍
    WebUSBͰσόΠεͷར༻ΛڐՄͨ͠Webϖʔδʹ
    XSS͕Մೳͳ੬ऑੑ͕͋ͬͨ৔߹
    USBσόΠε͕߈ܸऀͷखʹତͪΔڪΕ͕͋Δ
    (ྫ͑͹ѱҙ͋ΔϑΝʔϜ΢ΣΞΛ࢓ࠐ·ΕΔͱ͔)
    ͱ͸͍͑WebUSBΛୟ͘ʹ͸
    Secure Context಺Ͱ࣮ߦ͞ΕΔඞཁ͕͋Δҝ
    ͜ΕΛར༻͢ΔXSS͸༰қͰ͸ͳ͍ͱࢥΘΕΔ

    View full-size slide

  52. ChromeͷWebUSBͷ࣮૷͸
    Χʔωϧ͔ΒσόΠεΛσλον͠ͳ͍
    ΧʔωϧυϥΠό͕Ѳ͍ͬͯΔσόΠε͸
    Ϣʔβ͕໌ࣔతʹΧʔωϧ͔Βണ͕͞ͳ͍ݶΓ
    WebUSBʹѲΒͤΔࣄ͕Ͱ͖ͳ͍
    ͦΕ΄͍͠ ΍ΒΜ

    View full-size slide

  53. ·ͱΊ
    WebUSB͸ϒϥ΢βͱUSBσόΠε͕
    ௚઀ձ࿩͢Δ͜ͱΛՄೳʹ͢ΔAPIͰ͋Δ
    JavaScriptͰυϥΠόΛॻ͚͹
    ͲΜͳUSBσόΠεͰ΋ಈ͔͢͜ͱ͕Ͱ͖Δ
    Ͱ΋ϦϞʔτ͔Β߱ͬͯདྷͨ
    ಘମͷ஌Εͳ͍υϥΠό͸৴༻Ͱ͖Δͷ͔

    View full-size slide

  54. σόοά෩ܠ
    ͏·͘௨৴Ͱ͖ͳ͍࣌͸WiresharkͰUSBΛݟΑ͏

    View full-size slide