Nic Lin's Blog

喜歡在地上滾的工程師

1:1 攪亂器,如何用 Ruby 做可逆推序號

需求場景:預計 token 最多為 7 位數

1:1 可逆推的攪亂器需要三個數字

  1. 最大值 MAX
  2. 任一質數
  3. 質數 與 MAX 互質的數

只有最後一個可以靠前兩個推算,所以一開始的任務先找出最大值和任一質數就能反推。

找數值

進制最後一碼都是字串 “10”,所以進制數減 1 看最後一碼是什麼,以 32 進制來就去看 31 可以轉成什麼,就是 32 進制的最後一碼。

31.to_s(32)

# "v"

所以我們最多能夠接受七位數的話, 7 個 v 就是極限。

"vvvvvvv".to_i(32)

# 34359738367

我們找到了最大數值

所以 CP_MAX = 34359738367

這時候挑一個質數來用

require 'prime'

Prime.each(CP_MAX) do |prime|
  puts prime
end

假設我挑了 18195539 做為這次的質數,驗證一下是不是質數?

CP_COP = 18195539

Prime.prime?(CP_COP)

# true

接下來反算出 inverse

  def inverse(a, n)
    t = 0 ; newt = 1
    r = n ; newr = a
    while newr != 0
      quotient = r / newr
      t , newt = newt , t - quotient * newt
      r , newr = newr , r - quotient * newr
    end
    return "a is not invertible" if r > 1
    return t < 0 ? t + n : t
  end

CP_COI = inverse(CP_COP, MAX)

反推算出來 CP_COI = 30261274580

ID 攪亂實作

接下來做 ID 攪亂

class User < ApplicationRecord
  CP_MAX = 34359738367
  CP_COP = 18195539
  CP_COI = 30261274580

  def encode_id
    return (self.id.to_i * CP_COP % CP_MAX).to_s(32)
  end

  def self.decode_id(id)
    return id.to_i(32) * CP_COI % CP_MAX
  end
end

試試看 user.id = 1 會拿到什麼?

user = User.first
user.id
# 1

user.encode_id
# "hb92j"

假設拿到攪亂過後的,反推呢?

User.decode_id("hb92j")

# 1

總結

嗯,太煩了,Rails 的話直接用 SecureRandom 放一個欄位打 unique 就可以了。

放欄位還可以很方便 application 做搜尋

參考連結

comments powered by Disqus