$30 off During Our Annual Pro Sale. View Details »

WebUSBとは何か、何を引き起こしたか、今どうなっているか

Fadis
October 27, 2019

 WebUSBとは何か、何を引き起こしたか、今どうなっているか

これは WebUSBでレイヤーが低まるWeb開発 (
https://speakerdeck.com/fadis/webusbdereiyagadi-maruwebkai-fa-shibuya-dot-xssban ) にその後起こった問題についての話を書き足したものです

Fadis

October 27, 2019
Tweet

More Decks by Fadis

Other Decks in Programming

Transcript

  1. WebUSBͱ͸Կ͔
    ԿΛҾ͖ى͔ͨ͜͠
    ࠓͲ͏ͳ͍ͬͯΔ͔
    NAOMASA MATSUBAYASHI
    ιʔείʔυ: https://github.com/Fadis/fadis.github.io/tree/master/webusb

    View Slide

  2. 2017೥9݄
    WebUSBΛαϙʔτ͢ΔॳͷWebϒϥ΢β
    Google Chrome 61͕ϦϦʔε͞Εͨ

    View Slide

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

    View Slide

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

    View Slide

  5. σόΠεΛऔಘ͢Δ
    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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. σόΠεʹσʔλΛૹΔ/໯͏
    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 Slide

  11. ௨৴Ͱ͖Δঢ়ଶʹ͢Δ
    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 Slide

  12. σόΠεʹσʔλΛૹΔ/໯͏
    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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. $ 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 Slide

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

    View Slide

  18. 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 Slide

  19. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. 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 Slide

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

    View Slide

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

    View Slide

  28. 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 Slide

  29. 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 Slide

  30. 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 Slide

  31. 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 Slide

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

    View Slide

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

    View Slide

  34. 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 Slide

  35. FAT32
    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
    };
    return load_fat32file(
    fsinfo, fsinfo.rootdir_entry, 0
    ).then( raw_root_dir => {
    let root_dir =
    parse_fat32directory( raw_root_dir );
    fsinfo[ 'rootdir' ] = root_dir;
    FATʹ͸
    ࠓಡΜͰ͍ΔΫϥελͷ࣍ʹಡΉ΂͖Ϋϥελ͕
    Ͳ͔͕͜ه࿥͞Ε͍ͯΔ
    FATΛಡΉ

    View Slide

  36. 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 Slide

  37. 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 Slide

  38. 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 Slide

  39. 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 Slide

  40. 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 Slide

  41. ϒϥ΢βͰUSBϑϥογϡϝϞϦΛಡΉΑ!
    http://fadis.github.io/webusb/storage/
    ஫ҙ: ޙड़͢Δ࣮૷ͷมߋʹΑΓ
    ݱࡏͷGoogle ChromeͰ͸ಈ͔ͳ͘ͳ͍ͬͯ·͢

    View Slide

  42. USBϑϥογϡϝϞϦ͔ΒಡΜͩը૾Λ
    Webϖʔδʹදࣔ͢ΔΑ!
    http://fadis.github.io/webusb/storage/
    ஫ҙ: ޙड़͢Δ࣮૷ͷมߋʹΑΓ
    ݱࡏͷGoogle ChromeͰ͸ಈ͔ͳ͘ͳ͍ͬͯ·͢

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. ChromeͷWebUSBͷ࣮૷͸
    Χʔωϧ͔ΒσόΠεΛσλον͠ͳ͍
    WebUSB͸Χʔωϧ͕Ѳ͍ͬͯΔσόΠεΛ
    ࢖༻͢Δࣄ͸Ͱ͖ͳ͍
    ͦΕ΄͍͠ ΍ΒΜ
    γεςϜͷॏཁͳUSBσόΠε͸Χʔωϧ͕Ѳ͍ͬͯΔഺͳͷͰ
    ͦ͏ͨ͠σόΠε͸WebUSBʹΑΔૢ࡞͔Βอޢ͞ΕΔ

    View Slide

  53. ChromeͷWebUSBͷ࣮૷͸
    Χʔωϧ͔ΒσόΠεΛσλον͠ͳ͍
    ͦΕ΄͍͠ ΍ΒΜ
    γεςϜͷॏཁͳUSBσόΠε͸Χʔωϧ͕Ѳ͍ͬͯΔഺͳͷͰ
    ͦ͏ͨ͠σόΠε͸WebUSBʹΑΔૢ࡞͔Βอޢ͞ΕΔ
    ຊ౰ʹ?
    WebUSB͸Χʔωϧ͕Ѳ͍ͬͯΔσόΠεΛ
    ࢖༻͢Δࣄ͸Ͱ͖ͳ͍

    View Slide

  54. 2018೥3݄
    WebUSBΛ࢖ͬͯ
    Ұ෦ͷFIDO U2FυϯάϧΛ
    ୆ແ͠ʹग़དྷΔࣄ͕ࣔ͞Εͨ
    https://www.offensivecon.org/speakers/
    2018/markus-and-michele.html

    View Slide

  55. FIDO U2FͰϢʔβొ࿥
    Ϣʔβొ࿥
    伴ੜ੒Λཁٻ
    "QQ*%ʹରԠ͢Δ
    ൿີ伴ͱެ։伴Λ࡞Δ
    ެ։伴ͱೝূثূ໌ॻͱ
    ೝূثূ໌ॻͰ࡞ͬͨॺ໊Λฦ͢
    ॺ໊Λ࢖ͬͯ
    ৴པͰ͖ΔೝূثͰ͋ΔࣄΛ֬ೝ
    ެ։伴Λอଘ
    ొ࿥׬ྃ
    AppIDΛఴ͑ͯ伴ੜ੒Λཁٻ

    View Slide

  56. FIDO U2FͰϩάΠϯ
    ύεϫʔυೝূ
    "QQ*%ʹରԠ͢Δ
    ൿີ伴ͰDIBMMFOHFΛ҉߸Խ
    ҉߸Խͨ͠challengeΛฦ͢
    อଘͯ͋͠Δެ։伴Ͱ
    DIBMMFOHFΛ෮߸Ͱ͖ΔࣄΛ֬ೝ
    ೝূ׬ྃ
    ύεϫʔυΛ֬ೝ
    challengeΛૹ৴
    AppIDͱchallengeΛૹ৴

    View Slide

  57. FIDO U2FͰϩάΠϯ
    ύεϫʔυೝূ
    "QQ*%ʹରԠ͢Δ
    ൿີ伴ͰDIBMMFOHFΛ҉߸Խ
    ҉߸Խͨ͠challengeΛฦ͢
    อଘͯ͋͠Δެ։伴Ͱ
    DIBMMFOHFΛ෮߸Ͱ͖ΔࣄΛ֬ೝ
    ೝূ׬ྃ
    ύεϫʔυΛ֬ೝ
    challengeΛૹ৴
    AppIDͱchallengeΛૹ৴
    WebͰར༻͢Δ৔߹AppID͸
    web origin͕༻͍ΒΕΔ
    https://example.com:8080/path/to/the/file?param=value
    ͜ͷ෦෼
    σόΠε͸Webϒϥ΢β͕ਖ਼͍͠web originΛࢦఆ͢ΔࣄΛظ଴͍ͯ͠Δ

    View Slide

  58. WebAuthN
    /dev/hidraw*
    USB HID
    WebUSB
    FIDO U2Fυϯάϧ͸USB HID
    ΧʔωϧͷυϥΠό͕σόΠεΛѲ͍ͬͯΔҝ
    WebUSB͕͜ͷσόΠεΛ࢖༻͢Δࣄ͸Ͱ͖ͳ͍

    View Slide

  59. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC NFC
    WebAuthN
    /dev/hidraw*
    USB HID
    NFCͰ΋઀ଓͰ͖ΔΑ͏ʹ
    USB HIDΛ஻Δίϯτϩʔϥͱ
    ҉߸σόΠεΛ෼཭͢Δࣄ͕ଟ͍

    View Slide

  60. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC
    WebAuthN
    /dev/hidraw*
    USB HID
    Ұ෦ͷFIDO U2Fυϯάϧ͸
    NFCϦʔμʔͷͳ͍ϚγϯͰ
    ͜ͷσόΠεΛεϚʔτΧʔυͱͯ͠࢖͏ҝʹ
    CCIDΛੜ΍͍ͯͨ͠
    USB CCID

    View Slide

  61. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC
    WebAuthN
    /dev/hidraw*
    USB HID
    USB CCID
    WindowsΛআ͘ଟ͘ͷOSͰ͸USB CCID͸
    ΧʔωϧͰ͸ͳ͘ϢʔβۭؒͷσʔϞϯ͕໘౗ΛݟΔ
    ࢖͏ͭ΋Γ͕ͳ͚Ε͹σʔϞϯ͸ىಈ͞Εͳ͍ҝ
    σόΠε͸୭΋Ѳ͍ͬͯͳ͍ঢ়ଶʹͳΔ

    View Slide

  62. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC
    WebAuthN
    /dev/hidraw*
    USB HID
    USB CCID WebUSB
    ಡΊΔɺಡΊΔͧ!

    View Slide

  63. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC
    HID
    CCID WebUSB
    ຊ෺ͷWebαΠτ
    ϑΟογϯά
    ϑΟογϯάαΠτͷ
    web origin
    web origin͕ຊ෺ͷWebαΠτͱҧ͏ҝ
    ຊ෺ͷWebαΠτͰ࢖͑ͳ͍݁ՌΛฦ͢
    ͜ͷೝূ͸
    ࣦഊ͢Δ

    View Slide

  64. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC
    HID
    CCID WebUSB
    ຊ෺ͷWebαΠτ
    ϑΟογϯά
    ຊ෺ͷWebαΠτͷ
    web origin
    web origin͕ຊ෺ͷWebαΠτͷ෺ͳͷͰ
    ຊ෺ͷWebαΠτͰ࢖͑Δ݁ՌΛฦ͢
    ͜ͷೝূ͕
    ௨ͬͯ͠·͏
    ϑΟογϯά͕੒ཱ͢Δ

    View Slide

  65. WebUSB͸ϢʔβʹσόΠεΛ࢖ͬͯྑ͍͔֬ೝ͢Δ͕
    WebαΠτͷೝূͰ
    ࠓ·͞ʹσόΠεΛ࢖͓͏ͱ͍ͯ͠Δ
    Ϣʔβ͸͜ͷμΠΞϩάΛݟͨΒ
    ڐՄ͢Δ

    View Slide

  66. γεςϜͷॏཁͳUSBσόΠε͸Χʔωϧ͕Ѳ͍ͬͯΔഺͳͷͰ
    ͦ͏ͨ͠σόΠε͸WebUSBʹΑΔૢ࡞͔Βอޢ͞ΕΔ
    ͦΜͳ͜ͱͳ͔ͬͨ

    View Slide

  67. γεςϜͷॏཁͳUSBσόΠε͸Χʔωϧ͕Ѳ͍ͬͯΔഺͳͷͰ
    ͦ͏ͨ͠σόΠε͸WebUSBʹΑΔૢ࡞͔Βอޢ͞ΕΔ
    USBσόΠεΛWebϖʔδ͕࢖༻ͯ͠ྑ͍͔
    μΠΞϩάͰ֬ೝ͍ͯ͠ΔͨΊҙਤ͠ͳ͍σόΠε͸อޢ͞ΕΔ
    ͦͷঢ়گͰ֬ೝ͞ΕͨΒڐՄͯ͠͠·͏
    ͦΜͳ͜ͱͳ͔ͬͨ

    View Slide

  68. γεςϜͷॏཁͳUSBσόΠε͸Χʔωϧ͕Ѳ͍ͬͯΔഺͳͷͰ
    ͦ͏ͨ͠σόΠε͸WebUSBʹΑΔૢ࡞͔Βอޢ͞ΕΔ
    USBσόΠεΛWebϖʔδ͕࢖༻ͯ͠ྑ͍͔
    μΠΞϩάͰ֬ೝ͍ͯ͠ΔͨΊҙਤ͠ͳ͍σόΠε͸อޢ͞ΕΔ
    FIDO U2Fυϯάϧ͸σόΠευϥΠό͕ӕΛͭ͘ͱ
    ੒ཱ͠ͳ͍͕σόΠευϥΠό͸৴པͰ͖Δ
    ݁Ռ
    2ཁૉೝূʹΑΔϑΟογϯάରࡦ͕୆ແ͠ʹͳΔ
    ࠓճͷ৔߹߈ܸऀ͕͜ΕΛ༻ҙ͢Δ
    ͦͷঢ়گͰ֬ೝ͞ΕͨΒڐՄͯ͠͠·͏
    ͦΜͳ͜ͱͳ͔ͬͨ

    View Slide

  69. "Firefox͕WebUSBΛαϙʔτ͠ͳ͍ͷ͸·͞ʹ͜ͷͨΊͩ
    -- ͜Ε͸ຊ࣭తʹ҆શʹ͢Δࣄ͕Ͱ͖ͳ͍"
    https://twitter.com/adambroach/status/969342244653273089
    ͜ͷ੬ऑੑ͕ൃද͞Εͨ࣌ͷ
    MozillaͷதͷਓAdam RoachࢯͷTwitterͰͷ൓Ԡ

    View Slide

  70. /'$Ͱಈ͘
    ҉߸σόΠε
    /'$Ϧʔμʔ
    64#)*%Λ஻Δ
    ίϯτϩʔϥ
    NFC
    HID
    ຊ෺ͷWebαΠτ
    ϑΟογϯά
    ͦͷυϯάϧΛ
    WebUSBͰ࢖͍͍ͨͳ
    ҰํWindowsͰ͸
    Ͳ͏ͧ
    ͑ͬ
    ͑ͬ
    USB HIDΛWebUSBͰ࢖͓͏ͱ͢Δͱ
    σόΠεΛOS͔Βୣ͍औΔڻ͖ͷ࣮૷ʹͳ͍ͬͯͯ
    શͯͷυϯϧά͕ӨڹΛड͚ͨ

    View Slide

  71. 2018೥3݄
    Google ChromeͰWebUSBΛ࢖͑ͳ͘͢Δ
    ηΩϡϦςΟΞοϓσʔτ͕࣮ࢪ͞ΕΔ

    View Slide

  72. 2018೥5݄
    ੬ऑੑͷରࡦΛߦ͍
    WebUSB͕࠶౓ར༻Մೳʹͳͬͨ
    Google Chrome 67͕ϦϦʔε͞Εͨ

    View Slide

  73. // This list must be sorted according to CompareEntry.
    const UsbBlocklist::Entry kStaticEntries[] = {
    {0x096e, 0x0850, kMaxVersion}, // KEY-ID
    {0x096e, 0x0852, kMaxVersion}, // Feitian
    {0x096e, 0x0853, kMaxVersion}, // Feitian
    {0x096e, 0x0854, kMaxVersion}, // Feitian
    {0x096e, 0x0856, kMaxVersion}, // Feitian
    {0x096e, 0x0858, kMaxVersion}, // Feitian USB+NFC
    {0x096e, 0x085a, kMaxVersion}, // Feitian
    {0x096e, 0x085b, kMaxVersion}, // Feitian
    {0x096e, 0x0880, kMaxVersion}, // HyperFIDO
    {0x09c3, 0x0023, kMaxVersion}, // HID Global BlueTrust Token
    // Yubikey devices. https://crbug.com/818807
    {0x1050, 0x0010, kMaxVersion},
    {0x1050, 0x0018, kMaxVersion},
    {0x1050, 0x0030, kMaxVersion},
    {0x1050, 0x0110, kMaxVersion},
    {0x1050, 0x0111, kMaxVersion},
    {0x1050, 0x0112, kMaxVersion},
    {0x1050, 0x0113, kMaxVersion},
    {0x1050, 0x0114, kMaxVersion},
    {0x1050, 0x0115, kMaxVersion},
    {0x1050, 0x0116, kMaxVersion},
    {0x1050, 0x0120, kMaxVersion},
    {0x1050, 0x0200, kMaxVersion},
    {0x1050, 0x0211, kMaxVersion},
    {0x1050, 0x0401, kMaxVersion},
    {0x1050, 0x0402, kMaxVersion},
    {0x1050, 0x0403, kMaxVersion},
    WebUSB͔Β࢖ͬͯ͸͍͚ͳ͍
    σόΠεΛฒ΂ͨ
    ϒϥοΫϦετ͕Ͱ͖ͨ
    https://github.com/chromium/chromium/blob/master/chrome/browser/usb/usb_blocklist.cc

    View Slide

  74. bool USBDevice::IsProtectedInterfaceClass(wtf_size_t interface_index) const {
    DCHECK_NE(configuration_index_, kNotFound);
    DCHECK_NE(interface_index, kNotFound);
    // USB Class Codes are defined by the USB-IF:
    // http://www.usb.org/developers/defined_class
    const uint8_t kProtectedClasses[] = {
    0x01, // Audio
    0x03, // HID
    0x08, // Mass Storage
    0x0B, // Smart Card
    0x0E, // Video
    0x10, // Audio/Video
    0xE0, // Wireless Controller (Bluetooth and Wireless USB)
    };
    DCHECK(std::is_sorted(std::begin(kProtectedClasses),
    std::end(kProtectedClasses)));
    const auto& alternates = Info()
    .configurations[configuration_index_]
    ->interfaces[interface_index]
    ->alternates;
    for (const auto& alternate : alternates) {
    if (std::binary_search(std::begin(kProtectedClasses),
    std::end(kProtectedClasses),
    alternate->class_code)) {
    return true;
    }
    }
    return false;
    WebUSB͔Β࢖ͬͯ͸͍͚ͳ͍
    σόΠεΫϥεΛฒ΂ͨ
    ϒϥοΫϦετ͕Ͱ͖ͨ
    https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/modules/
    webusb/usb_device.cc
    • USB Audio
    • USB HID
    • USB Mass Storage
    • USB CCID
    • USB Video
    • USB Audio/Video
    • USB BluetoothϨγʔό
    Ҏ্ͷσόΠεΫϥε͸WebUSBېࢭ

    View Slide

  75. ͦͷޙWebUSBʹؔͯ͠
    ໨ཱͬͨ੬ऑੑͷใࠂ͸ͳ͍

    View Slide

  76. WebUSBͷࠓ

    View Slide

  77. ࠓ೔WebUSB͸
    ϚΠίϯϘʔυ΁ͷ
    ϓϩάϥϜͷॻ͖ࠐΈΛ
    ϒϥ΢β্͔Βߦ͏ͷʹ
    ༻͍ΒΕ͍ͯΔ
    https://support.microbit.org/support/solutions/articles/19000084059-beta-testing-web-usb
    micro:bit΁WebUSBΛ࢖ͬͯॻ͖ࠐΉํ๏ʹ͍ͭͯ

    View Slide

  78. https://developer.mozilla.org/en-US/docs/Web/API/USB
    Chrome
    WebUSBΛఏҊ͢ΔGoogleͷϒϥ΢β
    Opera
    GoogleͷBlinkΛ࠾༻ͨ͠Β
    WebUSBʹରԠͯ͠͠·ͬͨϒϥ΢β
    ͦͷଞ
    WebUSBʹରԠ͢Δؾ഑ͳ͠

    View Slide

  79. ·ͱΊ
    ϒϥ΢βͱUSBσόΠε͕௚઀ձ࿩͢Δ͜ͱΛՄೳʹ͢ΔAPI
    WebUSB
    WebUSBͷηΩϡϦςΟʹର͢Δ؁͍ಡΈ͕
    FIDO U2FΛແྗԽ͢Δͷʹ༻͍ΒΕͨ
    FIDO U2FυϯάϧΛϒϥοΫϦετʹొ࿥͢ΔࣄͰ
    ໰୊͸ҰԠղܾͨ͠

    View Slide

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

    View Slide