Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

σόΠεΛऔಘ͢Δ 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σόΠε΁ͷ઀ଓΛཁٻ͢Δ

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

σόΠεʹσʔλΛૹΔ/໯͏ 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 => { … } σόΠεͷ৘ใͷऔಘ΍ σόΠεͷॳظԽΛߦ͏

Slide 11

Slide 11 text

௨৴Ͱ͖Δঢ়ଶʹ͢Δ 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ͷબ୒ ඞཁͳΠϯλʔϑΣʔεΛ઎༗͢Δ

Slide 12

Slide 12 text

σόΠεʹσʔλΛૹΔ/໯͏ 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Ͱૹ৴݁Ռ͕ฦͬͯདྷΔ

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

$ 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͢Δ ϗετଆυϥΠό͸Ξϯϩʔυ͓ͯ͘͠

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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ʹΩʔೖྗ͕͋ͬͨΒ ΤϯυϙΠϯτʹೖྗ಺༰Λྲྀ͢

Slide 19

Slide 19 text

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ʹྲྀ͢

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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ͷ ϔομΛ͚ͭͯ σόΠεʹ౤͛Δ

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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ίϚϯυΛ࣮૷͍ͯ͘͠

Slide 29

Slide 29 text

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ίϚϯυΛ࣮૷͍ͯ͘͠

Slide 30

Slide 30 text

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ίϚϯυΛ࣮૷͍ͯ͘͠

Slide 31

Slide 31 text

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ίϚϯυΛ࣮૷͍ͯ͘͠

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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ͷ਺ͱαΠζͱ։࢝ηΫλ ϧʔτσΟϨΫτϦ͕ॻ͔Ε͍ͯΔҐஔ

Slide 35

Slide 35 text

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ΛಡΉ

Slide 36

Slide 36 text

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; }); }); }); }; ϧʔτσΟϨΫτϦΛಡΉ

Slide 37

Slide 37 text

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͔ΒಡΉඞཁ͕͋ΔΫϥελΛௐ΂ͯ

Slide 38

Slide 38 text

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; } ΫϥελνΣΠϯ ࿈ଓͨ͠Ϋϥελ͸·ͱΊͯ

Slide 39

Slide 39 text

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)

Slide 40

Slide 40 text

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 ); σΟϨΫτϦΤϯτϦ ͦΜͳʹෳࡶ͡Όͳ͍ͷͰؾ߹͍Ͱύʔε͢Δ

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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Λࢦఆ͢ΔࣄΛظ଴͍ͯ͠Δ

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

// 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

Slide 74

Slide 74 text

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ېࢭ

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

WebUSBͷࠓ

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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