jamiechoo

 

swift把接收到的 33 字节压缩格式公钥 转换为未压缩格式

Swift 中并没有内置的 API 可以直接将 33 字节的压缩格式公钥转换为未压缩格式,但可以手动解压缩。具体步骤包括解析压缩格式中的 x 坐标并计算 y 坐标。以下是如何通过手动解压缩将 33 字节的压缩格式公钥转换为 65 字节的未压缩格式。

压缩公钥格式

在压缩格式中,公钥的结构如下:

  • 第一个字节0x020x03,指定 y 坐标的奇偶性(0x02 表示偶数,0x03 表示奇数)。
  • 剩下的 32 字节:表示 x 坐标。

为了解压缩,我们需要使用椭圆曲线方程来计算 y 坐标:

    y^2 = x^3 + ax + b( mod  p)

对于 NIST P-256(secp256r1)曲线,参数如下:

  • p = 2^{256} - 2^{224} + 2^{192} + 2^{96} - 1
  • a=−3
  • b=41058363725152142129326129780047268409114441015993725554862021452031992827611

步骤

  1. 解析 x 坐标:读取压缩格式的公钥,获取 x 坐标和奇偶性前缀。
  2. 计算 y^2:使用曲线方程 y^2 = x^3 + ax + b mod p 计算 y^2
  3. 计算 y 坐标:取 y^2 的平方根,根据奇偶性选择正确的 y
  4. 组合未压缩格式:将 0x04 前缀、x 坐标和 y 坐标组合成未压缩格式。

代码实现

以下 Swift 代码使用了大整数计算库(例如 BigInt),因为 NIST P-256 椭圆曲线上的点计算需要高精度整数运算。此代码示例假设使用 Swift 的 BigInt 库(如 BigNumber 库)来进行大数计算。

import Foundation
import BigInt
 
// 椭圆曲线 NIST P-256 参数
let p = BigInt("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", radix: 16)!
let a = BigInt(-3)
let b = BigInt("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", radix: 16)! // 压缩格式的公钥 (33 字节, 示例数据)
let compressedPublicKeyData: [UInt8] = [0x02, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47,
                        0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77,
                        0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1,
                        0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96] // 提取 x 坐标
let x = BigInt(Data(compressedPublicKeyData[1...]).map {
    String(format: "%02x", $0) }.joined(), radix: 16)!
 
// Step 2: 计算 y^2 = x^3 + ax + b (mod p)
let xCubed = (x * x * x) % p
let ax = (a * x) % p
let ySquared = (xCubed + ax + b) % p
 
// Step 3: 求解 y 坐标(平方根)
func modularSquareRoot(_ n: BigInt, mod p: BigInt) -> BigInt? {
  // 快速计算模平方根的方法,需要实现Tonelli–Shanks算法或其他
  // 具体实现函数在如下
return nil
  // 示例中此处为假设
}
if let yRoot = modularSquareRoot(ySquared, mod: p) { // 根据前缀(0x02 或 0x03)决定 y 的奇偶性
  let isYOdd = (compressedPublicKeyData[0] == 0x03)
  let y = (yRoot % 2 == 0) == isYOdd ? (p - yRoot) : yRoot
 
// Step 4: 生成未压缩格式的公钥
  var uncompressedPublicKey: [UInt8] = [0x04] // 未压缩前缀
    uncompressedPublicKey += x.serialize()
    uncompressedPublicKey += y.serialize()
    print("未压缩公钥: \(uncompressedPublicKey.map { String(format: "%02x", $0) }.joined())")
      } else {
      print("未找到平方根,无法解压缩")
    }

代码解释

  1. 定义椭圆曲线参数p 为 NIST P-256 的素数,ab 为曲线参数。
  2. 解析 x 坐标:提取 33 字节压缩公钥中的 x 坐标部分,转换为 BigInt 格式。
  3. 计算 y^2:使用曲线方程计算 y^2,即 y^2 = x^3 + ax + b mod p
  4. 求解 y 坐标
    • 调用 modularSquareRoot 函数计算模平方根(Swift 中没有内置,需自行实现 Tonelli–Shanks 算法)。
    • 根据前缀判断 y 坐标的奇偶性,如果 y 的奇偶性与前缀不一致,则取 p - y
  5. 组合未压缩格式:用 0x04 作为前缀,依次添加 xy 坐标,生成 65 字节未压缩公钥。

注意事项

  • 平方根计算:模平方根计算较复杂,需要实现 Tonelli–Shanks 算法等。
  • 大整数库BigInt 或其他高精度整数库支持更精确的计算。

 

下面是 Tonelli–Shanks 算法在 Swift 中的实现,用于计算模平方根。Tonelli–Shanks 算法是一个有效的方法,用来找到形如 x^2 ≡ n (mod p) 的解,其中 p 是奇素数(在我们例子中适用于 NIST P-256 椭圆曲线的素数 p)。

Tonelli–Shanks 算法步骤

Tonelli–Shanks 算法的核心思想是利用二次剩余和平方数特性来找到模平方根。 对于方程 x^2 ≡ n (mod p)

  1. 检测平方剩余:首先检查 n 是否为 p 的平方剩余,否则无解。
  2. 求解特殊情况:如果 p ≡ 3 (mod 4),则可以直接计算平方根。
  3. 一般情况:如果 p ≡ 1 (mod 4),则使用 Tonelli–Shanks 算法进行求解。

Swift 实现

下面是使用 BigInt 库来计算模平方根的代码示例(例如 BigNumber 或其他类似库)。我们假设 p 是素数并满足 p ≡ 1 (mod 4)

import Foundation
import BigInt
 
/// Tonelli–Shanks 模平方根计算算法
/// 计算出使得 x^2 ≡ n (mod p) 的 `x`,如果没有解则返回 `nil`
/// - Parameters:
/// - n: 要计算平方根的值
/// - p: 模数,必须是奇素数
/// - Returns: `x` 的平方根(模 p),或者 `nil` 如果没有平方根
 
func modularSquareRoot(_ n: BigInt, mod p: BigInt) -> BigInt? {
    // Step 1: 检查是否为平方剩余(n^(p-1)/2 ≡ 1 (mod p))
    guard n.power((p - 1) / 2, modulus: p) == 1
    else {
    return nil // n 不是平方剩余,无解
   }
    // Step 2: 如果 p ≡ 3 (mod 4),可以直接计算
if p % 4 == 3 {
    return n.power((p + 1) / 4, modulus: p)
    }
    // Step 3: 使用 Tonelli–Shanks 算法
    var s = p - 1
    var e: BigInt = 0
    // 将 s 写成 s = q * 2^e,q 是奇数
    while s % 2 == 0 {
      s /= 2 e += 1
    }
    // 寻找非二次剩余 z
    var z: BigInt = 2 while z.power((p - 1) / 2, modulus: p) != p - 1 {
      z += 1
      }
    // 初始化变量
    var m = e
    var c = z.power(s, modulus: p)
    var t = n.power(s, modulus: p)
    var r = n.power((s + 1) / 2, modulus: p)
    // 迭代求解
    while t != 0 && t != 1 {
      var i: BigInt = 0
      var temp = t
      while temp != 1 && i < m {
        temp = (temp * temp) % p
        i += 1
        }
    // 找到解
    if i == 0 {
        return r
      }
    // 更新参数
    let b = c.power(BigInt(1) << (m - i - 1), modulus: p)
    m = i
    c = (b * b) %
    p t = (t * c) % p
    r = (r * b) % p
   }
    // 返回解
    return r
    }
    // 示例用法
  let p = BigInt("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", radix: 16)!
  let n = BigInt("4C98E6F1", radix: 16)!
  if let sqrtResult = modularSquareRoot(n, mod: p) {
    print("模平方根: \(sqrtResult)")
    } else {
    print("没有平方根")
    }

代码说明

  1. 平方剩余检测:通过 n^(p-1)/2 mod p 检测 n 是否为平方剩余。
  2. 快速求解情况:如果 p ≡ 3 (mod 4),我们可以用快速方法 n^((p+1)/4) mod p 直接计算平方根。
  3. Tonelli–Shanks 迭代:如果 p ≡ 1 (mod 4),则进入 Tonelli–Shanks 迭代求解。这个算法核心在于不断逼近解,通过迭代修正平方根。

注意事项

  • 平方剩余检测:如果 n 不是平方剩余,则该方程无解,返回 nil
  • BigInt 支持:需要一个支持模幂运算、乘法和除法的 BigInt 库,例如 Swift 的 BigInt 或其他高精度整数库。

这个代码示例会正确输出 n 的模平方根 x,满足 x^2 ≡ n (mod p)

posted on 2024-11-06 06:07  jamiechoo  阅读(25)  评论(0)    收藏  举报

导航