쿠...sal

[컴] iOS 보안-시스템 보안

iOS 부팅 절차 / boot time / booting process / enclave


ref. 1 의 System Security 의 일부를 번역했다. 자세한 내용은 ref. 1을 보도록 하자.

iOS 보안-시스템 보안

기기암호화(device encryption) 는 기본적으로 설정되어 있으며, 설정을 변경할 수 없다.

Secure Boot Chain

  1. iOS 켜기
  2. --> AP 가 Boot ROM 의 code 를 실행
    1. the hardware root of trust 라고 알려져 있다.
    2. the chain of trust 의 첫번째 단계(first step) 이다.
    3. 이 code 는 변경불가능(immutable code)
    4. 칩제조(chip fabrication) 때 만들어 진다.
    5. implicitly trusted
    6. Apple Root CA public key 를 가지고 있다.
      1. 이 public key 는 iBoot bootloader 가 load 되기 전에 Apple 에 의해 서명(sign) 됐다는 것을 확인(verify) 하는데 사용된다.
  3. Boot ROM 에 의해 추가적인 Low-Level Bootloader(LLB) stage 가 load 되고, 확인(verify) 된다.
    1. A9를 포함한 A9 이전의 A시리즈 processor 에만 존재.
  4. 만약 Boot ROM 이 LLB 또는 iBoot 을 load 하는 것을 실패하면,  기기는 DFU mode (Device Firmware Upgrade)로 들어가게 된다.
    1. 오래된 기기들에서 LLB 를 load 하는 것을 실패 하거나,
    2. 새로운 기기들에서 iBoot 을 load 하는 것을 실패하는 것
  5. LLB 나 iBoot 이 다음 작업(next step) 을 load 하거나 verify 하는 것을 실패하는 경우
    1. startup 은 멈춰지고(halt) iTunes 에 접속하라는 화면이 보여진다. 
    2. --> recovery mode 이다.
  6. DFU mode 로 가거나, recovery mode 로 들어가면, USB 를 이용해서 iTunes 에 접속해서 "공장 기본 세팅(factory default settings)" 로 복구되어 져야만 한다.
  7. 기기가 Recovery Mode 와 DFU Mode 로 들어가기 전에 The Boot Progress Register (BPR) 가 update 된다. 이 update 된 값을 보고 Secure Enclave 가 user data 에 대한 접근제한을 하게 된다.
    1. Recovery Mode: A10, S2 그리고 최신의 SoCs 들을 가진 기기에서 iBoot 에 의해 BPR 이 set 된다.
    2. DFU Mode : A12 SoC 를 가진 기기들에서 Boot ROM 에 의해 BPR 이 set 된다.
  8. --> iBoot 이 일들을 마치면
  9. --> iBoot 은 iOS kernel 을 확인(verify) 하고, 실행한다.

baseband subsystem 또한 이것과 비슷한 자신의 secure booting 을 이용한다.
Secure Enclave coprocessor 또한 secure boot process 를 이용하고, 그로인해 그것의 분리되 소프트웨어가 verified and signed by Apple 되었다는 것을 확신시켜준다.


시스템 소프트웨어 권한부여(System Software Authorization)

만약 과거버전의 iOS의 설치가 가능하다면 해커는 과거버전의 iOS를 깔고 권한을 가져올 수 있다. 이것을 막기 위해서 iOS는 System Software Authorization 을 이용한다. Secure Enclave 도 이것을 이용한다.


iOS update

iOS update 는 2가지 방식(iTunes, OTA) 으로 가능하다. iTunes 로 업데이트를 할 때는 iOS image 전체를 download 하지만 OTA 로 할 때는 patch 를 할 내용만 다운로드한다. 그리고 Content Caching 옵션을 켠 macOS High Sierra 를 돌리는 Mac 을 사용한다면, 미리 cache 되어 있게 되어서, iOS 의 update 를 바로 할 수 있게 해준다.

installation authorization server

iOS update 할때 기기는 Apple 의 installation authorization server 에 접근한다.
그리고

  1. bundle의 measurements: 설치될 설치 bundle들(iBoot, kernel, iOS Image)에 대한 암호화된 measurements
  2. nonce : signed data 를 가져다가 다른 기기에 쓰는 것을 막아주고, system software 를 변경하지 못하게 막는다.
  3. ECID: 기기의 고유한  ECID(Exclusive Chip Identification) 를 서버로 보내게 된다.

installation authorization server 에서 하는일

서버에서는  이 installation bundle 의 버전들이 맞는 구성인지를 확인한다. 예를 들면 특정버전의 iBoot 은 특정 version 의 kernel 을 써야만 하는식이다. 이렇게 버전이 맞으면 이 measurement 에 ECID 를 더해서 sign 을 하고 이 sign 된 data 를 기기에 보내준다.

boot-time verification

위에 설명한 startup process 가 Apple 이 sign 한 code 만 기기에 설치되는 것을 보장하게 해준다.
  • Apple 에게서 온 signature : boot time 에 일어나는 chain-of-trust evaluation 는 Apple 에게서 온 signature 를 verify 한다.
  • disk 에서 load 되는 item 의 measurement: "disk 에서 load 되는 item 의 measurement (기기의 ECID를 이용해서 만들어져 있다.)"와 "signature 에 의해 감싸진 measurement(위의 installation authorization server 에서 보내온)"가 맞는지를 verify 한다.

이 절차들이 특정기기에 대한 authorization 을 해주고, 기기에서 옛 iOS버전이 다른 곳으로 copy 안되도록 한다.


Secure Enclave

Secure Enclave 는 보조 프로세서이다. SoC 안에 들어있다.(fabricated)
암호화된 메모리를 사용하고, 하드웨어 난수발생기(random number generator) 를 포함한다.
Secure Enclave 는 데이터 보호 키 관리에게 (Data Protection key management) 모든 암호화 기능(cryptographic operations)들을 제공하고 심지어 커널이 허락한 상태에도(kernel has been compromised) Data Protection 의 integrity (데이터가 변경, 파괴되지 않은 상태)을 유지한다.
AP(Application processor) 와 Secure Enclave 사이의 통신은 interrupt-driven mailbox 와 shared memory data buffer 들과 분리되어 있다.


Secure Enclave 는 Secure Enclave Boot ROM 을 가지고 있다. application processor Boot ROM 과 비슷하게, Secure Enclave Boot ROM 은 변경이 불가능한 code (immutable code) 이다. Secure Enclave 를 위한 "하드웨어 root of trust" 를 확립한다.(establish)

Secure Enclave 는 "L4 마이크로 커널의 Apple-customized version" 에 기초해서 만들어진 Secure Enclave OS 를 실행한다. 이 Secure Enclave OS 는 Apple 에 의해 sign 되고, Secure Enclave Boot ROM 에 의해 verify 되고, 개인의 소프트웨어 업데이트 프로세스를 통해 update 된다.

device 가 시작될 때, Secure Enclave Boot ROM 이 수명이 짧은 메모리 보호 키(ephemeral memory protection key)를 만든다. 이 키는 기기의 UID 와 얽히게 만들고(entangle), 기기의 memory space 의 "Secure Enclave 부분"을 암호화 할 때 사용된다.

예외적으로 Apple 의 A7 에선, Secure Enclave memory "memory protection key" 로도 인증(authenticated) 이 가능하다. A11과 A11 이후 그리고 S4 SoC 들에서는 "memory protection key" 와 on-chip SRAM 에 저장된 nonce 들에 의해 인증된다.(authenticated)
그리고, A11과 A11 이후 그리고 S4 SoC 들에서 integrity tree 는 보안이 중요한 Secure Enclave memory 의 replay 를 막기위해 사용되어진다.

Secure Enclave 에 의해 file system 에 저장된 data 는 UID 와 entangled 된 key와 anti-replay counter 로 암호화되어 진다. anti-replay counter 는 dedicated 비휘발성 메모리 IC 안에 저장되어 있다.

A12 와 S4 SoC 들이 있는 기기들에서, Secure Enclave 는 anti-replay counter storage 를 위해 secure storage IC 와 짝을 이룬다. secure storage IC 는 immutable ROM code, 하드웨어 random number generator, 암호화엔진들, physical tamper detection(물리적인 조작 감지) 과 함께 디자인됐다. counter 들을 읽고 업데이트 하기 위해서, Secure Enclave 와 storage IC 는 counter 들에게 exclusive access 를 보장하는 '안전한 protocol '를 사용한다.

Secure Enclave 의 "anti-replay 서비스"들은 event 에 사용된 data 의 철회(revocation of data over events)를 위해 사용되어진다.

이 이벤트들은 anti-replay 의 경계들(boundaries)을 표시 해 준다.
이벤트는 다음것들을 포함한다. 하지만 제한되진 않는다.

  • Passcode change
  • Touch ID or Face ID enable/disable
  • Fingerprint add/delete
  • Face ID reset
  • Apple Pay card add/remove
  • Erase All Content and Settings

Secure Enclave 는 또한 지문을 처리하고, Touch ID 와 Face ID 센서들로 부터 오는 얼굴데이터를 처리하는 책임도 진다. 그리고 맞는지 여부를 결정하고, 그리고나서, 유저를 대신해서 access 또는 구매를 가능하게 한다.

Reference

  1. https://www.apple.com/business/site/docs/iOS_Security_Guide.pdf

[컴][웹] AdoniJS at Start up

아도니스 제이에스 / js


Adonisjs at startup

app.js

_loadPreLoadFiles 이전에 this._registerProviders()와 await this._bootProviders()를 호출한다. 이 때 app.js 에 접근해서 Providers 를 가져오고, 이 Provider 의 register() 와 boot() 을 호출한다.

preloaded files

아도니스js 에서 처음에 load 하는 file 들은 다음과 같다.(_loadPreLoadFiles)
  • 'start/routes',
  • 'start/events',
  • 'start/socket',
  • 'start/kernel',
  • 'start/wsKernel'
이 부분은 Ignitor source 에서 확인할 수 있다.
new Ignitor(require('@adonisjs/fold'))
  .appRoot(__dirname)
  .fireHttpServer()
  .catch(console.error)


class Ignitor {
  constructor (fold) {
    this._fold = fold
    this._appRoot = null
    this._modulesRoot = null
    this._loadCommands = false

    /**
     * Files to be preloaded
     *
     * @type {Array}
     */
    this._preLoadFiles = [
      'start/routes',
      'start/events',
      'start/socket',
      'start/kernel',
      'start/wsKernel'
    ]

    /**
     * Default app file
     *
     * @type {String}
     */
    this._appFile = 'start/app.js'
    ...
  }
  ...
  async fire () {
   ...
   this._registerProviders()
   await this._bootProviders()
   ...
   this._loadPreLoadFiles()
  }


  /**
   * Return the exported values from the appFile. Also
   * it will validate the exports object to have all
   * required keys.
   *
   * @method _getAppAttributes
   *
   * @return {Object}
   *
   * @private
   */
  _getAppAttributes () {
    return require(path.join(this._appRoot, this._appFile))
  }

  /**
   * Registers an array of providers to the Ioc container. This
   * method will make use of the `appFile` to get the providers
   * list.
   *
   * @method _registerProviders
   *
   * @return {void}
   *
   * @private
   */
  _registerProviders () {
    this._callHooks('before', 'providersRegistered')

    /**
     * Getting list of providers and registering them.
     */
    const { providers, aceProviders } = this._getAppAttributes()
    const providersToRegister = this._loadCommands ? providers.concat(aceProviders) : providers
    this._fold.registrar.providers(providersToRegister).register()

    debug('registered providers')
    this._callHooks('after', 'providersRegistered')
  }

  /**
   * Boot providers
   *
   * @method _bootProviders
   *
   * @return {void}
   *
   * @async
   *
   * @private
   */
  async _bootProviders () {
    this._callHooks('before', 'providersBooted')

    /**
     * The providers set set on `registrar` when they were registered. We
     * use the same set to boot the previously registered providers.
     */
    await this._fold.registrar.boot()

    debug('booted providers')
    this._callHooks('after', 'providersBooted')
  }

  ...
  _loadPreLoadFiles () {
    this._callHooks('before', 'preloading')
    ...

    this._preLoadFiles.forEach((file) => {
      const filePath = path.isAbsolute(file) ? file : path.join(this._appRoot, file)

      /**
       * Require file when it's not optional or when optional
       * file exists
       */
      if (!this._isOptional(file) || this._fileExists(filePath)) {
        require(filePath)
      }
    })

    this._callHooks('after', 'preloading')
  }
}



[컴][웹] laravel 에서 큰 Database result 를 처리할 때




Laravel 에서 큰 Database result 를 처리할 때

아래 글들을 참고하면 된다. 관련 source code 도 확인할 수 있다.

Fatal error: Allowed memory size of 37748736 bytes exhausted (tried to allocate 72 bytes) in /home/cocktail/apps/cocktail_lumen/vendor/illuminate/database/Eloquent/Model.php on line 279

chunk vs cursor

chunk 를 사용하면 일반적은 get() 을 사용하는 것보다 memory 를 많이 아낄 수 있다. 이보다 더 확실히 아낄 수 있는 것은 cursor 를 사용하는 것이다.



[컴][웹] 간단하게 주기적으로 message 를 수신하는 web page

EventSource / cors / jsonp / different host / subscribe model /


tornado

EventSource object | Javascript


EventSource object 로 server 에 연결(subscribe) 을 해서 주기적으로 event 를 받을 수 있다. 자세한 동작은 일단 생략한다. ref. 2 에서 어느정도 설명을 해준다.


different domain

domain 이 다른 곳에 event 를 날려주는 server 가 있어도 무리없이 사용할 수 있다. 보통 html(javascript) 에서 다른 domain 인 경우는 보안을 이유로 ajax 요청이 되지 않는다. 그래서 jsonp 등(일반적으로 Cross-Origin Resource Sharing 이라고 이야기하는 방법)을 이용한다.

하지만 이 EventSource 는 그런 것의 제약을 받지 않는다. [ref. 1]

아래처럼 다른 domain 인 경우에는 다른 domain 을 uri 로 적어주면 된다.

var evtSource = new EventSource("//api.example.com/ssedemo.php", { withCredentials: true } );

evtSource.addEventListener("ping", function(e) {
  var newElement = document.createElement("li");
 
  var obj = JSON.parse(e.data);
  newElement.innerHTML = "ping at " + obj.time;
  eventList.appendChild(newElement);
}, false);


이렇게 다른 domain 에 대한 제약이 없어서 좋은 점은 기존의 web server 와 관계없이 새로운 event 처리를 하는 server 를 사용할 수 있다는 점이다. 그러면 주기적인 push event 등을 처리하는 server 를 따로 둘 수 있다.



Server side event vs WebSocket

event driven page 를 만들 땐 SSE 를 사용해도 되고, WebSocket 을 사용해도 된다. 개인적으로는 필요한 부분은 단방향이라서, 양방향 통신이 되는 WebSocket 이 굳이 필요하지 않았다.

아래는 SSE 와 WebSocket 을 간략하게 정리했다.

SSE

  1. 2009년 4월 23 일 WHATWG 에서 승인했다. [ref. 7] 
  2. 하지만 SSE 가 좀 더 간편하다. 
  3. 그리고 http protocol 위에서 구현되었다. 
  4. 단방향 통신
  5. client 의 connection 이 종료되었는지 여부를 알 수는 없다.(http 라서 당연한 것일지도.)


WebSocket

  1. 반면에 WebSocket 은 Tcp/ip 로 다른 port 를 사용한다. 그래서 이 port 가 firewall 에 의해 막혀 있을 수도 있고, 
  2. 이 녀석을 이용하려면, protocol 을 또 정의해야 한다. (ref. 5 에서 좀 더 다양한 의견을 확인할 수 있다.)
  3. 양방향 통신






Reference

  1. Using server-sent events - Web APIs | MDN
  2. Stream Updates with Server-Sent Events - HTML5 Rocks
  3. Asynchronous connections - RethinkDB
  4. Build a real time data push engine using Python and Rethinkdb | IMPYTHONIST
  5. SSE vs Websockets - Streamdata.io
  6. Lessons Learned Architecting Realtime Applications | Lincoln Loop
  7. Python and Real-time Web | Eat at Joe's
  8. Tornado server-sent events · GitHub : tornado 로 SSE 구현 예제.
  9. Building RESTful APIs with Tornado | Dr Dobb's

[컴][웹] V8 에서 JavaScript 의 pipeline

v8 engine 의 동작 / 동작원리 / 크롬 자바스크립트 엔진 / 크롬 엔진 / 크롬 렌더링 엔진 /



V8 에서 JavaScript 의 pipeline

v5.9 이전의 pipeline

from: https://v8.dev/blog/ignition-interpreter

원래는 baseline compiler(위의 그림에서 Ignition 과 Full codegen 의 위치에 있는 것이라 여기면 될 듯 하다.) 가 machine code 를 빠르게 만들고, 이 code 가 실행되는 동안에 이 code를 분석하고 일부를 optimizing compiler 가 optimized code 로 다시 compile 한다.

이 때 사용되는 2개의 optimizing compiler 가 crankshaft, turbofan 이다.

  • TurboFan : Ignition의 bytecode를 바로 최적화된 machine code 로 바꿀 수 있다.[ref. 4]
  • Crankshaft: 소스코드를 다시 컴파일을 해서 최적화된 machine code 를 만든다.[ref. 4]
참고로, v5.9 부터 Full-codegen 과 Crankshaft 는 사용하지 않는다.[ref. 4]

Ignition 의 등장

그런데 이 상황에서 Ignition 을 만들어서 "baseline compiler 가 machine code 를 만드는 것"을 대신해서 Bytecode 를 만들게 했다.

Ignition 는 처음에 모바일에서 사용하기 위해서 만들었다.[ref. 4]  JIT 가 만든 machine code 의 size 가 커서 메모리를 너무 잡아먹었기 때문이다.  Ignition 에 의해서 chrome tab 마다 메모리를 약 5% 정도 아꼈다.

Ignition 은 bytecode 를 생성하는 compiler 이고, 이녀석은 이전의 baseline compiler 를 대체한다.  Ignition 이 bytecode 를 만들때도 당연히 최적화를 한다.

이 Ignition 은 register machine 이다. 이것은 stack machine 과 다르다. 이건 내 생각이지만, 안드로이드의 경험이 덕을 본듯 하다. (stack-based vs register-based)

Ignition 이 생기면서 고성능의 interpreter 가 생겼고 이 녀석이 Ignition 이 만든 bytecode 를 실행해주는데, 실제웹사이트에서 속도가 이전의 baseline compiler 가  만든 code 의 속도에 근접한다.

References

  1. Ignition · V8 : ignition 에 대한 여러자세한 설명들의 link 들이 있다.
  2. Firing up the Ignition interpreter · V8
  3. Home · v8/v8 Wiki · GitHub
  4. Launching Ignition and TurboFan · V8
  5. TurboFan · V8 : turbofan 에 대한 정보들이 모여 있다.


[컴][자료] Computer Science 관련 공부 자료

컴공 공부 자료 / 컴싸 자료 / 컴사 자료 / CS study resource / 공부 자료 / 이북 / ebook / 동영상 강의 / 동강 / study resources


ebook

site

실습 자료

[웹][컴] Digging into the TurboFan JIT 번역




TurboFan JIT

TurboFan JIT 은 이름에서 알 수 있듯이 Just In Time interpreter(runtime, vm 뭐라고 부르든 상관없을듯) 라고 보면 될 듯 하다.

ref. 1 은 TurboFan 의 design 에 대한 이야기이다. 여기서는 ref. 1 의 이야기를 정리하는 수준으로 작성할 것이다. 자세한 이야기는 ref. 1을 확인하자.

이전에 쓰던 JIT 이 CrankShft JIT 이다.
  • CrankShaft JIT --> TurboFan JIT

TurboFan 의 장점-layered architecture

  • 아래 3개를 좀 더 명확하게 분리 시켰다. --> 좀 더 명확하고, 견고한 code 를 가능하게 한다.
    • source-level language (JavaScript)
    • the VM's capabilities (V8)
    • the architecture's intricacies (from x86 to ARM to MIPS)
  • 최적화기능들(optimizations)과 기능(feature)들을 구현시 효과적으로 할 수 있다.
    • code 가 architecture-dependent backend 들과 분리돼서, 새롭게 추가되는 Javascript 의 기능들을 추가하기 수월해 졌다.
  • 좀 더 효과적인 unit test 를 작성할 수 있다.
  • code 도 줄여준다.(CrankShaft 에서 13,000~16,000 라인이던 부분이 3,000 라인 미만)
  • 여러 architecture 의 engineer 가 좀 더 효율적으로 작업할 수 있게 해준다.

좀 더 수준높은 최적화 방법들

TurboFan JIT 은 이전의 CrankShaft 보다 좀 더 발전된 기술을 이용해서 좀 더 공격적인 optimization 들을 한다.
  • Sea of Nodes IR: 디자인의 핵심은 코드의 "좀 더 유연해진 Sea of Nodes IR(internal representation)" 이다. 이것이 좀 더 효과적인 "reordering" 과 "최적화(optimization)" 을 가능하게 해준다.
  • TurboFan 은 범위 분석(Numerical range analysis) 을 통해 number-crunching code 를 이해하는 것을 돕는다.
  • The graph-based IR : graph 의 기반한 IR 은 대부분의 최적화기능들이 "simple local reductions" 들로 표현되는 것을 가능하게 해준다. simple local reductions 은 독립적으로 작성하고 테스트하기 좀 더 쉽다. 최적화 엔진은 이 local rules 들을 체계적이고, 빈틈없이 적용한다.
  • graphical representation  을 벗어나는 것은  코드를 loop 에서 덜 자주 사용되는 path들로 옮기기 위해 "혁신적은 scheduling 알고리즘"을 사용한다.
  • 이 알고리즘은 reordering freedom 을 이용한다.
  • architecture-specific optimizations: 최종적으로, 복잡한 "instruction selection" 같은 architecture-specific 최적화 기능들은 좋은 품질의 code 를 위해 각 target platform 의 기능들(features)을 이용한다.

See Also